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
192 reportUseAfterInvalidation(const Expr *IssueExpr, const Expr *UseExpr,
193 const Expr *InvalidationExpr,
194 llvm::ArrayRef<const Expr *> ExprChain) override {
195 auto WarnDiag = isa<CXXDeleteExpr>(Val: InvalidationExpr)
196 ? diag::warn_lifetime_safety_use_after_free
197 : diag::warn_lifetime_safety_invalidation;
198 std::string InvalidatedSubject = getDiagSubjectDescription(E: IssueExpr);
199 S.Diag(Loc: IssueExpr->getExprLoc(), DiagID: WarnDiag)
200 << InvalidatedSubject << IssueExpr->getSourceRange();
201 reportInvalidationSite(InvalidationExpr, InvalidatedSubject);
202 reportAliasingChain(OriginExprChain: ExprChain);
203 S.Diag(Loc: UseExpr->getExprLoc(), DiagID: diag::note_lifetime_safety_used_here)
204 << UseExpr->getSourceRange();
205 }
206 void
207 reportUseAfterInvalidation(const ParmVarDecl *PVD, const Expr *UseExpr,
208 const Expr *InvalidationExpr,
209 llvm::ArrayRef<const Expr *> ExprChain) override {
210
211 auto WarnDiag = isa<CXXDeleteExpr>(Val: InvalidationExpr)
212 ? diag::warn_lifetime_safety_use_after_free
213 : diag::warn_lifetime_safety_invalidation;
214 std::string InvalidatedSubject = getDiagSubjectDescription(VD: PVD);
215
216 S.Diag(Loc: PVD->getSourceRange().getBegin(), DiagID: WarnDiag)
217 << InvalidatedSubject << PVD->getSourceRange();
218 reportInvalidationSite(InvalidationExpr, InvalidatedSubject);
219 reportAliasingChain(OriginExprChain: ExprChain);
220 S.Diag(Loc: UseExpr->getExprLoc(), DiagID: diag::note_lifetime_safety_used_here)
221 << UseExpr->getSourceRange();
222 }
223
224 void reportInvalidatedField(const Expr *IssueExpr,
225 const FieldDecl *DanglingField,
226 const Expr *InvalidationExpr) override {
227 std::string InvalidatedSubject = getDiagSubjectDescription(E: IssueExpr);
228 S.Diag(Loc: IssueExpr->getExprLoc(),
229 DiagID: diag::warn_lifetime_safety_invalidated_field)
230 << InvalidatedSubject << getDiagSubjectDescription(VD: DanglingField)
231 << IssueExpr->getSourceRange();
232 reportInvalidationSite(InvalidationExpr, InvalidatedSubject);
233 S.Diag(Loc: DanglingField->getLocation(),
234 DiagID: diag::note_lifetime_safety_dangling_field_here)
235 << DanglingField->getEndLoc();
236 }
237
238 void reportInvalidatedField(const ParmVarDecl *PVD,
239 const FieldDecl *DanglingField,
240 const Expr *InvalidationExpr) override {
241 std::string InvalidatedSubject = getDiagSubjectDescription(VD: PVD);
242 S.Diag(Loc: PVD->getSourceRange().getBegin(),
243 DiagID: diag::warn_lifetime_safety_invalidated_field)
244 << InvalidatedSubject << getDiagSubjectDescription(VD: DanglingField)
245 << PVD->getSourceRange();
246 reportInvalidationSite(InvalidationExpr, InvalidatedSubject);
247 S.Diag(Loc: DanglingField->getLocation(),
248 DiagID: diag::note_lifetime_safety_dangling_field_here)
249 << DanglingField->getEndLoc();
250 }
251
252 void reportInvalidatedGlobal(const Expr *IssueExpr,
253 const VarDecl *DanglingGlobal,
254 const Expr *InvalidationExpr) override {
255 std::string InvalidatedSubject = getDiagSubjectDescription(E: IssueExpr);
256 S.Diag(Loc: IssueExpr->getExprLoc(),
257 DiagID: diag::warn_lifetime_safety_invalidated_global)
258 << InvalidatedSubject << getDiagSubjectDescription(VD: DanglingGlobal)
259 << IssueExpr->getSourceRange();
260 reportInvalidationSite(InvalidationExpr, InvalidatedSubject);
261 if (DanglingGlobal->isStaticLocal() || DanglingGlobal->isStaticDataMember())
262 S.Diag(Loc: DanglingGlobal->getLocation(),
263 DiagID: diag::note_lifetime_safety_dangling_static_here)
264 << DanglingGlobal->getEndLoc();
265 else
266 S.Diag(Loc: DanglingGlobal->getLocation(),
267 DiagID: diag::note_lifetime_safety_dangling_global_here)
268 << DanglingGlobal->getEndLoc();
269 }
270
271 void reportInvalidatedGlobal(const ParmVarDecl *PVD,
272 const VarDecl *DanglingGlobal,
273 const Expr *InvalidationExpr) override {
274 std::string InvalidatedSubject = getDiagSubjectDescription(VD: PVD);
275 S.Diag(Loc: PVD->getSourceRange().getBegin(),
276 DiagID: diag::warn_lifetime_safety_invalidated_global)
277 << InvalidatedSubject << getDiagSubjectDescription(VD: DanglingGlobal)
278 << PVD->getSourceRange();
279 reportInvalidationSite(InvalidationExpr, InvalidatedSubject);
280 if (DanglingGlobal->isStaticLocal() || DanglingGlobal->isStaticDataMember())
281 S.Diag(Loc: DanglingGlobal->getLocation(),
282 DiagID: diag::note_lifetime_safety_dangling_static_here)
283 << DanglingGlobal->getEndLoc();
284 else
285 S.Diag(Loc: DanglingGlobal->getLocation(),
286 DiagID: diag::note_lifetime_safety_dangling_global_here)
287 << DanglingGlobal->getEndLoc();
288 }
289
290 void suggestLifetimeboundToParmVar(WarningScope Scope,
291 const ParmVarDecl *ParmToAnnotate,
292 EscapingTarget Target) override {
293 unsigned DiagID;
294 if (isa<CXXConstructorDecl>(Val: ParmToAnnotate->getDeclContext()))
295 DiagID = (Scope == WarningScope::CrossTU)
296 ? diag::warn_lifetime_safety_cross_tu_ctor_param_suggestion
297 : diag::warn_lifetime_safety_intra_tu_ctor_param_suggestion;
298 else
299 DiagID = (Scope == WarningScope::CrossTU)
300 ? diag::warn_lifetime_safety_cross_tu_param_suggestion
301 : diag::warn_lifetime_safety_intra_tu_param_suggestion;
302
303 auto [InsertionPoint, FixItText] = getLifetimeBoundFixIt(Decl: ParmToAnnotate);
304
305 S.Diag(Loc: InsertionPoint, DiagID)
306 << ParmToAnnotate->getSourceRange()
307 << FixItHint::CreateInsertion(InsertionLoc: InsertionPoint, Code: FixItText);
308
309 if (const auto *EscapeExpr = Target.dyn_cast<const Expr *>())
310 S.Diag(Loc: EscapeExpr->getBeginLoc(),
311 DiagID: diag::note_lifetime_safety_suggestion_returned_here)
312 << EscapeExpr->getSourceRange();
313 else if (const auto *EscapeField = Target.dyn_cast<const FieldDecl *>())
314 S.Diag(Loc: EscapeField->getLocation(),
315 DiagID: diag::note_lifetime_safety_escapes_to_field_here)
316 << EscapeField->getSourceRange();
317 }
318
319 void reportLifetimeboundViolation(
320 const ParmVarDecl *ParmWithLifetimebound) override {
321 const auto *Attr = ParmWithLifetimebound->getAttr<LifetimeBoundAttr>();
322 StringRef ParamName = ParmWithLifetimebound->getName();
323 bool HasName = ParamName.size() > 0;
324 S.Diag(Loc: Attr->getLocation(),
325 DiagID: diag::warn_lifetime_safety_lifetimebound_violation)
326 << HasName << ParamName << Attr->getRange();
327 }
328
329 void reportLifetimeboundViolation(
330 const CXXMethodDecl *MDWithLifetimebound) override {
331 const auto *Attr =
332 getImplicitObjectParamLifetimeBoundAttr(FD: MDWithLifetimebound);
333 assert(Attr && "Expected lifetimebound attribute");
334 S.Diag(Loc: Attr->getLocation(),
335 DiagID: diag::warn_lifetime_safety_lifetimebound_violation)
336 << 2 << "" << Attr->getRange();
337 }
338
339 void reportMisplacedLifetimebound(WarningScope Scope,
340 const CXXMethodDecl *FDef,
341 const CXXMethodDecl *FDecl) override {
342 const auto *Attr = getDirectImplicitObjectLifetimeBoundAttr(FD: FDef);
343 assert(Attr && "Expected lifetimebound attribute");
344 unsigned DiagID =
345 Scope == WarningScope::CrossTU
346 ? diag::warn_lifetime_safety_cross_tu_misplaced_lifetimebound
347 : diag::warn_lifetime_safety_intra_tu_misplaced_lifetimebound;
348
349 auto [InsertionPoint, FixItText] = getLifetimeBoundFixIt(MD: FDecl);
350
351 // Do not emit fix-its in macros or at invalid locations.
352 bool IsMacro =
353 FDecl->getBeginLoc().isMacroID() || InsertionPoint.isMacroID();
354
355 if (IsMacro || InsertionPoint.isInvalid())
356 S.Diag(Loc: FDecl->getLocation(), DiagID);
357 else
358 S.Diag(Loc: InsertionPoint, DiagID)
359 << FixItHint::CreateInsertion(InsertionLoc: InsertionPoint, Code: FixItText);
360
361 S.Diag(Loc: Attr->getLocation(), DiagID: diag::note_lifetime_safety_lifetimebound_here)
362 << Attr->getRange();
363 }
364
365 void reportMisplacedLifetimebound(WarningScope Scope,
366 const ParmVarDecl *PVDDef,
367 const ParmVarDecl *PVDDecl) override {
368
369 const auto *Attr = PVDDef->getAttr<LifetimeBoundAttr>();
370 assert(Attr && "Expected lifetimebound attribute");
371 unsigned DiagID =
372 Scope == WarningScope::CrossTU
373 ? diag::warn_lifetime_safety_cross_tu_misplaced_lifetimebound
374 : diag::warn_lifetime_safety_intra_tu_misplaced_lifetimebound;
375
376 auto [InsertionPoint, FixItText] = getLifetimeBoundFixIt(Decl: PVDDecl);
377
378 // Do not emit fix-its in macros or at invalid locations.
379 bool IsMacro =
380 PVDDecl->getBeginLoc().isMacroID() || InsertionPoint.isMacroID();
381
382 if (IsMacro || InsertionPoint.isInvalid())
383 S.Diag(Loc: PVDDecl->getBeginLoc(), DiagID) << PVDDecl->getSourceRange();
384 else
385 S.Diag(Loc: InsertionPoint, DiagID)
386 << PVDDecl->getSourceRange()
387 << FixItHint::CreateInsertion(InsertionLoc: InsertionPoint, Code: FixItText);
388
389 S.Diag(Loc: Attr->getLocation(), DiagID: diag::note_lifetime_safety_lifetimebound_here)
390 << Attr->getRange();
391 }
392
393 void reportInapplicableLifetimebound(const ParmVarDecl *PVD) override {
394 assert(PVD->hasAttr<LifetimeBoundAttr>() &&
395 "Expected parameter to have lifetimebound attribute");
396 const auto *Attr = PVD->getAttr<LifetimeBoundAttr>();
397 S.Diag(Loc: Attr->getLocation(),
398 DiagID: diag::warn_lifetime_safety_inapplicable_lifetimebound)
399 << PVD->getType() << Attr->getRange();
400 }
401
402 void suggestLifetimeboundToImplicitThis(WarningScope Scope,
403 const CXXMethodDecl *MD,
404 const Expr *EscapeExpr) override {
405 unsigned DiagID = (Scope == WarningScope::CrossTU)
406 ? diag::warn_lifetime_safety_cross_tu_this_suggestion
407 : diag::warn_lifetime_safety_intra_tu_this_suggestion;
408
409 auto [InsertionPoint, FixItText] = getLifetimeBoundFixIt(MD);
410
411 S.Diag(Loc: InsertionPoint, DiagID)
412 << MD->getNameInfo().getSourceRange()
413 << FixItHint::CreateInsertion(InsertionLoc: InsertionPoint, Code: FixItText);
414
415 S.Diag(Loc: EscapeExpr->getBeginLoc(),
416 DiagID: diag::note_lifetime_safety_suggestion_returned_here)
417 << EscapeExpr->getSourceRange();
418 }
419
420 void reportNoescapeViolation(const ParmVarDecl *ParmWithNoescape,
421 const Expr *EscapeExpr) override {
422 S.Diag(Loc: ParmWithNoescape->getBeginLoc(),
423 DiagID: diag::warn_lifetime_safety_noescape_escapes)
424 << ParmWithNoescape->getSourceRange();
425
426 S.Diag(Loc: EscapeExpr->getBeginLoc(),
427 DiagID: diag::note_lifetime_safety_suggestion_returned_here)
428 << EscapeExpr->getSourceRange();
429 }
430
431 void reportNoescapeViolation(const ParmVarDecl *ParmWithNoescape,
432 const FieldDecl *EscapeField) override {
433 S.Diag(Loc: ParmWithNoescape->getBeginLoc(),
434 DiagID: diag::warn_lifetime_safety_noescape_escapes)
435 << ParmWithNoescape->getSourceRange();
436
437 S.Diag(Loc: EscapeField->getLocation(),
438 DiagID: diag::note_lifetime_safety_escapes_to_field_here)
439 << EscapeField->getEndLoc();
440 }
441
442 void reportNoescapeViolation(const ParmVarDecl *ParmWithNoescape,
443 const VarDecl *EscapeGlobal) override {
444 S.Diag(Loc: ParmWithNoescape->getBeginLoc(),
445 DiagID: diag::warn_lifetime_safety_noescape_escapes)
446 << ParmWithNoescape->getSourceRange();
447 if (EscapeGlobal->isStaticLocal() || EscapeGlobal->isStaticDataMember())
448 S.Diag(Loc: EscapeGlobal->getLocation(),
449 DiagID: diag::note_lifetime_safety_escapes_to_static_storage_here)
450 << EscapeGlobal->getEndLoc();
451 else
452 S.Diag(Loc: EscapeGlobal->getLocation(),
453 DiagID: diag::note_lifetime_safety_escapes_to_global_here)
454 << EscapeGlobal->getEndLoc();
455 }
456
457 void addLifetimeBoundToImplicitThis(const CXXMethodDecl *MD) override {
458 S.addLifetimeBoundToImplicitThis(MD: const_cast<CXXMethodDecl *>(MD));
459 }
460
461private:
462 struct LifetimeBoundMacroCache {
463 bool IsBuilt = false;
464 SmallVector<const IdentifierInfo *> Candidates;
465 };
466
467 void buildLifetimeBoundMacroCache(LifetimeBoundMacroCache &Cache,
468 ArrayRef<TokenValue> Tokens) {
469 if (Cache.IsBuilt)
470 return;
471
472 const Preprocessor &PP = S.getPreprocessor();
473 // Collect macro names that were ever defined as a lifetimebound attribute.
474 for (const auto &M : PP.macros()) {
475 const IdentifierInfo *II = M.first;
476 const MacroDirective *MD = PP.getLocalMacroDirectiveHistory(II);
477 if (!MD)
478 continue;
479
480 // Include earlier matching definitions to handle redefinitions.
481 for (MacroDirective::DefInfo Def = MD->getDefinition(); Def;
482 Def = Def.getPreviousDefinition()) {
483 const MacroInfo *MI = Def.getMacroInfo();
484 if (MI->isObjectLike() && Tokens.size() == MI->getNumTokens() &&
485 std::equal(first1: Tokens.begin(), last1: Tokens.end(), first2: MI->tokens_begin())) {
486 Cache.Candidates.push_back(Elt: II);
487 break;
488 }
489 }
490 }
491 Cache.IsBuilt = true;
492 }
493
494 StringRef getLastCachedMacroWithSpelling(SourceLocation Loc,
495 llvm::ArrayRef<TokenValue> Tokens,
496 LifetimeBoundMacroCache &Cache) {
497 if (Loc.isInvalid())
498 return {};
499
500 buildLifetimeBoundMacroCache(Cache, Tokens);
501
502 const Preprocessor &PP = S.getPreprocessor();
503 const SourceManager &SM = S.getSourceManager();
504 SourceLocation BestLocation;
505 StringRef BestSpelling;
506 for (const IdentifierInfo *II : Cache.Candidates) {
507 const MacroDirective *MD = PP.getLocalMacroDirectiveHistory(II);
508 const MacroDirective::DefInfo Def = MD->findDirectiveAtLoc(L: Loc, SM);
509 if (!Def || !Def.getMacroInfo())
510 continue;
511
512 // Ensure the macro definition active at Loc still has this spelling.
513 const MacroInfo *MI = Def.getMacroInfo();
514 if (!MI->isObjectLike() || Tokens.size() != MI->getNumTokens() ||
515 !std::equal(first1: Tokens.begin(), last1: Tokens.end(), first2: MI->tokens_begin()))
516 continue;
517
518 // Choose the matching macro defined latest before Loc.
519 SourceLocation Location = Def.getLocation();
520 assert(Location.isInvalid() ||
521 SM.isBeforeInTranslationUnit(Location, Loc));
522 if (BestLocation.isInvalid() ||
523 (Location.isValid() &&
524 SM.isBeforeInTranslationUnit(LHS: BestLocation, RHS: Location))) {
525 BestLocation = Location;
526 BestSpelling = II->getName();
527 }
528 }
529 return BestSpelling;
530 }
531
532 void reportInvalidationSite(const Expr *InvalidationExpr,
533 StringRef InvalidatedSubject) {
534 auto Diag = isa<CXXDeleteExpr>(Val: InvalidationExpr)
535 ? diag::note_lifetime_safety_freed_here
536 : diag::note_lifetime_safety_invalidated_here;
537 S.Diag(Loc: InvalidationExpr->getExprLoc(), DiagID: Diag)
538 << InvalidatedSubject << InvalidationExpr->getSourceRange();
539 }
540
541 std::string getLifetimeBoundFixItText(SourceLocation Loc, bool LeadingSpace,
542 bool AllowGNUAttrMacro = true) {
543 const bool UseCXX11AttrSpelling =
544 S.getLangOpts().CPlusPlus || S.getLangOpts().C23;
545 const StringRef Fallback = UseCXX11AttrSpelling
546 ? "[[clang::lifetimebound]]"
547 : "__attribute__((lifetimebound))";
548 StringRef Spelling = S.getLangOpts().LifetimeSafetyLifetimeBoundMacro;
549 if (Spelling.empty() && Loc.isValid()) {
550 const Preprocessor &PP = S.getPreprocessor();
551 if (UseCXX11AttrSpelling)
552 Spelling = getLastCachedMacroWithSpelling(
553 Loc,
554 Tokens: {tok::l_square, tok::l_square, PP.getIdentifierInfo(Name: "clang"),
555 tok::coloncolon, PP.getIdentifierInfo(Name: "lifetimebound"),
556 tok::r_square, tok::r_square},
557 Cache&: ClangLifetimeBoundMacroCache);
558
559 if (Spelling.empty() && AllowGNUAttrMacro)
560 Spelling = getLastCachedMacroWithSpelling(
561 Loc,
562 Tokens: {tok::kw___attribute, tok::l_paren, tok::l_paren,
563 PP.getIdentifierInfo(Name: "lifetimebound"), tok::r_paren, tok::r_paren},
564 Cache&: GNULifetimeBoundMacroCache);
565 }
566 const std::string Text = Spelling.empty() ? Fallback.str() : Spelling.str();
567 return LeadingSpace ? " " + Text : Text + " ";
568 }
569
570 std::pair<SourceLocation, std::string>
571 getLifetimeBoundFixIt(const ParmVarDecl *Decl) {
572 SourceLocation InsertionPoint = Lexer::getLocForEndOfToken(
573 Loc: Decl->getEndLoc(), Offset: 0, SM: S.getSourceManager(), LangOpts: S.getLangOpts());
574 bool LeadingSpace = true;
575
576 if (!Decl->getIdentifier()) {
577 // For unnamed parameters, placing attributes after the type would be
578 // parsed as a type attribute, not a parameter attribute.
579 InsertionPoint = Decl->getBeginLoc();
580 LeadingSpace = false;
581 } else if (Decl->hasDefaultArg()) {
582 // If the parameter has a default argument, place the attribute after the
583 // named argument.
584 InsertionPoint = Lexer::getLocForEndOfToken(
585 Loc: Decl->getLocation(), Offset: 0, SM: S.getSourceManager(), LangOpts: S.getLangOpts());
586 }
587 return {InsertionPoint,
588 getLifetimeBoundFixItText(Loc: InsertionPoint, LeadingSpace)};
589 }
590
591 std::pair<SourceLocation, std::string>
592 getLifetimeBoundFixIt(const CXXMethodDecl *MD) {
593 const auto MDL = MD->getTypeSourceInfo()->getTypeLoc();
594 SourceLocation InsertionPoint = Lexer::getLocForEndOfToken(
595 Loc: MDL.getEndLoc(), Offset: 0, SM: S.getSourceManager(), LangOpts: S.getLangOpts());
596
597 if (const auto *FPT = MD->getType()->getAs<FunctionProtoType>();
598 FPT && FPT->hasTrailingReturn()) {
599 // For trailing return types, 'getEndLoc()' includes the return type
600 // after '->', placing the attribute in an invalid position.
601 // Instead use 'getLocalRangeEnd()' which gives the '->' location
602 // for trailing returns, so find the last token before it.
603 const auto FTL = MDL.getAs<FunctionTypeLoc>();
604 assert(FTL);
605 InsertionPoint = Lexer::getLocForEndOfToken(
606 Loc: Lexer::findPreviousToken(Loc: FTL.getLocalRangeEnd(), SM: S.getSourceManager(),
607 LangOpts: S.getLangOpts(),
608 /*IncludeComments=*/IncludeComments: false)
609 ->getLocation(),
610 Offset: 0, SM: S.getSourceManager(), LangOpts: S.getLangOpts());
611 }
612 return {InsertionPoint,
613 getLifetimeBoundFixItText(Loc: InsertionPoint, /*LeadingSpace=*/LeadingSpace: true,
614 /*AllowGNUAttrMacro=*/AllowGNUAttrMacro: false)};
615 }
616
617 std::string getDiagSubjectDescription(const ValueDecl *VD) {
618 std::string Res;
619 llvm::raw_string_ostream OS(Res);
620 if (isa<FieldDecl>(Val: VD)) {
621 OS << "field";
622 } else if (isa<ParmVarDecl>(Val: VD)) {
623 OS << "parameter";
624 } else if (const auto *Var = dyn_cast<VarDecl>(Val: VD)) {
625 if (Var->isStaticLocal() || Var->isStaticDataMember())
626 OS << "static variable";
627 else if (Var->hasGlobalStorage())
628 OS << "global variable";
629 else
630 OS << "local variable";
631 } else {
632 OS << "variable";
633 }
634 OS << " '";
635 VD->getNameForDiagnostic(OS, Policy: S.getPrintingPolicy(), /*Qualified=*/Qualified: false);
636 OS << "'";
637 return Res;
638 }
639
640 std::string getDiagSubjectDescription(const Expr *E) {
641 E = E->IgnoreImpCasts();
642 if (isa<MaterializeTemporaryExpr>(Val: E))
643 return "temporary object";
644 if (isa<CXXNewExpr>(Val: E))
645 return "allocated object";
646 if (const auto *DRE = dyn_cast<DeclRefExpr>(Val: E))
647 return getDiagSubjectDescription(VD: DRE->getDecl());
648
649 if (const auto *CE = dyn_cast<CallExpr>(Val: E)) {
650 const auto *FD = CE->getDirectCallee();
651 if (!FD)
652 return "result of call";
653 if (FD->isOverloadedOperator() || isa<CXXConversionDecl>(Val: FD))
654 return "expression";
655 std::string Name;
656 llvm::raw_string_ostream OS(Name);
657 FD->getNameForDiagnostic(OS, Policy: S.getPrintingPolicy(),
658 /*Qualified=*/Qualified: false);
659 return "result of call to '" + Name + "'";
660 }
661
662 // TODO: Handle other expression types.
663 return "expression";
664 }
665
666 bool shouldShowInAliasChain(const Expr *CurrExpr, const Expr *LastExpr) {
667 CurrExpr = CurrExpr->IgnoreImpCasts();
668 LastExpr = LastExpr->IgnoreImpCasts();
669
670 if (!isa<CallExpr, DeclRefExpr>(Val: CurrExpr))
671 return false;
672 // Source ranges can be used to filter out many implicit expressions,
673 // because operations between class objects often involve numerous implicit
674 // conversions, yet they share the same source range.
675 return CurrExpr->getSourceRange() != LastExpr->getSourceRange();
676 }
677
678 void reportAliasingChain(llvm::ArrayRef<const Expr *> OriginExprChain) {
679 if (OriginExprChain.empty())
680 return;
681
682 const Expr *LastExpr = OriginExprChain.back();
683 std::string IssueStr = getDiagSubjectDescription(E: LastExpr);
684
685 for (const Expr *CurrExpr : reverse(C: OriginExprChain.drop_back())) {
686 if (!shouldShowInAliasChain(CurrExpr, LastExpr))
687 continue;
688 S.Diag(Loc: CurrExpr->getBeginLoc(),
689 DiagID: diag::note_lifetime_safety_aliases_storage)
690 << CurrExpr->getSourceRange() << getDiagSubjectDescription(E: CurrExpr)
691 << IssueStr;
692 LastExpr = CurrExpr;
693 }
694 }
695
696 LifetimeBoundMacroCache ClangLifetimeBoundMacroCache;
697 LifetimeBoundMacroCache GNULifetimeBoundMacroCache;
698 Sema &S;
699};
700
701} // namespace clang::lifetimes
702
703#endif // LLVM_CLANG_LIB_SEMA_SEMALIFETIMESAFETY_H
704