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
14#include "clang/Analysis/Analyses/LifetimeSafety/Checker.h"
15#include "clang/AST/Decl.h"
16#include "clang/AST/Expr.h"
17#include "clang/Analysis/Analyses/LifetimeSafety/Facts.h"
18#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h"
19#include "clang/Analysis/Analyses/LifetimeSafety/LiveOrigins.h"
20#include "clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h"
21#include "clang/Analysis/Analyses/LifetimeSafety/Loans.h"
22#include "clang/Analysis/Analyses/PostOrderCFGView.h"
23#include "clang/Analysis/AnalysisDeclContext.h"
24#include "clang/Basic/SourceLocation.h"
25#include "clang/Basic/SourceManager.h"
26#include "llvm/ADT/DenseMap.h"
27#include "llvm/Support/ErrorHandling.h"
28#include "llvm/Support/TimeProfiler.h"
29
30namespace clang::lifetimes::internal {
31
32static bool causingFactDominatesExpiry(LivenessKind K) {
33 switch (K) {
34 case LivenessKind::Must:
35 return true;
36 case LivenessKind::Maybe:
37 case LivenessKind::Dead:
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 Decl *FD;
71 const LifetimeSafetyOpts &LSOpts;
72
73 static SourceLocation
74 GetFactLoc(llvm::PointerUnion<const UseFact *, const OriginEscapesFact *> F) {
75 if (const auto *UF = F.dyn_cast<const UseFact *>())
76 return UF->getUseExpr()->getExprLoc();
77 if (const auto *OEF = F.dyn_cast<const OriginEscapesFact *>()) {
78 if (auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(Val: OEF))
79 return ReturnEsc->getReturnExpr()->getExprLoc();
80 if (auto *FieldEsc = dyn_cast<FieldEscapeFact>(Val: OEF))
81 return FieldEsc->getFieldDecl()->getLocation();
82 }
83 llvm_unreachable("unhandled causing fact in PointerUnion");
84 }
85
86public:
87 LifetimeChecker(const LoanPropagationAnalysis &LoanPropagation,
88 const MovedLoansAnalysis &MovedLoans,
89 const LiveOriginsAnalysis &LiveOrigins, FactManager &FM,
90 AnalysisDeclContext &ADC,
91 LifetimeSafetySemaHelper *SemaHelper,
92 const LifetimeSafetyOpts &LSOpts)
93 : LoanPropagation(LoanPropagation), MovedLoans(MovedLoans),
94 LiveOrigins(LiveOrigins), FactMgr(FM), SemaHelper(SemaHelper),
95 AST(ADC.getASTContext()), FD(ADC.getDecl()), LSOpts(LSOpts) {
96 for (const CFGBlock *B : *ADC.getAnalysis<PostOrderCFGView>())
97 for (const Fact *F : FactMgr.getFacts(B))
98 if (const auto *EF = F->getAs<ExpireFact>())
99 checkExpiry(EF);
100 else if (const auto *IOF = F->getAs<InvalidateOriginFact>())
101 checkInvalidation(IOF);
102 else if (const auto *OEF = F->getAs<OriginEscapesFact>())
103 checkAnnotations(OEF);
104 issuePendingWarnings();
105 suggestAnnotations();
106 reportNoescapeViolations();
107 reportLifetimeboundViolations();
108 reportMisplacedLifetimebound();
109 reportInapplicableLifetimebound();
110 // Annotation inference is currently guarded by a frontend flag. In the
111 // future, this might be replaced by a design that differentiates between
112 // explicit and inferred findings with separate warning groups.
113 if (AST.getLangOpts().EnableLifetimeSafetyInference)
114 inferAnnotations();
115 }
116
117 /// Checks if an escaping origin holds a placeholder loan, indicating a
118 /// missing [[clang::lifetimebound]] annotation or a violation of
119 /// [[clang::noescape]].
120 void checkAnnotations(const OriginEscapesFact *OEF) {
121 OriginID EscapedOID = OEF->getEscapedOriginID();
122 LoanSet EscapedLoans = LoanPropagation.getLoans(OID: EscapedOID, P: OEF);
123 auto CheckParam = [&](const ParmVarDecl *PVD, bool IsMoved) {
124 // NoEscape param should not escape.
125 if (PVD->hasAttr<NoEscapeAttr>()) {
126 if (auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(Val: OEF))
127 NoescapeWarningsMap.try_emplace(Key: PVD, Args: ReturnEsc->getReturnExpr());
128 if (auto *FieldEsc = dyn_cast<FieldEscapeFact>(Val: OEF))
129 NoescapeWarningsMap.try_emplace(Key: PVD, Args: FieldEsc->getFieldDecl());
130 if (auto *GlobalEsc = dyn_cast<GlobalEscapeFact>(Val: OEF))
131 NoescapeWarningsMap.try_emplace(Key: PVD, Args: GlobalEsc->getGlobal());
132 return;
133 }
134 // Skip annotation suggestion for moved loans, as ownership transfer
135 // obscures the lifetime relationship (e.g., shared_ptr from unique_ptr).
136 if (IsMoved)
137 return;
138 if (PVD->hasAttr<LifetimeBoundAttr>()) {
139 // Track that this lifetimebound parameter correctly escapes
140 // (via return or via field assignment in a constructor).
141 if (isa<ReturnEscapeFact>(Val: OEF) ||
142 (isa<FieldEscapeFact>(Val: OEF) && isa<CXXConstructorDecl>(Val: FD)))
143 VerifiedLiftimeboundEscapes.insert(V: PVD);
144 } else {
145 // Otherwise, suggest lifetimebound for parameter escaping through
146 // return or a field in constructor.
147 if (auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(Val: OEF))
148 AnnotationWarningsMap.try_emplace(Key: PVD, Args: ReturnEsc->getReturnExpr());
149 else if (auto *FieldEsc = dyn_cast<FieldEscapeFact>(Val: OEF);
150 FieldEsc && isa<CXXConstructorDecl>(Val: FD))
151 AnnotationWarningsMap.try_emplace(Key: PVD, Args: FieldEsc->getFieldDecl());
152 }
153 // TODO: Suggest lifetime_capture_by(this) for parameter escaping to a
154 // field!
155 };
156 auto CheckImplicitThis = [&](const CXXMethodDecl *MD) {
157 if (auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(Val: OEF)) {
158 if (implicitObjectParamIsLifetimeBound(FD: MD))
159 VerifiedLiftimeboundEscapes.insert(V: MD);
160 else
161 AnnotationWarningsMap.try_emplace(Key: MD, Args: ReturnEsc->getReturnExpr());
162 }
163 };
164 auto MovedAtEscape = MovedLoans.getMovedLoans(P: OEF);
165 for (LoanID LID : EscapedLoans) {
166 const Loan *L = FactMgr.getLoanMgr().getLoan(ID: LID);
167 const AccessPath &AP = L->getAccessPath();
168 if (const auto *PVD = AP.getAsPlaceholderParam())
169 CheckParam(PVD, /*IsMoved=*/MovedAtEscape.lookup(K: LID));
170 else if (const auto *MD = AP.getAsPlaceholderThis())
171 CheckImplicitThis(MD);
172 }
173 }
174
175 /// Checks for use-after-free & use-after-return errors when an access path
176 /// expires (e.g., a variable goes out of scope).
177 ///
178 /// When a path expires, all loans having this path expires.
179 /// This method examines all live origins and reports warnings for loans they
180 /// hold that are prefixed by the expired path.
181 void checkExpiry(const ExpireFact *EF) {
182 const AccessPath &ExpiredPath = EF->getAccessPath();
183 LivenessMap Origins = LiveOrigins.getLiveOriginsAt(P: EF);
184 for (auto &[OID, LiveInfo] : Origins) {
185 LoanSet HeldLoans = LoanPropagation.getLoans(OID, P: EF);
186 for (LoanID HeldLoanID : HeldLoans) {
187 const Loan *HeldLoan = FactMgr.getLoanMgr().getLoan(ID: HeldLoanID);
188 if (ExpiredPath != HeldLoan->getAccessPath())
189 continue;
190 // HeldLoan is expired because its AccessPath is expired.
191 PendingWarning &CurWarning = FinalWarningsMap[HeldLoan->getID()];
192 const Expr *MovedExpr = nullptr;
193 if (auto *ME = MovedLoans.getMovedLoans(P: EF).lookup(K: HeldLoanID))
194 MovedExpr = *ME;
195 // Skip if we already have a dominating causing fact.
196 if (CurWarning.CausingFactDominatesExpiry)
197 continue;
198 if (causingFactDominatesExpiry(K: LiveInfo.Kind))
199 CurWarning.CausingFactDominatesExpiry = true;
200 CurWarning.CausingFact = LiveInfo.CausingFact;
201 CurWarning.ExpiryLoc = EF->getExpiryLoc();
202 CurWarning.MovedExpr = MovedExpr;
203 CurWarning.InvalidatedByExpr = nullptr;
204 }
205 }
206 }
207
208 /// Checks for use-after-invalidation errors when a container is modified.
209 ///
210 /// This method identifies origins that are live at the point of invalidation
211 /// and checks if they hold loans that are invalidated by the operation
212 /// (e.g., iterators into a vector that is being pushed to).
213 void checkInvalidation(const InvalidateOriginFact *IOF) {
214 OriginID InvalidatedOrigin = IOF->getInvalidatedOrigin();
215 /// Get loans directly pointing to the invalidated container
216 LoanSet DirectlyInvalidatedLoans =
217 LoanPropagation.getLoans(OID: InvalidatedOrigin, P: IOF);
218 auto IsInvalidated = [&](const Loan *L) {
219 for (LoanID InvalidID : DirectlyInvalidatedLoans) {
220 const Loan *InvalidL = FactMgr.getLoanMgr().getLoan(ID: InvalidID);
221 if (InvalidL->getAccessPath() == L->getAccessPath())
222 return true;
223 }
224 return false;
225 };
226 // For each live origin, check if it holds an invalidated loan and report.
227 LivenessMap Origins = LiveOrigins.getLiveOriginsAt(P: IOF);
228 for (auto &[OID, LiveInfo] : Origins) {
229 LoanSet HeldLoans = LoanPropagation.getLoans(OID, P: IOF);
230 for (LoanID LiveLoanID : HeldLoans)
231 if (IsInvalidated(FactMgr.getLoanMgr().getLoan(ID: LiveLoanID))) {
232 bool CurDomination = causingFactDominatesExpiry(K: LiveInfo.Kind);
233 bool LastDomination =
234 FinalWarningsMap.lookup(Val: LiveLoanID).CausingFactDominatesExpiry;
235 if (!LastDomination) {
236 FinalWarningsMap[LiveLoanID] = {
237 /*ExpiryLoc=*/{},
238 /*CausingFact=*/LiveInfo.CausingFact,
239 /*MovedExpr=*/nullptr,
240 /*InvalidatedByExpr=*/IOF->getInvalidationExpr(),
241 /*CausingFactDominatesExpiry=*/CurDomination};
242 }
243 }
244 }
245 }
246
247 void issuePendingWarnings() {
248 if (!SemaHelper)
249 return;
250 for (const auto &[LID, Warning] : FinalWarningsMap) {
251 const Loan *L = FactMgr.getLoanMgr().getLoan(ID: LID);
252 const Expr *IssueExpr = L->getIssuingExpr();
253 llvm::PointerUnion<const UseFact *, const OriginEscapesFact *>
254 CausingFact = Warning.CausingFact;
255 const ParmVarDecl *InvalidatedPVD =
256 L->getAccessPath().getAsPlaceholderParam();
257 const Expr *MovedExpr = Warning.MovedExpr;
258 SourceLocation ExpiryLoc = Warning.ExpiryLoc;
259
260 if (const auto *UF = CausingFact.dyn_cast<const UseFact *>()) {
261 if (Warning.InvalidatedByExpr) {
262 if (IssueExpr)
263 // Use-after-invalidation of an object on stack.
264 SemaHelper->reportUseAfterInvalidation(IssueExpr, UseExpr: UF->getUseExpr(),
265 InvalidationExpr: Warning.InvalidatedByExpr);
266 else if (InvalidatedPVD)
267 // Use-after-invalidation of a parameter.
268 SemaHelper->reportUseAfterInvalidation(
269 PVD: InvalidatedPVD, UseExpr: UF->getUseExpr(), InvalidationExpr: Warning.InvalidatedByExpr);
270
271 } else
272 // Scope-based expiry (use-after-scope).
273 SemaHelper->reportUseAfterScope(
274 IssueExpr, UseExpr: UF->getUseExpr(), MovedExpr, FreeLoc: ExpiryLoc,
275 ExprChain: getExprChain(OriginFlowChain: LoanPropagation.buildOriginFlowChain(UF, TargetLoan: LID)));
276
277 } else if (const auto *OEF =
278 CausingFact.dyn_cast<const OriginEscapesFact *>()) {
279 if (Warning.InvalidatedByExpr) {
280 if (const auto *FieldEscape = dyn_cast<FieldEscapeFact>(Val: OEF)) {
281 // Invalidated object escapes to a field.
282 if (IssueExpr)
283 // Invalidated object on stack escapes to a field.
284 SemaHelper->reportInvalidatedField(IssueExpr,
285 Field: FieldEscape->getFieldDecl(),
286 InvalidationExpr: Warning.InvalidatedByExpr);
287 else if (InvalidatedPVD)
288 // Invalidated parameter escapes to a field.
289 SemaHelper->reportInvalidatedField(PVD: InvalidatedPVD,
290 Field: FieldEscape->getFieldDecl(),
291 InvalidationExpr: Warning.InvalidatedByExpr);
292 } else if (const auto *GlobalEscape =
293 dyn_cast<GlobalEscapeFact>(Val: OEF)) {
294 // Invalidated object escapes to global or static storage.
295 if (IssueExpr)
296 // Invalidated object on stack escapes to global or static
297 // storage.
298 SemaHelper->reportInvalidatedGlobal(IssueExpr,
299 Global: GlobalEscape->getGlobal(),
300 InvalidationExpr: Warning.InvalidatedByExpr);
301 else if (InvalidatedPVD)
302 // Invalidated parameter escapes to global or static storage.
303 SemaHelper->reportInvalidatedGlobal(PVD: InvalidatedPVD,
304 Global: GlobalEscape->getGlobal(),
305 InvalidationExpr: Warning.InvalidatedByExpr);
306 } else if (isa<ReturnEscapeFact>(Val: OEF)) {
307 // FIXME: Diagnose invalidated return escapes separately.
308 } else
309 llvm_unreachable("Unhandled OriginEscapesFact type");
310 } else if (const auto *RetEscape = dyn_cast<ReturnEscapeFact>(Val: OEF))
311 // Return stack address.
312 SemaHelper->reportUseAfterReturn(
313 IssueExpr, ReturnExpr: RetEscape->getReturnExpr(), MovedExpr);
314 else if (const auto *FieldEscape = dyn_cast<FieldEscapeFact>(Val: OEF))
315 // Dangling field.
316 SemaHelper->reportDanglingField(
317 IssueExpr, Field: FieldEscape->getFieldDecl(), MovedExpr, ExpiryLoc);
318 else if (const auto *GlobalEscape = dyn_cast<GlobalEscapeFact>(Val: OEF))
319 // Global escape.
320 SemaHelper->reportDanglingGlobal(IssueExpr, DanglingGlobal: GlobalEscape->getGlobal(),
321 MovedExpr, ExpiryLoc);
322 else
323 llvm_unreachable("Unhandled OriginEscapesFact type");
324 } else
325 llvm_unreachable("Unhandled CausingFact type");
326 }
327 }
328
329 // Returns declarations that should be annotated with lifetime attributes
330 // in order to annotate FDef: the canonical declaration and the earliest
331 // redeclarations in each other file. This defines the placement policy for
332 // lifetime annotations. Each target is paired with its corresponding warning
333 // scope.
334 llvm::SmallVector<std::pair<const FunctionDecl *, WarningScope>, 2>
335 getTargetDeclsForAttr(const FunctionDecl *FDef) {
336 if (!FDef)
337 return {};
338
339 assert(FDef->isThisDeclarationADefinition() &&
340 "Expected FunctionDecl to be a definition");
341
342 const auto &SM = FDef->getASTContext().getSourceManager();
343
344 auto GetFile = [&SM](const FunctionDecl *FD) {
345 return SM.getFileID(SpellingLoc: SM.getExpansionLoc(Loc: FD->getLocation()));
346 };
347
348 const FileID DefFile = GetFile(FDef);
349 const FunctionDecl *CanonicalDecl = FDef->getCanonicalDecl();
350 llvm::SmallVector<std::pair<const FunctionDecl *, WarningScope>, 2> Targets{
351 {CanonicalDecl, GetFile(CanonicalDecl) == DefFile
352 ? WarningScope::IntraTU
353 : WarningScope::CrossTU}};
354
355 // Find the earliest redeclaration in each file other than the definition
356 // file.
357 auto AddCrossTUDecl = [&](const FunctionDecl *FD) {
358 FileID File = GetFile(FD);
359 if (File == DefFile)
360 return;
361 for (auto [SeenFD, _] : Targets)
362 if (GetFile(SeenFD) == File)
363 return;
364 Targets.push_back(Elt: {FD, WarningScope::CrossTU});
365 };
366
367 // We iterate in reverse order (from most recent to oldest) to find
368 // the first declaration in each file.
369
370 // Store in temporary variable to manually extend lifetime
371 auto redecls = llvm::to_vector(Range: FDef->redecls());
372
373 for (const FunctionDecl *Redecl : llvm::reverse(C&: redecls))
374 AddCrossTUDecl(Redecl);
375
376 return Targets;
377 }
378
379 void suggestWithScopeForParmVar(const ParmVarDecl *PVD,
380 EscapingTarget EscapeTarget) {
381 if (llvm::isa<const VarDecl *>(Val: EscapeTarget))
382 return;
383
384 for (auto [Decl, Scope] : getTargetDeclsForAttr(FDef: cast<FunctionDecl>(Val: FD))) {
385 const auto *ParmToAnnotate =
386 Decl->getParamDecl(i: PVD->getFunctionScopeIndex());
387 SemaHelper->suggestLifetimeboundToParmVar(Scope, ParmToAnnotate,
388 Target: EscapeTarget);
389 }
390 }
391
392 void suggestWithScopeForImplicitThis(const CXXMethodDecl *MD,
393 const Expr *EscapeExpr) {
394 for (auto [Decl, Scope] : getTargetDeclsForAttr(FDef: MD)) {
395 SemaHelper->suggestLifetimeboundToImplicitThis(
396 Scope, MD: cast<CXXMethodDecl>(Val: Decl), EscapeExpr);
397 }
398 }
399
400 void suggestAnnotations() {
401 if (!SemaHelper)
402 return;
403 if (!LSOpts.SuggestAnnotations)
404 return;
405 llvm::TimeTraceScope TimeTrace("SuggestAnnotations");
406 for (auto [Target, EscapeTarget] : AnnotationWarningsMap) {
407 if (const auto *PVD = Target.dyn_cast<const ParmVarDecl *>())
408 suggestWithScopeForParmVar(PVD, EscapeTarget);
409 else if (const auto *MD = Target.dyn_cast<const CXXMethodDecl *>()) {
410 if (const auto *EscapeExpr = EscapeTarget.dyn_cast<const Expr *>())
411 suggestWithScopeForImplicitThis(MD, EscapeExpr);
412 else
413 llvm_unreachable("Implicit this can only escape via Expr (return)");
414 }
415 }
416 }
417
418 void reportNoescapeViolations() {
419 for (auto [PVD, EscapeTarget] : NoescapeWarningsMap) {
420 if (const auto *E = EscapeTarget.dyn_cast<const Expr *>())
421 SemaHelper->reportNoescapeViolation(ParmWithNoescape: PVD, EscapeExpr: E);
422 else if (const auto *FD = EscapeTarget.dyn_cast<const FieldDecl *>())
423 SemaHelper->reportNoescapeViolation(ParmWithNoescape: PVD, EscapeField: FD);
424 else if (const auto *G = EscapeTarget.dyn_cast<const VarDecl *>())
425 SemaHelper->reportNoescapeViolation(ParmWithNoescape: PVD, EscapeGlobal: G);
426 else
427 llvm_unreachable("Unhandled EscapingTarget type");
428 }
429 }
430
431 void reportLifetimeboundViolations() {
432 if (!isa<FunctionDecl>(Val: FD))
433 return;
434 if (const auto *MD = dyn_cast<CXXMethodDecl>(Val: FD);
435 MD && getImplicitObjectParamLifetimeBoundAttr(FD: MD) &&
436 !VerifiedLiftimeboundEscapes.contains(V: MD))
437 SemaHelper->reportLifetimeboundViolation(MDWithLifetimebound: MD);
438 for (const ParmVarDecl *PVD : cast<FunctionDecl>(Val: FD)->parameters()) {
439 if (!PVD->hasAttr<LifetimeBoundAttr>())
440 continue;
441 bool isImplicit = PVD->getAttr<LifetimeBoundAttr>()->isImplicit();
442 bool Escapes = VerifiedLiftimeboundEscapes.contains(V: PVD);
443 assert((!isImplicit || Escapes || isInStlNamespace(FD)) &&
444 "Implicit lifetimebound parameters "
445 "should escape through return");
446 if (!isImplicit && !Escapes)
447 SemaHelper->reportLifetimeboundViolation(ParmWithLifetimebound: PVD);
448 }
449 }
450
451 // Reports lifetimebound attributes that are placed on a function definition
452 // but not on the corresponding declaration.
453 void reportMisplacedLifetimebound() {
454 const FunctionDecl *FDef = dyn_cast<FunctionDecl>(Val: FD);
455 if (!FDef)
456 return;
457
458 auto TargetDecls = getTargetDeclsForAttr(FDef);
459 // Check if implicit 'this' has lifetimebound on definition but not on
460 // declaration.
461 if (const auto *MDef = dyn_cast<CXXMethodDecl>(Val: FDef);
462 MDef && getDirectImplicitObjectLifetimeBoundAttr(FD: MDef))
463 for (auto [Decl, Scope] : TargetDecls) {
464 const auto *MDecl = cast<CXXMethodDecl>(Val: Decl);
465 if (!getDirectImplicitObjectLifetimeBoundAttr(FD: MDecl))
466 SemaHelper->reportMisplacedLifetimebound(Scope, FDef: MDef, FDecl: MDecl);
467 }
468
469 // Check each parameter for explicit lifetimebound on definition but not on
470 // declaration.
471 for (const auto *PDef : FDef->parameters()) {
472 const auto *Attr = PDef->getAttr<LifetimeBoundAttr>();
473 if (!Attr || Attr->isImplicit())
474 continue;
475 for (auto [Decl, Scope] : TargetDecls) {
476 const auto *PDecl = Decl->getParamDecl(i: PDef->getFunctionScopeIndex());
477 if (!PDecl->hasAttr<LifetimeBoundAttr>())
478 SemaHelper->reportMisplacedLifetimebound(Scope, PVDDef: PDef, PVDDecl: PDecl);
479 }
480 }
481 }
482
483 void reportInapplicableLifetimebound() {
484 const auto *FDef = dyn_cast<FunctionDecl>(Val: FD);
485 if (!FDef)
486 return;
487
488 // If analyzed function is a template definition or an implicit
489 // instantiation, skip.
490 if (FDef->getTemplatedKind() == FunctionDecl::TK_FunctionTemplate ||
491 FDef->getTemplateSpecializationKind() == TSK_ImplicitInstantiation)
492 return;
493
494 for (const auto &PVD : FDef->parameters())
495 if (PVD->hasAttr<LifetimeBoundAttr>() &&
496 !FactMgr.getOriginMgr().hasOrigins(QT: PVD->getType(),
497 /*IntrinsicOnly=*/true))
498 SemaHelper->reportInapplicableLifetimebound(PVD);
499 }
500
501 void inferAnnotations() {
502 for (auto [Target, EscapeTarget] : AnnotationWarningsMap) {
503 if (const auto *MD = Target.dyn_cast<const CXXMethodDecl *>()) {
504 if (!implicitObjectParamIsLifetimeBound(FD: MD))
505 SemaHelper->addLifetimeBoundToImplicitThis(MD: cast<CXXMethodDecl>(Val: MD));
506 } else if (const auto *PVD = Target.dyn_cast<const ParmVarDecl *>()) {
507 const auto *FD = dyn_cast<FunctionDecl>(Val: PVD->getDeclContext());
508 if (!FD)
509 continue;
510 // Propagates inferred attributes via the most recent declaration to
511 // ensure visibility for callers in post-order analysis.
512 FD = getDeclWithMergedLifetimeBoundAttrs(FD);
513 ParmVarDecl *InferredPVD = const_cast<ParmVarDecl *>(
514 FD->getParamDecl(i: PVD->getFunctionScopeIndex()));
515 if (!InferredPVD->hasAttr<LifetimeBoundAttr>())
516 InferredPVD->addAttr(
517 A: LifetimeBoundAttr::CreateImplicit(Ctx&: AST, Range: PVD->getLocation()));
518 }
519 }
520 }
521
522 /// Extract expressions from the origin flow chain for diagnostic purposes.
523 ///
524 /// Given a chain of origins that shows how a loan propagates, this function
525 /// extracts the corresponding expressions for each origin. Origins that refer
526 /// to declarations (rather than expressions) are skipped.
527 llvm::SmallVector<const Expr *>
528 getExprChain(llvm::ArrayRef<OriginID> OriginFlowChain) {
529 llvm::SmallVector<const Expr *> rs;
530 for (const OriginID CurrOID : OriginFlowChain)
531 if (const Expr *CurrExpr =
532 FactMgr.getOriginMgr().getOrigin(ID: CurrOID).getExpr())
533 rs.push_back(Elt: CurrExpr);
534 return rs;
535 }
536};
537} // namespace
538
539void runLifetimeChecker(const LoanPropagationAnalysis &LP,
540 const MovedLoansAnalysis &MovedLoans,
541 const LiveOriginsAnalysis &LO, FactManager &FactMgr,
542 AnalysisDeclContext &ADC,
543 LifetimeSafetySemaHelper *SemaHelper,
544 const LifetimeSafetyOpts &LSOpts) {
545 llvm::TimeTraceScope TimeProfile("LifetimeChecker");
546 LifetimeChecker Checker(LP, MovedLoans, LO, FactMgr, ADC, SemaHelper, LSOpts);
547}
548
549} // namespace clang::lifetimes::internal
550