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"
22#include "clang/Sema/Sema.h"
23
24namespace clang::lifetimes {
25
26inline bool IsLifetimeSafetyEnabled(Sema &S, const Decl *D) {
27 if (S.getLangOpts().DebugRunLifetimeSafety)
28 return true;
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_noescape_escapes,
42 diag::warn_lifetime_safety_lifetimebound_violation,
43 };
44 for (unsigned DiagID : DiagIDs)
45 if (!Diags.isIgnored(DiagID, D->getBeginLoc()))
46 return true;
47 return false;
48}
49
51
52public:
54
55 void reportUseAfterScope(const Expr *IssueExpr, const Expr *UseExpr,
56 const Expr *MovedExpr,
57 SourceLocation FreeLoc) override {
58 S.Diag(IssueExpr->getExprLoc(),
59 MovedExpr ? diag::warn_lifetime_safety_use_after_scope_moved
60 : diag::warn_lifetime_safety_use_after_scope)
61 << IssueExpr->getSourceRange();
62 if (MovedExpr)
63 S.Diag(MovedExpr->getExprLoc(), diag::note_lifetime_safety_moved_here)
64 << MovedExpr->getSourceRange();
65 S.Diag(FreeLoc, diag::note_lifetime_safety_destroyed_here);
66 S.Diag(UseExpr->getExprLoc(), diag::note_lifetime_safety_used_here)
67 << UseExpr->getSourceRange();
68 }
69
70 void reportUseAfterReturn(const Expr *IssueExpr, const Expr *ReturnExpr,
71 const Expr *MovedExpr,
72 SourceLocation ExpiryLoc) override {
73 S.Diag(IssueExpr->getExprLoc(),
74 MovedExpr ? diag::warn_lifetime_safety_return_stack_addr_moved
75 : diag::warn_lifetime_safety_return_stack_addr)
76 << IssueExpr->getSourceRange();
77 if (MovedExpr)
78 S.Diag(MovedExpr->getExprLoc(), diag::note_lifetime_safety_moved_here)
79 << MovedExpr->getSourceRange();
80 S.Diag(ReturnExpr->getExprLoc(), diag::note_lifetime_safety_returned_here)
81 << ReturnExpr->getSourceRange();
82 }
83
84 void reportDanglingField(const Expr *IssueExpr,
85 const FieldDecl *DanglingField,
86 const Expr *MovedExpr,
87 SourceLocation ExpiryLoc) override {
88 S.Diag(IssueExpr->getExprLoc(),
89 MovedExpr ? diag::warn_lifetime_safety_dangling_field_moved
90 : diag::warn_lifetime_safety_dangling_field)
91 << IssueExpr->getSourceRange();
92 if (MovedExpr)
93 S.Diag(MovedExpr->getExprLoc(), diag::note_lifetime_safety_moved_here)
94 << MovedExpr->getSourceRange();
95 S.Diag(DanglingField->getLocation(),
96 diag::note_lifetime_safety_dangling_field_here)
97 << DanglingField->getEndLoc();
98 }
99
100 void reportDanglingGlobal(const Expr *IssueExpr,
101 const VarDecl *DanglingGlobal,
102 const Expr *MovedExpr,
103 SourceLocation ExpiryLoc) override {
104 S.Diag(IssueExpr->getExprLoc(),
105 MovedExpr ? diag::warn_lifetime_safety_dangling_global_moved
106 : diag::warn_lifetime_safety_dangling_global)
107 << IssueExpr->getSourceRange();
108 if (MovedExpr)
109 S.Diag(MovedExpr->getExprLoc(), diag::note_lifetime_safety_moved_here)
110 << MovedExpr->getSourceRange();
111 if (DanglingGlobal->isStaticLocal() || DanglingGlobal->isStaticDataMember())
112 S.Diag(DanglingGlobal->getLocation(),
113 diag::note_lifetime_safety_dangling_static_here)
114 << DanglingGlobal->getEndLoc();
115 else
116 S.Diag(DanglingGlobal->getLocation(),
117 diag::note_lifetime_safety_dangling_global_here)
118 << DanglingGlobal->getEndLoc();
119 }
120
121 void reportUseAfterInvalidation(const Expr *IssueExpr, const Expr *UseExpr,
122 const Expr *InvalidationExpr) override {
123 auto WarnDiag = isa<CXXDeleteExpr>(InvalidationExpr)
124 ? diag::warn_lifetime_safety_use_after_free
125 : diag::warn_lifetime_safety_invalidation;
126 auto UseDiag = isa<CXXDeleteExpr>(InvalidationExpr)
127 ? diag::note_lifetime_safety_freed_here
128 : diag::note_lifetime_safety_invalidated_here;
129 S.Diag(IssueExpr->getExprLoc(), WarnDiag)
130 << false << IssueExpr->getSourceRange();
131 S.Diag(InvalidationExpr->getExprLoc(), UseDiag)
132 << InvalidationExpr->getSourceRange();
133 S.Diag(UseExpr->getExprLoc(), diag::note_lifetime_safety_used_here)
134 << UseExpr->getSourceRange();
135 }
136 void reportUseAfterInvalidation(const ParmVarDecl *PVD, const Expr *UseExpr,
137 const Expr *InvalidationExpr) override {
138
139 auto WarnDiag = isa<CXXDeleteExpr>(InvalidationExpr)
140 ? diag::warn_lifetime_safety_use_after_free
141 : diag::warn_lifetime_safety_invalidation;
142 auto UseDiag = isa<CXXDeleteExpr>(InvalidationExpr)
143 ? diag::note_lifetime_safety_freed_here
144 : diag::note_lifetime_safety_invalidated_here;
145
146 S.Diag(PVD->getSourceRange().getBegin(), WarnDiag)
147 << true << PVD->getSourceRange();
148 S.Diag(InvalidationExpr->getExprLoc(), UseDiag)
149 << InvalidationExpr->getSourceRange();
150 S.Diag(UseExpr->getExprLoc(), diag::note_lifetime_safety_used_here)
151 << UseExpr->getSourceRange();
152 }
153
154 void reportInvalidatedField(const Expr *IssueExpr,
155 const FieldDecl *DanglingField,
156 const Expr *InvalidationExpr) override {
157 auto InvalidationDiag = isa<CXXDeleteExpr>(InvalidationExpr)
158 ? diag::note_lifetime_safety_freed_here
159 : diag::note_lifetime_safety_invalidated_here;
160 S.Diag(IssueExpr->getExprLoc(),
161 diag::warn_lifetime_safety_invalidated_field)
162 << false << IssueExpr->getSourceRange();
163 S.Diag(InvalidationExpr->getExprLoc(), InvalidationDiag)
164 << InvalidationExpr->getSourceRange();
165 S.Diag(DanglingField->getLocation(),
166 diag::note_lifetime_safety_dangling_field_here)
167 << DanglingField->getEndLoc();
168 }
169
171 const FieldDecl *DanglingField,
172 const Expr *InvalidationExpr) override {
173 auto InvalidationDiag = isa<CXXDeleteExpr>(InvalidationExpr)
174 ? diag::note_lifetime_safety_freed_here
175 : diag::note_lifetime_safety_invalidated_here;
176 S.Diag(PVD->getSourceRange().getBegin(),
177 diag::warn_lifetime_safety_invalidated_field)
178 << true << PVD->getSourceRange();
179 S.Diag(InvalidationExpr->getExprLoc(), InvalidationDiag)
180 << InvalidationExpr->getSourceRange();
181 S.Diag(DanglingField->getLocation(),
182 diag::note_lifetime_safety_dangling_field_here)
183 << DanglingField->getEndLoc();
184 }
185
186 void reportInvalidatedGlobal(const Expr *IssueExpr,
187 const VarDecl *DanglingGlobal,
188 const Expr *InvalidationExpr) override {
189 auto InvalidationDiag = isa<CXXDeleteExpr>(InvalidationExpr)
190 ? diag::note_lifetime_safety_freed_here
191 : diag::note_lifetime_safety_invalidated_here;
192 S.Diag(IssueExpr->getExprLoc(),
193 diag::warn_lifetime_safety_invalidated_global)
194 << false << IssueExpr->getSourceRange();
195 S.Diag(InvalidationExpr->getExprLoc(), InvalidationDiag)
196 << InvalidationExpr->getSourceRange();
197 if (DanglingGlobal->isStaticLocal() || DanglingGlobal->isStaticDataMember())
198 S.Diag(DanglingGlobal->getLocation(),
199 diag::note_lifetime_safety_dangling_static_here)
200 << DanglingGlobal->getEndLoc();
201 else
202 S.Diag(DanglingGlobal->getLocation(),
203 diag::note_lifetime_safety_dangling_global_here)
204 << DanglingGlobal->getEndLoc();
205 }
206
208 const VarDecl *DanglingGlobal,
209 const Expr *InvalidationExpr) override {
210 auto InvalidationDiag = isa<CXXDeleteExpr>(InvalidationExpr)
211 ? diag::note_lifetime_safety_freed_here
212 : diag::note_lifetime_safety_invalidated_here;
213 S.Diag(PVD->getSourceRange().getBegin(),
214 diag::warn_lifetime_safety_invalidated_global)
215 << true << PVD->getSourceRange();
216 S.Diag(InvalidationExpr->getExprLoc(), InvalidationDiag)
217 << InvalidationExpr->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
229 const ParmVarDecl *ParmToAnnotate,
230 EscapingTarget Target) override {
231 unsigned DiagID =
233 ? diag::warn_lifetime_safety_cross_tu_param_suggestion
234 : diag::warn_lifetime_safety_intra_tu_param_suggestion;
236 ParmToAnnotate->getEndLoc(), 0, S.getSourceManager(), S.getLangOpts());
237 StringRef FixItText = " [[clang::lifetimebound]]";
238 if (!ParmToAnnotate->getIdentifier()) {
239 // For unnamed parameters, placing attributes after the type would be
240 // parsed as a type attribute, not a parameter attribute.
241 InsertionPoint = ParmToAnnotate->getBeginLoc();
242 FixItText = "[[clang::lifetimebound]] ";
243 }
244 S.Diag(ParmToAnnotate->getBeginLoc(), DiagID)
245 << ParmToAnnotate->getSourceRange()
246 << FixItHint::CreateInsertion(InsertionPoint, FixItText);
247
248 if (const auto *EscapeExpr = Target.dyn_cast<const Expr *>())
249 S.Diag(EscapeExpr->getBeginLoc(),
250 diag::note_lifetime_safety_suggestion_returned_here)
251 << EscapeExpr->getSourceRange();
252 else if (const auto *EscapeField = Target.dyn_cast<const FieldDecl *>())
253 S.Diag(EscapeField->getLocation(),
254 diag::note_lifetime_safety_escapes_to_field_here)
255 << EscapeField->getSourceRange();
256 }
257
259 const ParmVarDecl *ParmWithLifetimebound) override {
260 const auto *Attr = ParmWithLifetimebound->getAttr<LifetimeBoundAttr>();
261 StringRef ParamName = ParmWithLifetimebound->getName();
262 bool HasName = ParamName.size() > 0;
263 S.Diag(Attr->getLocation(),
264 diag::warn_lifetime_safety_lifetimebound_violation)
265 << HasName << ParamName << Attr->getRange();
266 }
267
269 const CXXMethodDecl *MDWithLifetimebound) override {
270 const auto *Attr =
271 getImplicitObjectParamLifetimeBoundAttr(MDWithLifetimebound);
272 assert(Attr && "Expected lifetimebound attribute");
273 S.Diag(Attr->getLocation(),
274 diag::warn_lifetime_safety_lifetimebound_violation)
275 << 2 << "" << Attr->getRange();
276 }
277
279 const CXXMethodDecl *MD,
280 const Expr *EscapeExpr) override {
281 unsigned DiagID = (Scope == SuggestionScope::CrossTU)
282 ? diag::warn_lifetime_safety_cross_tu_this_suggestion
283 : diag::warn_lifetime_safety_intra_tu_this_suggestion;
284 const auto MDL = MD->getTypeSourceInfo()->getTypeLoc();
286 MDL.getEndLoc(), 0, S.getSourceManager(), S.getLangOpts());
287 if (const auto *FPT = MD->getType()->getAs<FunctionProtoType>();
288 FPT && FPT->hasTrailingReturn()) {
289 // For trailing return types, 'getEndLoc()' includes the return type
290 // after '->', placing the attribute in an invalid position.
291 // Instead use 'getLocalRangeEnd()' which gives the '->' location
292 // for trailing returns, so find the last token before it.
293 const auto FTL = MDL.getAs<FunctionTypeLoc>();
294 assert(FTL);
295 InsertionPoint = Lexer::getLocForEndOfToken(
296 Lexer::findPreviousToken(FTL.getLocalRangeEnd(), S.getSourceManager(),
297 S.getLangOpts(),
298 /*IncludeComments=*/false)
299 ->getLocation(),
300 0, S.getSourceManager(), S.getLangOpts());
301 }
302 S.Diag(InsertionPoint, DiagID)
303 << MD->getNameInfo().getSourceRange()
304 << FixItHint::CreateInsertion(InsertionPoint,
305 " [[clang::lifetimebound]]");
306 S.Diag(EscapeExpr->getBeginLoc(),
307 diag::note_lifetime_safety_suggestion_returned_here)
308 << EscapeExpr->getSourceRange();
309 }
310
311 void reportNoescapeViolation(const ParmVarDecl *ParmWithNoescape,
312 const Expr *EscapeExpr) override {
313 S.Diag(ParmWithNoescape->getBeginLoc(),
314 diag::warn_lifetime_safety_noescape_escapes)
315 << ParmWithNoescape->getSourceRange();
316
317 S.Diag(EscapeExpr->getBeginLoc(),
318 diag::note_lifetime_safety_suggestion_returned_here)
319 << EscapeExpr->getSourceRange();
320 }
321
322 void reportNoescapeViolation(const ParmVarDecl *ParmWithNoescape,
323 const FieldDecl *EscapeField) override {
324 S.Diag(ParmWithNoescape->getBeginLoc(),
325 diag::warn_lifetime_safety_noescape_escapes)
326 << ParmWithNoescape->getSourceRange();
327
328 S.Diag(EscapeField->getLocation(),
329 diag::note_lifetime_safety_escapes_to_field_here)
330 << EscapeField->getEndLoc();
331 }
332
333 void reportNoescapeViolation(const ParmVarDecl *ParmWithNoescape,
334 const VarDecl *EscapeGlobal) override {
335 S.Diag(ParmWithNoescape->getBeginLoc(),
336 diag::warn_lifetime_safety_noescape_escapes)
337 << ParmWithNoescape->getSourceRange();
338 if (EscapeGlobal->isStaticLocal() || EscapeGlobal->isStaticDataMember())
339 S.Diag(EscapeGlobal->getLocation(),
340 diag::note_lifetime_safety_escapes_to_static_storage_here)
341 << EscapeGlobal->getEndLoc();
342 else
343 S.Diag(EscapeGlobal->getLocation(),
344 diag::note_lifetime_safety_escapes_to_global_here)
345 << EscapeGlobal->getEndLoc();
346 }
347
349 S.addLifetimeBoundToImplicitThis(const_cast<CXXMethodDecl *>(MD));
350 }
351
352private:
353 Sema &S;
354};
355
356} // namespace clang::lifetimes
357
358#endif // LLVM_CLANG_LIB_SEMA_SEMALIFETIMESAFETY_H
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:2132
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
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:282
Represents a member of a struct/union/class.
Definition Decl.h:3178
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:2229
Represents a prototype with parameter type info, e.g.
Definition TypeBase.h:5369
bool hasTrailingReturn() const
Whether this function prototype has a trailing return type.
Definition TypeBase.h:5789
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:1407
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
IdentifierInfo * getIdentifier() const
Get the identifier that names this declaration, if there is one.
Definition Decl.h:295
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:1808
SourceRange getSourceRange() const override LLVM_READONLY
Source range that this declaration covers.
Definition Decl.cpp:2975
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
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
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:9275
QualType getType() const
Definition Decl.h:723
Represents a variable declaration or definition.
Definition Decl.h:924
bool isStaticDataMember() const
Determines whether this is a static data member.
Definition Decl.h:1296
bool isStaticLocal() const
Returns true if a variable with function scope is a static local variable.
Definition Decl.h:1206
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 reportInvalidatedGlobal(const ParmVarDecl *PVD, const VarDecl *DanglingGlobal, const Expr *InvalidationExpr) 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 reportInvalidatedField(const Expr *IssueExpr, const FieldDecl *DanglingField, const Expr *InvalidationExpr) 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 reportInvalidatedGlobal(const Expr *IssueExpr, const VarDecl *DanglingGlobal, const Expr *InvalidationExpr) override
void reportLifetimeboundViolation(const ParmVarDecl *ParmWithLifetimebound) 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.
const LifetimeBoundAttr * getImplicitObjectParamLifetimeBoundAttr(const FunctionDecl *FD)
bool IsLifetimeSafetyEnabled(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.