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