clang 24.0.0git
SemaLifetimeSafety.h
Go to the documentation of this file.
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
21#include "clang/Lex/Lexer.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)
41
42 // Per-function mode: analysis runs on each function/method individually.
43 // Skip TU-level calls when per-function mode is enabled.
45 return false;
46
47 // Enable per-function mode via debug flag or specific diagnostics.
48 if (S.getLangOpts().DebugRunLifetimeSafety)
49 return true;
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, D->getBeginLoc()))
77 return true;
78 return false;
79}
80
81inline bool ShouldSuggestLifetimeAnnotations(Sema &S, const Decl *D) {
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, D->getBeginLoc()))
92 return true;
93 return false;
94}
95
97 LifetimeSafetyOpts LSOpts;
98 LSOpts.MaxCFGBlocks = S.getLangOpts().LifetimeSafetyMaxCFGBlocks;
100 return LSOpts;
101}
102
104
105public:
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(IssueExpr);
115
116 S.Diag(IssueExpr->getExprLoc(), DiagID)
117 << DestroyedSubject << IssueExpr->getSourceRange();
118 if (MovedExpr)
119 S.Diag(MovedExpr->getExprLoc(), diag::note_lifetime_safety_moved_here)
120 << MovedExpr->getSourceRange();
121 S.Diag(FreeLoc, diag::note_lifetime_safety_destroyed_here)
122 << DestroyedSubject;
123
124 reportAliasingChain(ExprChain);
125
126 S.Diag(UseExpr->getExprLoc(), 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(IssueExpr->getExprLoc(), DiagID)
137 << getDiagSubjectDescription(IssueExpr) << IssueExpr->getSourceRange();
138
139 if (MovedExpr)
140 S.Diag(MovedExpr->getExprLoc(), diag::note_lifetime_safety_moved_here)
141 << MovedExpr->getSourceRange();
142 S.Diag(ReturnExpr->getExprLoc(), 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, bool IsCapturedByLambda,
149 SourceLocation ExpiryLoc) override {
150 unsigned DiagID =
151 IsCapturedByLambda
152 ? diag::warn_lifetime_safety_dangling_field_lambda_capture
153 : (MovedExpr ? diag::warn_lifetime_safety_dangling_field_moved
154 : diag::warn_lifetime_safety_dangling_field);
155
156 S.Diag(IssueExpr->getExprLoc(), DiagID)
157 << getDiagSubjectDescription(IssueExpr)
158 << getDiagSubjectDescription(DanglingField)
159 << IssueExpr->getSourceRange();
160 if (MovedExpr)
161 S.Diag(MovedExpr->getExprLoc(), diag::note_lifetime_safety_moved_here)
162 << MovedExpr->getSourceRange();
163 S.Diag(DanglingField->getLocation(),
164 diag::note_lifetime_safety_dangling_field_here)
165 << DanglingField->getEndLoc();
166 }
167
168 void reportDanglingGlobal(const Expr *IssueExpr,
169 const VarDecl *DanglingGlobal,
170 const Expr *MovedExpr,
171 SourceLocation ExpiryLoc) override {
172 unsigned DiagID = MovedExpr
173 ? diag::warn_lifetime_safety_dangling_global_moved
174 : diag::warn_lifetime_safety_dangling_global;
175
176 S.Diag(IssueExpr->getExprLoc(), DiagID)
177 << getDiagSubjectDescription(IssueExpr)
178 << getDiagSubjectDescription(DanglingGlobal)
179 << IssueExpr->getSourceRange();
180 if (MovedExpr)
181 S.Diag(MovedExpr->getExprLoc(), diag::note_lifetime_safety_moved_here)
182 << MovedExpr->getSourceRange();
183 if (DanglingGlobal->isStaticLocal() || DanglingGlobal->isStaticDataMember())
184 S.Diag(DanglingGlobal->getLocation(),
185 diag::note_lifetime_safety_dangling_static_here)
186 << DanglingGlobal->getEndLoc();
187 else
188 S.Diag(DanglingGlobal->getLocation(),
189 diag::note_lifetime_safety_dangling_global_here)
190 << DanglingGlobal->getEndLoc();
191 }
192
193 void
194 reportUseAfterInvalidation(const Expr *IssueExpr, const Expr *UseExpr,
195 const Expr *InvalidationExpr,
196 llvm::ArrayRef<const Expr *> ExprChain) override {
197 auto WarnDiag = isa<CXXDeleteExpr>(InvalidationExpr)
198 ? diag::warn_lifetime_safety_use_after_free
199 : diag::warn_lifetime_safety_invalidation;
200 std::string InvalidatedSubject = getDiagSubjectDescription(IssueExpr);
201 S.Diag(IssueExpr->getExprLoc(), WarnDiag)
202 << InvalidatedSubject << IssueExpr->getSourceRange();
203 reportInvalidationSite(InvalidationExpr, InvalidatedSubject);
204 reportAliasingChain(ExprChain);
205 S.Diag(UseExpr->getExprLoc(), diag::note_lifetime_safety_used_here)
206 << UseExpr->getSourceRange();
207 }
208 void
209 reportUseAfterInvalidation(const ParmVarDecl *PVD, const Expr *UseExpr,
210 const Expr *InvalidationExpr,
211 llvm::ArrayRef<const Expr *> ExprChain) override {
212
213 auto WarnDiag = isa<CXXDeleteExpr>(InvalidationExpr)
214 ? diag::warn_lifetime_safety_use_after_free
215 : diag::warn_lifetime_safety_invalidation;
216 std::string InvalidatedSubject = getDiagSubjectDescription(PVD);
217
218 S.Diag(PVD->getSourceRange().getBegin(), WarnDiag)
219 << InvalidatedSubject << PVD->getSourceRange();
220 reportInvalidationSite(InvalidationExpr, InvalidatedSubject);
221 reportAliasingChain(ExprChain);
222 S.Diag(UseExpr->getExprLoc(), diag::note_lifetime_safety_used_here)
223 << UseExpr->getSourceRange();
224 }
225
226 void reportInvalidatedField(const Expr *IssueExpr,
227 const FieldDecl *DanglingField,
228 const Expr *InvalidationExpr) override {
229 std::string InvalidatedSubject = getDiagSubjectDescription(IssueExpr);
230 S.Diag(IssueExpr->getExprLoc(),
231 diag::warn_lifetime_safety_invalidated_field)
232 << InvalidatedSubject << getDiagSubjectDescription(DanglingField)
233 << IssueExpr->getSourceRange();
234 reportInvalidationSite(InvalidationExpr, InvalidatedSubject);
235 S.Diag(DanglingField->getLocation(),
236 diag::note_lifetime_safety_dangling_field_here)
237 << DanglingField->getEndLoc();
238 }
239
241 const FieldDecl *DanglingField,
242 const Expr *InvalidationExpr) override {
243 std::string InvalidatedSubject = getDiagSubjectDescription(PVD);
244 S.Diag(PVD->getSourceRange().getBegin(),
245 diag::warn_lifetime_safety_invalidated_field)
246 << InvalidatedSubject << getDiagSubjectDescription(DanglingField)
247 << PVD->getSourceRange();
248 reportInvalidationSite(InvalidationExpr, InvalidatedSubject);
249 S.Diag(DanglingField->getLocation(),
250 diag::note_lifetime_safety_dangling_field_here)
251 << DanglingField->getEndLoc();
252 }
253
254 void reportInvalidatedGlobal(const Expr *IssueExpr,
255 const VarDecl *DanglingGlobal,
256 const Expr *InvalidationExpr) override {
257 std::string InvalidatedSubject = getDiagSubjectDescription(IssueExpr);
258 S.Diag(IssueExpr->getExprLoc(),
259 diag::warn_lifetime_safety_invalidated_global)
260 << InvalidatedSubject << getDiagSubjectDescription(DanglingGlobal)
261 << IssueExpr->getSourceRange();
262 reportInvalidationSite(InvalidationExpr, InvalidatedSubject);
263 if (DanglingGlobal->isStaticLocal() || DanglingGlobal->isStaticDataMember())
264 S.Diag(DanglingGlobal->getLocation(),
265 diag::note_lifetime_safety_dangling_static_here)
266 << DanglingGlobal->getEndLoc();
267 else
268 S.Diag(DanglingGlobal->getLocation(),
269 diag::note_lifetime_safety_dangling_global_here)
270 << DanglingGlobal->getEndLoc();
271 }
272
274 const VarDecl *DanglingGlobal,
275 const Expr *InvalidationExpr) override {
276 std::string InvalidatedSubject = getDiagSubjectDescription(PVD);
277 S.Diag(PVD->getSourceRange().getBegin(),
278 diag::warn_lifetime_safety_invalidated_global)
279 << InvalidatedSubject << getDiagSubjectDescription(DanglingGlobal)
280 << PVD->getSourceRange();
281 reportInvalidationSite(InvalidationExpr, InvalidatedSubject);
282 if (DanglingGlobal->isStaticLocal() || DanglingGlobal->isStaticDataMember())
283 S.Diag(DanglingGlobal->getLocation(),
284 diag::note_lifetime_safety_dangling_static_here)
285 << DanglingGlobal->getEndLoc();
286 else
287 S.Diag(DanglingGlobal->getLocation(),
288 diag::note_lifetime_safety_dangling_global_here)
289 << DanglingGlobal->getEndLoc();
290 }
291
293 const ParmVarDecl *ParmToAnnotate,
294 EscapingTarget Target) override {
295 unsigned DiagID;
296 if (isa<CXXConstructorDecl>(ParmToAnnotate->getDeclContext()))
297 DiagID = (Scope == WarningScope::CrossTU)
298 ? diag::warn_lifetime_safety_cross_tu_ctor_param_suggestion
299 : diag::warn_lifetime_safety_intra_tu_ctor_param_suggestion;
300 else
301 DiagID = (Scope == WarningScope::CrossTU)
302 ? diag::warn_lifetime_safety_cross_tu_param_suggestion
303 : diag::warn_lifetime_safety_intra_tu_param_suggestion;
304
305 auto [InsertionPoint, FixItText] = getLifetimeBoundFixIt(ParmToAnnotate);
306
307 S.Diag(InsertionPoint, DiagID)
308 << ParmToAnnotate->getSourceRange()
309 << FixItHint::CreateInsertion(InsertionPoint, FixItText);
310
311 if (const auto *EscapeExpr = Target.dyn_cast<const Expr *>())
312 S.Diag(EscapeExpr->getBeginLoc(),
313 diag::note_lifetime_safety_suggestion_returned_here)
314 << EscapeExpr->getSourceRange();
315 else if (const auto *EscapeField = Target.dyn_cast<const FieldDecl *>())
316 S.Diag(EscapeField->getLocation(),
317 diag::note_lifetime_safety_escapes_to_field_here)
318 << EscapeField->getSourceRange();
319 }
320
322 const ParmVarDecl *ParmWithLifetimebound) override {
323 const auto *Attr = ParmWithLifetimebound->getAttr<LifetimeBoundAttr>();
324 StringRef ParamName = ParmWithLifetimebound->getName();
325 bool HasName = ParamName.size() > 0;
326 S.Diag(Attr->getLocation(),
327 diag::warn_lifetime_safety_lifetimebound_violation)
328 << HasName << ParamName << Attr->getRange();
329 }
330
332 const CXXMethodDecl *MDWithLifetimebound) override {
333 const auto *Attr =
334 getImplicitObjectParamLifetimeBoundAttr(MDWithLifetimebound);
335 assert(Attr && "Expected lifetimebound attribute");
336 S.Diag(Attr->getLocation(),
337 diag::warn_lifetime_safety_lifetimebound_violation)
338 << 2 << "" << Attr->getRange();
339 }
340
342 const CXXMethodDecl *FDef,
343 const CXXMethodDecl *FDecl) override {
345 assert(Attr && "Expected lifetimebound attribute");
346 unsigned DiagID =
348 ? diag::warn_lifetime_safety_cross_tu_misplaced_lifetimebound
349 : diag::warn_lifetime_safety_intra_tu_misplaced_lifetimebound;
350
351 auto [InsertionPoint, FixItText] = getLifetimeBoundFixIt(FDecl);
352
353 // Do not emit fix-its in macros or at invalid locations.
354 bool IsMacro =
355 FDecl->getBeginLoc().isMacroID() || InsertionPoint.isMacroID();
356
357 if (IsMacro || InsertionPoint.isInvalid())
358 S.Diag(FDecl->getLocation(), DiagID);
359 else
360 S.Diag(InsertionPoint, DiagID)
361 << FixItHint::CreateInsertion(InsertionPoint, FixItText);
362
363 S.Diag(Attr->getLocation(), diag::note_lifetime_safety_lifetimebound_here)
364 << Attr->getRange();
365 }
366
368 const ParmVarDecl *PVDDef,
369 const ParmVarDecl *PVDDecl) override {
370
371 const auto *Attr = PVDDef->getAttr<LifetimeBoundAttr>();
372 assert(Attr && "Expected lifetimebound attribute");
373 unsigned DiagID =
375 ? diag::warn_lifetime_safety_cross_tu_misplaced_lifetimebound
376 : diag::warn_lifetime_safety_intra_tu_misplaced_lifetimebound;
377
378 auto [InsertionPoint, FixItText] = getLifetimeBoundFixIt(PVDDecl);
379
380 // Do not emit fix-its in macros or at invalid locations.
381 bool IsMacro =
382 PVDDecl->getBeginLoc().isMacroID() || InsertionPoint.isMacroID();
383
384 if (IsMacro || InsertionPoint.isInvalid())
385 S.Diag(PVDDecl->getBeginLoc(), DiagID) << PVDDecl->getSourceRange();
386 else
387 S.Diag(InsertionPoint, DiagID)
388 << PVDDecl->getSourceRange()
389 << FixItHint::CreateInsertion(InsertionPoint, FixItText);
390
391 S.Diag(Attr->getLocation(), diag::note_lifetime_safety_lifetimebound_here)
392 << Attr->getRange();
393 }
394
396 assert(PVD->hasAttr<LifetimeBoundAttr>() &&
397 "Expected parameter to have lifetimebound attribute");
398 const auto *Attr = PVD->getAttr<LifetimeBoundAttr>();
399 S.Diag(Attr->getLocation(),
400 diag::warn_lifetime_safety_inapplicable_lifetimebound)
401 << PVD->getType() << Attr->getRange();
402 }
403
405 const CXXMethodDecl *MD,
406 const Expr *EscapeExpr) override {
407 unsigned DiagID = (Scope == WarningScope::CrossTU)
408 ? diag::warn_lifetime_safety_cross_tu_this_suggestion
409 : diag::warn_lifetime_safety_intra_tu_this_suggestion;
410
411 auto [InsertionPoint, FixItText] = getLifetimeBoundFixIt(MD);
412
413 S.Diag(InsertionPoint, DiagID)
414 << MD->getNameInfo().getSourceRange()
415 << FixItHint::CreateInsertion(InsertionPoint, FixItText);
416
417 S.Diag(EscapeExpr->getBeginLoc(),
418 diag::note_lifetime_safety_suggestion_returned_here)
419 << EscapeExpr->getSourceRange();
420 }
421
422 void reportNoescapeViolation(const ParmVarDecl *ParmWithNoescape,
423 const Expr *EscapeExpr) override {
424 S.Diag(ParmWithNoescape->getBeginLoc(),
425 diag::warn_lifetime_safety_noescape_escapes)
426 << ParmWithNoescape->getSourceRange();
427
428 S.Diag(EscapeExpr->getBeginLoc(),
429 diag::note_lifetime_safety_suggestion_returned_here)
430 << EscapeExpr->getSourceRange();
431 }
432
433 void reportNoescapeViolation(const ParmVarDecl *ParmWithNoescape,
434 const FieldDecl *EscapeField) override {
435 S.Diag(ParmWithNoescape->getBeginLoc(),
436 diag::warn_lifetime_safety_noescape_escapes)
437 << ParmWithNoescape->getSourceRange();
438
439 S.Diag(EscapeField->getLocation(),
440 diag::note_lifetime_safety_escapes_to_field_here)
441 << EscapeField->getEndLoc();
442 }
443
444 void reportNoescapeViolation(const ParmVarDecl *ParmWithNoescape,
445 const VarDecl *EscapeGlobal) override {
446 S.Diag(ParmWithNoescape->getBeginLoc(),
447 diag::warn_lifetime_safety_noescape_escapes)
448 << ParmWithNoescape->getSourceRange();
449 if (EscapeGlobal->isStaticLocal() || EscapeGlobal->isStaticDataMember())
450 S.Diag(EscapeGlobal->getLocation(),
451 diag::note_lifetime_safety_escapes_to_static_storage_here)
452 << EscapeGlobal->getEndLoc();
453 else
454 S.Diag(EscapeGlobal->getLocation(),
455 diag::note_lifetime_safety_escapes_to_global_here)
456 << EscapeGlobal->getEndLoc();
457 }
458
460 S.addLifetimeBoundToImplicitThis(const_cast<CXXMethodDecl *>(MD));
461 }
462
463private:
464 struct LifetimeBoundMacroCache {
465 bool IsBuilt = false;
467 };
468
469 void buildLifetimeBoundMacroCache(LifetimeBoundMacroCache &Cache,
470 ArrayRef<TokenValue> Tokens) {
471 if (Cache.IsBuilt)
472 return;
473
474 const Preprocessor &PP = S.getPreprocessor();
475 // Collect macro names that were ever defined as a lifetimebound attribute.
476 for (const auto &M : PP.macros()) {
477 const IdentifierInfo *II = M.first;
479 if (!MD)
480 continue;
481
482 // Include earlier matching definitions to handle redefinitions.
483 for (MacroDirective::DefInfo Def = MD->getDefinition(); Def;
484 Def = Def.getPreviousDefinition()) {
485 const MacroInfo *MI = Def.getMacroInfo();
486 if (MI->isObjectLike() && Tokens.size() == MI->getNumTokens() &&
487 std::equal(Tokens.begin(), Tokens.end(), MI->tokens_begin())) {
488 Cache.Candidates.push_back(II);
489 break;
490 }
491 }
492 }
493 Cache.IsBuilt = true;
494 }
495
496 StringRef getLastCachedMacroWithSpelling(SourceLocation Loc,
497 llvm::ArrayRef<TokenValue> Tokens,
498 LifetimeBoundMacroCache &Cache) {
499 if (Loc.isInvalid())
500 return {};
501
502 buildLifetimeBoundMacroCache(Cache, Tokens);
503
504 const Preprocessor &PP = S.getPreprocessor();
505 const SourceManager &SM = S.getSourceManager();
506 SourceLocation BestLocation;
507 StringRef BestSpelling;
508 for (const IdentifierInfo *II : Cache.Candidates) {
509 const MacroDirective *MD = PP.getLocalMacroDirectiveHistory(II);
510 const MacroDirective::DefInfo Def = MD->findDirectiveAtLoc(Loc, SM);
511 if (!Def || !Def.getMacroInfo())
512 continue;
513
514 // Ensure the macro definition active at Loc still has this spelling.
515 const MacroInfo *MI = Def.getMacroInfo();
516 if (!MI->isObjectLike() || Tokens.size() != MI->getNumTokens() ||
517 !std::equal(Tokens.begin(), Tokens.end(), MI->tokens_begin()))
518 continue;
519
520 // Choose the matching macro defined latest before Loc.
521 SourceLocation Location = Def.getLocation();
522 assert(Location.isInvalid() ||
523 SM.isBeforeInTranslationUnit(Location, Loc));
524 if (BestLocation.isInvalid() ||
525 (Location.isValid() &&
526 SM.isBeforeInTranslationUnit(BestLocation, Location))) {
527 BestLocation = Location;
528 BestSpelling = II->getName();
529 }
530 }
531 return BestSpelling;
532 }
533
534 void reportInvalidationSite(const Expr *InvalidationExpr,
535 StringRef InvalidatedSubject) {
536 auto Diag = isa<CXXDeleteExpr>(InvalidationExpr)
537 ? diag::note_lifetime_safety_freed_here
538 : diag::note_lifetime_safety_invalidated_here;
539 S.Diag(InvalidationExpr->getExprLoc(), Diag)
540 << InvalidatedSubject << InvalidationExpr->getSourceRange();
541 }
542
543 std::string getLifetimeBoundFixItText(SourceLocation Loc, bool LeadingSpace,
544 bool AllowGNUAttrMacro = true) {
545 const bool UseCXX11AttrSpelling =
546 S.getLangOpts().CPlusPlus || S.getLangOpts().C23;
547 const StringRef Fallback = UseCXX11AttrSpelling
548 ? "[[clang::lifetimebound]]"
549 : "__attribute__((lifetimebound))";
550 StringRef Spelling = S.getLangOpts().LifetimeSafetyLifetimeBoundMacro;
551 if (Spelling.empty() && Loc.isValid()) {
552 const Preprocessor &PP = S.getPreprocessor();
553 if (UseCXX11AttrSpelling)
554 Spelling = getLastCachedMacroWithSpelling(
555 Loc,
556 {tok::l_square, tok::l_square, PP.getIdentifierInfo("clang"),
557 tok::coloncolon, PP.getIdentifierInfo("lifetimebound"),
558 tok::r_square, tok::r_square},
559 ClangLifetimeBoundMacroCache);
560
561 if (Spelling.empty() && AllowGNUAttrMacro)
562 Spelling = getLastCachedMacroWithSpelling(
563 Loc,
564 {tok::kw___attribute, tok::l_paren, tok::l_paren,
565 PP.getIdentifierInfo("lifetimebound"), tok::r_paren, tok::r_paren},
566 GNULifetimeBoundMacroCache);
567 }
568 const std::string Text = Spelling.empty() ? Fallback.str() : Spelling.str();
569 return LeadingSpace ? " " + Text : Text + " ";
570 }
571
572 std::pair<SourceLocation, std::string>
573 getLifetimeBoundFixIt(const ParmVarDecl *Decl) {
574 SourceLocation InsertionPoint = Lexer::getLocForEndOfToken(
575 Decl->getEndLoc(), 0, S.getSourceManager(), S.getLangOpts());
576 bool LeadingSpace = true;
577
578 if (!Decl->getIdentifier()) {
579 // For unnamed parameters, placing attributes after the type would be
580 // parsed as a type attribute, not a parameter attribute.
581 InsertionPoint = Decl->getBeginLoc();
582 LeadingSpace = false;
583 } else if (Decl->hasDefaultArg()) {
584 // If the parameter has a default argument, place the attribute after the
585 // named argument.
586 InsertionPoint = Lexer::getLocForEndOfToken(
587 Decl->getLocation(), 0, S.getSourceManager(), S.getLangOpts());
588 }
589 return {InsertionPoint,
590 getLifetimeBoundFixItText(InsertionPoint, LeadingSpace)};
591 }
592
593 std::pair<SourceLocation, std::string>
594 getLifetimeBoundFixIt(const CXXMethodDecl *MD) {
595 const auto MDL = MD->getTypeSourceInfo()->getTypeLoc();
596 SourceLocation InsertionPoint = Lexer::getLocForEndOfToken(
597 MDL.getEndLoc(), 0, S.getSourceManager(), S.getLangOpts());
598
599 if (const auto *FPT = MD->getType()->getAs<FunctionProtoType>();
600 FPT && FPT->hasTrailingReturn()) {
601 // For trailing return types, 'getEndLoc()' includes the return type
602 // after '->', placing the attribute in an invalid position.
603 // Instead use 'getLocalRangeEnd()' which gives the '->' location
604 // for trailing returns, so find the last token before it.
605 const auto FTL = MDL.getAs<FunctionTypeLoc>();
606 assert(FTL);
607 InsertionPoint = Lexer::getLocForEndOfToken(
608 Lexer::findPreviousToken(FTL.getLocalRangeEnd(), S.getSourceManager(),
609 S.getLangOpts(),
610 /*IncludeComments=*/false)
611 ->getLocation(),
612 0, S.getSourceManager(), S.getLangOpts());
613 }
614 return {InsertionPoint,
615 getLifetimeBoundFixItText(InsertionPoint, /*LeadingSpace=*/true,
616 /*AllowGNUAttrMacro=*/false)};
617 }
618
619 std::string getDiagSubjectDescription(const ValueDecl *VD) {
620 std::string Res;
621 llvm::raw_string_ostream OS(Res);
622 if (isa<FieldDecl>(VD)) {
623 OS << "field";
624 } else if (isa<ParmVarDecl>(VD)) {
625 OS << "parameter";
626 } else if (const auto *Var = dyn_cast<VarDecl>(VD)) {
627 if (Var->isStaticLocal() || Var->isStaticDataMember())
628 OS << "static variable";
629 else if (Var->hasGlobalStorage())
630 OS << "global variable";
631 else
632 OS << "local variable";
633 } else {
634 OS << "variable";
635 }
636 OS << " '";
637 VD->getNameForDiagnostic(OS, S.getPrintingPolicy(), /*Qualified=*/false);
638 OS << "'";
639 return Res;
640 }
641
642 std::string getDiagSubjectDescription(const Expr *E) {
643 E = E->IgnoreImpCasts();
645 return "temporary object";
646 if (isa<CXXNewExpr>(E))
647 return "allocated object";
648 if (const auto *DRE = dyn_cast<DeclRefExpr>(E))
649 return getDiagSubjectDescription(DRE->getDecl());
650
651 if (const auto *CE = dyn_cast<CallExpr>(E)) {
652 const auto *FD = CE->getDirectCallee();
653 if (!FD)
654 return "result of call";
655 if (FD->isOverloadedOperator() || isa<CXXConversionDecl>(FD))
656 return "expression";
657 std::string Name;
658 llvm::raw_string_ostream OS(Name);
659 FD->getNameForDiagnostic(OS, S.getPrintingPolicy(),
660 /*Qualified=*/false);
661 return "result of call to '" + Name + "'";
662 }
663
664 // TODO: Handle other expression types.
665 return "expression";
666 }
667
668 bool shouldShowInAliasChain(const Expr *CurrExpr, const Expr *LastExpr) {
669 CurrExpr = CurrExpr->IgnoreImpCasts();
670 LastExpr = LastExpr->IgnoreImpCasts();
671
672 if (!isa<CallExpr, DeclRefExpr>(CurrExpr))
673 return false;
674 // Source ranges can be used to filter out many implicit expressions,
675 // because operations between class objects often involve numerous implicit
676 // conversions, yet they share the same source range.
677 return CurrExpr->getSourceRange() != LastExpr->getSourceRange();
678 }
679
680 void reportAliasingChain(llvm::ArrayRef<const Expr *> OriginExprChain) {
681 if (OriginExprChain.empty())
682 return;
683
684 const Expr *LastExpr = OriginExprChain.back();
685 std::string IssueStr = getDiagSubjectDescription(LastExpr);
686
687 for (const Expr *CurrExpr : reverse(OriginExprChain.drop_back())) {
688 if (!shouldShowInAliasChain(CurrExpr, LastExpr))
689 continue;
690 S.Diag(CurrExpr->getBeginLoc(),
691 diag::note_lifetime_safety_aliases_storage)
692 << CurrExpr->getSourceRange() << getDiagSubjectDescription(CurrExpr)
693 << IssueStr;
694 LastExpr = CurrExpr;
695 }
696 }
697
698 LifetimeBoundMacroCache ClangLifetimeBoundMacroCache;
699 LifetimeBoundMacroCache GNULifetimeBoundMacroCache;
700 Sema &S;
701};
702
703} // namespace clang::lifetimes
704
705#endif // LLVM_CLANG_LIB_SEMA_SEMALIFETIMESAFETY_H
static DiagnosticBuilder Diag(DiagnosticsEngine *Diags, const LangOptions &Features, FullSourceLoc TokLoc, const char *TokBegin, const char *TokRangeBegin, const char *TokRangeEnd, unsigned DiagID)
Produce a diagnostic highlighting some portion of a literal.
Defines the clang::Preprocessor interface.
Attr - This represents one attribute.
Definition Attr.h:46
SourceLocation getLocation() const
Definition Attr.h:99
Represents a static or instance method of a struct/union/class.
Definition DeclCXX.h:2145
Decl - This represents one declaration (or definition), e.g.
Definition DeclBase.h:86
SourceLocation getEndLoc() const LLVM_READONLY
Definition DeclBase.h:443
T * getAttr() const
Definition DeclBase.h:581
SourceLocation getLocation() const
Definition DeclBase.h:447
DeclContext * getDeclContext()
Definition DeclBase.h:456
SourceLocation getBeginLoc() const LLVM_READONLY
Definition DeclBase.h:439
bool hasAttr() const
Definition DeclBase.h:585
SourceLocation getBeginLoc() const LLVM_READONLY
Definition Decl.h:831
Concrete class used by the front-end to report problems and issues.
Definition Diagnostic.h:234
bool isIgnored(unsigned DiagID, SourceLocation Loc) const
Determine whether the diagnostic is known to be ignored.
Definition Diagnostic.h:972
This represents one expression.
Definition Expr.h:112
SourceLocation getExprLoc() const LLVM_READONLY
getExprLoc - Return the preferred location for the arrow when diagnosing a problem with a generic exp...
Definition Expr.cpp:283
Represents a member of a struct/union/class.
Definition Decl.h:3204
static FixItHint CreateInsertion(SourceLocation InsertionLoc, StringRef Code, bool BeforePreviousInsertions=false)
Create a code modification hint that inserts the given code string at a specific location.
Definition Diagnostic.h:105
DeclarationNameInfo getNameInfo() const
Definition Decl.h:2247
One of these records is kept for each identifier that is lexed.
static std::optional< Token > findPreviousToken(SourceLocation Loc, const SourceManager &SM, const LangOptions &LangOpts, bool IncludeComments)
Finds the token that comes before the given location.
Definition Lexer.cpp:1407
static SourceLocation getLocForEndOfToken(SourceLocation Loc, unsigned Offset, const SourceManager &SM, const LangOptions &LangOpts)
Computes the source location just past the end of the token at this source location.
Definition Lexer.cpp:882
Encapsulates changes to the "macros namespace" (the location where the macro name became active,...
Definition MacroInfo.h:314
const DefInfo findDirectiveAtLoc(SourceLocation L, const SourceManager &SM) const
Find macro definition active in the specified source location.
DefInfo getDefinition()
Traverses the macro directives history and returns the next macro definition directive along with inf...
Encapsulates the data about a macro definition (e.g.
Definition MacroInfo.h:40
const_tokens_iterator tokens_begin() const
Definition MacroInfo.h:245
unsigned getNumTokens() const
Return the number of tokens that this macro expands to.
Definition MacroInfo.h:236
bool isObjectLike() const
Definition MacroInfo.h:203
StringRef getName() const
Get the name of identifier for this declaration as a StringRef.
Definition Decl.h:301
Represents a parameter to a function.
Definition Decl.h:1819
SourceRange getSourceRange() const override LLVM_READONLY
Source range that this declaration covers.
Definition Decl.cpp:2959
Engages in a tight little dance with the lexer to efficiently preprocess tokens.
MacroDirective * getLocalMacroDirectiveHistory(const IdentifierInfo *II) const
Given an identifier, return the latest non-imported macro directive for that identifier.
llvm::iterator_range< macro_iterator > macros(bool IncludeExternalMacros=true) const
SourceManager & getSourceManager() const
Scope - A scope is a transient data structure that is used while parsing the program.
Definition Scope.h:41
Sema - This implements semantic analysis and AST building for C.
Definition Sema.h:869
Preprocessor & getPreprocessor() const
Definition Sema.h:940
DiagnosticsEngine & getDiagnostics() const
Definition Sema.h:938
const LangOptions & getLangOpts() const
Definition Sema.h:934
Encodes a location in the source.
SourceLocation getBegin() const
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
Definition Stmt.cpp:343
SourceLocation getBeginLoc() const LLVM_READONLY
Definition Stmt.cpp:355
QualType getType() const
Definition Decl.h:723
Represents a variable declaration or definition.
Definition Decl.h:932
bool isStaticDataMember() const
Determines whether this is a static data member.
Definition Decl.h:1306
bool isStaticLocal() const
Returns true if a variable with function scope is a static local variable.
Definition Decl.h:1214
void reportUseAfterReturn(const Expr *IssueExpr, const Expr *ReturnExpr, const Expr *MovedExpr) override
void reportMisplacedLifetimebound(WarningScope Scope, const ParmVarDecl *PVDDef, const ParmVarDecl *PVDDecl) override
void reportNoescapeViolation(const ParmVarDecl *ParmWithNoescape, const Expr *EscapeExpr) override
void reportDanglingField(const Expr *IssueExpr, const FieldDecl *DanglingField, const Expr *MovedExpr, bool IsCapturedByLambda, SourceLocation ExpiryLoc) override
void reportInvalidatedField(const ParmVarDecl *PVD, const FieldDecl *DanglingField, const Expr *InvalidationExpr) override
void reportLifetimeboundViolation(const CXXMethodDecl *MDWithLifetimebound) override
void reportNoescapeViolation(const ParmVarDecl *ParmWithNoescape, const VarDecl *EscapeGlobal) override
void suggestLifetimeboundToImplicitThis(WarningScope Scope, const CXXMethodDecl *MD, const Expr *EscapeExpr) override
void reportInvalidatedGlobal(const ParmVarDecl *PVD, const VarDecl *DanglingGlobal, const Expr *InvalidationExpr) override
void suggestLifetimeboundToParmVar(WarningScope Scope, const ParmVarDecl *ParmToAnnotate, EscapingTarget Target) override
void reportDanglingGlobal(const Expr *IssueExpr, const VarDecl *DanglingGlobal, const Expr *MovedExpr, SourceLocation ExpiryLoc) override
void reportUseAfterScope(const Expr *IssueExpr, const Expr *UseExpr, const Expr *MovedExpr, SourceLocation FreeLoc, llvm::ArrayRef< const Expr * > ExprChain) override
void reportInvalidatedField(const Expr *IssueExpr, const FieldDecl *DanglingField, const Expr *InvalidationExpr) override
void reportUseAfterInvalidation(const Expr *IssueExpr, const Expr *UseExpr, const Expr *InvalidationExpr, llvm::ArrayRef< const Expr * > ExprChain) override
void reportInapplicableLifetimebound(const ParmVarDecl *PVD) override
void reportUseAfterInvalidation(const ParmVarDecl *PVD, const Expr *UseExpr, const Expr *InvalidationExpr, llvm::ArrayRef< const Expr * > ExprChain) override
void addLifetimeBoundToImplicitThis(const CXXMethodDecl *MD) override
void reportMisplacedLifetimebound(WarningScope Scope, const CXXMethodDecl *FDef, const CXXMethodDecl *FDecl) override
void reportNoescapeViolation(const ParmVarDecl *ParmWithNoescape, const FieldDecl *EscapeField) override
void reportInvalidatedGlobal(const Expr *IssueExpr, const VarDecl *DanglingGlobal, const Expr *InvalidationExpr) override
void reportLifetimeboundViolation(const ParmVarDecl *ParmWithLifetimebound) override
llvm::PointerUnion< const Expr *, const FieldDecl *, const VarDecl * > EscapingTarget
@ OS
Indicates that the tracking object is a descendant of a referenced-counted OSObject,...
const LifetimeBoundAttr * getDirectImplicitObjectLifetimeBoundAttr(const FunctionDecl *FD)
LifetimeSafetyOpts GetLifetimeSafetyOpts(Sema &S, const Decl *D)
WarningScope
Enum to track functions visible across or within TU.
const LifetimeBoundAttr * getImplicitObjectParamLifetimeBoundAttr(const FunctionDecl *FD)
bool ShouldSuggestLifetimeAnnotations(Sema &S, const Decl *D)
bool IsLifetimeSafetyEnabled(Sema &S, const Decl *D)
std::variant< struct RequiresDecl, struct HeaderDecl, struct UmbrellaDirDecl, struct ModuleDecl, struct ExcludeDecl, struct ExportDecl, struct ExportAsDecl, struct ExternModuleDecl, struct UseDecl, struct LinkDecl, struct ConfigMacrosDecl, struct ConflictDecl > Decl
All declarations that can appear in a module declaration.
bool isa(CodeGen::Address addr)
Definition Address.h:330
SourceRange getSourceRange() const LLVM_READONLY
getSourceRange - The range of the declaration name.
bool SuggestAnnotations
Whether to suggest lifetime annotations.
size_t MaxCFGBlocks
Maximum number of CFG blocks to analyze.