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