26#include "llvm/ADT/DenseMap.h"
27#include "llvm/Support/ErrorHandling.h"
28#include "llvm/Support/TimeProfiler.h"
41 llvm_unreachable(
"unknown liveness kind");
47struct PendingWarning {
49 llvm::PointerUnion<const UseFact *, const OriginEscapesFact *> CausingFact;
53using AnnotationTarget =
54 llvm::PointerUnion<const ParmVarDecl *, const CXXMethodDecl *>;
55using EscapingTarget = llvm::PointerUnion<const Expr *, const FieldDecl *>;
57class LifetimeChecker {
59 llvm::DenseMap<LoanID, PendingWarning> FinalWarningsMap;
60 llvm::DenseMap<AnnotationTarget, const Expr *> AnnotationWarningsMap;
61 llvm::DenseMap<const ParmVarDecl *, EscapingTarget> NoescapeWarningsMap;
62 const LoanPropagationAnalysis &LoanPropagation;
63 const LiveOriginsAnalysis &LiveOrigins;
64 const FactManager &FactMgr;
65 LifetimeSafetySemaHelper *SemaHelper;
69 LifetimeChecker(
const LoanPropagationAnalysis &LoanPropagation,
70 const LiveOriginsAnalysis &LiveOrigins,
const FactManager &FM,
72 LifetimeSafetySemaHelper *SemaHelper)
73 : LoanPropagation(LoanPropagation), LiveOrigins(LiveOrigins), FactMgr(FM),
74 SemaHelper(SemaHelper), AST(ADC.getASTContext()) {
76 for (
const Fact *F : FactMgr.getFacts(B))
77 if (
const auto *EF = F->getAs<ExpireFact>())
79 else if (
const auto *OEF = F->getAs<OriginEscapesFact>())
80 checkAnnotations(OEF);
81 issuePendingWarnings();
83 reportNoescapeViolations();
87 if (AST.
getLangOpts().EnableLifetimeSafetyInference)
94 void checkAnnotations(
const OriginEscapesFact *OEF) {
95 OriginID EscapedOID = OEF->getEscapedOriginID();
96 LoanSet EscapedLoans = LoanPropagation.getLoans(EscapedOID, OEF);
97 auto CheckParam = [&](
const ParmVarDecl *PVD) {
99 if (PVD->hasAttr<NoEscapeAttr>()) {
100 if (
auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF))
101 NoescapeWarningsMap.try_emplace(PVD, ReturnEsc->getReturnExpr());
102 if (
auto *FieldEsc = dyn_cast<FieldEscapeFact>(OEF))
103 NoescapeWarningsMap.try_emplace(PVD, FieldEsc->getFieldDecl());
107 if (!PVD->hasAttr<LifetimeBoundAttr>())
108 if (
auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF))
109 AnnotationWarningsMap.try_emplace(PVD, ReturnEsc->getReturnExpr());
113 auto CheckImplicitThis = [&](
const CXXMethodDecl *MD) {
115 if (
auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF))
116 AnnotationWarningsMap.try_emplace(MD, ReturnEsc->getReturnExpr());
118 for (
LoanID LID : EscapedLoans) {
119 const Loan *L = FactMgr.getLoanMgr().getLoan(LID);
120 const auto *PL = dyn_cast<PlaceholderLoan>(L);
123 if (
const auto *PVD = PL->getParmVarDecl())
125 else if (
const auto *MD = PL->getMethodDecl())
126 CheckImplicitThis(MD);
141 void checkExpiry(
const ExpireFact *EF) {
142 LoanID ExpiredLoan = EF->getLoanID();
143 LivenessMap Origins = LiveOrigins.getLiveOriginsAt(EF);
147 llvm::PointerUnion<const UseFact *, const OriginEscapesFact *>
148 BestCausingFact =
nullptr;
150 for (
auto &[OID, LiveInfo] : Origins) {
151 LoanSet HeldLoans = LoanPropagation.getLoans(OID, EF);
152 if (!HeldLoans.contains(ExpiredLoan))
156 if (CurConfidence < NewConfidence) {
157 CurConfidence = NewConfidence;
158 BestCausingFact = LiveInfo.CausingFact;
161 if (!BestCausingFact)
164 Confidence LastConf = FinalWarningsMap.lookup(ExpiredLoan).ConfidenceLevel;
165 if (LastConf >= CurConfidence)
167 FinalWarningsMap[ExpiredLoan] = {EF->getExpiryLoc(),
172 void issuePendingWarnings() {
175 for (
const auto &[LID,
Warning] : FinalWarningsMap) {
176 const Loan *L = FactMgr.getLoanMgr().getLoan(LID);
178 const Expr *IssueExpr = BL->getIssueExpr();
179 llvm::PointerUnion<const UseFact *, const OriginEscapesFact *>
180 CausingFact =
Warning.CausingFact;
182 SourceLocation ExpiryLoc =
Warning.ExpiryLoc;
184 if (
const auto *UF = CausingFact.dyn_cast<
const UseFact *>())
185 SemaHelper->reportUseAfterFree(IssueExpr, UF->getUseExpr(), ExpiryLoc,
187 else if (
const auto *OEF =
188 CausingFact.dyn_cast<
const OriginEscapesFact *>()) {
189 if (
const auto *RetEscape = dyn_cast<ReturnEscapeFact>(OEF))
190 SemaHelper->reportUseAfterReturn(
191 IssueExpr, RetEscape->getReturnExpr(), ExpiryLoc,
Confidence);
192 else if (
const auto *FieldEscape = dyn_cast<FieldEscapeFact>(OEF))
193 SemaHelper->reportDanglingField(
194 IssueExpr, FieldEscape->getFieldDecl(), ExpiryLoc);
196 llvm_unreachable(
"Unhandled OriginEscapesFact type");
198 llvm_unreachable(
"Unhandled CausingFact type");
204 static const FunctionDecl *getCrossTUDecl(
const FunctionDecl &FD,
206 if (!FD.isExternallyVisible())
208 const FileID DefinitionFile =
SM.getFileID(FD.getLocation());
209 for (
const FunctionDecl *Redecl : FD.redecls())
210 if (
SM.getFileID(Redecl->getLocation()) != DefinitionFile)
216 static const FunctionDecl *getCrossTUDecl(
const ParmVarDecl &PVD,
218 if (
const auto *FD = dyn_cast<FunctionDecl>(PVD.getDeclContext()))
219 return getCrossTUDecl(*FD,
SM);
223 static void suggestWithScopeForParmVar(LifetimeSafetySemaHelper *SemaHelper,
224 const ParmVarDecl *PVD,
226 const Expr *EscapeExpr) {
227 if (
const FunctionDecl *CrossTUDecl = getCrossTUDecl(*PVD,
SM))
228 SemaHelper->suggestLifetimeboundToParmVar(
230 CrossTUDecl->getParamDecl(PVD->getFunctionScopeIndex()), EscapeExpr);
237 suggestWithScopeForImplicitThis(LifetimeSafetySemaHelper *SemaHelper,
238 const CXXMethodDecl *MD, SourceManager &
SM,
239 const Expr *EscapeExpr) {
240 if (
const FunctionDecl *CrossTUDecl = getCrossTUDecl(*MD,
SM))
241 SemaHelper->suggestLifetimeboundToImplicitThis(
249 void suggestAnnotations() {
252 SourceManager &
SM = AST.getSourceManager();
253 for (
auto [
Target, EscapeExpr] : AnnotationWarningsMap) {
254 if (
const auto *PVD =
Target.dyn_cast<
const ParmVarDecl *>())
255 suggestWithScopeForParmVar(SemaHelper, PVD,
SM, EscapeExpr);
256 else if (
const auto *MD =
Target.dyn_cast<
const CXXMethodDecl *>())
257 suggestWithScopeForImplicitThis(SemaHelper, MD,
SM, EscapeExpr);
261 void reportNoescapeViolations() {
262 for (
auto [PVD, EscapeTarget] : NoescapeWarningsMap) {
263 if (
const auto *E = EscapeTarget.dyn_cast<
const Expr *>())
264 SemaHelper->reportNoescapeViolation(PVD, E);
265 else if (
const auto *FD = EscapeTarget.dyn_cast<
const FieldDecl *>())
266 SemaHelper->reportNoescapeViolation(PVD, FD);
268 llvm_unreachable(
"Unhandled EscapingTarget type");
272 void inferAnnotations() {
273 for (
auto [
Target, EscapeExpr] : AnnotationWarningsMap) {
274 if (
const auto *MD =
Target.dyn_cast<
const CXXMethodDecl *>()) {
277 }
else if (
const auto *PVD =
Target.dyn_cast<
const ParmVarDecl *>()) {
278 const auto *FD = dyn_cast<FunctionDecl>(PVD->getDeclContext());
284 ParmVarDecl *InferredPVD =
const_cast<ParmVarDecl *
>(
285 FD->getParamDecl(PVD->getFunctionScopeIndex()));
286 if (!InferredPVD->hasAttr<LifetimeBoundAttr>())
287 InferredPVD->addAttr(
288 LifetimeBoundAttr::CreateImplicit(AST, PVD->getLocation()));
299 llvm::TimeTraceScope TimeProfile(
"LifetimeChecker");
300 LifetimeChecker Checker(LP, LO, FactMgr, ADC, SemaHelper);
This file defines AnalysisDeclContext, a class that manages the analysis context data for context sen...
Defines the clang::SourceLocation class and associated facilities.
Defines the SourceManager interface.
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
const LangOptions & getLangOpts() const
AnalysisDeclContext contains the context data for the function, method or block under analysis.
Represents a single basic block in a source-level CFG.
Encodes a location in the source.
Abstract interface for operations requiring Sema access.
llvm::ImmutableSet< LoanID > LoanSet
utils::ID< struct LoanTag > LoanID
utils::ID< struct OriginTag > OriginID
llvm::ImmutableMap< OriginID, LivenessInfo > LivenessMap
void runLifetimeChecker(const LoanPropagationAnalysis &LoanPropagation, const LiveOriginsAnalysis &LiveOrigins, const FactManager &FactMgr, AnalysisDeclContext &ADC, LifetimeSafetySemaHelper *SemaHelper)
Runs the lifetime checker, which detects use-after-free errors by examining loan expiration points an...
static Confidence livenessKindToConfidence(LivenessKind K)
bool implicitObjectParamIsLifetimeBound(const FunctionDecl *FD)
const FunctionDecl * getDeclWithMergedLifetimeBoundAttrs(const FunctionDecl *FD)
Confidence
Enum to track the confidence level of a potential error.
if(T->getSizeExpr()) TRY_TO(TraverseStmt(const_cast< Expr * >(T -> getSizeExpr())))
for(const auto &A :T->param_types())
U cast(CodeGen::Address addr)