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 reportUseAfterFree(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 S.Diag(IssueExpr->getExprLoc(), diag::warn_lifetime_safety_invalidation)
115 << false << IssueExpr->getSourceRange();
116 S.Diag(InvalidationExpr->getExprLoc(),
117 diag::note_lifetime_safety_invalidated_here)
118 << InvalidationExpr->getSourceRange();
119 S.Diag(UseExpr->getExprLoc(), diag::note_lifetime_safety_used_here)
120 << UseExpr->getSourceRange();
121 }
122 void reportUseAfterInvalidation(const ParmVarDecl *PVD, const Expr *UseExpr,
123 const Expr *InvalidationExpr) override {
124 S.Diag(PVD->getSourceRange().getBegin(),
125 diag::warn_lifetime_safety_invalidation)
126 << true << PVD->getSourceRange();
127 S.Diag(InvalidationExpr->getExprLoc(),
128 diag::note_lifetime_safety_invalidated_here)
129 << InvalidationExpr->getSourceRange();
130 S.Diag(UseExpr->getExprLoc(), diag::note_lifetime_safety_used_here)
131 << UseExpr->getSourceRange();
132 }
133
135 const ParmVarDecl *ParmToAnnotate,
136 const Expr *EscapeExpr) override {
137 unsigned DiagID =
139 ? diag::warn_lifetime_safety_cross_tu_param_suggestion
140 : diag::warn_lifetime_safety_intra_tu_param_suggestion;
142 ParmToAnnotate->getEndLoc(), 0, S.getSourceManager(), S.getLangOpts());
143 StringRef FixItText = " [[clang::lifetimebound]]";
144 if (!ParmToAnnotate->getIdentifier()) {
145 // For unnamed parameters, placing attributes after the type would be
146 // parsed as a type attribute, not a parameter attribute.
147 InsertionPoint = ParmToAnnotate->getBeginLoc();
148 FixItText = "[[clang::lifetimebound]] ";
149 }
150 S.Diag(ParmToAnnotate->getBeginLoc(), DiagID)
151 << ParmToAnnotate->getSourceRange()
152 << FixItHint::CreateInsertion(InsertionPoint, FixItText);
153 S.Diag(EscapeExpr->getBeginLoc(),
154 diag::note_lifetime_safety_suggestion_returned_here)
155 << EscapeExpr->getSourceRange();
156 }
157
159 const CXXMethodDecl *MD,
160 const Expr *EscapeExpr) override {
161 unsigned DiagID = (Scope == SuggestionScope::CrossTU)
162 ? diag::warn_lifetime_safety_cross_tu_this_suggestion
163 : diag::warn_lifetime_safety_intra_tu_this_suggestion;
164 const auto MDL = MD->getTypeSourceInfo()->getTypeLoc();
166 MDL.getEndLoc(), 0, S.getSourceManager(), S.getLangOpts());
167 if (const auto *FPT = MD->getType()->getAs<FunctionProtoType>();
168 FPT && FPT->hasTrailingReturn()) {
169 // For trailing return types, 'getEndLoc()' includes the return type
170 // after '->', placing the attribute in an invalid position.
171 // Instead use 'getLocalRangeEnd()' which gives the '->' location
172 // for trailing returns, so find the last token before it.
173 const auto FTL = MDL.getAs<FunctionTypeLoc>();
174 assert(FTL);
175 InsertionPoint = Lexer::getLocForEndOfToken(
176 Lexer::findPreviousToken(FTL.getLocalRangeEnd(), S.getSourceManager(),
177 S.getLangOpts(),
178 /*IncludeComments=*/false)
179 ->getLocation(),
180 0, S.getSourceManager(), S.getLangOpts());
181 }
182 S.Diag(InsertionPoint, DiagID)
183 << MD->getNameInfo().getSourceRange()
184 << FixItHint::CreateInsertion(InsertionPoint,
185 " [[clang::lifetimebound]]");
186 S.Diag(EscapeExpr->getBeginLoc(),
187 diag::note_lifetime_safety_suggestion_returned_here)
188 << EscapeExpr->getSourceRange();
189 }
190
191 void reportNoescapeViolation(const ParmVarDecl *ParmWithNoescape,
192 const Expr *EscapeExpr) override {
193 S.Diag(ParmWithNoescape->getBeginLoc(),
194 diag::warn_lifetime_safety_noescape_escapes)
195 << ParmWithNoescape->getSourceRange();
196
197 S.Diag(EscapeExpr->getBeginLoc(),
198 diag::note_lifetime_safety_suggestion_returned_here)
199 << EscapeExpr->getSourceRange();
200 }
201
202 void reportNoescapeViolation(const ParmVarDecl *ParmWithNoescape,
203 const FieldDecl *EscapeField) override {
204 S.Diag(ParmWithNoescape->getBeginLoc(),
205 diag::warn_lifetime_safety_noescape_escapes)
206 << ParmWithNoescape->getSourceRange();
207
208 S.Diag(EscapeField->getLocation(),
209 diag::note_lifetime_safety_escapes_to_field_here)
210 << EscapeField->getEndLoc();
211 }
212
213 void reportNoescapeViolation(const ParmVarDecl *ParmWithNoescape,
214 const VarDecl *EscapeGlobal) override {
215 S.Diag(ParmWithNoescape->getBeginLoc(),
216 diag::warn_lifetime_safety_noescape_escapes)
217 << ParmWithNoescape->getSourceRange();
218 if (EscapeGlobal->isStaticLocal() || EscapeGlobal->isStaticDataMember())
219 S.Diag(EscapeGlobal->getLocation(),
220 diag::note_lifetime_safety_escapes_to_static_storage_here)
221 << EscapeGlobal->getEndLoc();
222 else
223 S.Diag(EscapeGlobal->getLocation(),
224 diag::note_lifetime_safety_escapes_to_global_here)
225 << EscapeGlobal->getEndLoc();
226 }
227
229 S.addLifetimeBoundToImplicitThis(const_cast<CXXMethodDecl *>(MD));
230 }
231
232private:
233 Sema &S;
234};
235
236} // namespace clang::lifetimes
237
238#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:435
SourceLocation getLocation() const
Definition DeclBase.h:439
SourceLocation getBeginLoc() const LLVM_READONLY
Definition DeclBase.h:431
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:232
bool isIgnored(unsigned DiagID, SourceLocation Loc) const
Determine whether the diagnostic is known to be ignored.
Definition Diagnostic.h:959
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:3160
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:2211
Represents a prototype with parameter type info, e.g.
Definition TypeBase.h:5315
bool hasTrailingReturn() const
Whether this function prototype has a trailing return type.
Definition TypeBase.h:5735
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:1370
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:858
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:1790
SourceRange getSourceRange() const override LLVM_READONLY
Source range that this declaration covers.
Definition Decl.cpp:2981
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:9218
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:1283
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 suggestLifetimeboundToParmVar(SuggestionScope Scope, const ParmVarDecl *ParmToAnnotate, const Expr *EscapeExpr) override
void reportDanglingGlobal(const Expr *IssueExpr, const VarDecl *DanglingGlobal, const Expr *MovedExpr, SourceLocation ExpiryLoc) override
void reportUseAfterFree(const Expr *IssueExpr, const Expr *UseExpr, const Expr *MovedExpr, SourceLocation FreeLoc) 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 reportNoescapeViolation(const ParmVarDecl *ParmWithNoescape, const FieldDecl *EscapeField) override
void reportUseAfterReturn(const Expr *IssueExpr, const Expr *ReturnExpr, const Expr *MovedExpr, SourceLocation ExpiryLoc) override
SuggestionScope
Enum to track functions visible across or within TU.
bool IsLifetimeSafetyDiagnosticEnabled(Sema &S, const Decl *D)
SourceRange getSourceRange() const LLVM_READONLY
getSourceRange - The range of the declaration name.