18#include "llvm/ADT/FoldingSet.h"
19#include "llvm/ADT/ImmutableMap.h"
20#include "llvm/ADT/ImmutableSet.h"
21#include "llvm/ADT/PointerUnion.h"
22#include "llvm/ADT/SmallBitVector.h"
23#include "llvm/ADT/SmallVector.h"
24#include "llvm/Support/Debug.h"
25#include "llvm/Support/TimeProfiler.h"
55 void dump(llvm::raw_ostream &OS)
const {
56 OS <<
ID <<
" (Path: ";
57 OS <<
Path.D->getNameAsString() <<
")";
74 llvm::PointerUnion<const clang::ValueDecl *, const clang::Expr *>
Ptr;
93 AllLoans.emplace_back(getNextLoanID(), Path, IssueExpr);
94 return AllLoans.back();
98 assert(
ID.
Value < AllLoans.size());
104 LoanID getNextLoanID() {
return NextLoanID++; }
119 AllOrigins.emplace_back(
ID, &D);
120 return AllOrigins.back();
123 AllOrigins.emplace_back(
ID, &E);
124 return AllOrigins.back();
129 auto It = ExprToOriginID.find(&E);
130 if (It != ExprToOriginID.end())
136 if (
const auto *DRE = dyn_cast<DeclRefExpr>(&E))
137 return get(*DRE->getDecl());
145 auto It = DeclToOriginID.find(&D);
149 if (It == DeclToOriginID.end())
156 auto It = ExprToOriginID.find(&E);
157 if (It != ExprToOriginID.end())
162 ExprToOriginID[&E] = NewID;
167 assert(
ID.
Value < AllOrigins.size());
174 auto It = DeclToOriginID.find(&D);
175 if (It != DeclToOriginID.end())
179 DeclToOriginID[&D] = NewID;
187 OS <<
"Decl: " << VD->getNameAsString();
189 OS <<
"Expr: " << E->getStmtClassName();
196 OriginID getNextOriginID() {
return NextOriginID++; }
202 llvm::DenseMap<const clang::ValueDecl *, OriginID> DeclToOriginID;
203 llvm::DenseMap<const clang::Expr *, OriginID> ExprToOriginID;
239 template <
typename T>
const T *
getAs()
const {
240 if (T::classof(
this))
241 return static_cast<const T *
>(
this);
247 OS <<
"Fact (Kind: " <<
static_cast<int>(K) <<
")\n";
265 OS <<
", ToOrigin: ";
306 KillDest(KillDest) {}
314 OS <<
"OriginFlow (Dest: ";
335 OS <<
"ReturnOfOrigin (";
345 bool IsWritten =
false;
364 OS <<
", " << (
isWritten() ?
"Write" :
"Read") <<
")\n";
371 StringRef Annotation;
383 OS <<
"TestPoint (Annotation: \"" <<
getAnnotation() <<
"\")\n";
390 auto It = BlockToFactsMap.find(B);
391 if (It != BlockToFactsMap.end())
397 if (!NewFacts.empty())
398 BlockToFactsMap[B].assign(NewFacts.begin(), NewFacts.end());
401 template <
typename FactType,
typename... Args>
403 void *Mem = FactAllocator.Allocate<FactType>();
404 return new (Mem) FactType(std::forward<Args>(args)...);
408 llvm::dbgs() <<
"==========================================\n";
409 llvm::dbgs() <<
" Lifetime Analysis Facts:\n";
410 llvm::dbgs() <<
"==========================================\n";
412 if (
const auto *ND = dyn_cast<NamedDecl>(D))
413 llvm::dbgs() <<
"Function: " << ND->getQualifiedNameAsString() <<
"\n";
416 llvm::dbgs() <<
" Block B" << B->getBlockID() <<
":\n";
417 auto It = BlockToFactsMap.find(B);
418 if (It != BlockToFactsMap.end()) {
419 for (
const Fact *F : It->second) {
421 F->
dump(llvm::dbgs(), LoanMgr, OriginMgr);
424 llvm::dbgs() <<
" End of Block\n";
434 llvm::DenseMap<const clang::CFGBlock *, llvm::SmallVector<const Fact *>>
436 llvm::BumpPtrAllocator FactAllocator;
444 : FactMgr(FactMgr), AC(AC) {}
447 llvm::TimeTraceScope TimeProfile(
"FactGenerator");
451 CurrentBlockFacts.clear();
452 for (
unsigned I = 0; I <
Block->size(); ++I) {
454 if (std::optional<CFGStmt> CS = Element.
getAs<
CFGStmt>())
455 Visit(CS->getStmt());
456 else if (std::optional<CFGAutomaticObjDtor> DtorOpt =
460 FactMgr.addBlockFacts(
Block, CurrentBlockFacts);
466 if (
const auto *VD = dyn_cast<VarDecl>(D))
468 if (
const Expr *InitExpr = VD->getInit())
469 killAndFlowOrigin(*VD, *InitExpr);
484 if (!isPointerType(DRE->
getType())) {
485 if (
const Loan *L = createLoan(DRE)) {
486 OriginID ExprOID = FactMgr.getOriginMgr().getOrCreate(*DRE);
487 CurrentBlockFacts.push_back(
488 FactMgr.createFact<
IssueFact>(L->ID, ExprOID));
494 if (isGslPointerType(CCE->
getType())) {
495 handleGSLPointerConstruction(CCE);
503 if (isGslPointerType(MCE->
getType()) &&
507 {MCE->getImplicitObjectArgument()},
517 handleFunctionCall(MCE,
Method, Args,
false);
523 {CE->getArgs(), CE->getNumArgs()});
529 FactMgr.getOriginMgr().getOrCreate(*N);
545 if (isPointerType(SubExpr->
getType()))
551 killAndFlowOrigin(*UO, *SubExpr);
557 if (hasOrigin(RetExpr)) {
558 OriginID OID = FactMgr.getOriginMgr().getOrCreate(*RetExpr);
559 CurrentBlockFacts.push_back(
578 {OCE->getArgs(), OCE->getNumArgs()},
585 if (handleTestPoint(FCE))
587 if (isGslPointerType(FCE->
getType()))
597 killAndFlowOrigin(*ILE, *ILE->
getInit(0));
620 for (
const Loan &L : FactMgr.getLoanMgr().getLoans()) {
624 if (LoanPath.
D == DestructedVD)
625 CurrentBlockFacts.push_back(FactMgr.createFact<
ExpireFact>(
631 static bool isGslPointerType(
QualType QT) {
634 if (
auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(RD))
635 return CTSD->getSpecializedTemplate()
637 ->hasAttr<PointerAttr>();
638 return RD->hasAttr<PointerAttr>();
643 static bool isPointerType(
QualType QT) {
647 static bool hasOrigin(
const Expr *E) {
648 return E->isGLValue() || isPointerType(E->getType());
651 static bool hasOrigin(
const VarDecl *VD) {
652 return isPointerType(VD->getType());
655 void handleGSLPointerConstruction(
const CXXConstructExpr *CCE) {
656 assert(isGslPointerType(CCE->getType()));
657 if (CCE->getNumArgs() != 1)
659 if (hasOrigin(CCE->getArg(0)))
660 killAndFlowOrigin(*CCE, *CCE->getArg(0));
663 handleFunctionCall(CCE, CCE->getConstructor(),
664 {CCE->getArgs(), CCE->getNumArgs()},
672 void handleFunctionCall(
const Expr *
Call,
const FunctionDecl *FD,
673 ArrayRef<const Expr *> Args,
674 bool IsGslConstruction =
false) {
676 if (!FD || !hasOrigin(
Call))
678 auto IsArgLifetimeBound = [FD](
unsigned I) ->
bool {
679 const ParmVarDecl *PVD =
nullptr;
680 if (
const auto *
Method = dyn_cast<CXXMethodDecl>(FD);
685 if ((I - 1) <
Method->getNumParams())
688 PVD =
Method->getParamDecl(I - 1);
689 }
else if (I < FD->getNumParams())
691 PVD = FD->getParamDecl(I);
692 return PVD ? PVD->hasAttr<clang::LifetimeBoundAttr>() :
false;
696 bool killedSrc =
false;
697 for (
unsigned I = 0; I < Args.size(); ++I)
698 if (IsGslConstruction || IsArgLifetimeBound(I)) {
701 killAndFlowOrigin(*
Call, *Args[I]);
703 flowOrigin(*
Call, *Args[I]);
711 const Loan *createLoan(
const DeclRefExpr *DRE) {
712 if (
const auto *VD = dyn_cast<ValueDecl>(DRE->getDecl())) {
715 return &FactMgr.getLoanMgr().addLoan(Path, DRE);
720 template <
typename Destination,
typename Source>
721 void flowOrigin(
const Destination &D,
const Source &S) {
722 OriginID DestOID = FactMgr.getOriginMgr().getOrCreate(D);
723 OriginID SrcOID = FactMgr.getOriginMgr().get(S);
724 CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>(
725 DestOID, SrcOID,
false));
728 template <
typename Destination,
typename Source>
729 void killAndFlowOrigin(
const Destination &D,
const Source &S) {
730 OriginID DestOID = FactMgr.getOriginMgr().getOrCreate(D);
731 OriginID SrcOID = FactMgr.getOriginMgr().get(S);
732 CurrentBlockFacts.push_back(
733 FactMgr.createFact<OriginFlowFact>(DestOID, SrcOID,
true));
738 bool handleTestPoint(
const CXXFunctionalCastExpr *FCE) {
739 if (!FCE->getType()->isVoidType())
742 const auto *SubExpr = FCE->getSubExpr()->IgnoreParenImpCasts();
743 if (
const auto *SL = dyn_cast<StringLiteral>(SubExpr)) {
744 llvm::StringRef LiteralValue = SL->getString();
745 const std::string Prefix =
"__lifetime_test_point_";
747 if (LiteralValue.starts_with(Prefix)) {
748 StringRef Annotation = LiteralValue.drop_front(Prefix.length());
749 CurrentBlockFacts.push_back(
750 FactMgr.createFact<TestPointFact>(Annotation));
757 void handleAssignment(
const Expr *LHSExpr,
const Expr *RHSExpr) {
758 if (!hasOrigin(LHSExpr))
761 if (
const auto *DRE_LHS =
762 dyn_cast<DeclRefExpr>(LHSExpr->IgnoreParenImpCasts())) {
763 markUseAsWrite(DRE_LHS);
764 if (
const auto *VD_LHS = dyn_cast<ValueDecl>(DRE_LHS->getDecl())) {
767 killAndFlowOrigin(*VD_LHS, *RHSExpr);
775 void handleUse(
const DeclRefExpr *DRE) {
776 if (isPointerType(DRE->getType())) {
777 UseFact *UF = FactMgr.createFact<UseFact>(DRE);
778 CurrentBlockFacts.push_back(UF);
779 assert(!UseFacts.contains(DRE));
784 void markUseAsWrite(
const DeclRefExpr *DRE) {
785 if (!isPointerType(DRE->getType()))
787 assert(UseFacts.contains(DRE));
788 UseFacts[DRE]->markAsWritten();
791 FactManager &FactMgr;
792 AnalysisDeclContext &AC;
793 llvm::SmallVector<Fact *> CurrentBlockFacts;
799 llvm::DenseMap<const DeclRefExpr *, UseFact *> UseFacts;
833template <
typename Derived,
typename LatticeType, Direction Dir>
844 llvm::DenseMap<const CFGBlock *, Lattice> InStates;
846 llvm::DenseMap<const CFGBlock *, Lattice> OutStates;
850 llvm::DenseMap<ProgramPoint, Lattice> PerPointStates;
863 Derived &D =
static_cast<Derived &
>(*this);
864 llvm::TimeTraceScope Time(D.getAnalysisName());
871 const CFGBlock *Start = isForward() ? &Cfg.getEntry() : &Cfg.getExit();
872 InStates[Start] = D.getInitialState();
873 W.enqueueBlock(Start);
875 llvm::SmallBitVector Visited(Cfg.getNumBlockIDs() + 1);
877 while (
const CFGBlock *B = W.dequeue()) {
879 Lattice StateOut = transferBlock(B, StateIn);
880 OutStates[B] = StateOut;
881 Visited.set(B->getBlockID());
882 for (
const CFGBlock *AdjacentB : isForward() ? B->
succs() : B->preds()) {
886 Lattice NewInState = D.join(OldInState, StateOut);
889 if (!Visited.test(AdjacentB->getBlockID()) ||
890 NewInState != OldInState) {
891 InStates[AdjacentB] = NewInState;
892 W.enqueueBlock(AdjacentB);
906 const Derived *D =
static_cast<const Derived *
>(
this);
907 llvm::dbgs() <<
"==========================================\n";
908 llvm::dbgs() << D->getAnalysisName() <<
" results:\n";
909 llvm::dbgs() <<
"==========================================\n";
910 const CFGBlock &B = isForward() ? Cfg.getExit() : Cfg.getEntry();
919 if constexpr (isForward()) {
920 for (
const Fact *F : Facts) {
921 State = transferFact(State, F);
922 PerPointStates[F] = State;
925 for (
const Fact *F : llvm::reverse(Facts)) {
927 PerPointStates[F] = State;
928 State = transferFact(State, F);
936 Derived *D =
static_cast<Derived *
>(
this);
937 switch (F->getKind()) {
939 return D->transfer(In, *F->getAs<IssueFact>());
941 return D->transfer(In, *F->getAs<ExpireFact>());
943 return D->transfer(In, *F->getAs<OriginFlowFact>());
945 return D->transfer(In, *F->getAs<ReturnOfOriginFact>());
947 return D->transfer(In, *F->getAs<UseFact>());
949 return D->transfer(In, *F->getAs<TestPointFact>());
951 llvm_unreachable(
"Unknown fact kind");
967static llvm::ImmutableSet<T>
join(llvm::ImmutableSet<T> A,
968 llvm::ImmutableSet<T> B,
969 typename llvm::ImmutableSet<T>::Factory &F) {
970 if (A.getHeight() < B.getHeight())
980 const llvm::ImmutableSet<T> &B) {
985 for (
const T &Elem : A)
986 if (!B.contains(Elem))
995template <
typename K,
typename V,
typename Joiner>
996static llvm::ImmutableMap<K, V>
997join(llvm::ImmutableMap<K, V> A, llvm::ImmutableMap<K, V> B,
998 typename llvm::ImmutableMap<K, V>::Factory &F, Joiner JoinValues) {
999 if (A.getHeight() < B.getHeight())
1004 for (
const auto &Entry : B) {
1005 const K &Key = Entry.first;
1006 const V &ValB = Entry.second;
1007 if (
const V *ValA = A.lookup(Key))
1008 A = F.add(A, Key, JoinValues(*ValA, ValB));
1010 A = F.add(A, Key, ValB);
1051 return !(*
this ==
Other);
1054 void dump(llvm::raw_ostream &OS)
const {
1055 OS <<
"LoanPropagationLattice State:\n";
1058 for (
const auto &Entry :
Origins) {
1059 if (Entry.second.isEmpty())
1060 OS <<
" Origin " << Entry.first <<
" contains no loans\n";
1061 for (
const LoanID &LID : Entry.second)
1062 OS <<
" Origin " << Entry.first <<
" contains Loan " << LID <<
"\n";
1070 Direction::Forward> {
1071 OriginLoanMap::Factory &OriginLoanMapFactory;
1072 LoanSet::Factory &LoanSetFactory;
1078 OriginLoanMapFactory(LFactory.OriginMapFactory),
1079 LoanSetFactory(LFactory.LoanSetFactory) {}
1093 return utils::join(S1, S2, LoanSetFactory);
1095 return Lattice(JoinedOrigins);
1104 LoanSetFactory.add(LoanSetFactory.getEmptySet(), LID)));
1120 OriginLoanMapFactory.add(In.Origins, DestOID, MergedLoans));
1129 if (
auto *Loans = L.Origins.lookup(OID))
1131 return LoanSetFactory.getEmptySet();
1151 return !(*
this ==
Other);
1154 void dump(llvm::raw_ostream &OS)
const {
1155 OS <<
"ExpiredLattice State:\n";
1159 OS <<
" Loan " <<
ID <<
" is expired\n";
1166 Direction::Forward> {
1168 ExpiredLoanMap::Factory &Factory;
1187 return F1->getExpiryLoc() > F2->getExpiryLoc() ? F1 : F2;
1241 llvm::DenseMap<LoanID, PendingWarning> FinalWarningsMap;
1252 : LoanPropagation(LPA), ExpiredLoans(ELA), FactMgr(FM), ADC(ADC),
1253 Reporter(Reporter) {}
1256 llvm::TimeTraceScope TimeProfile(
"LifetimeChecker");
1258 for (
const Fact *F : FactMgr.getFacts(B))
1259 if (
const auto *UF = F->getAs<
UseFact>())
1275 LoanSet HeldLoans = LoanPropagation.getLoans(O, UF);
1278 ExpiredLoanMap AllExpiredLoans = ExpiredLoans.getExpiredLoans(UF);
1282 if (HeldLoans.isEmpty() || AllExpiredLoans.isEmpty())
1290 bool IsDefiniteError =
true;
1291 for (
LoanID L : HeldLoans) {
1292 if (AllExpiredLoans.contains(L))
1293 DefaultedLoans.push_back(L);
1296 IsDefiniteError =
false;
1299 if (DefaultedLoans.empty())
1307 for (
LoanID DefaultedLoan : DefaultedLoans) {
1310 if (FinalWarningsMap.count(DefaultedLoan) &&
1311 CurrentConfidence <= FinalWarningsMap[DefaultedLoan].ConfidenceLevel)
1314 auto *EF = AllExpiredLoans.lookup(DefaultedLoan);
1315 assert(EF &&
"Could not find ExpireFact for an expired loan.");
1317 FinalWarningsMap[DefaultedLoan] = {(*EF)->getExpiryLoc(),
1326 for (
const auto &[LID,
Warning] : FinalWarningsMap) {
1327 const Loan &L = FactMgr.getLoanMgr().getLoan(LID);
1329 Reporter->reportUseAfterFree(IssueExpr,
Warning.UseExpr,
1348 llvm::TimeTraceScope TimeProfile(
"LifetimeSafetyAnalysis");
1350 const CFG &Cfg = *AC.getCFG();
1351 DEBUG_WITH_TYPE(
"PrintCFG", Cfg.
dump(AC.getASTContext().getLangOpts(),
1356 DEBUG_WITH_TYPE(
"LifetimeFacts", FactMgr->dump(Cfg, AC));
1370 std::make_unique<LoanPropagationAnalysis>(Cfg, AC, *FactMgr, *Factory);
1371 LoanPropagation->run();
1374 std::make_unique<ExpiredLoansAnalysis>(Cfg, AC, *FactMgr, *Factory);
1375 ExpiredLoans->run();
1377 LifetimeChecker Checker(*LoanPropagation, *ExpiredLoans, *FactMgr, AC,
1384 assert(LoanPropagation &&
"Analysis has not been run.");
1385 return LoanPropagation->getLoans(OID, PP);
1390 assert(ExpiredLoans &&
"ExpiredLoansAnalysis has not been run.");
1391 std::vector<LoanID>
Result;
1392 for (
const auto &pair : ExpiredLoans->getExpiredLoans(PP))
1393 Result.push_back(pair.first);
1397std::optional<OriginID>
1399 assert(FactMgr &&
"FactManager not initialized");
1403 return FactMgr->getOriginMgr().get(*D);
1408 assert(FactMgr &&
"FactManager not initialized");
1409 std::vector<LoanID>
Result;
1410 for (
const Loan &L : FactMgr->getLoanMgr().getLoans())
1417 assert(FactMgr &&
"FactManager not initialized");
1418 llvm::StringMap<ProgramPoint> AnnotationToPointMap;
1420 for (
const Fact *F : FactMgr->getFacts(
Block)) {
1422 StringRef PointName = TPF->getAnnotation();
1423 assert(AnnotationToPointMap.find(PointName) ==
1424 AnnotationToPointMap.end() &&
1425 "more than one test points with the same name");
1426 AnnotationToPointMap[PointName] = F;
1430 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.
Expr * getImplicitObjectArgument() const
Retrieve the implicit object argument for the member call.
Represents a static or instance method of a struct/union/class.
The null pointer literal (C++11 [lex.nullptr])
A call to an overloaded operator written using operator syntax.
static bool isAssignmentOp(OverloadedOperatorKind Opc)
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
FunctionDecl * getDirectCallee()
If the callee is a FunctionDecl, return it. Otherwise return null.
unsigned getNumArgs() const
getNumArgs - Return the number of actual arguments to this call.
Expr ** getArgs()
Retrieve the call arguments.
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.
Lattice getInState(const CFGBlock *B) const
Lattice transfer(Lattice In, const OriginFlowFact &)
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 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 VisitCallExpr(const CallExpr *CE)
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).
@ OriginFlow
An origin is propagated from a source to a destination (e.g., p = q).
@ Use
An origin is used (eg. appears as l-value expression like DeclRefExpr).
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 join(Lattice A, Lattice B)
Merges two lattices by taking the union of loans for each origin.
StringRef getAnalysisName() const
Lattice transfer(Lattice In, const OriginFlowFact &F)
A flow from source to destination.
LoanSet getLoans(OriginID OID, ProgramPoint P)
static bool classof(const Fact *F)
void dump(llvm::raw_ostream &OS, const LoanManager &, const OriginManager &OM) const override
OriginID getSrcOriginID() const
OriginID getDestOriginID() const
OriginFlowFact(OriginID OIDDest, OriginID OIDSrc, bool KillDest)
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.
bool implicitObjectParamIsLifetimeBound(const FunctionDecl *FD)
Returns true if the implicit object parameter (this) should be considered lifetimebound,...
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
llvm::BumpPtrAllocator Allocator
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