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