clang 24.0.0git
Checker.cpp
Go to the documentation of this file.
1//===- Checker.cpp - C++ Lifetime Safety Checker ----------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file implements the LifetimeChecker, which detects use-after-free
10// errors by checking if live origins hold loans that have expired.
11//
12//===----------------------------------------------------------------------===//
13
15#include "clang/AST/Decl.h"
16#include "clang/AST/Expr.h"
26#include "llvm/ADT/DenseMap.h"
27#include "llvm/Support/ErrorHandling.h"
28#include "llvm/Support/TimeProfiler.h"
29
31
33 switch (K) {
35 return true;
38 return false;
39 }
40 llvm_unreachable("unknown liveness kind");
41}
42
43namespace {
44
45/// Struct to store the complete context for a potential lifetime violation.
46struct PendingWarning {
47 SourceLocation ExpiryLoc; // Where the loan expired.
48 llvm::PointerUnion<const UseFact *, const OriginEscapesFact *> CausingFact;
49 const Expr *MovedExpr;
50 const Expr *InvalidatedByExpr;
51 bool CausingFactDominatesExpiry;
52};
53
54using AnnotationTarget =
55 llvm::PointerUnion<const ParmVarDecl *, const CXXMethodDecl *>;
56using EscapingTarget = LifetimeSafetySemaHelper::EscapingTarget;
57
58class LifetimeChecker {
59private:
60 llvm::DenseMap<LoanID, PendingWarning> FinalWarningsMap;
61 llvm::DenseMap<AnnotationTarget, EscapingTarget> AnnotationWarningsMap;
62 llvm::DenseMap<const ParmVarDecl *, EscapingTarget> NoescapeWarningsMap;
63 llvm::DenseSet<const Decl *> VerifiedLiftimeboundEscapes;
64 const LoanPropagationAnalysis &LoanPropagation;
65 const MovedLoansAnalysis &MovedLoans;
66 const LiveOriginsAnalysis &LiveOrigins;
67 FactManager &FactMgr;
68 LifetimeSafetySemaHelper *SemaHelper;
69 ASTContext &AST;
70 const CFG *Cfg;
71 const Decl *FD;
72 const LifetimeSafetyOpts &LSOpts;
73
74 static SourceLocation
75 GetFactLoc(llvm::PointerUnion<const UseFact *, const OriginEscapesFact *> F) {
76 if (const auto *UF = F.dyn_cast<const UseFact *>())
77 return UF->getUseExpr()->getExprLoc();
78 if (const auto *OEF = F.dyn_cast<const OriginEscapesFact *>()) {
79 if (auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF))
80 return ReturnEsc->getReturnExpr()->getExprLoc();
81 if (auto *FieldEsc = dyn_cast<FieldEscapeFact>(OEF))
82 return FieldEsc->getFieldDecl()->getLocation();
83 }
84 llvm_unreachable("unhandled causing fact in PointerUnion");
85 }
86
87public:
88 LifetimeChecker(const LoanPropagationAnalysis &LoanPropagation,
89 const MovedLoansAnalysis &MovedLoans,
90 const LiveOriginsAnalysis &LiveOrigins, FactManager &FM,
91 AnalysisDeclContext &ADC,
92 LifetimeSafetySemaHelper *SemaHelper,
93 const LifetimeSafetyOpts &LSOpts)
94 : LoanPropagation(LoanPropagation), MovedLoans(MovedLoans),
95 LiveOrigins(LiveOrigins), FactMgr(FM), SemaHelper(SemaHelper),
96 AST(ADC.getASTContext()), Cfg(ADC.getCFG()), FD(ADC.getDecl()),
97 LSOpts(LSOpts) {
98 for (const CFGBlock *B : *ADC.getAnalysis<PostOrderCFGView>())
99 for (const Fact *F : FactMgr.getFacts(B))
100 if (const auto *EF = F->getAs<ExpireFact>())
101 checkExpiry(EF);
102 else if (const auto *IOF = F->getAs<InvalidateOriginFact>())
103 checkInvalidation(IOF);
104 else if (const auto *OEF = F->getAs<OriginEscapesFact>())
105 checkAnnotations(OEF);
106 issuePendingWarnings();
107 suggestAnnotations();
108 if (LSOpts.CheckNoescapeViolations)
109 reportNoescapeViolations();
110 if (LSOpts.CheckLifetimeboundViolations)
111 reportLifetimeboundViolations();
112 if (LSOpts.CheckMisplacedLifetimebound)
113 reportMisplacedLifetimebound();
114 if (LSOpts.CheckInapplicableLifetimebound)
115 reportInapplicableLifetimebound();
116 // Annotation inference is currently guarded by a frontend flag. In the
117 // future, this might be replaced by a design that differentiates between
118 // explicit and inferred findings with separate warning groups.
119 if (AST.getLangOpts().EnableLifetimeSafetyInference)
120 inferAnnotations();
121 }
122
123 /// Checks if an escaping origin holds a placeholder loan, indicating a
124 /// missing [[clang::lifetimebound]] annotation or a violation of
125 /// [[clang::noescape]].
126 void checkAnnotations(const OriginEscapesFact *OEF) {
127 OriginID EscapedOID = OEF->getEscapedOriginID();
128 LoanSet EscapedLoans = LoanPropagation.getLoans(EscapedOID, OEF);
129 auto CheckParam = [&](const ParmVarDecl *PVD, bool IsMoved) {
130 // NoEscape param should not escape.
131 if (PVD->hasAttr<NoEscapeAttr>()) {
132 if (auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF))
133 NoescapeWarningsMap.try_emplace(PVD, ReturnEsc->getReturnExpr());
134 if (auto *FieldEsc = dyn_cast<FieldEscapeFact>(OEF))
135 NoescapeWarningsMap.try_emplace(PVD, FieldEsc->getFieldDecl());
136 if (auto *GlobalEsc = dyn_cast<GlobalEscapeFact>(OEF))
137 NoescapeWarningsMap.try_emplace(PVD, GlobalEsc->getGlobal());
138 return;
139 }
140 // Skip annotation suggestion for moved loans, as ownership transfer
141 // obscures the lifetime relationship (e.g., shared_ptr from unique_ptr).
142 if (IsMoved)
143 return;
144 if (PVD->hasAttr<LifetimeBoundAttr>()) {
145 // Track that this lifetimebound parameter correctly escapes
146 // (via return or via field assignment in a constructor).
147 if (isa<ReturnEscapeFact>(OEF) ||
149 VerifiedLiftimeboundEscapes.insert(PVD);
150 } else {
151 // Otherwise, suggest lifetimebound for parameter escaping through
152 // return or a field in constructor.
153 if (auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF))
154 AnnotationWarningsMap.try_emplace(PVD, ReturnEsc->getReturnExpr());
155 else if (auto *FieldEsc = dyn_cast<FieldEscapeFact>(OEF);
156 FieldEsc && isa<CXXConstructorDecl>(FD))
157 AnnotationWarningsMap.try_emplace(PVD, FieldEsc->getFieldDecl());
158 }
159 // TODO: Suggest lifetime_capture_by(this) for parameter escaping to a
160 // field!
161 };
162 auto CheckImplicitThis = [&](const CXXMethodDecl *MD) {
163 if (auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF)) {
165 VerifiedLiftimeboundEscapes.insert(MD);
166 else
167 AnnotationWarningsMap.try_emplace(MD, ReturnEsc->getReturnExpr());
168 }
169 };
170 auto MovedAtEscape = MovedLoans.getMovedLoans(OEF);
171 for (LoanID LID : EscapedLoans) {
172 const Loan *L = FactMgr.getLoanMgr().getLoan(LID);
173 const PlaceholderBase *PB = L->getAccessPath().getAsPlaceholderBase();
174 if (!PB)
175 continue;
176 if (const auto *PVD = PB->getParmVarDecl())
177 CheckParam(PVD, /*IsMoved=*/MovedAtEscape.lookup(LID));
178 else if (const auto *MD = PB->getImplicitThisParent())
179 CheckImplicitThis(MD);
180 }
181 }
182
183 /// Checks for use-after-free & use-after-return errors when an access path
184 /// expires (e.g., a variable goes out of scope).
185 ///
186 /// When a path expires, all loans prefixed by that path expire. For example,
187 /// if `x` expires, loans to `x`, `x.field`, and `x.field.*` all expire.
188 /// This method examines all live origins and reports warnings for loans they
189 /// hold that are prefixed by the expired path.
190 void checkExpiry(const ExpireFact *EF) {
191 const AccessPath &ExpiredPath = EF->getAccessPath();
192 LiveOriginSet Origins = LiveOrigins.getLiveOriginsAt(EF);
193 for (const LivenessMap &Live : {Origins.Persistent, Origins.BlockLocal})
194 for (auto &[OID, LiveInfo] : Live) {
195 LoanSet HeldLoans = LoanPropagation.getLoans(OID, EF);
196 for (LoanID HeldLoanID : HeldLoans) {
197 const Loan *HeldLoan = FactMgr.getLoanMgr().getLoan(HeldLoanID);
198 if (!ExpiredPath.isPrefixOf(HeldLoan->getAccessPath()))
199 continue;
200 // HeldLoan is expired because its base or itself is expired.
201 PendingWarning &CurWarning = FinalWarningsMap[HeldLoan->getID()];
202 const Expr *MovedExpr = nullptr;
203 if (auto *ME = MovedLoans.getMovedLoans(EF).lookup(HeldLoanID))
204 MovedExpr = *ME;
205 // Skip if we already have a dominating causing fact.
206 if (CurWarning.CausingFactDominatesExpiry)
207 continue;
208 if (causingFactDominatesExpiry(LiveInfo.Kind))
209 CurWarning.CausingFactDominatesExpiry = true;
210 CurWarning.CausingFact = LiveInfo.CausingFact;
211 CurWarning.ExpiryLoc = EF->getExpiryLoc();
212 CurWarning.MovedExpr = MovedExpr;
213 CurWarning.InvalidatedByExpr = nullptr;
214 }
215 }
216 }
217
218 /// Checks for use-after-invalidation errors when a container is modified.
219 ///
220 /// When a container is invalidated, loans pointing into its interior are
221 /// invalidated. For example, if container `v` is invalidated, iterators with
222 /// loans to `v.*` are invalidated. This method finds live origins holding
223 /// such loans and reports warnings. A loan is invalidated if its path extends
224 /// an invalidated container's path (e.g., `v.*` extends `v`).
225 void checkInvalidation(const InvalidateOriginFact *IOF) {
226 OriginID InvalidatedOrigin = IOF->getInvalidatedOrigin();
227 /// Get loans directly pointing to the invalidated container
228 LoanSet DirectlyInvalidatedLoans =
229 LoanPropagation.getLoans(InvalidatedOrigin, IOF);
230 auto IsInvalidated = [&](const Loan *L) {
231 for (LoanID InvalidID : DirectlyInvalidatedLoans) {
232 const Loan *InvalidL = FactMgr.getLoanMgr().getLoan(InvalidID);
233 if (InvalidL->getAccessPath().isPrefixOf(L->getAccessPath()))
234 return true;
235 }
236 return false;
237 };
238 // For each live origin, check if it holds an invalidated loan and report.
239 LiveOriginSet Origins = LiveOrigins.getLiveOriginsAt(IOF);
240 for (const LivenessMap &Live : {Origins.Persistent, Origins.BlockLocal})
241 for (auto &[OID, LiveInfo] : Live) {
242 LoanSet HeldLoans = LoanPropagation.getLoans(OID, IOF);
243 for (LoanID LiveLoanID : HeldLoans)
244 if (IsInvalidated(FactMgr.getLoanMgr().getLoan(LiveLoanID))) {
245 bool CurDomination = causingFactDominatesExpiry(LiveInfo.Kind);
246 bool LastDomination =
247 FinalWarningsMap.lookup(LiveLoanID).CausingFactDominatesExpiry;
248 if (!LastDomination) {
249 FinalWarningsMap[LiveLoanID] = {
250 /*ExpiryLoc=*/{},
251 /*CausingFact=*/LiveInfo.CausingFact,
252 /*MovedExpr=*/nullptr,
253 /*InvalidatedByExpr=*/IOF->getInvalidationExpr(),
254 /*CausingFactDominatesExpiry=*/CurDomination};
255 }
256 }
257 }
258 }
259
260 void issuePendingWarnings() {
261 llvm::TimeTraceScope TimeTrace("IssuePendingWarnings");
262 if (!SemaHelper)
263 return;
264 for (const auto &[LID, Warning] : FinalWarningsMap) {
265 const Loan *L = FactMgr.getLoanMgr().getLoan(LID);
266 const Expr *IssueExpr = L->getIssueExpr();
267 const ParmVarDecl *InvalidatedPVD = nullptr;
268 if (const PlaceholderBase *PB = L->getAccessPath().getAsPlaceholderBase())
269 InvalidatedPVD = PB->getParmVarDecl();
270
271 llvm::PointerUnion<const UseFact *, const OriginEscapesFact *>
272 CausingFact = Warning.CausingFact;
273 const Expr *MovedExpr = Warning.MovedExpr;
274 SourceLocation ExpiryLoc = Warning.ExpiryLoc;
275
276 if (const auto *UF = CausingFact.dyn_cast<const UseFact *>()) {
277 llvm::SmallVector<const Expr *> ExprChain =
278 getExprChain(LoanPropagation.buildOriginFlowChain(UF, LID, Cfg));
279 if (Warning.InvalidatedByExpr) {
280 if (IssueExpr)
281 // Use-after-invalidation of an object on stack.
282 SemaHelper->reportUseAfterInvalidation(IssueExpr, UF->getUseExpr(),
283 Warning.InvalidatedByExpr,
284 ExprChain);
285 else if (InvalidatedPVD)
286 // Use-after-invalidation of a parameter.
287 SemaHelper->reportUseAfterInvalidation(
288 InvalidatedPVD, UF->getUseExpr(), Warning.InvalidatedByExpr,
289 ExprChain);
290
291 } else
292 // Scope-based expiry (use-after-scope).
293 SemaHelper->reportUseAfterScope(IssueExpr, UF->getUseExpr(),
294 MovedExpr, ExpiryLoc, ExprChain);
295
296 } else if (const auto *OEF =
297 CausingFact.dyn_cast<const OriginEscapesFact *>()) {
298 if (Warning.InvalidatedByExpr) {
299 if (const auto *FieldEscape = dyn_cast<FieldEscapeFact>(OEF)) {
300 // Invalidated object escapes to a field.
301 if (IssueExpr)
302 // Invalidated object on stack escapes to a field.
303 SemaHelper->reportInvalidatedField(IssueExpr,
304 FieldEscape->getFieldDecl(),
305 Warning.InvalidatedByExpr);
306 else if (InvalidatedPVD)
307 // Invalidated parameter escapes to a field.
308 SemaHelper->reportInvalidatedField(InvalidatedPVD,
309 FieldEscape->getFieldDecl(),
310 Warning.InvalidatedByExpr);
311 } else if (const auto *GlobalEscape =
312 dyn_cast<GlobalEscapeFact>(OEF)) {
313 // Invalidated object escapes to global or static storage.
314 if (IssueExpr)
315 // Invalidated object on stack escapes to global or static
316 // storage.
317 SemaHelper->reportInvalidatedGlobal(IssueExpr,
318 GlobalEscape->getGlobal(),
319 Warning.InvalidatedByExpr);
320 else if (InvalidatedPVD)
321 // Invalidated parameter escapes to global or static storage.
322 SemaHelper->reportInvalidatedGlobal(InvalidatedPVD,
323 GlobalEscape->getGlobal(),
324 Warning.InvalidatedByExpr);
325 } else if (isa<ReturnEscapeFact>(OEF)) {
326 // FIXME: Diagnose invalidated return escapes separately.
327 } else
328 llvm_unreachable("Unhandled OriginEscapesFact type");
329 } else if (const auto *RetEscape = dyn_cast<ReturnEscapeFact>(OEF))
330 // Return stack address.
331 SemaHelper->reportUseAfterReturn(
332 IssueExpr, RetEscape->getReturnExpr(), MovedExpr);
333 else if (const auto *FieldEscape = dyn_cast<FieldEscapeFact>(OEF)) {
334 // Dangling field.
335 bool IsCapturedByLambda =
336 FactMgr.isFieldCapturedByLambda(FieldEscape->getFieldDecl());
337 SemaHelper->reportDanglingField(
338 IssueExpr, FieldEscape->getFieldDecl(), MovedExpr,
339 IsCapturedByLambda, ExpiryLoc);
340 } else if (const auto *GlobalEscape = dyn_cast<GlobalEscapeFact>(OEF)) {
341 // Global escape.
342 bool IsMain = false;
343 if (const auto *Func = dyn_cast_if_present<FunctionDecl>(FD))
344 IsMain = Func->isMain();
345 SemaHelper->reportDanglingGlobal(IssueExpr, GlobalEscape->getGlobal(),
346 MovedExpr, ExpiryLoc, IsMain);
347 } else
348 llvm_unreachable("Unhandled OriginEscapesFact type");
349 } else
350 llvm_unreachable("Unhandled CausingFact type");
351 }
352 }
353
354 // Returns declarations that should be annotated with lifetime attributes
355 // in order to annotate FDef: the canonical declaration and the earliest
356 // redeclarations in each other file. This defines the placement policy for
357 // lifetime annotations. Each target is paired with its corresponding warning
358 // scope.
359 llvm::SmallVector<std::pair<const FunctionDecl *, WarningScope>, 2>
360 getTargetDeclsForAttr(const FunctionDecl *FDef) {
361 if (!FDef)
362 return {};
363
364 assert(FDef->isThisDeclarationADefinition() &&
365 "Expected FunctionDecl to be a definition");
366
367 const auto &SM = FDef->getASTContext().getSourceManager();
368
369 auto GetFile = [&SM](const FunctionDecl *FD) {
370 return SM.getFileID(SM.getExpansionLoc(FD->getLocation()));
371 };
372
373 const FileID DefFile = GetFile(FDef);
374 const FunctionDecl *CanonicalDecl = FDef->getCanonicalDecl();
375 llvm::SmallVector<std::pair<const FunctionDecl *, WarningScope>, 2> Targets{
376 {CanonicalDecl, GetFile(CanonicalDecl) == DefFile
379
380 // Find the earliest redeclaration in each file other than the definition
381 // file.
382 auto AddCrossTUDecl = [&](const FunctionDecl *FD) {
383 FileID File = GetFile(FD);
384 if (File == DefFile)
385 return;
386 for (auto [SeenFD, _] : Targets)
387 if (GetFile(SeenFD) == File)
388 return;
389 Targets.push_back({FD, WarningScope::CrossTU});
390 };
391
392 // We iterate in reverse order (from most recent to oldest) to find
393 // the first declaration in each file.
394
395 // Store in temporary variable to manually extend lifetime
396 auto redecls = llvm::to_vector(FDef->redecls());
397
398 for (const FunctionDecl *Redecl : llvm::reverse(redecls))
399 AddCrossTUDecl(Redecl);
400
401 return Targets;
402 }
403
404 void suggestWithScopeForParmVar(const ParmVarDecl *PVD,
405 EscapingTarget EscapeTarget) {
406 if (llvm::isa<const VarDecl *>(EscapeTarget))
407 return;
408
409 for (auto [Decl, Scope] : getTargetDeclsForAttr(cast<FunctionDecl>(FD))) {
410 const auto *ParmToAnnotate =
411 Decl->getParamDecl(PVD->getFunctionScopeIndex());
412 SemaHelper->suggestLifetimeboundToParmVar(Scope, ParmToAnnotate,
413 EscapeTarget);
414 }
415 }
416
417 void suggestWithScopeForImplicitThis(const CXXMethodDecl *MD,
418 const Expr *EscapeExpr) {
419 for (auto [Decl, Scope] : getTargetDeclsForAttr(MD)) {
420 SemaHelper->suggestLifetimeboundToImplicitThis(
421 Scope, cast<CXXMethodDecl>(Decl), EscapeExpr);
422 }
423 }
424
425 void suggestAnnotations() {
426 if (!SemaHelper)
427 return;
428 if (!LSOpts.SuggestAnnotations)
429 return;
430 llvm::TimeTraceScope TimeTrace("SuggestAnnotations");
431 for (auto [Target, EscapeTarget] : AnnotationWarningsMap) {
432 if (const auto *PVD = Target.dyn_cast<const ParmVarDecl *>())
433 suggestWithScopeForParmVar(PVD, EscapeTarget);
434 else if (const auto *MD = Target.dyn_cast<const CXXMethodDecl *>()) {
435 if (const auto *EscapeExpr = EscapeTarget.dyn_cast<const Expr *>())
436 suggestWithScopeForImplicitThis(MD, EscapeExpr);
437 else
438 llvm_unreachable("Implicit this can only escape via Expr (return)");
439 }
440 }
441 }
442
443 void reportNoescapeViolations() {
444 llvm::TimeTraceScope TimeTrace("ReportNoescapeViolations");
445 for (auto [PVD, EscapeTarget] : NoescapeWarningsMap) {
446 if (const auto *E = EscapeTarget.dyn_cast<const Expr *>())
447 SemaHelper->reportNoescapeViolation(PVD, E);
448 else if (const auto *FD = EscapeTarget.dyn_cast<const FieldDecl *>())
449 SemaHelper->reportNoescapeViolation(PVD, FD);
450 else if (const auto *G = EscapeTarget.dyn_cast<const VarDecl *>())
451 SemaHelper->reportNoescapeViolation(PVD, G);
452 else
453 llvm_unreachable("Unhandled EscapingTarget type");
454 }
455 }
456
457 void reportLifetimeboundViolations() {
458 llvm::TimeTraceScope TimeTrace("ReportLifetimeboundViolations");
459 if (!isa<FunctionDecl>(FD))
460 return;
461 if (const auto *MD = dyn_cast<CXXMethodDecl>(FD);
463 !VerifiedLiftimeboundEscapes.contains(MD))
464 SemaHelper->reportLifetimeboundViolation(MD);
465 for (const ParmVarDecl *PVD : cast<FunctionDecl>(FD)->parameters()) {
466 if (!PVD->hasAttr<LifetimeBoundAttr>())
467 continue;
468 bool isImplicit = PVD->getAttr<LifetimeBoundAttr>()->isImplicit();
469 bool Escapes = VerifiedLiftimeboundEscapes.contains(PVD);
470 assert((!isImplicit || Escapes || isInStlNamespace(FD)) &&
471 "Implicit lifetimebound parameters "
472 "should escape through return");
473 if (!isImplicit && !Escapes)
474 SemaHelper->reportLifetimeboundViolation(PVD);
475 }
476 }
477
478 // Reports lifetimebound attributes that are placed on a function definition
479 // but not on the corresponding declaration.
480 void reportMisplacedLifetimebound() {
481 llvm::TimeTraceScope TimeTrace("ReportMisplacedLifetimebound");
482 const FunctionDecl *FDef = dyn_cast<FunctionDecl>(FD);
483 if (!FDef)
484 return;
485
486 auto TargetDecls = getTargetDeclsForAttr(FDef);
487 // Check if implicit 'this' has lifetimebound on definition but not on
488 // declaration.
489 if (const auto *MDef = dyn_cast<CXXMethodDecl>(FDef);
491 for (auto [Decl, Scope] : TargetDecls) {
492 const auto *MDecl = cast<CXXMethodDecl>(Decl);
494 SemaHelper->reportMisplacedLifetimebound(Scope, MDef, MDecl);
495 }
496
497 // Check each parameter for explicit lifetimebound on definition but not on
498 // declaration.
499 for (const auto *PDef : FDef->parameters()) {
500 const auto *Attr = PDef->getAttr<LifetimeBoundAttr>();
501 if (!Attr || Attr->isImplicit())
502 continue;
503 for (auto [Decl, Scope] : TargetDecls) {
504 const auto *PDecl = Decl->getParamDecl(PDef->getFunctionScopeIndex());
505 if (!PDecl->hasAttr<LifetimeBoundAttr>())
506 SemaHelper->reportMisplacedLifetimebound(Scope, PDef, PDecl);
507 }
508 }
509 }
510
511 void reportInapplicableLifetimebound() {
512 llvm::TimeTraceScope TimeTrace("ReportInapplicableLifetimebound");
513 const auto *FDef = dyn_cast<FunctionDecl>(FD);
514 if (!FDef)
515 return;
516
517 // If analyzed function is a template definition or an implicit
518 // instantiation, skip.
519 if (FDef->getTemplatedKind() == FunctionDecl::TK_FunctionTemplate ||
520 FDef->getTemplateSpecializationKind() == TSK_ImplicitInstantiation)
521 return;
522
523 for (const auto &PVD : FDef->parameters())
524 if (PVD->hasAttr<LifetimeBoundAttr>() &&
525 !FactMgr.getOriginMgr().hasOrigins(PVD->getType(),
526 /*IntrinsicOnly=*/true))
527 SemaHelper->reportInapplicableLifetimebound(PVD);
528 }
529
530 void inferAnnotations() {
531 for (auto [Target, EscapeTarget] : AnnotationWarningsMap) {
532 if (const auto *MD = Target.dyn_cast<const CXXMethodDecl *>()) {
534 SemaHelper->addLifetimeBoundToImplicitThis(cast<CXXMethodDecl>(MD));
535 } else if (const auto *PVD = Target.dyn_cast<const ParmVarDecl *>()) {
536 const auto *FD = dyn_cast<FunctionDecl>(PVD->getDeclContext());
537 if (!FD)
538 continue;
539 // Propagates inferred attributes via the most recent declaration to
540 // ensure visibility for callers in post-order analysis.
542 ParmVarDecl *InferredPVD = const_cast<ParmVarDecl *>(
543 FD->getParamDecl(PVD->getFunctionScopeIndex()));
544 if (!InferredPVD->hasAttr<LifetimeBoundAttr>())
545 InferredPVD->addAttr(
546 LifetimeBoundAttr::CreateImplicit(AST, PVD->getLocation()));
547 }
548 }
549 }
550
551 /// Extract expressions from the origin flow chain for diagnostic purposes.
552 ///
553 /// Given a chain of origins that shows how a loan propagates, this function
554 /// extracts the corresponding expressions for each origin. Origins that refer
555 /// to declarations (rather than expressions) are skipped.
556 llvm::SmallVector<const Expr *>
557 getExprChain(llvm::ArrayRef<OriginID> OriginFlowChain) {
558 llvm::SmallVector<const Expr *> rs;
559 for (const OriginID CurrOID : OriginFlowChain)
560 if (const Expr *CurrExpr =
561 FactMgr.getOriginMgr().getOrigin(CurrOID).getExpr())
562 rs.push_back(CurrExpr);
563 return rs;
564 }
565};
566} // namespace
567
569 const MovedLoansAnalysis &MovedLoans,
570 const LiveOriginsAnalysis &LO, FactManager &FactMgr,
572 LifetimeSafetySemaHelper *SemaHelper,
573 const LifetimeSafetyOpts &LSOpts) {
574 llvm::TimeTraceScope TimeProfile("LifetimeChecker");
575 LifetimeChecker Checker(LP, MovedLoans, LO, FactMgr, ADC, SemaHelper, LSOpts);
576}
577
578} // namespace clang::lifetimes::internal
This file defines AnalysisDeclContext, a class that manages the analysis context data for context sen...
Defines the clang::SourceLocation class and associated facilities.
Defines the SourceManager interface.
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition ASTContext.h:239
AnalysisDeclContext contains the context data for the function, method or block under analysis.
Represents a source-level, intra-procedural CFG that represents the control-flow of a Stmt.
Definition CFG.h:1271
Decl - This represents one declaration (or definition), e.g.
Definition DeclBase.h:86
This represents one expression.
Definition Expr.h:113
Encodes a location in the source.
Abstract interface for operations requiring Sema access.
llvm::PointerUnion< const Expr *, const FieldDecl *, const VarDecl * > EscapingTarget
utils::ID< struct LoanTag > LoanID
Definition Loans.h:27
utils::ID< struct OriginTag > OriginID
Definition Origins.h:28
static bool causingFactDominatesExpiry(LivenessKind K)
Definition Checker.cpp:32
utils::SetTy< LoanID > LoanSet
void runLifetimeChecker(const LoanPropagationAnalysis &LoanPropagation, const MovedLoansAnalysis &MovedLoans, const LiveOriginsAnalysis &LiveOrigins, FactManager &FactMgr, AnalysisDeclContext &ADC, LifetimeSafetySemaHelper *SemaHelper, const LifetimeSafetyOpts &LSOpts)
Runs the lifetime checker, which detects use-after-free errors by examining loan expiration points an...
Definition Checker.cpp:568
utils::MapTy< OriginID, LivenessInfo > LivenessMap
Definition LiveOrigins.h:76
const LifetimeBoundAttr * getDirectImplicitObjectLifetimeBoundAttr(const FunctionDecl *FD)
bool implicitObjectParamIsLifetimeBound(const FunctionDecl *FD)
const LifetimeBoundAttr * getImplicitObjectParamLifetimeBoundAttr(const FunctionDecl *FD)
const FunctionDecl * getDeclWithMergedLifetimeBoundAttrs(const FunctionDecl *FD)
bool isInStlNamespace(const Decl *D)
std::variant< struct RequiresDecl, struct HeaderDecl, struct UmbrellaDirDecl, struct ModuleDecl, struct ExcludeDecl, struct ExportDecl, struct ExportAsDecl, struct ExternModuleDecl, struct UseDecl, struct LinkDecl, struct ConfigMacrosDecl, struct ConflictDecl > Decl
All declarations that can appear in a module declaration.
bool isa(CodeGen::Address addr)
Definition Address.h:330
if(T->getSizeExpr()) TRY_TO(TraverseStmt(const_cast< Expr * >(T -> getSizeExpr())))
for(const auto &A :T->param_types())
@ TSK_ImplicitInstantiation
This template specialization was implicitly instantiated from a template.
Definition Specifiers.h:195
U cast(CodeGen::Address addr)
Definition Address.h:327