1//===--- SemaLifetimeSafety.h - Sema support for lifetime safety =---------==//
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 defines the Sema-specific implementation for lifetime safety
10// analysis. It provides diagnostic reporting and helper functions that bridge
11// the lifetime safety analysis framework with Sema's diagnostic engine.
12//
13//===----------------------------------------------------------------------===//
14
15#ifndef LLVM_CLANG_LIB_SEMA_SEMALIFETIMESAFETY_H
16#define LLVM_CLANG_LIB_SEMA_SEMALIFETIMESAFETY_H
17
18#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h"
19#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h"
20#include "clang/Basic/DiagnosticSema.h"
21#include "clang/Lex/Lexer.h"
22#include "clang/Lex/Preprocessor.h"
23#include "clang/Sema/Sema.h"
24#include <string>
25
26namespace clang::lifetimes {
27
28inline bool IsLifetimeSafetyEnabled(Sema &S, const Decl *D) {
29 // TODO: Enable ObjectiveC later when we know it's stable enough.
30 if (S.getLangOpts().ObjC)
31 return false;
32
33 // TODO: Default this flag to on in the future.
34 if (!S.getLangOpts().CPlusPlus && !S.getLangOpts().EnableLifetimeSafetyInC)
35 return false;
36
37 // Translation-unit mode: whole-program analysis runs once on TU.
38 // Individual function analysis is disabled when TU mode is enabled.
39 if (S.getLangOpts().EnableLifetimeSafetyTUAnalysis)
40 return isa<TranslationUnitDecl>(Val: D);
41
42 // Per-function mode: analysis runs on each function/method individually.
43 // Skip TU-level calls when per-function mode is enabled.
44 if (isa<TranslationUnitDecl>(Val: D))
45 return false;
46
47 // Enable per-function mode via debug flag or specific diagnostics.
48 if (S.getLangOpts().DebugRunLifetimeSafety)
49 return true;
50 DiagnosticsEngine &Diags = S.getDiagnostics();
51 constexpr unsigned DiagIDs[] = {
52 diag::warn_lifetime_safety_use_after_scope,
53 diag::warn_lifetime_safety_use_after_scope_moved,
54 diag::warn_lifetime_safety_use_after_free,
55 diag::warn_lifetime_safety_return_stack_addr,
56 diag::warn_lifetime_safety_return_stack_addr_moved,
57 diag::warn_lifetime_safety_invalidation,
58 diag::warn_lifetime_safety_dangling_field,
59 diag::warn_lifetime_safety_dangling_field_moved,
60 diag::warn_lifetime_safety_dangling_global,
61 diag::warn_lifetime_safety_dangling_global_moved,
62 diag::warn_lifetime_safety_noescape_escapes,
63 diag::warn_lifetime_safety_lifetimebound_violation,
64 diag::warn_lifetime_safety_cross_tu_misplaced_lifetimebound,
65 diag::warn_lifetime_safety_intra_tu_misplaced_lifetimebound,
66 diag::warn_lifetime_safety_invalidated_field,
67 diag::warn_lifetime_safety_invalidated_global,
68 diag::warn_lifetime_safety_cross_tu_param_suggestion,
69 diag::warn_lifetime_safety_intra_tu_param_suggestion,
70 diag::warn_lifetime_safety_cross_tu_ctor_param_suggestion,
71 diag::warn_lifetime_safety_intra_tu_ctor_param_suggestion,
72 diag::warn_lifetime_safety_cross_tu_this_suggestion,
73 diag::warn_lifetime_safety_intra_tu_this_suggestion,
74 diag::warn_lifetime_safety_inapplicable_lifetimebound};
75 for (unsigned DiagID : DiagIDs)
76 if (!Diags.isIgnored(DiagID, Loc: D->getBeginLoc()))
77 return true;
78 return false;
79}
80
81inline bool ShouldSuggestLifetimeAnnotations(Sema &S, const Decl *D) {
82 DiagnosticsEngine &Diags = S.getDiagnostics();
83 constexpr unsigned DiagIDs[] = {
84 diag::warn_lifetime_safety_intra_tu_param_suggestion,
85 diag::warn_lifetime_safety_cross_tu_param_suggestion,
86 diag::warn_lifetime_safety_intra_tu_ctor_param_suggestion,
87 diag::warn_lifetime_safety_cross_tu_ctor_param_suggestion,
88 diag::warn_lifetime_safety_intra_tu_this_suggestion,
89 diag::warn_lifetime_safety_cross_tu_this_suggestion};
90 for (unsigned DiagID : DiagIDs)
91 if (!Diags.isIgnored(DiagID, Loc: D->getBeginLoc()))
92 return true;
93 return false;
94}
95
96inline LifetimeSafetyOpts GetLifetimeSafetyOpts(Sema &S, const Decl *D) {
97 LifetimeSafetyOpts LSOpts;
98 LSOpts.MaxCFGBlocks = S.getLangOpts().LifetimeSafetyMaxCFGBlocks;
99 LSOpts.SuggestAnnotations = ShouldSuggestLifetimeAnnotations(S, D);
100 return LSOpts;
101}
102
103class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
104
105public:
106 LifetimeSafetySemaHelperImpl(Sema &S) : S(S) {}
107
108 void reportUseAfterScope(const Expr *IssueExpr, const Expr *UseExpr,
109 const Expr *MovedExpr, SourceLocation FreeLoc,
110 llvm::ArrayRef<const Expr *> ExprChain) override {
111 unsigned DiagID = MovedExpr
112 ? diag::warn_lifetime_safety_use_after_scope_moved
113 : diag::warn_lifetime_safety_use_after_scope;
114 std::string DestroyedSubject = getDiagSubjectDescription(E: IssueExpr);
115
116 S.Diag(Loc: IssueExpr->getExprLoc(), DiagID)
117 << DestroyedSubject << IssueExpr->getSourceRange();
118 if (MovedExpr)
119 S.Diag(Loc: MovedExpr->getExprLoc(), DiagID: diag::note_lifetime_safety_moved_here)
120 << MovedExpr->getSourceRange();
121 S.Diag(Loc: FreeLoc, DiagID: diag::note_lifetime_safety_destroyed_here)
122 << DestroyedSubject;
123
124 reportAliasingChain(OriginExprChain: ExprChain);
125
126 S.Diag(Loc: UseExpr->getExprLoc(), DiagID: diag::note_lifetime_safety_used_here)
127 << UseExpr->getSourceRange();
128 }
129
130 void reportUseAfterReturn(const Expr *IssueExpr, const Expr *ReturnExpr,
131 const Expr *MovedExpr) override {
132 unsigned DiagID = MovedExpr
133 ? diag::warn_lifetime_safety_return_stack_addr_moved
134 : diag::warn_lifetime_safety_return_stack_addr;
135
136 S.Diag(Loc: IssueExpr->getExprLoc(), DiagID)
137 << getDiagSubjectDescription(E: IssueExpr) << IssueExpr->getSourceRange();
138
139 if (MovedExpr)
140 S.Diag(Loc: MovedExpr->getExprLoc(), DiagID: diag::note_lifetime_safety_moved_here)
141 << MovedExpr->getSourceRange();
142 S.Diag(Loc: ReturnExpr->getExprLoc(), DiagID: diag::note_lifetime_safety_returned_here)
143 << ReturnExpr->getSourceRange();
144 }
145
146 void reportDanglingField(const Expr *IssueExpr,
147 const FieldDecl *DanglingField,
148 const Expr *MovedExpr,
149 SourceLocation ExpiryLoc) override {
150 unsigned DiagID = MovedExpr
151 ? diag::warn_lifetime_safety_dangling_field_moved
152 : diag::warn_lifetime_safety_dangling_field;
153
154 S.Diag(Loc: IssueExpr->getExprLoc(), DiagID)
155 << getDiagSubjectDescription(E: IssueExpr)
156 << getDiagSubjectDescription(VD: DanglingField)
157 << IssueExpr->getSourceRange();
158 if (MovedExpr)
159 S.Diag(Loc: MovedExpr->getExprLoc(), DiagID: diag::note_lifetime_safety_moved_here)
160 << MovedExpr->getSourceRange();
161 S.Diag(Loc: DanglingField->getLocation(),
162 DiagID: diag::note_lifetime_safety_dangling_field_here)
163 << DanglingField->getEndLoc();
164 }
165
166 void reportDanglingGlobal(const Expr *IssueExpr,
167 const VarDecl *DanglingGlobal,
168 const Expr *MovedExpr,
169 SourceLocation ExpiryLoc) override {
170 unsigned DiagID = MovedExpr
171 ? diag::warn_lifetime_safety_dangling_global_moved
172 : diag::warn_lifetime_safety_dangling_global;
173
174 S.Diag(Loc: IssueExpr->getExprLoc(), DiagID)
175 << getDiagSubjectDescription(E: IssueExpr)
176 << getDiagSubjectDescription(VD: DanglingGlobal)
177 << IssueExpr->getSourceRange();
178 if (MovedExpr)
179 S.Diag(Loc: MovedExpr->getExprLoc(), DiagID: diag::note_lifetime_safety_moved_here)
180 << MovedExpr->getSourceRange();
181 if (DanglingGlobal->isStaticLocal() || DanglingGlobal->isStaticDataMember())
182 S.Diag(Loc: DanglingGlobal->getLocation(),
183 DiagID: diag::note_lifetime_safety_dangling_static_here)
184 << DanglingGlobal->getEndLoc();
185 else
186 S.Diag(Loc: DanglingGlobal->getLocation(),
187 DiagID: diag::note_lifetime_safety_dangling_global_here)
188 << DanglingGlobal->getEndLoc();
189 }
190
191 void reportUseAfterInvalidation(const Expr *IssueExpr, const Expr *UseExpr,
192 const Expr *InvalidationExpr) override {
193 auto WarnDiag = isa<CXXDeleteExpr>(Val: InvalidationExpr)
194 ? diag::warn_lifetime_safety_use_after_free
195 : diag::warn_lifetime_safety_invalidation;
196 std::string InvalidatedSubject = getDiagSubjectDescription(E: IssueExpr);
197 S.Diag(Loc: IssueExpr->getExprLoc(), DiagID: WarnDiag)
198 << InvalidatedSubject << IssueExpr->getSourceRange();
199 reportInvalidationSite(InvalidationExpr, InvalidatedSubject);
200 S.Diag(Loc: UseExpr->getExprLoc(), DiagID: diag::note_lifetime_safety_used_here)
201 << UseExpr->getSourceRange();
202 }
203 void reportUseAfterInvalidation(const ParmVarDecl *PVD, const Expr *UseExpr,
204 const Expr *InvalidationExpr) override {
205
206 auto WarnDiag = isa<CXXDeleteExpr>(Val: InvalidationExpr)
207 ? diag::warn_lifetime_safety_use_after_free
208 : diag::warn_lifetime_safety_invalidation;
209 std::string InvalidatedSubject = getDiagSubjectDescription(VD: PVD);
210
211 S.Diag(Loc: PVD->getSourceRange().getBegin(), DiagID: WarnDiag)
212 << InvalidatedSubject << PVD->getSourceRange();
213 reportInvalidationSite(InvalidationExpr, InvalidatedSubject);
214 S.Diag(Loc: UseExpr->getExprLoc(), DiagID: diag::note_lifetime_safety_used_here)
215 << UseExpr->getSourceRange();
216 }
217
218 void reportInvalidatedField(const Expr *IssueExpr,
219 const FieldDecl *DanglingField,
220 const Expr *InvalidationExpr) override {
221 std::string InvalidatedSubject = getDiagSubjectDescription(E: IssueExpr);
222 S.Diag(Loc: IssueExpr->getExprLoc(),
223 DiagID: diag::warn_lifetime_safety_invalidated_field)
224 << InvalidatedSubject << getDiagSubjectDescription(VD: DanglingField)
225 << IssueExpr->getSourceRange();
226 reportInvalidationSite(InvalidationExpr, InvalidatedSubject);
227 S.Diag(Loc: DanglingField->getLocation(),
228 DiagID: diag::note_lifetime_safety_dangling_field_here)
229 << DanglingField->getEndLoc();
230 }
231
232 void reportInvalidatedField(const ParmVarDecl *PVD,
233 const FieldDecl *DanglingField,
234 const Expr *InvalidationExpr) override {
235 std::string InvalidatedSubject = getDiagSubjectDescription(VD: PVD);
236 S.Diag(Loc: PVD->getSourceRange().getBegin(),
237 DiagID: diag::warn_lifetime_safety_invalidated_field)
238 << InvalidatedSubject << getDiagSubjectDescription(VD: DanglingField)
239 << PVD->getSourceRange();
240 reportInvalidationSite(InvalidationExpr, InvalidatedSubject);
241 S.Diag(Loc: DanglingField->getLocation(),
242 DiagID: diag::note_lifetime_safety_dangling_field_here)
243 << DanglingField->getEndLoc();
244 }
245
246 void reportInvalidatedGlobal(const Expr *IssueExpr,
247 const VarDecl *DanglingGlobal,
248 const Expr *InvalidationExpr) override {
249 std::string InvalidatedSubject = getDiagSubjectDescription(E: IssueExpr);
250 S.Diag(Loc: IssueExpr->getExprLoc(),
251 DiagID: diag::warn_lifetime_safety_invalidated_global)
252 << InvalidatedSubject << getDiagSubjectDescription(VD: DanglingGlobal)
253 << IssueExpr->getSourceRange();
254 reportInvalidationSite(InvalidationExpr, InvalidatedSubject);
255 if (DanglingGlobal->isStaticLocal() || DanglingGlobal->isStaticDataMember())
256 S.Diag(Loc: DanglingGlobal->getLocation(),
257 DiagID: diag::note_lifetime_safety_dangling_static_here)
258 << DanglingGlobal->getEndLoc();
259 else
260 S.Diag(Loc: DanglingGlobal->getLocation(),
261 DiagID: diag::note_lifetime_safety_dangling_global_here)
262 << DanglingGlobal->getEndLoc();
263 }
264
265 void reportInvalidatedGlobal(const ParmVarDecl *PVD,
266 const VarDecl *DanglingGlobal,
267 const Expr *InvalidationExpr) override {
268 std::string InvalidatedSubject = getDiagSubjectDescription(VD: PVD);
269 S.Diag(Loc: PVD->getSourceRange().getBegin(),
270 DiagID: diag::warn_lifetime_safety_invalidated_global)
271 << InvalidatedSubject << getDiagSubjectDescription(VD: DanglingGlobal)
272 << PVD->getSourceRange();
273 reportInvalidationSite(InvalidationExpr, InvalidatedSubject);
274 if (DanglingGlobal->isStaticLocal() || DanglingGlobal->isStaticDataMember())
275 S.Diag(Loc: DanglingGlobal->getLocation(),
276 DiagID: diag::note_lifetime_safety_dangling_static_here)
277 << DanglingGlobal->getEndLoc();
278 else
279 S.Diag(Loc: DanglingGlobal->getLocation(),
280 DiagID: diag::note_lifetime_safety_dangling_global_here)
281 << DanglingGlobal->getEndLoc();
282 }
283
284 void suggestLifetimeboundToParmVar(WarningScope Scope,
285 const ParmVarDecl *ParmToAnnotate,
286 EscapingTarget Target) override {
287 unsigned DiagID;
288 if (isa<CXXConstructorDecl>(Val: ParmToAnnotate->getDeclContext()))
289 DiagID = (Scope == WarningScope::CrossTU)
290 ? diag::warn_lifetime_safety_cross_tu_ctor_param_suggestion
291 : diag::warn_lifetime_safety_intra_tu_ctor_param_suggestion;
292 else
293 DiagID = (Scope == WarningScope::CrossTU)
294 ? diag::warn_lifetime_safety_cross_tu_param_suggestion
295 : diag::warn_lifetime_safety_intra_tu_param_suggestion;
296
297 auto [InsertionPoint, FixItText] = getLifetimeBoundFixIt(Decl: ParmToAnnotate);
298
299 S.Diag(Loc: ParmToAnnotate->getBeginLoc(), DiagID)
300 << ParmToAnnotate->getSourceRange()
301 << FixItHint::CreateInsertion(InsertionLoc: InsertionPoint, Code: FixItText);
302
303 if (const auto *EscapeExpr = Target.dyn_cast<const Expr *>())
304 S.Diag(Loc: EscapeExpr->getBeginLoc(),
305 DiagID: diag::note_lifetime_safety_suggestion_returned_here)
306 << EscapeExpr->getSourceRange();
307 else if (const auto *EscapeField = Target.dyn_cast<const FieldDecl *>())
308 S.Diag(Loc: EscapeField->getLocation(),
309 DiagID: diag::note_lifetime_safety_escapes_to_field_here)
310 << EscapeField->getSourceRange();
311 }
312
313 void reportLifetimeboundViolation(
314 const ParmVarDecl *ParmWithLifetimebound) override {
315 const auto *Attr = ParmWithLifetimebound->getAttr<LifetimeBoundAttr>();
316 StringRef ParamName = ParmWithLifetimebound->getName();
317 bool HasName = ParamName.size() > 0;
318 S.Diag(Loc: Attr->getLocation(),
319 DiagID: diag::warn_lifetime_safety_lifetimebound_violation)
320 << HasName << ParamName << Attr->getRange();
321 }
322
323 void reportLifetimeboundViolation(
324 const CXXMethodDecl *MDWithLifetimebound) override {
325 const auto *Attr =
326 getImplicitObjectParamLifetimeBoundAttr(FD: MDWithLifetimebound);
327 assert(Attr && "Expected lifetimebound attribute");
328 S.Diag(Loc: Attr->getLocation(),
329 DiagID: diag::warn_lifetime_safety_lifetimebound_violation)
330 << 2 << "" << Attr->getRange();
331 }
332
333 void reportMisplacedLifetimebound(WarningScope Scope,
334 const CXXMethodDecl *FDef,
335 const CXXMethodDecl *FDecl) override {
336 const auto *Attr = getDirectImplicitObjectLifetimeBoundAttr(FD: FDef);
337 assert(Attr && "Expected lifetimebound attribute");
338 unsigned DiagID =
339 Scope == WarningScope::CrossTU
340 ? diag::warn_lifetime_safety_cross_tu_misplaced_lifetimebound
341 : diag::warn_lifetime_safety_intra_tu_misplaced_lifetimebound;
342
343 auto [InsertionPoint, FixItText] = getLifetimeBoundFixIt(MD: FDecl);
344
345 // Do not emit fix-its in macros or at invalid locations.
346 bool IsMacro =
347 FDecl->getBeginLoc().isMacroID() || InsertionPoint.isMacroID();
348
349 if (IsMacro || InsertionPoint.isInvalid())
350 S.Diag(Loc: FDecl->getLocation(), DiagID);
351 else
352 S.Diag(Loc: InsertionPoint, DiagID)
353 << FixItHint::CreateInsertion(InsertionLoc: InsertionPoint, Code: FixItText);
354
355 S.Diag(Loc: Attr->getLocation(), DiagID: diag::note_lifetime_safety_lifetimebound_here)
356 << Attr->getRange();
357 }
358
359 void reportMisplacedLifetimebound(WarningScope Scope,
360 const ParmVarDecl *PVDDef,
361 const ParmVarDecl *PVDDecl) override {
362
363 const auto *Attr = PVDDef->getAttr<LifetimeBoundAttr>();
364 assert(Attr && "Expected lifetimebound attribute");
365 unsigned DiagID =
366 Scope == WarningScope::CrossTU
367 ? diag::warn_lifetime_safety_cross_tu_misplaced_lifetimebound
368 : diag::warn_lifetime_safety_intra_tu_misplaced_lifetimebound;
369
370 auto [InsertionPoint, FixItText] = getLifetimeBoundFixIt(Decl: PVDDecl);
371
372 // Do not emit fix-its in macros or at invalid locations.
373 bool IsMacro =
374 PVDDecl->getBeginLoc().isMacroID() || InsertionPoint.isMacroID();
375
376 if (IsMacro || InsertionPoint.isInvalid())
377 S.Diag(Loc: PVDDecl->getBeginLoc(), DiagID) << PVDDecl->getSourceRange();
378 else
379 S.Diag(Loc: PVDDecl->getBeginLoc(), DiagID)
380 << PVDDecl->getSourceRange()
381 << FixItHint::CreateInsertion(InsertionLoc: InsertionPoint, Code: FixItText);
382
383 S.Diag(Loc: Attr->getLocation(), DiagID: diag::note_lifetime_safety_lifetimebound_here)
384 << Attr->getRange();
385 }
386
387 void reportInapplicableLifetimebound(const ParmVarDecl *PVD) override {
388 assert(PVD->hasAttr<LifetimeBoundAttr>() &&
389 "Expected parameter to have lifetimebound attribute");
390 const auto *Attr = PVD->getAttr<LifetimeBoundAttr>();
391 S.Diag(Loc: Attr->getLocation(),
392 DiagID: diag::warn_lifetime_safety_inapplicable_lifetimebound)
393 << PVD->getType() << Attr->getRange();
394 }
395
396 void suggestLifetimeboundToImplicitThis(WarningScope Scope,
397 const CXXMethodDecl *MD,
398 const Expr *EscapeExpr) override {
399 unsigned DiagID = (Scope == WarningScope::CrossTU)
400 ? diag::warn_lifetime_safety_cross_tu_this_suggestion
401 : diag::warn_lifetime_safety_intra_tu_this_suggestion;
402
403 auto [InsertionPoint, FixItText] = getLifetimeBoundFixIt(MD);
404
405 S.Diag(Loc: InsertionPoint, DiagID)
406 << MD->getNameInfo().getSourceRange()
407 << FixItHint::CreateInsertion(InsertionLoc: InsertionPoint, Code: FixItText);
408
409 S.Diag(Loc: EscapeExpr->getBeginLoc(),
410 DiagID: diag::note_lifetime_safety_suggestion_returned_here)
411 << EscapeExpr->getSourceRange();
412 }
413
414 void reportNoescapeViolation(const ParmVarDecl *ParmWithNoescape,
415 const Expr *EscapeExpr) override {
416 S.Diag(Loc: ParmWithNoescape->getBeginLoc(),
417 DiagID: diag::warn_lifetime_safety_noescape_escapes)
418 << ParmWithNoescape->getSourceRange();
419
420 S.Diag(Loc: EscapeExpr->getBeginLoc(),
421 DiagID: diag::note_lifetime_safety_suggestion_returned_here)
422 << EscapeExpr->getSourceRange();
423 }
424
425 void reportNoescapeViolation(const ParmVarDecl *ParmWithNoescape,
426 const FieldDecl *EscapeField) override {
427 S.Diag(Loc: ParmWithNoescape->getBeginLoc(),
428 DiagID: diag::warn_lifetime_safety_noescape_escapes)
429 << ParmWithNoescape->getSourceRange();
430
431 S.Diag(Loc: EscapeField->getLocation(),
432 DiagID: diag::note_lifetime_safety_escapes_to_field_here)
433 << EscapeField->getEndLoc();
434 }
435
436 void reportNoescapeViolation(const ParmVarDecl *ParmWithNoescape,
437 const VarDecl *EscapeGlobal) override {
438 S.Diag(Loc: ParmWithNoescape->getBeginLoc(),
439 DiagID: diag::warn_lifetime_safety_noescape_escapes)
440 << ParmWithNoescape->getSourceRange();
441 if (EscapeGlobal->isStaticLocal() || EscapeGlobal->isStaticDataMember())
442 S.Diag(Loc: EscapeGlobal->getLocation(),
443 DiagID: diag::note_lifetime_safety_escapes_to_static_storage_here)
444 << EscapeGlobal->getEndLoc();
445 else
446 S.Diag(Loc: EscapeGlobal->getLocation(),
447 DiagID: diag::note_lifetime_safety_escapes_to_global_here)
448 << EscapeGlobal->getEndLoc();
449 }
450
451 void addLifetimeBoundToImplicitThis(const CXXMethodDecl *MD) override {
452 S.addLifetimeBoundToImplicitThis(MD: const_cast<CXXMethodDecl *>(MD));
453 }
454
455private:
456 struct LifetimeBoundMacroCache {
457 bool IsBuilt = false;
458 SmallVector<const IdentifierInfo *> Candidates;
459 };
460
461 void buildLifetimeBoundMacroCache(LifetimeBoundMacroCache &Cache,
462 ArrayRef<TokenValue> Tokens) {
463 if (Cache.IsBuilt)
464 return;
465
466 const Preprocessor &PP = S.getPreprocessor();
467 // Collect macro names that were ever defined as a lifetimebound attribute.
468 for (const auto &M : PP.macros()) {
469 const IdentifierInfo *II = M.first;
470 const MacroDirective *MD = PP.getLocalMacroDirectiveHistory(II);
471 if (!MD)
472 continue;
473
474 // Include earlier matching definitions to handle redefinitions.
475 for (MacroDirective::DefInfo Def = MD->getDefinition(); Def;
476 Def = Def.getPreviousDefinition()) {
477 const MacroInfo *MI = Def.getMacroInfo();
478 if (MI->isObjectLike() && Tokens.size() == MI->getNumTokens() &&
479 std::equal(first1: Tokens.begin(), last1: Tokens.end(), first2: MI->tokens_begin())) {
480 Cache.Candidates.push_back(Elt: II);
481 break;
482 }
483 }
484 }
485 Cache.IsBuilt = true;
486 }
487
488 StringRef getLastCachedMacroWithSpelling(SourceLocation Loc,
489 llvm::ArrayRef<TokenValue> Tokens,
490 LifetimeBoundMacroCache &Cache) {
491 if (Loc.isInvalid())
492 return {};
493
494 buildLifetimeBoundMacroCache(Cache, Tokens);
495
496 const Preprocessor &PP = S.getPreprocessor();
497 const SourceManager &SM = S.getSourceManager();
498 SourceLocation BestLocation;
499 StringRef BestSpelling;
500 for (const IdentifierInfo *II : Cache.Candidates) {
501 const MacroDirective *MD = PP.getLocalMacroDirectiveHistory(II);
502 const MacroDirective::DefInfo Def = MD->findDirectiveAtLoc(L: Loc, SM);
503 if (!Def || !Def.getMacroInfo())
504 continue;
505
506 // Ensure the macro definition active at Loc still has this spelling.
507 const MacroInfo *MI = Def.getMacroInfo();
508 if (!MI->isObjectLike() || Tokens.size() != MI->getNumTokens() ||
509 !std::equal(first1: Tokens.begin(), last1: Tokens.end(), first2: MI->tokens_begin()))
510 continue;
511
512 // Choose the matching macro defined latest before Loc.
513 SourceLocation Location = Def.getLocation();
514 assert(Location.isInvalid() ||
515 SM.isBeforeInTranslationUnit(Location, Loc));
516 if (BestLocation.isInvalid() ||
517 (Location.isValid() &&
518 SM.isBeforeInTranslationUnit(LHS: BestLocation, RHS: Location))) {
519 BestLocation = Location;
520 BestSpelling = II->getName();
521 }
522 }
523 return BestSpelling;
524 }
525
526 void reportInvalidationSite(const Expr *InvalidationExpr,
527 StringRef InvalidatedSubject) {
528 auto Diag = isa<CXXDeleteExpr>(Val: InvalidationExpr)
529 ? diag::note_lifetime_safety_freed_here
530 : diag::note_lifetime_safety_invalidated_here;
531 S.Diag(Loc: InvalidationExpr->getExprLoc(), DiagID: Diag)
532 << InvalidatedSubject << InvalidationExpr->getSourceRange();
533 }
534
535 std::string getLifetimeBoundFixItText(SourceLocation Loc, bool LeadingSpace,
536 bool AllowGNUAttrMacro = true) {
537 StringRef Spelling = S.getLangOpts().LifetimeSafetyLifetimeBoundMacro;
538 if (Spelling.empty() && Loc.isValid()) {
539 const Preprocessor &PP = S.getPreprocessor();
540 Spelling = getLastCachedMacroWithSpelling(
541 Loc,
542 Tokens: {tok::l_square, tok::l_square, PP.getIdentifierInfo(Name: "clang"),
543 tok::coloncolon, PP.getIdentifierInfo(Name: "lifetimebound"),
544 tok::r_square, tok::r_square},
545 Cache&: ClangLifetimeBoundMacroCache);
546
547 if (Spelling.empty() && AllowGNUAttrMacro)
548 Spelling = getLastCachedMacroWithSpelling(
549 Loc,
550 Tokens: {tok::kw___attribute, tok::l_paren, tok::l_paren,
551 PP.getIdentifierInfo(Name: "lifetimebound"), tok::r_paren, tok::r_paren},
552 Cache&: GNULifetimeBoundMacroCache);
553 }
554 const std::string Text =
555 Spelling.empty() ? "[[clang::lifetimebound]]" : Spelling.str();
556 return LeadingSpace ? " " + Text : Text + " ";
557 }
558
559 std::pair<SourceLocation, std::string>
560 getLifetimeBoundFixIt(const ParmVarDecl *Decl) {
561 SourceLocation InsertionPoint = Lexer::getLocForEndOfToken(
562 Loc: Decl->getEndLoc(), Offset: 0, SM: S.getSourceManager(), LangOpts: S.getLangOpts());
563 bool LeadingSpace = true;
564
565 if (!Decl->getIdentifier()) {
566 // For unnamed parameters, placing attributes after the type would be
567 // parsed as a type attribute, not a parameter attribute.
568 InsertionPoint = Decl->getBeginLoc();
569 LeadingSpace = false;
570 } else if (Decl->hasDefaultArg()) {
571 // If the parameter has a default argument, place the attribute after the
572 // named argument.
573 InsertionPoint = Lexer::getLocForEndOfToken(
574 Loc: Decl->getLocation(), Offset: 0, SM: S.getSourceManager(), LangOpts: S.getLangOpts());
575 }
576 return {InsertionPoint,
577 getLifetimeBoundFixItText(Loc: InsertionPoint, LeadingSpace)};
578 }
579
580 std::pair<SourceLocation, std::string>
581 getLifetimeBoundFixIt(const CXXMethodDecl *MD) {
582 const auto MDL = MD->getTypeSourceInfo()->getTypeLoc();
583 SourceLocation InsertionPoint = Lexer::getLocForEndOfToken(
584 Loc: MDL.getEndLoc(), Offset: 0, SM: S.getSourceManager(), LangOpts: S.getLangOpts());
585
586 if (const auto *FPT = MD->getType()->getAs<FunctionProtoType>();
587 FPT && FPT->hasTrailingReturn()) {
588 // For trailing return types, 'getEndLoc()' includes the return type
589 // after '->', placing the attribute in an invalid position.
590 // Instead use 'getLocalRangeEnd()' which gives the '->' location
591 // for trailing returns, so find the last token before it.
592 const auto FTL = MDL.getAs<FunctionTypeLoc>();
593 assert(FTL);
594 InsertionPoint = Lexer::getLocForEndOfToken(
595 Loc: Lexer::findPreviousToken(Loc: FTL.getLocalRangeEnd(), SM: S.getSourceManager(),
596 LangOpts: S.getLangOpts(),
597 /*IncludeComments=*/IncludeComments: false)
598 ->getLocation(),
599 Offset: 0, SM: S.getSourceManager(), LangOpts: S.getLangOpts());
600 }
601 return {InsertionPoint,
602 getLifetimeBoundFixItText(Loc: InsertionPoint, /*LeadingSpace=*/LeadingSpace: true,
603 /*AllowGNUAttrMacro=*/AllowGNUAttrMacro: false)};
604 }
605
606 std::string getDiagSubjectDescription(const ValueDecl *VD) {
607 std::string Res;
608 llvm::raw_string_ostream OS(Res);
609 if (isa<FieldDecl>(Val: VD)) {
610 OS << "field";
611 } else if (isa<ParmVarDecl>(Val: VD)) {
612 OS << "parameter";
613 } else if (const auto *Var = dyn_cast<VarDecl>(Val: VD)) {
614 if (Var->isStaticLocal() || Var->isStaticDataMember())
615 OS << "static variable";
616 else if (Var->hasGlobalStorage())
617 OS << "global variable";
618 else
619 OS << "local variable";
620 } else {
621 OS << "variable";
622 }
623 OS << " '";
624 VD->getNameForDiagnostic(OS, Policy: S.getPrintingPolicy(), /*Qualified=*/Qualified: false);
625 OS << "'";
626 return Res;
627 }
628
629 std::string getDiagSubjectDescription(const Expr *E) {
630 E = E->IgnoreImpCasts();
631 if (isa<MaterializeTemporaryExpr>(Val: E))
632 return "temporary object";
633 if (isa<CXXNewExpr>(Val: E))
634 return "allocated object";
635 if (const auto *DRE = dyn_cast<DeclRefExpr>(Val: E))
636 return getDiagSubjectDescription(VD: DRE->getDecl());
637
638 if (const auto *CE = dyn_cast<CallExpr>(Val: E)) {
639 const auto *FD = CE->getDirectCallee();
640 if (!FD)
641 return "result of call";
642 if (FD->isOverloadedOperator() || isa<CXXConversionDecl>(Val: FD))
643 return "expression";
644 std::string Name;
645 llvm::raw_string_ostream OS(Name);
646 FD->getNameForDiagnostic(OS, Policy: S.getPrintingPolicy(),
647 /*Qualified=*/Qualified: false);
648 return "result of call to '" + Name + "'";
649 }
650
651 // TODO: Handle other expression types.
652 return "expression";
653 }
654
655 bool shouldShowInAliasChain(const Expr *CurrExpr, const Expr *LastExpr) {
656 CurrExpr = CurrExpr->IgnoreImpCasts();
657 LastExpr = LastExpr->IgnoreImpCasts();
658
659 if (!isa<CallExpr, DeclRefExpr>(Val: CurrExpr))
660 return false;
661 // Source ranges can be used to filter out many implicit expressions,
662 // because operations between class objects often involve numerous implicit
663 // conversions, yet they share the same source range.
664 return CurrExpr->getSourceRange() != LastExpr->getSourceRange();
665 }
666
667 void reportAliasingChain(llvm::ArrayRef<const Expr *> OriginExprChain) {
668 if (OriginExprChain.empty())
669 return;
670
671 const Expr *LastExpr = OriginExprChain.back();
672 std::string IssueStr = getDiagSubjectDescription(E: LastExpr);
673
674 for (const Expr *CurrExpr : reverse(C: OriginExprChain.drop_back())) {
675 if (!shouldShowInAliasChain(CurrExpr, LastExpr))
676 continue;
677 S.Diag(Loc: CurrExpr->getBeginLoc(),
678 DiagID: diag::note_lifetime_safety_aliases_storage)
679 << CurrExpr->getSourceRange() << getDiagSubjectDescription(E: CurrExpr)
680 << IssueStr;
681 LastExpr = CurrExpr;
682 }
683 }
684
685 LifetimeBoundMacroCache ClangLifetimeBoundMacroCache;
686 LifetimeBoundMacroCache GNULifetimeBoundMacroCache;
687 Sema &S;
688};
689
690} // namespace clang::lifetimes
691
692#endif // LLVM_CLANG_LIB_SEMA_SEMALIFETIMESAFETY_H
693