17#include "llvm/ADT/FoldingSet.h"
18#include "llvm/ADT/ImmutableMap.h"
19#include "llvm/ADT/ImmutableSet.h"
20#include "llvm/ADT/PointerUnion.h"
21#include "llvm/ADT/SmallBitVector.h"
22#include "llvm/ADT/SmallVector.h"
23#include "llvm/Support/Debug.h"
24#include "llvm/Support/TimeProfiler.h"
54 void dump(llvm::raw_ostream &OS)
const {
55 OS <<
ID <<
" (Path: ";
56 OS <<
Path.D->getNameAsString() <<
")";
73 llvm::PointerUnion<const clang::ValueDecl *, const clang::Expr *>
Ptr;
92 AllLoans.emplace_back(getNextLoanID(), Path, IssueExpr);
93 return AllLoans.back();
97 assert(
ID.
Value < AllLoans.size());
103 LoanID getNextLoanID() {
return NextLoanID++; }
118 AllOrigins.emplace_back(
ID, &D);
119 return AllOrigins.back();
122 AllOrigins.emplace_back(
ID, &E);
123 return AllOrigins.back();
128 auto It = ExprToOriginID.find(&E);
129 if (It != ExprToOriginID.end())
135 if (
const auto *DRE = dyn_cast<DeclRefExpr>(&E))
136 return get(*DRE->getDecl());
144 auto It = DeclToOriginID.find(&D);
148 if (It == DeclToOriginID.end())
155 auto It = ExprToOriginID.find(&E);
156 if (It != ExprToOriginID.end())
161 ExprToOriginID[&E] = NewID;
166 assert(
ID.
Value < AllOrigins.size());
173 auto It = DeclToOriginID.find(&D);
174 if (It != DeclToOriginID.end())
178 DeclToOriginID[&D] = NewID;
186 OS <<
"Decl: " << VD->getNameAsString();
188 OS <<
"Expr: " << E->getStmtClassName();
195 OriginID getNextOriginID() {
return NextOriginID++; }
201 llvm::DenseMap<const clang::ValueDecl *, OriginID> DeclToOriginID;
202 llvm::DenseMap<const clang::Expr *, OriginID> ExprToOriginID;
235 template <
typename T>
const T *
getAs()
const {
236 if (T::classof(
this))
237 return static_cast<const T *
>(
this);
243 OS <<
"Fact (Kind: " <<
static_cast<int>(K) <<
")\n";
261 OS <<
", ToOrigin: ";
303 OS <<
"AssignOrigin (Dest: ";
323 OS <<
"ReturnOfOrigin (";
333 bool IsWritten =
false;
352 OS <<
", " << (
isWritten() ?
"Write" :
"Read") <<
")\n";
359 StringRef Annotation;
371 OS <<
"TestPoint (Annotation: \"" <<
getAnnotation() <<
"\")\n";
378 auto It = BlockToFactsMap.find(B);
379 if (It != BlockToFactsMap.end())
385 if (!NewFacts.empty())
386 BlockToFactsMap[B].assign(NewFacts.begin(), NewFacts.end());
389 template <
typename FactType,
typename... Args>
391 void *Mem = FactAllocator.Allocate<FactType>();
392 return new (Mem) FactType(std::forward<Args>(args)...);
396 llvm::dbgs() <<
"==========================================\n";
397 llvm::dbgs() <<
" Lifetime Analysis Facts:\n";
398 llvm::dbgs() <<
"==========================================\n";
400 if (
const auto *ND = dyn_cast<NamedDecl>(D))
401 llvm::dbgs() <<
"Function: " << ND->getQualifiedNameAsString() <<
"\n";
404 llvm::dbgs() <<
" Block B" << B->getBlockID() <<
":\n";
405 auto It = BlockToFactsMap.find(B);
406 if (It != BlockToFactsMap.end()) {
407 for (
const Fact *F : It->second) {
409 F->
dump(llvm::dbgs(), LoanMgr, OriginMgr);
412 llvm::dbgs() <<
" End of Block\n";
422 llvm::DenseMap<const clang::CFGBlock *, llvm::SmallVector<const Fact *>>
424 llvm::BumpPtrAllocator FactAllocator;
432 : FactMgr(FactMgr), AC(AC) {}
435 llvm::TimeTraceScope TimeProfile(
"FactGenerator");
439 CurrentBlockFacts.clear();
440 for (
unsigned I = 0; I <
Block->size(); ++I) {
442 if (std::optional<CFGStmt> CS = Element.
getAs<
CFGStmt>())
443 Visit(CS->getStmt());
444 else if (std::optional<CFGAutomaticObjDtor> DtorOpt =
448 FactMgr.addBlockFacts(
Block, CurrentBlockFacts);
454 if (
const auto *VD = dyn_cast<VarDecl>(D))
456 if (
const Expr *InitExpr = VD->getInit())
457 addAssignOriginFact(*VD, *InitExpr);
472 if (!isPointerType(DRE->
getType())) {
473 if (
const Loan *L = createLoan(DRE)) {
474 OriginID ExprOID = FactMgr.getOriginMgr().getOrCreate(*DRE);
475 CurrentBlockFacts.push_back(
476 FactMgr.createFact<
IssueFact>(L->ID, ExprOID));
482 if (isGslPointerType(CCE->
getType())) {
483 handleGSLPointerConstruction(CCE);
491 if (isGslPointerType(MCE->
getType()) &&
495 {MCE->getImplicitObjectArgument()});
503 FactMgr.getOriginMgr().getOrCreate(*N);
511 addAssignOriginFact(*ICE, *ICE->
getSubExpr());
519 if (isPointerType(SubExpr->
getType()))
525 addAssignOriginFact(*UO, *SubExpr);
531 if (hasOrigin(RetExpr)) {
532 OriginID OID = FactMgr.getOriginMgr().getOrCreate(*RetExpr);
533 CurrentBlockFacts.push_back(
552 if (handleTestPoint(FCE))
554 if (isGslPointerType(FCE->
getType()))
555 addAssignOriginFact(*FCE, *FCE->
getSubExpr());
564 addAssignOriginFact(*ILE, *ILE->
getInit(0));
572 addAssignOriginFact(*MTE, *MTE->
getSubExpr());
587 for (
const Loan &L : FactMgr.getLoanMgr().getLoans()) {
591 if (LoanPath.
D == DestructedVD)
592 CurrentBlockFacts.push_back(FactMgr.createFact<
ExpireFact>(
598 static bool isGslPointerType(
QualType QT) {
601 if (
auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(RD))
602 return CTSD->getSpecializedTemplate()
604 ->hasAttr<PointerAttr>();
605 return RD->hasAttr<PointerAttr>();
610 static bool isPointerType(
QualType QT) {
614 static bool hasOrigin(
const Expr *E) {
615 return E->isGLValue() || isPointerType(E->getType());
618 static bool hasOrigin(
const VarDecl *VD) {
619 return isPointerType(VD->getType());
622 void handleGSLPointerConstruction(
const CXXConstructExpr *CCE) {
623 assert(isGslPointerType(CCE->getType()));
624 if (CCE->getNumArgs() != 1)
626 if (hasOrigin(CCE->getArg(0)))
627 addAssignOriginFact(*CCE, *CCE->getArg(0));
630 handleFunctionCall(CCE, CCE->getConstructor(),
631 {CCE->getArgs(), CCE->getNumArgs()});
636 void handleFunctionCall(
const Expr *
Call,
const FunctionDecl *FD,
637 ArrayRef<const Expr *> Args) {
641 for (
unsigned I = 0; I <= 0 ; ++I) {
642 const Expr *ArgExpr = Args[I];
645 if (FD->isCXXClassMember() && I == 0) {
646 addAssignOriginFact(*
Call, *ArgExpr);
652 if (hasOrigin(ArgExpr))
653 addAssignOriginFact(*
Call, *ArgExpr);
661 const Loan *createLoan(
const DeclRefExpr *DRE) {
662 if (
const auto *VD = dyn_cast<ValueDecl>(DRE->getDecl())) {
665 return &FactMgr.getLoanMgr().addLoan(Path, DRE);
670 template <
typename Destination,
typename Source>
671 void addAssignOriginFact(
const Destination &D,
const Source &S) {
672 OriginID DestOID = FactMgr.getOriginMgr().getOrCreate(D);
673 OriginID SrcOID = FactMgr.getOriginMgr().get(S);
674 CurrentBlockFacts.push_back(
675 FactMgr.createFact<AssignOriginFact>(DestOID, SrcOID));
680 bool handleTestPoint(
const CXXFunctionalCastExpr *FCE) {
681 if (!FCE->getType()->isVoidType())
684 const auto *SubExpr = FCE->getSubExpr()->IgnoreParenImpCasts();
685 if (
const auto *SL = dyn_cast<StringLiteral>(SubExpr)) {
686 llvm::StringRef LiteralValue = SL->getString();
687 const std::string Prefix =
"__lifetime_test_point_";
689 if (LiteralValue.starts_with(Prefix)) {
690 StringRef Annotation = LiteralValue.drop_front(Prefix.length());
691 CurrentBlockFacts.push_back(
692 FactMgr.createFact<TestPointFact>(Annotation));
699 void handleAssignment(
const Expr *LHSExpr,
const Expr *RHSExpr) {
700 if (!hasOrigin(LHSExpr))
703 if (
const auto *DRE_LHS =
704 dyn_cast<DeclRefExpr>(LHSExpr->IgnoreParenImpCasts())) {
705 markUseAsWrite(DRE_LHS);
706 if (
const auto *VD_LHS = dyn_cast<ValueDecl>(DRE_LHS->getDecl()))
711 addAssignOriginFact(*VD_LHS, *RHSExpr);
718 void handleUse(
const DeclRefExpr *DRE) {
719 if (isPointerType(DRE->getType())) {
720 UseFact *UF = FactMgr.createFact<UseFact>(DRE);
721 CurrentBlockFacts.push_back(UF);
722 assert(!UseFacts.contains(DRE));
727 void markUseAsWrite(
const DeclRefExpr *DRE) {
728 if (!isPointerType(DRE->getType()))
730 assert(UseFacts.contains(DRE));
731 UseFacts[DRE]->markAsWritten();
734 FactManager &FactMgr;
735 AnalysisDeclContext &AC;
736 llvm::SmallVector<Fact *> CurrentBlockFacts;
742 llvm::DenseMap<const DeclRefExpr *, UseFact *> UseFacts;
776template <
typename Derived,
typename LatticeType, Direction Dir>
787 llvm::DenseMap<const CFGBlock *, Lattice> InStates;
789 llvm::DenseMap<const CFGBlock *, Lattice> OutStates;
793 llvm::DenseMap<ProgramPoint, Lattice> PerPointStates;
806 Derived &D =
static_cast<Derived &
>(*this);
807 llvm::TimeTraceScope Time(D.getAnalysisName());
814 const CFGBlock *Start = isForward() ? &Cfg.getEntry() : &Cfg.getExit();
815 InStates[Start] = D.getInitialState();
816 W.enqueueBlock(Start);
818 llvm::SmallBitVector Visited(Cfg.getNumBlockIDs() + 1);
820 while (
const CFGBlock *B = W.dequeue()) {
822 Lattice StateOut = transferBlock(B, StateIn);
823 OutStates[B] = StateOut;
824 Visited.set(B->getBlockID());
825 for (
const CFGBlock *AdjacentB : isForward() ? B->
succs() : B->preds()) {
829 Lattice NewInState = D.join(OldInState, StateOut);
832 if (!Visited.test(AdjacentB->getBlockID()) ||
833 NewInState != OldInState) {
834 InStates[AdjacentB] = NewInState;
835 W.enqueueBlock(AdjacentB);
849 const Derived *D =
static_cast<const Derived *
>(
this);
850 llvm::dbgs() <<
"==========================================\n";
851 llvm::dbgs() << D->getAnalysisName() <<
" results:\n";
852 llvm::dbgs() <<
"==========================================\n";
853 const CFGBlock &B = isForward() ? Cfg.getExit() : Cfg.getEntry();
862 if constexpr (isForward()) {
863 for (
const Fact *F : Facts) {
864 State = transferFact(State, F);
865 PerPointStates[F] = State;
868 for (
const Fact *F : llvm::reverse(Facts)) {
870 PerPointStates[F] = State;
871 State = transferFact(State, F);
879 Derived *D =
static_cast<Derived *
>(
this);
880 switch (F->getKind()) {
882 return D->transfer(In, *F->getAs<IssueFact>());
884 return D->transfer(In, *F->getAs<ExpireFact>());
886 return D->transfer(In, *F->getAs<AssignOriginFact>());
888 return D->transfer(In, *F->getAs<ReturnOfOriginFact>());
890 return D->transfer(In, *F->getAs<UseFact>());
892 return D->transfer(In, *F->getAs<TestPointFact>());
894 llvm_unreachable(
"Unknown fact kind");
910static llvm::ImmutableSet<T>
join(llvm::ImmutableSet<T> A,
911 llvm::ImmutableSet<T> B,
912 typename llvm::ImmutableSet<T>::Factory &F) {
913 if (A.getHeight() < B.getHeight())
923 const llvm::ImmutableSet<T> &B) {
928 for (
const T &Elem : A)
929 if (!B.contains(Elem))
938template <
typename K,
typename V,
typename Joiner>
939static llvm::ImmutableMap<K, V>
940join(llvm::ImmutableMap<K, V> A, llvm::ImmutableMap<K, V> B,
941 typename llvm::ImmutableMap<K, V>::Factory &F, Joiner JoinValues) {
942 if (A.getHeight() < B.getHeight())
947 for (
const auto &Entry : B) {
948 const K &Key = Entry.first;
949 const V &ValB = Entry.second;
950 if (
const V *ValA = A.lookup(Key))
951 A = F.add(A, Key, JoinValues(*ValA, ValB));
953 A = F.add(A, Key, ValB);
992 return !(*
this ==
Other);
995 void dump(llvm::raw_ostream &OS)
const {
996 OS <<
"LoanPropagationLattice State:\n";
999 for (
const auto &Entry :
Origins) {
1000 if (Entry.second.isEmpty())
1001 OS <<
" Origin " << Entry.first <<
" contains no loans\n";
1002 for (
const LoanID &LID : Entry.second)
1003 OS <<
" Origin " << Entry.first <<
" contains Loan " << LID <<
"\n";
1011 Direction::Forward> {
1012 OriginLoanMap::Factory &OriginLoanMapFactory;
1013 LoanSet::Factory &LoanSetFactory;
1019 OriginLoanMapFactory(LFactory.OriginMapFactory),
1020 LoanSetFactory(LFactory.LoanSetFactory) {}
1034 return utils::join(S1, S2, LoanSetFactory);
1036 return Lattice(JoinedOrigins);
1045 LoanSetFactory.add(LoanSetFactory.getEmptySet(), LID)));
1055 OriginLoanMapFactory.add(In.Origins, DestOID, SrcLoans));
1064 if (
auto *Loans = L.Origins.lookup(OID))
1066 return LoanSetFactory.getEmptySet();
1086 return !(*
this ==
Other);
1089 void dump(llvm::raw_ostream &OS)
const {
1090 OS <<
"ExpiredLattice State:\n";
1094 OS <<
" Loan " <<
ID <<
" is expired\n";
1101 Direction::Forward> {
1103 ExpiredLoanMap::Factory &Factory;
1122 return F1->getExpiryLoc() > F2->getExpiryLoc() ? F1 : F2;
1176 llvm::DenseMap<LoanID, PendingWarning> FinalWarningsMap;
1187 : LoanPropagation(LPA), ExpiredLoans(ELA), FactMgr(FM), ADC(ADC),
1188 Reporter(Reporter) {}
1191 llvm::TimeTraceScope TimeProfile(
"LifetimeChecker");
1193 for (
const Fact *F : FactMgr.getFacts(B))
1194 if (
const auto *UF = F->getAs<
UseFact>())
1210 LoanSet HeldLoans = LoanPropagation.getLoans(O, UF);
1213 ExpiredLoanMap AllExpiredLoans = ExpiredLoans.getExpiredLoans(UF);
1217 if (HeldLoans.isEmpty() || AllExpiredLoans.isEmpty())
1225 bool IsDefiniteError =
true;
1226 for (
LoanID L : HeldLoans) {
1227 if (AllExpiredLoans.contains(L))
1228 DefaultedLoans.push_back(L);
1231 IsDefiniteError =
false;
1234 if (DefaultedLoans.empty())
1242 for (
LoanID DefaultedLoan : DefaultedLoans) {
1245 if (FinalWarningsMap.count(DefaultedLoan) &&
1246 CurrentConfidence <= FinalWarningsMap[DefaultedLoan].ConfidenceLevel)
1249 auto *EF = AllExpiredLoans.lookup(DefaultedLoan);
1250 assert(EF &&
"Could not find ExpireFact for an expired loan.");
1252 FinalWarningsMap[DefaultedLoan] = {(*EF)->getExpiryLoc(),
1261 for (
const auto &[LID,
Warning] : FinalWarningsMap) {
1262 const Loan &L = FactMgr.getLoanMgr().getLoan(LID);
1264 Reporter->reportUseAfterFree(IssueExpr,
Warning.UseExpr,
1283 llvm::TimeTraceScope TimeProfile(
"LifetimeSafetyAnalysis");
1285 const CFG &Cfg = *AC.getCFG();
1286 DEBUG_WITH_TYPE(
"PrintCFG", Cfg.
dump(AC.getASTContext().getLangOpts(),
1291 DEBUG_WITH_TYPE(
"LifetimeFacts", FactMgr->dump(Cfg, AC));
1305 std::make_unique<LoanPropagationAnalysis>(Cfg, AC, *FactMgr, *Factory);
1306 LoanPropagation->run();
1309 std::make_unique<ExpiredLoansAnalysis>(Cfg, AC, *FactMgr, *Factory);
1310 ExpiredLoans->run();
1312 LifetimeChecker Checker(*LoanPropagation, *ExpiredLoans, *FactMgr, AC,
1319 assert(LoanPropagation &&
"Analysis has not been run.");
1320 return LoanPropagation->getLoans(OID, PP);
1325 assert(ExpiredLoans &&
"ExpiredLoansAnalysis has not been run.");
1326 std::vector<LoanID>
Result;
1327 for (
const auto &pair : ExpiredLoans->getExpiredLoans(PP))
1328 Result.push_back(pair.first);
1332std::optional<OriginID>
1334 assert(FactMgr &&
"FactManager not initialized");
1338 return FactMgr->getOriginMgr().get(*D);
1343 assert(FactMgr &&
"FactManager not initialized");
1344 std::vector<LoanID>
Result;
1345 for (
const Loan &L : FactMgr->getLoanMgr().getLoans())
1352 assert(FactMgr &&
"FactManager not initialized");
1353 llvm::StringMap<ProgramPoint> AnnotationToPointMap;
1355 for (
const Fact *F : FactMgr->getFacts(
Block)) {
1357 StringRef PointName = TPF->getAnnotation();
1358 assert(AnnotationToPointMap.find(PointName) ==
1359 AnnotationToPointMap.end() &&
1360 "more than one test points with the same name");
1361 AnnotationToPointMap[PointName] = F;
1365 return AnnotationToPointMap;
This file defines AnalysisDeclContext, a class that manages the analysis context data for context sen...
C Language Family Type Representation.
AnalysisDeclContext contains the context data for the function, method or block under analysis.
const Decl * getDecl() const
A builtin binary operation expression such as "x + y" or "x <= y".
static bool isAssignmentOp(Opcode Opc)
Represents C++ object destructor implicitly generated for automatic object or temporary bound to cons...
const VarDecl * getVarDecl() const
const Stmt * getTriggerStmt() const
Represents a single basic block in a source-level CFG.
Represents a top-level expression in a basic block.
std::optional< T > getAs() const
Convert to the specified CFGElement type, returning std::nullopt if this CFGElement is not of the des...
Represents a source-level, intra-procedural CFG that represents the control-flow of a Stmt.
void dump(const LangOptions &LO, bool ShowColors) const
dump - A simple pretty printer of a CFG that outputs to stderr.
Represents a call to a C++ constructor.
Represents an explicit C++ type conversion that uses "functional" notation (C++ [expr....
Represents a call to a member function that may be written either with member call syntax (e....
CXXMethodDecl * getMethodDecl() const
Retrieve the declaration of the called method.
The null pointer literal (C++11 [lex.nullptr])
A call to an overloaded operator written using operator syntax.
static bool isAssignmentOp(OverloadedOperatorKind Opc)
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
unsigned getNumArgs() const
getNumArgs - Return the number of actual arguments to this call.
ConstStmtVisitor - This class implements a simple visitor for Stmt subclasses.
A reference to a declared variable, function, enum, etc.
DeclStmt - Adaptor class for mixing declarations with statements and expressions.
Decl - This represents one declaration (or definition), e.g.
This represents one expression.
ImplicitCastExpr - Allows us to explicitly represent implicit type conversions, which have no direct ...
Describes an C or C++ initializer list.
unsigned getNumInits() const
const Expr * getInit(unsigned Init) const
Represents a prvalue temporary that is written into memory so that a reference can bind to it.
Expr * getSubExpr() const
Retrieve the temporary-generating subexpression whose value will be materialized into a glvalue.
A (possibly-)qualified type.
ReturnStmt - This represents a return, optionally of an expression: return; return 4;.
Encodes a location in the source.
RetTy Visit(PTR(Stmt) S, ParamTys... P)
SourceLocation getEndLoc() const LLVM_READONLY
CXXRecordDecl * getAsCXXRecordDecl() const
Retrieves the CXXRecordDecl that this type refers to, either because the type is a RecordType or beca...
bool isPointerOrReferenceType() const
UnaryOperator - This represents the unary-expression's (except sizeof and alignof),...
Expr * getSubExpr() const
Represent the declaration of a variable (in which case it is an lvalue) a function (in which case it ...
Represents a variable declaration or definition.
AssignOriginFact(OriginID OIDDest, OriginID OIDSrc)
void dump(llvm::raw_ostream &OS, const LoanManager &, const OriginManager &OM) const override
OriginID getSrcOriginID() const
static bool classof(const Fact *F)
OriginID getDestOriginID() const
Lattice getInState(const CFGBlock *B) const
Lattice transfer(Lattice In, const TestPointFact &)
Lattice getOutState(const CFGBlock *B) const
DataflowAnalysis(const CFG &C, AnalysisDeclContext &AC, FactManager &F)
Lattice transfer(Lattice In, const ReturnOfOriginFact &)
Lattice transfer(Lattice In, const UseFact &)
Lattice transfer(Lattice In, const IssueFact &)
Lattice transfer(Lattice In, const AssignOriginFact &)
Lattice transfer(Lattice In, const ExpireFact &)
DataflowAnalysis< Derived, Lattice, Dir > Base
Lattice getState(ProgramPoint P) const
SourceLocation getExpiryLoc() const
static bool classof(const Fact *F)
ExpireFact(LoanID LID, SourceLocation ExpiryLoc)
void dump(llvm::raw_ostream &OS, const LoanManager &LM, const OriginManager &) const override
The analysis that tracks which loans have expired.
Lattice transfer(Lattice In, const ExpireFact &F)
StringRef getAnalysisName() const
ExpiredLoanMap getExpiredLoans(ProgramPoint P)
Lattice transfer(Lattice In, const IssueFact &F)
ExpiredLoansAnalysis(const CFG &C, AnalysisDeclContext &AC, FactManager &F, LifetimeFactory &Factory)
Lattice join(Lattice L1, Lattice L2)
Merges two lattices by taking the union of the two expired loans.
Lattice getInitialState()
void VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *MTE)
void VisitUnaryOperator(const UnaryOperator *UO)
void VisitInitListExpr(const InitListExpr *ILE)
void VisitCXXNullPtrLiteralExpr(const CXXNullPtrLiteralExpr *N)
void VisitDeclStmt(const DeclStmt *DS)
void VisitImplicitCastExpr(const ImplicitCastExpr *ICE)
void VisitBinaryOperator(const BinaryOperator *BO)
void VisitCXXConstructExpr(const CXXConstructExpr *CCE)
void handleDestructor(const CFGAutomaticObjDtor &DtorOpt)
void VisitCXXMemberCallExpr(const CXXMemberCallExpr *MCE)
FactGenerator(FactManager &FactMgr, AnalysisDeclContext &AC)
void VisitCXXFunctionalCastExpr(const CXXFunctionalCastExpr *FCE)
void VisitDeclRefExpr(const DeclRefExpr *DRE)
void VisitReturnStmt(const ReturnStmt *RS)
void VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *OCE)
llvm::ArrayRef< const Fact * > getFacts(const CFGBlock *B) const
FactType * createFact(Args &&...args)
OriginManager & getOriginMgr()
void dump(const CFG &Cfg, AnalysisDeclContext &AC) const
LoanManager & getLoanMgr()
void addBlockFacts(const CFGBlock *B, llvm::ArrayRef< Fact * > NewFacts)
An abstract base class for a single, atomic lifetime-relevant event.
@ TestPoint
A marker for a specific point in the code, for testing.
@ Expire
A loan expires as its underlying storage is freed (e.g., variable goes out of scope).
@ ReturnOfOrigin
An origin escapes the function by flowing into the return value.
@ Issue
A new loan is issued from a borrow expression (e.g., &x).
@ Use
An origin is used (eg. dereferencing a pointer).
@ AssignOrigin
An origin is propagated from a source to a destination (e.g., p = q).
virtual void dump(llvm::raw_ostream &OS, const LoanManager &, const OriginManager &) const
void dump(llvm::raw_ostream &OS, const LoanManager &LM, const OriginManager &OM) const override
OriginID getOriginID() const
IssueFact(LoanID LID, OriginID OID)
static bool classof(const Fact *F)
void issuePendingWarnings()
LifetimeChecker(LoanPropagationAnalysis &LPA, ExpiredLoansAnalysis &ELA, FactManager &FM, AnalysisDeclContext &ADC, LifetimeSafetyReporter *Reporter)
void checkUse(const UseFact *UF)
Checks for use-after-free errors for a given use of an Origin.
Running the lifetime safety analysis and querying its results.
LoanSet getLoansAtPoint(OriginID OID, ProgramPoint PP) const
Returns the set of loans an origin holds at a specific program point.
std::optional< OriginID > getOriginIDForDecl(const ValueDecl *D) const
Finds the OriginID for a given declaration.
std::vector< LoanID > getExpiredLoansAtPoint(ProgramPoint PP) const
Returns the set of loans that have expired at a specific program point.
std::vector< LoanID > getLoanIDForVar(const VarDecl *VD) const
Finds the LoanID's for the loan created with the specific variable as their Path.
llvm::StringMap< ProgramPoint > getTestPoints() const
Retrieves program points that were specially marked in the source code for testing.
LifetimeSafetyAnalysis(AnalysisDeclContext &AC, LifetimeSafetyReporter *Reporter)
~LifetimeSafetyAnalysis()
Manages the creation, storage and retrieval of loans.
const Loan & getLoan(LoanID ID) const
Loan & addLoan(AccessPath Path, const Expr *IssueExpr)
llvm::ArrayRef< Loan > getLoans() const
The analysis that tracks which loans belong to which origins.
LoanPropagationAnalysis(const CFG &C, AnalysisDeclContext &AC, FactManager &F, LifetimeFactory &LFactory)
Lattice getInitialState()
Lattice transfer(Lattice In, const IssueFact &F)
A new loan is issued to the origin. Old loans are erased.
Lattice transfer(Lattice In, const AssignOriginFact &F)
The destination origin's loan set is replaced by the source's.
Lattice join(Lattice A, Lattice B)
Merges two lattices by taking the union of loans for each origin.
StringRef getAnalysisName() const
LoanSet getLoans(OriginID OID, ProgramPoint P)
Manages the creation, storage, and retrieval of origins for pointer-like variables and expressions.
OriginID getOrCreate(const ValueDecl &D)
OriginID get(const ValueDecl &D)
const Origin & getOrigin(OriginID ID) const
Origin & addOrigin(OriginID ID, const clang::Expr &E)
OriginID getOrCreate(const Expr &E)
llvm::ArrayRef< Origin > getOrigins() const
void dump(OriginID OID, llvm::raw_ostream &OS) const
Origin & addOrigin(OriginID ID, const clang::ValueDecl &D)
OriginID get(const Expr &E)
void dump(llvm::raw_ostream &OS, const LoanManager &, const OriginManager &OM) const override
ReturnOfOriginFact(OriginID OID)
OriginID getReturnedOriginID() const
static bool classof(const Fact *F)
A dummy-fact used to mark a specific point in the code for testing.
void dump(llvm::raw_ostream &OS, const LoanManager &, const OriginManager &) const override
static bool classof(const Fact *F)
StringRef getAnnotation() const
TestPointFact(StringRef Annotation)
const Expr * getUseExpr() const
void dump(llvm::raw_ostream &OS, const LoanManager &, const OriginManager &OM) const override
OriginID getUsedOrigin(const OriginManager &OM) const
UseFact(const Expr *UseExpr)
static bool classof(const Fact *F)
static bool isSubsetOf(const llvm::ImmutableSet< T > &A, const llvm::ImmutableSet< T > &B)
Checks if set A is a subset of set B.
static llvm::ImmutableSet< T > join(llvm::ImmutableSet< T > A, llvm::ImmutableSet< T > B, typename llvm::ImmutableSet< T >::Factory &F)
Computes the union of two ImmutableSets.
const Fact * ProgramPoint
A ProgramPoint identifies a location in the CFG by pointing to a specific Fact.
llvm::ImmutableSet< LoanID > LoanSet
llvm::ImmutableMap< OriginID, LoanSet > OriginLoanMap
ID< struct OriginTag > OriginID
llvm::ImmutableMap< LoanID, const ExpireFact * > ExpiredLoanMap
ID< struct LoanTag > LoanID
void runLifetimeSafetyAnalysis(AnalysisDeclContext &AC, LifetimeSafetyReporter *Reporter)
The main entry point for the analysis.
Confidence
Enum to track the confidence level of a potential error.
bool isa(CodeGen::Address addr)
nullptr
This class represents a compute construct, representing a 'Kind' of ‘parallel’, 'serial',...
@ Result
The result type of a method or function.
const FunctionProtoType * T
@ Other
Other implicit parameter.
A worklist implementation for backward dataflow analysis.
A worklist implementation for forward dataflow analysis.
Represents the storage location being borrowed, e.g., a specific stack variable.
AccessPath(const clang::ValueDecl *D)
const clang::ValueDecl * D
ExpiredLoanMap Expired
Map from an expired LoanID to the ExpireFact that made it expire.
bool operator==(const ExpiredLattice &Other) const
void dump(llvm::raw_ostream &OS) const
ExpiredLattice(ExpiredLoanMap M)
bool operator!=(const ExpiredLattice &Other) const
A generic, type-safe wrapper for an ID, distinguished by its Tag type.
An object to hold the factories for immutable collections, ensuring that all created states share the...
ExpiredLoanMap::Factory ExpiredLoanMapFactory
OriginLoanMap::Factory OriginMapFactory
LoanSet::Factory LoanSetFactory
Represents the dataflow lattice for loan propagation.
bool operator!=(const LoanPropagationLattice &Other) const
OriginLoanMap Origins
The map from an origin to the set of loans it contains.
LoanPropagationLattice()=default
LoanPropagationLattice(const OriginLoanMap &S)
void dump(llvm::raw_ostream &OS) const
bool operator==(const LoanPropagationLattice &Other) const
Information about a single borrow, or "Loan".
Loan(LoanID id, AccessPath path, const Expr *IssueExpr)
const Expr * IssueExpr
The expression that creates the loan, e.g., &x.
LoanID ID
TODO: Represent opaque loans.
void dump(llvm::raw_ostream &OS) const
An Origin is a symbolic identifier that represents the set of possible loans a pointer-like object co...
Origin(OriginID ID, const clang::Expr *E)
const clang::Expr * getExpr() const
const clang::ValueDecl * getDecl() const
llvm::PointerUnion< const clang::ValueDecl *, const clang::Expr * > Ptr
A pointer to the AST node that this origin represents.
Origin(OriginID ID, const clang::ValueDecl *D)
Struct to store the complete context for a potential lifetime violation.
Confidence ConfidenceLevel