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
20#include "clang/Lex/Lexer.h"
21#include "clang/Sema/Sema.h"
22
23namespace clang::lifetimes {
24
25inline bool IsLifetimeSafetyDiagnosticEnabled(Sema &S, const Decl *D) {
27 return !Diags.isIgnored(diag::warn_lifetime_safety_use_after_scope,
28 D->getBeginLoc()) ||
29 !Diags.isIgnored(diag::warn_lifetime_safety_use_after_scope_moved,
30 D->getBeginLoc()) ||
31 !Diags.isIgnored(diag::warn_lifetime_safety_return_stack_addr,
32 D->getBeginLoc()) ||
33 !Diags.isIgnored(diag::warn_lifetime_safety_return_stack_addr_moved,
34 D->getBeginLoc()) ||
35 !Diags.isIgnored(diag::warn_lifetime_safety_invalidation,
36 D->getBeginLoc()) ||
37 !Diags.isIgnored(diag::warn_lifetime_safety_noescape_escapes,
38 D->getBeginLoc());
39}
40
42
43public:
45
46 void reportUseAfterScope(const Expr *IssueExpr, const Expr *UseExpr,
47 const Expr *MovedExpr,
48 SourceLocation FreeLoc) override {
49 S.Diag(IssueExpr->getExprLoc(),
50 MovedExpr ? diag::warn_lifetime_safety_use_after_scope_moved
51 : diag::warn_lifetime_safety_use_after_scope)
52 << IssueExpr->getSourceRange();
53 if (MovedExpr)
54 S.Diag(MovedExpr->getExprLoc(), diag::note_lifetime_safety_moved_here)
55 << MovedExpr->getSourceRange();
56 S.Diag(FreeLoc, diag::note_lifetime_safety_destroyed_here);
57 S.Diag(UseExpr->getExprLoc(), diag::note_lifetime_safety_used_here)
58 << UseExpr->getSourceRange();
59 }
60
61 void reportUseAfterReturn(const Expr *IssueExpr, const Expr *ReturnExpr,
62 const Expr *MovedExpr,
63 SourceLocation ExpiryLoc) override {
64 S.Diag(IssueExpr->getExprLoc(),
65 MovedExpr ? diag::warn_lifetime_safety_return_stack_addr_moved
66 : diag::warn_lifetime_safety_return_stack_addr)
67 << IssueExpr->getSourceRange();
68 if (MovedExpr)
69 S.Diag(MovedExpr->getExprLoc(), diag::note_lifetime_safety_moved_here)
70 << MovedExpr->getSourceRange();
71 S.Diag(ReturnExpr->getExprLoc(), diag::note_lifetime_safety_returned_here)
72 << ReturnExpr->getSourceRange();
73 }
74
75 void reportDanglingField(const Expr *IssueExpr,
76 const FieldDecl *DanglingField,
77 const Expr *MovedExpr,
78 SourceLocation ExpiryLoc) override {
79 S.Diag(IssueExpr->getExprLoc(),
80 MovedExpr ? diag::warn_lifetime_safety_dangling_field_moved
81 : diag::warn_lifetime_safety_dangling_field)
82 << IssueExpr->getSourceRange();
83 if (MovedExpr)
84 S.Diag(MovedExpr->getExprLoc(), diag::note_lifetime_safety_moved_here)
85 << MovedExpr->getSourceRange();
86 S.Diag(DanglingField->getLocation(),
87 diag::note_lifetime_safety_dangling_field_here)
88 << DanglingField->getEndLoc();
89 }
90
91 void reportDanglingGlobal(const Expr *IssueExpr,
92 const VarDecl *DanglingGlobal,
93 const Expr *MovedExpr,
94 SourceLocation ExpiryLoc) override {
95 S.Diag(IssueExpr->getExprLoc(),
96 MovedExpr ? diag::warn_lifetime_safety_dangling_global_moved
97 : diag::warn_lifetime_safety_dangling_global)
98 << IssueExpr->getSourceRange();
99 if (MovedExpr)
100 S.Diag(MovedExpr->getExprLoc(), diag::note_lifetime_safety_moved_here)
101 << MovedExpr->getSourceRange();
102 if (DanglingGlobal->isStaticLocal() || DanglingGlobal->isStaticDataMember())
103 S.Diag(DanglingGlobal->getLocation(),
104 diag::note_lifetime_safety_dangling_static_here)
105 << DanglingGlobal->getEndLoc();
106 else
107 S.Diag(DanglingGlobal->getLocation(),
108 diag::note_lifetime_safety_dangling_global_here)
109 << DanglingGlobal->getEndLoc();
110 }
111
112 void reportUseAfterInvalidation(const Expr *IssueExpr, const Expr *UseExpr,
113 const Expr *InvalidationExpr) override {
114 auto WarnDiag = isa<CXXDeleteExpr>(InvalidationExpr)
115 ? diag::warn_lifetime_safety_use_after_free
116 : diag::warn_lifetime_safety_invalidation;
117 auto UseDiag = isa<CXXDeleteExpr>(InvalidationExpr)
118 ? diag::note_lifetime_safety_freed_here
119 : diag::note_lifetime_safety_invalidated_here;
120 S.Diag(IssueExpr->getExprLoc(), WarnDiag)
121 << false << IssueExpr->getSourceRange();
122 S.Diag(InvalidationExpr->getExprLoc(), UseDiag)
123 << InvalidationExpr->getSourceRange();
124 S.Diag(UseExpr->getExprLoc(), diag::note_lifetime_safety_used_here)
125 << UseExpr->getSourceRange();
126 }
127 void reportUseAfterInvalidation(const ParmVarDecl *PVD, const Expr *UseExpr,
128 const Expr *InvalidationExpr) override {
129
130 auto WarnDiag = isa<CXXDeleteExpr>(InvalidationExpr)
131 ? diag::warn_lifetime_safety_use_after_free
132 : diag::warn_lifetime_safety_invalidation;
133 auto UseDiag = isa<CXXDeleteExpr>(InvalidationExpr)
134 ? diag::note_lifetime_safety_freed_here
135 : diag::note_lifetime_safety_invalidated_here;
136
137 S.Diag(PVD->getSourceRange().getBegin(), WarnDiag)
138 << true << PVD->getSourceRange();
139 S.Diag(InvalidationExpr->getExprLoc(), UseDiag)
140 << InvalidationExpr->getSourceRange();
141 S.Diag(UseExpr->getExprLoc(), diag::note_lifetime_safety_used_here)
142 << UseExpr->getSourceRange();
143 }
144
146 const ParmVarDecl *ParmToAnnotate,
147 EscapingTarget Target) override {
148 unsigned DiagID =
150 ? diag::warn_lifetime_safety_cross_tu_param_suggestion
151 : diag::warn_lifetime_safety_intra_tu_param_suggestion;
153 ParmToAnnotate->getEndLoc(), 0, S.getSourceManager(), S.getLangOpts());
154 StringRef FixItText = " [[clang::lifetimebound]]";
155 if (!ParmToAnnotate->getIdentifier()) {
156 // For unnamed parameters, placing attributes after the type would be
157 // parsed as a type attribute, not a parameter attribute.
158 InsertionPoint = ParmToAnnotate->getBeginLoc();
159 FixItText = "[[clang::lifetimebound]] ";
160 }
161 S.Diag(ParmToAnnotate->getBeginLoc(), DiagID)
162 << ParmToAnnotate->getSourceRange()
163 << FixItHint::CreateInsertion(InsertionPoint, FixItText);
164
165 if (const auto *EscapeExpr = Target.dyn_cast<const Expr *>())
166 S.Diag(EscapeExpr->getBeginLoc(),
167 diag::note_lifetime_safety_suggestion_returned_here)
168 << EscapeExpr->getSourceRange();
169 else if (const auto *EscapeField = Target.dyn_cast<const FieldDecl *>())
170 S.Diag(EscapeField->getLocation(),
171 diag::note_lifetime_safety_escapes_to_field_here)
172 << EscapeField->getSourceRange();
173 }
174
176 const CXXMethodDecl *MD,
177 const Expr *EscapeExpr) override {
178 unsigned DiagID = (Scope == SuggestionScope::CrossTU)
179 ? diag::warn_lifetime_safety_cross_tu_this_suggestion
180 : diag::warn_lifetime_safety_intra_tu_this_suggestion;
181 const auto MDL = MD->getTypeSourceInfo()->getTypeLoc();
183 MDL.getEndLoc(), 0, S.getSourceManager(), S.getLangOpts());
184 if (const auto *FPT = MD->getType()->getAs<FunctionProtoType>();
185 FPT && FPT->hasTrailingReturn()) {
186 // For trailing return types, 'getEndLoc()' includes the return type
187 // after '->', placing the attribute in an invalid position.
188 // Instead use 'getLocalRangeEnd()' which gives the '->' location
189 // for trailing returns, so find the last token before it.
190 const auto FTL = MDL.getAs<FunctionTypeLoc>();
191 assert(FTL);
192 InsertionPoint = Lexer::getLocForEndOfToken(
193 Lexer::findPreviousToken(FTL.getLocalRangeEnd(), S.getSourceManager(),
194 S.getLangOpts(),
195 /*IncludeComments=*/false)
196 ->getLocation(),
197 0, S.getSourceManager(), S.getLangOpts());
198 }
199 S.Diag(InsertionPoint, DiagID)
200 << MD->getNameInfo().getSourceRange()
201 << FixItHint::CreateInsertion(InsertionPoint,
202 " [[clang::lifetimebound]]");
203 S.Diag(EscapeExpr->getBeginLoc(),
204 diag::note_lifetime_safety_suggestion_returned_here)
205 << EscapeExpr->getSourceRange();
206 }
207
208 void reportNoescapeViolation(const ParmVarDecl *ParmWithNoescape,
209 const Expr *EscapeExpr) override {
210 S.Diag(ParmWithNoescape->getBeginLoc(),
211 diag::warn_lifetime_safety_noescape_escapes)
212 << ParmWithNoescape->getSourceRange();
213
214 S.Diag(EscapeExpr->getBeginLoc(),
215 diag::note_lifetime_safety_suggestion_returned_here)
216 << EscapeExpr->getSourceRange();
217 }
218
219 void reportNoescapeViolation(const ParmVarDecl *ParmWithNoescape,
220 const FieldDecl *EscapeField) override {
221 S.Diag(ParmWithNoescape->getBeginLoc(),
222 diag::warn_lifetime_safety_noescape_escapes)
223 << ParmWithNoescape->getSourceRange();
224
225 S.Diag(EscapeField->getLocation(),
226 diag::note_lifetime_safety_escapes_to_field_here)
227 << EscapeField->getEndLoc();
228 }
229
230 void reportNoescapeViolation(const ParmVarDecl *ParmWithNoescape,
231 const VarDecl *EscapeGlobal) override {
232 S.Diag(ParmWithNoescape->getBeginLoc(),
233 diag::warn_lifetime_safety_noescape_escapes)
234 << ParmWithNoescape->getSourceRange();
235 if (EscapeGlobal->isStaticLocal() || EscapeGlobal->isStaticDataMember())
236 S.Diag(EscapeGlobal->getLocation(),
237 diag::note_lifetime_safety_escapes_to_static_storage_here)
238 << EscapeGlobal->getEndLoc();
239 else
240 S.Diag(EscapeGlobal->getLocation(),
241 diag::note_lifetime_safety_escapes_to_global_here)
242 << EscapeGlobal->getEndLoc();
243 }
244
246 S.addLifetimeBoundToImplicitThis(const_cast<CXXMethodDecl *>(MD));
247 }
248
249private:
250 Sema &S;
251};
252
253} // namespace clang::lifetimes
254
255#endif // LLVM_CLANG_LIB_SEMA_SEMALIFETIMESAFETY_H
Represents a static or instance method of a struct/union/class.
Definition DeclCXX.h:2136
Decl - This represents one declaration (or definition), e.g.
Definition DeclBase.h:86
SourceLocation getEndLoc() const LLVM_READONLY
Definition DeclBase.h:443
SourceLocation getLocation() const
Definition DeclBase.h:447
SourceLocation getBeginLoc() const LLVM_READONLY
Definition DeclBase.h:439
SourceLocation getBeginLoc() const LLVM_READONLY
Definition Decl.h:831
TypeSourceInfo * getTypeSourceInfo() const
Definition Decl.h:809
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:277
Represents a member of a struct/union/class.
Definition Decl.h:3175
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:2226
Represents a prototype with parameter type info, e.g.
Definition TypeBase.h:5362
bool hasTrailingReturn() const
Whether this function prototype has a trailing return type.
Definition TypeBase.h:5782
Wrapper for source info for functions.
Definition TypeLoc.h:1644
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:1384
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:859
IdentifierInfo * getIdentifier() const
Get the identifier that names this declaration, if there is one.
Definition Decl.h:295
Represents a parameter to a function.
Definition Decl.h:1805
SourceRange getSourceRange() const override LLVM_READONLY
Source range that this declaration covers.
Definition Decl.cpp:2946
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
DiagnosticsEngine & getDiagnostics() const
Definition Sema.h:936
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
TypeLoc getTypeLoc() const
Return the TypeLoc wrapper for the type source info.
Definition TypeLoc.h:267
const T * getAs() const
Member-template getAs<specific type>'.
Definition TypeBase.h:9266
QualType getType() const
Definition Decl.h:723
Represents a variable declaration or definition.
Definition Decl.h:926
bool isStaticDataMember() const
Determines whether this is a static data member.
Definition Decl.h:1298
bool isStaticLocal() const
Returns true if a variable with function scope is a static local variable.
Definition Decl.h:1208
void reportNoescapeViolation(const ParmVarDecl *ParmWithNoescape, const Expr *EscapeExpr) override
void reportUseAfterInvalidation(const ParmVarDecl *PVD, const Expr *UseExpr, const Expr *InvalidationExpr) override
void reportNoescapeViolation(const ParmVarDecl *ParmWithNoescape, const VarDecl *EscapeGlobal) override
void suggestLifetimeboundToImplicitThis(SuggestionScope Scope, const CXXMethodDecl *MD, const Expr *EscapeExpr) override
void reportDanglingGlobal(const Expr *IssueExpr, const VarDecl *DanglingGlobal, const Expr *MovedExpr, SourceLocation ExpiryLoc) override
void reportDanglingField(const Expr *IssueExpr, const FieldDecl *DanglingField, const Expr *MovedExpr, SourceLocation ExpiryLoc) override
void suggestLifetimeboundToParmVar(SuggestionScope Scope, const ParmVarDecl *ParmToAnnotate, EscapingTarget Target) override
void addLifetimeBoundToImplicitThis(const CXXMethodDecl *MD) override
void reportUseAfterInvalidation(const Expr *IssueExpr, const Expr *UseExpr, const Expr *InvalidationExpr) override
void reportUseAfterScope(const Expr *IssueExpr, const Expr *UseExpr, const Expr *MovedExpr, SourceLocation FreeLoc) override
void reportNoescapeViolation(const ParmVarDecl *ParmWithNoescape, const FieldDecl *EscapeField) override
void reportUseAfterReturn(const Expr *IssueExpr, const Expr *ReturnExpr, const Expr *MovedExpr, SourceLocation ExpiryLoc) override
llvm::PointerUnion< const Expr *, const FieldDecl *, const VarDecl * > EscapingTarget
SuggestionScope
Enum to track functions visible across or within TU.
bool IsLifetimeSafetyDiagnosticEnabled(Sema &S, const Decl *D)
bool isa(CodeGen::Address addr)
Definition Address.h:330
SourceRange getSourceRange() const LLVM_READONLY
getSourceRange - The range of the declaration name.