clang 23.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_this_suggestion,
71 diag::warn_lifetime_safety_intra_tu_this_suggestion,
72 diag::warn_lifetime_safety_inapplicable_lifetimebound};
73 for (unsigned DiagID : DiagIDs)
74 if (!Diags.isIgnored(DiagID, D->getBeginLoc()))
75 return true;
76 return false;
77}
78
79inline bool ShouldSuggestLifetimeAnnotations(Sema &S, const Decl *D) {
81 constexpr unsigned DiagIDs[] = {
82 diag::warn_lifetime_safety_intra_tu_param_suggestion,
83 diag::warn_lifetime_safety_cross_tu_param_suggestion,
84 diag::warn_lifetime_safety_intra_tu_this_suggestion,
85 diag::warn_lifetime_safety_cross_tu_this_suggestion};
86 for (unsigned DiagID : DiagIDs)
87 if (!Diags.isIgnored(DiagID, D->getBeginLoc()))
88 return true;
89 return false;
90}
91
93 LifetimeSafetyOpts LSOpts;
94 LSOpts.MaxCFGBlocks = S.getLangOpts().LifetimeSafetyMaxCFGBlocks;
96 return LSOpts;
97}
98
100
101public:
103
104 void reportUseAfterScope(const Expr *IssueExpr, const Expr *UseExpr,
105 const Expr *MovedExpr, SourceLocation FreeLoc,
106 llvm::ArrayRef<const Expr *> ExprChain) override {
107 unsigned DiagID = MovedExpr
108 ? diag::warn_lifetime_safety_use_after_scope_moved
109 : diag::warn_lifetime_safety_use_after_scope;
110 std::string DestroyedSubject = getDiagSubjectDescription(IssueExpr);
111
112 S.Diag(IssueExpr->getExprLoc(), DiagID)
113 << DestroyedSubject << IssueExpr->getSourceRange();
114 if (MovedExpr)
115 S.Diag(MovedExpr->getExprLoc(), diag::note_lifetime_safety_moved_here)
116 << MovedExpr->getSourceRange();
117 S.Diag(FreeLoc, diag::note_lifetime_safety_destroyed_here)
118 << DestroyedSubject;
119
120 reportAliasingChain(ExprChain);
121
122 S.Diag(UseExpr->getExprLoc(), diag::note_lifetime_safety_used_here)
123 << UseExpr->getSourceRange();
124 }
125
126 void reportUseAfterReturn(const Expr *IssueExpr, const Expr *ReturnExpr,
127 const Expr *MovedExpr) override {
128 unsigned DiagID = MovedExpr
129 ? diag::warn_lifetime_safety_return_stack_addr_moved
130 : diag::warn_lifetime_safety_return_stack_addr;
131
132 S.Diag(IssueExpr->getExprLoc(), DiagID)
133 << getDiagSubjectDescription(IssueExpr) << IssueExpr->getSourceRange();
134
135 if (MovedExpr)
136 S.Diag(MovedExpr->getExprLoc(), diag::note_lifetime_safety_moved_here)
137 << MovedExpr->getSourceRange();
138 S.Diag(ReturnExpr->getExprLoc(), diag::note_lifetime_safety_returned_here)
139 << ReturnExpr->getSourceRange();
140 }
141
142 void reportDanglingField(const Expr *IssueExpr,
143 const FieldDecl *DanglingField,
144 const Expr *MovedExpr,
145 SourceLocation ExpiryLoc) override {
146 unsigned DiagID = MovedExpr
147 ? diag::warn_lifetime_safety_dangling_field_moved
148 : diag::warn_lifetime_safety_dangling_field;
149
150 S.Diag(IssueExpr->getExprLoc(), DiagID)
151 << getDiagSubjectDescription(IssueExpr)
152 << getDiagSubjectDescription(DanglingField)
153 << IssueExpr->getSourceRange();
154 if (MovedExpr)
155 S.Diag(MovedExpr->getExprLoc(), diag::note_lifetime_safety_moved_here)
156 << MovedExpr->getSourceRange();
157 S.Diag(DanglingField->getLocation(),
158 diag::note_lifetime_safety_dangling_field_here)
159 << DanglingField->getEndLoc();
160 }
161
162 void reportDanglingGlobal(const Expr *IssueExpr,
163 const VarDecl *DanglingGlobal,
164 const Expr *MovedExpr,
165 SourceLocation ExpiryLoc) override {
166 unsigned DiagID = MovedExpr
167 ? diag::warn_lifetime_safety_dangling_global_moved
168 : diag::warn_lifetime_safety_dangling_global;
169
170 S.Diag(IssueExpr->getExprLoc(), DiagID)
171 << getDiagSubjectDescription(IssueExpr)
172 << getDiagSubjectDescription(DanglingGlobal)
173 << IssueExpr->getSourceRange();
174 if (MovedExpr)
175 S.Diag(MovedExpr->getExprLoc(), diag::note_lifetime_safety_moved_here)
176 << MovedExpr->getSourceRange();
177 if (DanglingGlobal->isStaticLocal() || DanglingGlobal->isStaticDataMember())
178 S.Diag(DanglingGlobal->getLocation(),
179 diag::note_lifetime_safety_dangling_static_here)
180 << DanglingGlobal->getEndLoc();
181 else
182 S.Diag(DanglingGlobal->getLocation(),
183 diag::note_lifetime_safety_dangling_global_here)
184 << DanglingGlobal->getEndLoc();
185 }
186
187 void reportUseAfterInvalidation(const Expr *IssueExpr, const Expr *UseExpr,
188 const Expr *InvalidationExpr) override {
189 auto WarnDiag = isa<CXXDeleteExpr>(InvalidationExpr)
190 ? diag::warn_lifetime_safety_use_after_free
191 : diag::warn_lifetime_safety_invalidation;
192 std::string InvalidatedSubject = getDiagSubjectDescription(IssueExpr);
193 S.Diag(IssueExpr->getExprLoc(), WarnDiag)
194 << InvalidatedSubject << IssueExpr->getSourceRange();
195 reportInvalidationSite(InvalidationExpr, InvalidatedSubject);
196 S.Diag(UseExpr->getExprLoc(), diag::note_lifetime_safety_used_here)
197 << UseExpr->getSourceRange();
198 }
199 void reportUseAfterInvalidation(const ParmVarDecl *PVD, const Expr *UseExpr,
200 const Expr *InvalidationExpr) override {
201
202 auto WarnDiag = isa<CXXDeleteExpr>(InvalidationExpr)
203 ? diag::warn_lifetime_safety_use_after_free
204 : diag::warn_lifetime_safety_invalidation;
205 std::string InvalidatedSubject = getDiagSubjectDescription(PVD);
206
207 S.Diag(PVD->getSourceRange().getBegin(), WarnDiag)
208 << InvalidatedSubject << PVD->getSourceRange();
209 reportInvalidationSite(InvalidationExpr, InvalidatedSubject);
210 S.Diag(UseExpr->getExprLoc(), diag::note_lifetime_safety_used_here)
211 << UseExpr->getSourceRange();
212 }
213
214 void reportInvalidatedField(const Expr *IssueExpr,
215 const FieldDecl *DanglingField,
216 const Expr *InvalidationExpr) override {
217 std::string InvalidatedSubject = getDiagSubjectDescription(IssueExpr);
218 S.Diag(IssueExpr->getExprLoc(),
219 diag::warn_lifetime_safety_invalidated_field)
220 << InvalidatedSubject << getDiagSubjectDescription(DanglingField)
221 << IssueExpr->getSourceRange();
222 reportInvalidationSite(InvalidationExpr, InvalidatedSubject);
223 S.Diag(DanglingField->getLocation(),
224 diag::note_lifetime_safety_dangling_field_here)
225 << DanglingField->getEndLoc();
226 }
227
229 const FieldDecl *DanglingField,
230 const Expr *InvalidationExpr) override {
231 std::string InvalidatedSubject = getDiagSubjectDescription(PVD);
232 S.Diag(PVD->getSourceRange().getBegin(),
233 diag::warn_lifetime_safety_invalidated_field)
234 << InvalidatedSubject << getDiagSubjectDescription(DanglingField)
235 << PVD->getSourceRange();
236 reportInvalidationSite(InvalidationExpr, InvalidatedSubject);
237 S.Diag(DanglingField->getLocation(),
238 diag::note_lifetime_safety_dangling_field_here)
239 << DanglingField->getEndLoc();
240 }
241
242 void reportInvalidatedGlobal(const Expr *IssueExpr,
243 const VarDecl *DanglingGlobal,
244 const Expr *InvalidationExpr) override {
245 std::string InvalidatedSubject = getDiagSubjectDescription(IssueExpr);
246 S.Diag(IssueExpr->getExprLoc(),
247 diag::warn_lifetime_safety_invalidated_global)
248 << InvalidatedSubject << getDiagSubjectDescription(DanglingGlobal)
249 << IssueExpr->getSourceRange();
250 reportInvalidationSite(InvalidationExpr, InvalidatedSubject);
251 if (DanglingGlobal->isStaticLocal() || DanglingGlobal->isStaticDataMember())
252 S.Diag(DanglingGlobal->getLocation(),
253 diag::note_lifetime_safety_dangling_static_here)
254 << DanglingGlobal->getEndLoc();
255 else
256 S.Diag(DanglingGlobal->getLocation(),
257 diag::note_lifetime_safety_dangling_global_here)
258 << DanglingGlobal->getEndLoc();
259 }
260
262 const VarDecl *DanglingGlobal,
263 const Expr *InvalidationExpr) override {
264 std::string InvalidatedSubject = getDiagSubjectDescription(PVD);
265 S.Diag(PVD->getSourceRange().getBegin(),
266 diag::warn_lifetime_safety_invalidated_global)
267 << InvalidatedSubject << getDiagSubjectDescription(DanglingGlobal)
268 << PVD->getSourceRange();
269 reportInvalidationSite(InvalidationExpr, InvalidatedSubject);
270 if (DanglingGlobal->isStaticLocal() || DanglingGlobal->isStaticDataMember())
271 S.Diag(DanglingGlobal->getLocation(),
272 diag::note_lifetime_safety_dangling_static_here)
273 << DanglingGlobal->getEndLoc();
274 else
275 S.Diag(DanglingGlobal->getLocation(),
276 diag::note_lifetime_safety_dangling_global_here)
277 << DanglingGlobal->getEndLoc();
278 }
279
281 const ParmVarDecl *ParmToAnnotate,
282 EscapingTarget Target) override {
283 unsigned DiagID =
285 ? diag::warn_lifetime_safety_cross_tu_param_suggestion
286 : diag::warn_lifetime_safety_intra_tu_param_suggestion;
287
288 auto [InsertionPoint, FixItText] = getLifetimeBoundFixIt(ParmToAnnotate);
289
290 S.Diag(ParmToAnnotate->getBeginLoc(), DiagID)
291 << ParmToAnnotate->getSourceRange()
292 << FixItHint::CreateInsertion(InsertionPoint, FixItText);
293
294 if (const auto *EscapeExpr = Target.dyn_cast<const Expr *>())
295 S.Diag(EscapeExpr->getBeginLoc(),
296 diag::note_lifetime_safety_suggestion_returned_here)
297 << EscapeExpr->getSourceRange();
298 else if (const auto *EscapeField = Target.dyn_cast<const FieldDecl *>())
299 S.Diag(EscapeField->getLocation(),
300 diag::note_lifetime_safety_escapes_to_field_here)
301 << EscapeField->getSourceRange();
302 }
303
305 const ParmVarDecl *ParmWithLifetimebound) override {
306 const auto *Attr = ParmWithLifetimebound->getAttr<LifetimeBoundAttr>();
307 StringRef ParamName = ParmWithLifetimebound->getName();
308 bool HasName = ParamName.size() > 0;
309 S.Diag(Attr->getLocation(),
310 diag::warn_lifetime_safety_lifetimebound_violation)
311 << HasName << ParamName << Attr->getRange();
312 }
313
315 const CXXMethodDecl *MDWithLifetimebound) override {
316 const auto *Attr =
317 getImplicitObjectParamLifetimeBoundAttr(MDWithLifetimebound);
318 assert(Attr && "Expected lifetimebound attribute");
319 S.Diag(Attr->getLocation(),
320 diag::warn_lifetime_safety_lifetimebound_violation)
321 << 2 << "" << Attr->getRange();
322 }
323
325 const CXXMethodDecl *FDef,
326 const CXXMethodDecl *FDecl) override {
328 assert(Attr && "Expected lifetimebound attribute");
329 unsigned DiagID =
331 ? diag::warn_lifetime_safety_cross_tu_misplaced_lifetimebound
332 : diag::warn_lifetime_safety_intra_tu_misplaced_lifetimebound;
333
334 auto [InsertionPoint, FixItText] = getLifetimeBoundFixIt(FDecl);
335
336 // Do not emit fix-its in macros or at invalid locations.
337 bool IsMacro =
338 FDecl->getBeginLoc().isMacroID() || InsertionPoint.isMacroID();
339
340 if (IsMacro || InsertionPoint.isInvalid())
341 S.Diag(FDecl->getLocation(), DiagID);
342 else
343 S.Diag(InsertionPoint, DiagID)
344 << FixItHint::CreateInsertion(InsertionPoint, FixItText);
345
346 S.Diag(Attr->getLocation(), diag::note_lifetime_safety_lifetimebound_here)
347 << Attr->getRange();
348 }
349
351 const ParmVarDecl *PVDDef,
352 const ParmVarDecl *PVDDecl) override {
353
354 const auto *Attr = PVDDef->getAttr<LifetimeBoundAttr>();
355 assert(Attr && "Expected lifetimebound attribute");
356 unsigned DiagID =
358 ? diag::warn_lifetime_safety_cross_tu_misplaced_lifetimebound
359 : diag::warn_lifetime_safety_intra_tu_misplaced_lifetimebound;
360
361 auto [InsertionPoint, FixItText] = getLifetimeBoundFixIt(PVDDecl);
362
363 // Do not emit fix-its in macros or at invalid locations.
364 bool IsMacro =
365 PVDDecl->getBeginLoc().isMacroID() || InsertionPoint.isMacroID();
366
367 if (IsMacro || InsertionPoint.isInvalid())
368 S.Diag(PVDDecl->getBeginLoc(), DiagID) << PVDDecl->getSourceRange();
369 else
370 S.Diag(PVDDecl->getBeginLoc(), DiagID)
371 << PVDDecl->getSourceRange()
372 << FixItHint::CreateInsertion(InsertionPoint, FixItText);
373
374 S.Diag(Attr->getLocation(), diag::note_lifetime_safety_lifetimebound_here)
375 << Attr->getRange();
376 }
377
379 assert(PVD->hasAttr<LifetimeBoundAttr>() &&
380 "Expected parameter to have lifetimebound attribute");
381 const auto *Attr = PVD->getAttr<LifetimeBoundAttr>();
382 S.Diag(Attr->getLocation(),
383 diag::warn_lifetime_safety_inapplicable_lifetimebound)
384 << PVD->getType() << Attr->getRange();
385 }
386
388 const CXXMethodDecl *MD,
389 const Expr *EscapeExpr) override {
390 unsigned DiagID = (Scope == WarningScope::CrossTU)
391 ? diag::warn_lifetime_safety_cross_tu_this_suggestion
392 : diag::warn_lifetime_safety_intra_tu_this_suggestion;
393
394 auto [InsertionPoint, FixItText] = getLifetimeBoundFixIt(MD);
395
396 S.Diag(InsertionPoint, DiagID)
397 << MD->getNameInfo().getSourceRange()
398 << FixItHint::CreateInsertion(InsertionPoint, FixItText);
399
400 S.Diag(EscapeExpr->getBeginLoc(),
401 diag::note_lifetime_safety_suggestion_returned_here)
402 << EscapeExpr->getSourceRange();
403 }
404
405 void reportNoescapeViolation(const ParmVarDecl *ParmWithNoescape,
406 const Expr *EscapeExpr) override {
407 S.Diag(ParmWithNoescape->getBeginLoc(),
408 diag::warn_lifetime_safety_noescape_escapes)
409 << ParmWithNoescape->getSourceRange();
410
411 S.Diag(EscapeExpr->getBeginLoc(),
412 diag::note_lifetime_safety_suggestion_returned_here)
413 << EscapeExpr->getSourceRange();
414 }
415
416 void reportNoescapeViolation(const ParmVarDecl *ParmWithNoescape,
417 const FieldDecl *EscapeField) override {
418 S.Diag(ParmWithNoescape->getBeginLoc(),
419 diag::warn_lifetime_safety_noescape_escapes)
420 << ParmWithNoescape->getSourceRange();
421
422 S.Diag(EscapeField->getLocation(),
423 diag::note_lifetime_safety_escapes_to_field_here)
424 << EscapeField->getEndLoc();
425 }
426
427 void reportNoescapeViolation(const ParmVarDecl *ParmWithNoescape,
428 const VarDecl *EscapeGlobal) override {
429 S.Diag(ParmWithNoescape->getBeginLoc(),
430 diag::warn_lifetime_safety_noescape_escapes)
431 << ParmWithNoescape->getSourceRange();
432 if (EscapeGlobal->isStaticLocal() || EscapeGlobal->isStaticDataMember())
433 S.Diag(EscapeGlobal->getLocation(),
434 diag::note_lifetime_safety_escapes_to_static_storage_here)
435 << EscapeGlobal->getEndLoc();
436 else
437 S.Diag(EscapeGlobal->getLocation(),
438 diag::note_lifetime_safety_escapes_to_global_here)
439 << EscapeGlobal->getEndLoc();
440 }
441
443 S.addLifetimeBoundToImplicitThis(const_cast<CXXMethodDecl *>(MD));
444 }
445
446private:
447 struct LifetimeBoundMacroCache {
448 bool IsBuilt = false;
450 };
451
452 void buildLifetimeBoundMacroCache(LifetimeBoundMacroCache &Cache,
453 ArrayRef<TokenValue> Tokens) {
454 if (Cache.IsBuilt)
455 return;
456
457 const Preprocessor &PP = S.getPreprocessor();
458 // Collect macro names that were ever defined as a lifetimebound attribute.
459 for (const auto &M : PP.macros()) {
460 const IdentifierInfo *II = M.first;
462 if (!MD)
463 continue;
464
465 // Include earlier matching definitions to handle redefinitions.
466 for (MacroDirective::DefInfo Def = MD->getDefinition(); Def;
467 Def = Def.getPreviousDefinition()) {
468 const MacroInfo *MI = Def.getMacroInfo();
469 if (MI->isObjectLike() && Tokens.size() == MI->getNumTokens() &&
470 std::equal(Tokens.begin(), Tokens.end(), MI->tokens_begin())) {
471 Cache.Candidates.push_back(II);
472 break;
473 }
474 }
475 }
476 Cache.IsBuilt = true;
477 }
478
479 StringRef getLastCachedMacroWithSpelling(SourceLocation Loc,
480 llvm::ArrayRef<TokenValue> Tokens,
481 LifetimeBoundMacroCache &Cache) {
482 if (Loc.isInvalid())
483 return {};
484
485 buildLifetimeBoundMacroCache(Cache, Tokens);
486
487 const Preprocessor &PP = S.getPreprocessor();
488 const SourceManager &SM = S.getSourceManager();
489 SourceLocation BestLocation;
490 StringRef BestSpelling;
491 for (const IdentifierInfo *II : Cache.Candidates) {
492 const MacroDirective *MD = PP.getLocalMacroDirectiveHistory(II);
493 const MacroDirective::DefInfo Def = MD->findDirectiveAtLoc(Loc, SM);
494 if (!Def || !Def.getMacroInfo())
495 continue;
496
497 // Ensure the macro definition active at Loc still has this spelling.
498 const MacroInfo *MI = Def.getMacroInfo();
499 if (!MI->isObjectLike() || Tokens.size() != MI->getNumTokens() ||
500 !std::equal(Tokens.begin(), Tokens.end(), MI->tokens_begin()))
501 continue;
502
503 // Choose the matching macro defined latest before Loc.
504 SourceLocation Location = Def.getLocation();
505 assert(Location.isInvalid() ||
506 SM.isBeforeInTranslationUnit(Location, Loc));
507 if (BestLocation.isInvalid() ||
508 (Location.isValid() &&
509 SM.isBeforeInTranslationUnit(BestLocation, Location))) {
510 BestLocation = Location;
511 BestSpelling = II->getName();
512 }
513 }
514 return BestSpelling;
515 }
516
517 void reportInvalidationSite(const Expr *InvalidationExpr,
518 StringRef InvalidatedSubject) {
519 auto Diag = isa<CXXDeleteExpr>(InvalidationExpr)
520 ? diag::note_lifetime_safety_freed_here
521 : diag::note_lifetime_safety_invalidated_here;
522 S.Diag(InvalidationExpr->getExprLoc(), Diag)
523 << InvalidatedSubject << InvalidationExpr->getSourceRange();
524 }
525
526 std::string getLifetimeBoundFixItText(SourceLocation Loc, bool LeadingSpace,
527 bool AllowGNUAttrMacro = true) {
528 StringRef Spelling = S.getLangOpts().LifetimeSafetyLifetimeBoundMacro;
529 if (Spelling.empty() && Loc.isValid()) {
530 const Preprocessor &PP = S.getPreprocessor();
531 Spelling = getLastCachedMacroWithSpelling(
532 Loc,
533 {tok::l_square, tok::l_square, PP.getIdentifierInfo("clang"),
534 tok::coloncolon, PP.getIdentifierInfo("lifetimebound"),
535 tok::r_square, tok::r_square},
536 ClangLifetimeBoundMacroCache);
537
538 if (Spelling.empty() && AllowGNUAttrMacro)
539 Spelling = getLastCachedMacroWithSpelling(
540 Loc,
541 {tok::kw___attribute, tok::l_paren, tok::l_paren,
542 PP.getIdentifierInfo("lifetimebound"), tok::r_paren, tok::r_paren},
543 GNULifetimeBoundMacroCache);
544 }
545 const std::string Text =
546 Spelling.empty() ? "[[clang::lifetimebound]]" : Spelling.str();
547 return LeadingSpace ? " " + Text : Text + " ";
548 }
549
550 std::pair<SourceLocation, std::string>
551 getLifetimeBoundFixIt(const ParmVarDecl *Decl) {
552 SourceLocation InsertionPoint = Lexer::getLocForEndOfToken(
553 Decl->getEndLoc(), 0, S.getSourceManager(), S.getLangOpts());
554 bool LeadingSpace = true;
555
556 if (!Decl->getIdentifier()) {
557 // For unnamed parameters, placing attributes after the type would be
558 // parsed as a type attribute, not a parameter attribute.
559 InsertionPoint = Decl->getBeginLoc();
560 LeadingSpace = false;
561 } else if (Decl->hasDefaultArg()) {
562 // If the parameter has a default argument, place the attribute after the
563 // named argument.
564 InsertionPoint = Lexer::getLocForEndOfToken(
565 Decl->getLocation(), 0, S.getSourceManager(), S.getLangOpts());
566 }
567 return {InsertionPoint,
568 getLifetimeBoundFixItText(InsertionPoint, LeadingSpace)};
569 }
570
571 std::pair<SourceLocation, std::string>
572 getLifetimeBoundFixIt(const CXXMethodDecl *MD) {
573 const auto MDL = MD->getTypeSourceInfo()->getTypeLoc();
574 SourceLocation InsertionPoint = Lexer::getLocForEndOfToken(
575 MDL.getEndLoc(), 0, S.getSourceManager(), S.getLangOpts());
576
577 if (const auto *FPT = MD->getType()->getAs<FunctionProtoType>();
578 FPT && FPT->hasTrailingReturn()) {
579 // For trailing return types, 'getEndLoc()' includes the return type
580 // after '->', placing the attribute in an invalid position.
581 // Instead use 'getLocalRangeEnd()' which gives the '->' location
582 // for trailing returns, so find the last token before it.
583 const auto FTL = MDL.getAs<FunctionTypeLoc>();
584 assert(FTL);
585 InsertionPoint = Lexer::getLocForEndOfToken(
586 Lexer::findPreviousToken(FTL.getLocalRangeEnd(), S.getSourceManager(),
587 S.getLangOpts(),
588 /*IncludeComments=*/false)
589 ->getLocation(),
590 0, S.getSourceManager(), S.getLangOpts());
591 }
592 return {InsertionPoint,
593 getLifetimeBoundFixItText(InsertionPoint, /*LeadingSpace=*/true,
594 /*AllowGNUAttrMacro=*/false)};
595 }
596
597 std::string getDiagSubjectDescription(const ValueDecl *VD) {
598 std::string Res;
599 llvm::raw_string_ostream OS(Res);
600 if (isa<FieldDecl>(VD)) {
601 OS << "field";
602 } else if (isa<ParmVarDecl>(VD)) {
603 OS << "parameter";
604 } else if (const auto *Var = dyn_cast<VarDecl>(VD)) {
605 if (Var->isStaticLocal() || Var->isStaticDataMember())
606 OS << "static variable";
607 else if (Var->hasGlobalStorage())
608 OS << "global variable";
609 else
610 OS << "local variable";
611 } else {
612 OS << "variable";
613 }
614 OS << " '";
615 VD->getNameForDiagnostic(OS, S.getPrintingPolicy(), /*Qualified=*/false);
616 OS << "'";
617 return Res;
618 }
619
620 std::string getDiagSubjectDescription(const Expr *E) {
621 E = E->IgnoreImpCasts();
623 return "temporary object";
624 if (isa<CXXNewExpr>(E))
625 return "allocated object";
626 if (const auto *DRE = dyn_cast<DeclRefExpr>(E))
627 return getDiagSubjectDescription(DRE->getDecl());
628
629 if (const auto *CE = dyn_cast<CallExpr>(E)) {
630 const auto *FD = CE->getDirectCallee();
631 if (!FD)
632 return "result of call";
633 if (FD->isOverloadedOperator() || isa<CXXConversionDecl>(FD))
634 return "expression";
635 std::string Name;
636 llvm::raw_string_ostream OS(Name);
637 FD->getNameForDiagnostic(OS, S.getPrintingPolicy(),
638 /*Qualified=*/false);
639 return "result of call to '" + Name + "'";
640 }
641
642 // TODO: Handle other expression types.
643 return "expression";
644 }
645
646 bool shouldShowInAliasChain(const Expr *CurrExpr, const Expr *LastExpr) {
647 CurrExpr = CurrExpr->IgnoreImpCasts();
648 LastExpr = LastExpr->IgnoreImpCasts();
649
650 if (!isa<CallExpr, DeclRefExpr>(CurrExpr))
651 return false;
652 // Source ranges can be used to filter out many implicit expressions,
653 // because operations between class objects often involve numerous implicit
654 // conversions, yet they share the same source range.
655 return CurrExpr->getSourceRange() != LastExpr->getSourceRange();
656 }
657
658 void reportAliasingChain(llvm::ArrayRef<const Expr *> OriginExprChain) {
659 if (OriginExprChain.empty())
660 return;
661
662 const Expr *LastExpr = OriginExprChain.back();
663 std::string IssueStr = getDiagSubjectDescription(LastExpr);
664
665 for (const Expr *CurrExpr : reverse(OriginExprChain.drop_back())) {
666 if (!shouldShowInAliasChain(CurrExpr, LastExpr))
667 continue;
668 S.Diag(CurrExpr->getBeginLoc(),
669 diag::note_lifetime_safety_aliases_storage)
670 << CurrExpr->getSourceRange() << getDiagSubjectDescription(CurrExpr)
671 << IssueStr;
672 LastExpr = CurrExpr;
673 }
674 }
675
676 LifetimeBoundMacroCache ClangLifetimeBoundMacroCache;
677 LifetimeBoundMacroCache GNULifetimeBoundMacroCache;
678 Sema &S;
679};
680
681} // namespace clang::lifetimes
682
683#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
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:233
bool isIgnored(unsigned DiagID, SourceLocation Loc) const
Determine whether the diagnostic is known to be ignored.
Definition Diagnostic.h:960
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:3191
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:104
DeclarationNameInfo getNameInfo() const
Definition Decl.h:2238
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:1817
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:868
Preprocessor & getPreprocessor() const
Definition Sema.h:938
DiagnosticsEngine & getDiagnostics() const
Definition Sema.h:936
const LangOptions & getLangOpts() const
Definition Sema.h:932
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:1304
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 reportUseAfterInvalidation(const ParmVarDecl *PVD, const Expr *UseExpr, const Expr *InvalidationExpr) 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 reportInapplicableLifetimebound(const ParmVarDecl *PVD) override
void reportDanglingField(const Expr *IssueExpr, const FieldDecl *DanglingField, const Expr *MovedExpr, SourceLocation ExpiryLoc) override
void addLifetimeBoundToImplicitThis(const CXXMethodDecl *MD) override
void reportUseAfterInvalidation(const Expr *IssueExpr, const Expr *UseExpr, const Expr *InvalidationExpr) 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.