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