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,
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(IssueExpr->getExprLoc(), DiagID)
155 << getDiagSubjectDescription(IssueExpr)
156 << getDiagSubjectDescription(DanglingField)
157 << IssueExpr->getSourceRange();
158 if (MovedExpr)
159 S.Diag(MovedExpr->getExprLoc(), diag::note_lifetime_safety_moved_here)
160 << MovedExpr->getSourceRange();
161 S.Diag(DanglingField->getLocation(),
162 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(IssueExpr->getExprLoc(), DiagID)
175 << getDiagSubjectDescription(IssueExpr)
176 << getDiagSubjectDescription(DanglingGlobal)
177 << IssueExpr->getSourceRange();
178 if (MovedExpr)
179 S.Diag(MovedExpr->getExprLoc(), diag::note_lifetime_safety_moved_here)
180 << MovedExpr->getSourceRange();
181 if (DanglingGlobal->isStaticLocal() || DanglingGlobal->isStaticDataMember())
182 S.Diag(DanglingGlobal->getLocation(),
183 diag::note_lifetime_safety_dangling_static_here)
184 << DanglingGlobal->getEndLoc();
185 else
186 S.Diag(DanglingGlobal->getLocation(),
187 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>(InvalidationExpr)
196 ? diag::warn_lifetime_safety_use_after_free
197 : diag::warn_lifetime_safety_invalidation;
198 std::string InvalidatedSubject = getDiagSubjectDescription(IssueExpr);
199 S.Diag(IssueExpr->getExprLoc(), WarnDiag)
200 << InvalidatedSubject << IssueExpr->getSourceRange();
201 reportInvalidationSite(InvalidationExpr, InvalidatedSubject);
202 reportAliasingChain(ExprChain);
203 S.Diag(UseExpr->getExprLoc(), 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>(InvalidationExpr)
212 ? diag::warn_lifetime_safety_use_after_free
213 : diag::warn_lifetime_safety_invalidation;
214 std::string InvalidatedSubject = getDiagSubjectDescription(PVD);
215
216 S.Diag(PVD->getSourceRange().getBegin(), WarnDiag)
217 << InvalidatedSubject << PVD->getSourceRange();
218 reportInvalidationSite(InvalidationExpr, InvalidatedSubject);
219 reportAliasingChain(ExprChain);
220 S.Diag(UseExpr->getExprLoc(), 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(IssueExpr);
228 S.Diag(IssueExpr->getExprLoc(),
229 diag::warn_lifetime_safety_invalidated_field)
230 << InvalidatedSubject << getDiagSubjectDescription(DanglingField)
231 << IssueExpr->getSourceRange();
232 reportInvalidationSite(InvalidationExpr, InvalidatedSubject);
233 S.Diag(DanglingField->getLocation(),
234 diag::note_lifetime_safety_dangling_field_here)
235 << DanglingField->getEndLoc();
236 }
237
239 const FieldDecl *DanglingField,
240 const Expr *InvalidationExpr) override {
241 std::string InvalidatedSubject = getDiagSubjectDescription(PVD);
242 S.Diag(PVD->getSourceRange().getBegin(),
243 diag::warn_lifetime_safety_invalidated_field)
244 << InvalidatedSubject << getDiagSubjectDescription(DanglingField)
245 << PVD->getSourceRange();
246 reportInvalidationSite(InvalidationExpr, InvalidatedSubject);
247 S.Diag(DanglingField->getLocation(),
248 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(IssueExpr);
256 S.Diag(IssueExpr->getExprLoc(),
257 diag::warn_lifetime_safety_invalidated_global)
258 << InvalidatedSubject << getDiagSubjectDescription(DanglingGlobal)
259 << IssueExpr->getSourceRange();
260 reportInvalidationSite(InvalidationExpr, InvalidatedSubject);
261 if (DanglingGlobal->isStaticLocal() || DanglingGlobal->isStaticDataMember())
262 S.Diag(DanglingGlobal->getLocation(),
263 diag::note_lifetime_safety_dangling_static_here)
264 << DanglingGlobal->getEndLoc();
265 else
266 S.Diag(DanglingGlobal->getLocation(),
267 diag::note_lifetime_safety_dangling_global_here)
268 << DanglingGlobal->getEndLoc();
269 }
270
272 const VarDecl *DanglingGlobal,
273 const Expr *InvalidationExpr) override {
274 std::string InvalidatedSubject = getDiagSubjectDescription(PVD);
275 S.Diag(PVD->getSourceRange().getBegin(),
276 diag::warn_lifetime_safety_invalidated_global)
277 << InvalidatedSubject << getDiagSubjectDescription(DanglingGlobal)
278 << PVD->getSourceRange();
279 reportInvalidationSite(InvalidationExpr, InvalidatedSubject);
280 if (DanglingGlobal->isStaticLocal() || DanglingGlobal->isStaticDataMember())
281 S.Diag(DanglingGlobal->getLocation(),
282 diag::note_lifetime_safety_dangling_static_here)
283 << DanglingGlobal->getEndLoc();
284 else
285 S.Diag(DanglingGlobal->getLocation(),
286 diag::note_lifetime_safety_dangling_global_here)
287 << DanglingGlobal->getEndLoc();
288 }
289
291 const ParmVarDecl *ParmToAnnotate,
292 EscapingTarget Target) override {
293 unsigned DiagID;
294 if (isa<CXXConstructorDecl>(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(ParmToAnnotate);
304
305 S.Diag(InsertionPoint, DiagID)
306 << ParmToAnnotate->getSourceRange()
307 << FixItHint::CreateInsertion(InsertionPoint, FixItText);
308
309 if (const auto *EscapeExpr = Target.dyn_cast<const Expr *>())
310 S.Diag(EscapeExpr->getBeginLoc(),
311 diag::note_lifetime_safety_suggestion_returned_here)
312 << EscapeExpr->getSourceRange();
313 else if (const auto *EscapeField = Target.dyn_cast<const FieldDecl *>())
314 S.Diag(EscapeField->getLocation(),
315 diag::note_lifetime_safety_escapes_to_field_here)
316 << EscapeField->getSourceRange();
317 }
318
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(Attr->getLocation(),
325 diag::warn_lifetime_safety_lifetimebound_violation)
326 << HasName << ParamName << Attr->getRange();
327 }
328
330 const CXXMethodDecl *MDWithLifetimebound) override {
331 const auto *Attr =
332 getImplicitObjectParamLifetimeBoundAttr(MDWithLifetimebound);
333 assert(Attr && "Expected lifetimebound attribute");
334 S.Diag(Attr->getLocation(),
335 diag::warn_lifetime_safety_lifetimebound_violation)
336 << 2 << "" << Attr->getRange();
337 }
338
340 const CXXMethodDecl *FDef,
341 const CXXMethodDecl *FDecl) override {
343 assert(Attr && "Expected lifetimebound attribute");
344 unsigned DiagID =
346 ? diag::warn_lifetime_safety_cross_tu_misplaced_lifetimebound
347 : diag::warn_lifetime_safety_intra_tu_misplaced_lifetimebound;
348
349 auto [InsertionPoint, FixItText] = getLifetimeBoundFixIt(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(FDecl->getLocation(), DiagID);
357 else
358 S.Diag(InsertionPoint, DiagID)
359 << FixItHint::CreateInsertion(InsertionPoint, FixItText);
360
361 S.Diag(Attr->getLocation(), diag::note_lifetime_safety_lifetimebound_here)
362 << Attr->getRange();
363 }
364
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 =
373 ? diag::warn_lifetime_safety_cross_tu_misplaced_lifetimebound
374 : diag::warn_lifetime_safety_intra_tu_misplaced_lifetimebound;
375
376 auto [InsertionPoint, FixItText] = getLifetimeBoundFixIt(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(PVDDecl->getBeginLoc(), DiagID) << PVDDecl->getSourceRange();
384 else
385 S.Diag(InsertionPoint, DiagID)
386 << PVDDecl->getSourceRange()
387 << FixItHint::CreateInsertion(InsertionPoint, FixItText);
388
389 S.Diag(Attr->getLocation(), diag::note_lifetime_safety_lifetimebound_here)
390 << Attr->getRange();
391 }
392
394 assert(PVD->hasAttr<LifetimeBoundAttr>() &&
395 "Expected parameter to have lifetimebound attribute");
396 const auto *Attr = PVD->getAttr<LifetimeBoundAttr>();
397 S.Diag(Attr->getLocation(),
398 diag::warn_lifetime_safety_inapplicable_lifetimebound)
399 << PVD->getType() << Attr->getRange();
400 }
401
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(InsertionPoint, DiagID)
412 << MD->getNameInfo().getSourceRange()
413 << FixItHint::CreateInsertion(InsertionPoint, FixItText);
414
415 S.Diag(EscapeExpr->getBeginLoc(),
416 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(ParmWithNoescape->getBeginLoc(),
423 diag::warn_lifetime_safety_noescape_escapes)
424 << ParmWithNoescape->getSourceRange();
425
426 S.Diag(EscapeExpr->getBeginLoc(),
427 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(ParmWithNoescape->getBeginLoc(),
434 diag::warn_lifetime_safety_noescape_escapes)
435 << ParmWithNoescape->getSourceRange();
436
437 S.Diag(EscapeField->getLocation(),
438 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(ParmWithNoescape->getBeginLoc(),
445 diag::warn_lifetime_safety_noescape_escapes)
446 << ParmWithNoescape->getSourceRange();
447 if (EscapeGlobal->isStaticLocal() || EscapeGlobal->isStaticDataMember())
448 S.Diag(EscapeGlobal->getLocation(),
449 diag::note_lifetime_safety_escapes_to_static_storage_here)
450 << EscapeGlobal->getEndLoc();
451 else
452 S.Diag(EscapeGlobal->getLocation(),
453 diag::note_lifetime_safety_escapes_to_global_here)
454 << EscapeGlobal->getEndLoc();
455 }
456
458 S.addLifetimeBoundToImplicitThis(const_cast<CXXMethodDecl *>(MD));
459 }
460
461private:
462 struct LifetimeBoundMacroCache {
463 bool IsBuilt = false;
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;
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(Tokens.begin(), Tokens.end(), MI->tokens_begin())) {
486 Cache.Candidates.push_back(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(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(Tokens.begin(), Tokens.end(), 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(BestLocation, 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>(InvalidationExpr)
535 ? diag::note_lifetime_safety_freed_here
536 : diag::note_lifetime_safety_invalidated_here;
537 S.Diag(InvalidationExpr->getExprLoc(), 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 {tok::l_square, tok::l_square, PP.getIdentifierInfo("clang"),
555 tok::coloncolon, PP.getIdentifierInfo("lifetimebound"),
556 tok::r_square, tok::r_square},
557 ClangLifetimeBoundMacroCache);
558
559 if (Spelling.empty() && AllowGNUAttrMacro)
560 Spelling = getLastCachedMacroWithSpelling(
561 Loc,
562 {tok::kw___attribute, tok::l_paren, tok::l_paren,
563 PP.getIdentifierInfo("lifetimebound"), tok::r_paren, tok::r_paren},
564 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 Decl->getEndLoc(), 0, S.getSourceManager(), 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 Decl->getLocation(), 0, S.getSourceManager(), S.getLangOpts());
586 }
587 return {InsertionPoint,
588 getLifetimeBoundFixItText(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 MDL.getEndLoc(), 0, S.getSourceManager(), 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 Lexer::findPreviousToken(FTL.getLocalRangeEnd(), S.getSourceManager(),
607 S.getLangOpts(),
608 /*IncludeComments=*/false)
609 ->getLocation(),
610 0, S.getSourceManager(), S.getLangOpts());
611 }
612 return {InsertionPoint,
613 getLifetimeBoundFixItText(InsertionPoint, /*LeadingSpace=*/true,
614 /*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>(VD)) {
621 OS << "field";
622 } else if (isa<ParmVarDecl>(VD)) {
623 OS << "parameter";
624 } else if (const auto *Var = dyn_cast<VarDecl>(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, S.getPrintingPolicy(), /*Qualified=*/false);
636 OS << "'";
637 return Res;
638 }
639
640 std::string getDiagSubjectDescription(const Expr *E) {
641 E = E->IgnoreImpCasts();
643 return "temporary object";
644 if (isa<CXXNewExpr>(E))
645 return "allocated object";
646 if (const auto *DRE = dyn_cast<DeclRefExpr>(E))
647 return getDiagSubjectDescription(DRE->getDecl());
648
649 if (const auto *CE = dyn_cast<CallExpr>(E)) {
650 const auto *FD = CE->getDirectCallee();
651 if (!FD)
652 return "result of call";
653 if (FD->isOverloadedOperator() || isa<CXXConversionDecl>(FD))
654 return "expression";
655 std::string Name;
656 llvm::raw_string_ostream OS(Name);
657 FD->getNameForDiagnostic(OS, S.getPrintingPolicy(),
658 /*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>(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(LastExpr);
684
685 for (const Expr *CurrExpr : reverse(OriginExprChain.drop_back())) {
686 if (!shouldShowInAliasChain(CurrExpr, LastExpr))
687 continue;
688 S.Diag(CurrExpr->getBeginLoc(),
689 diag::note_lifetime_safety_aliases_storage)
690 << CurrExpr->getSourceRange() << getDiagSubjectDescription(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
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.
#define SM(sm)
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:961
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:1406
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:881
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:2957
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 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 reportDanglingField(const Expr *IssueExpr, const FieldDecl *DanglingField, const Expr *MovedExpr, SourceLocation ExpiryLoc) 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.