[clang] [LifetimeSafety] Associate origins to all l-valued expressions (PR #156896)
Utkarsh Saxena via cfe-commits
cfe-commits at lists.llvm.org
Mon Sep 8 05:05:10 PDT 2025
https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/156896
>From f7906c207782f94873936a5c4545c7900165534e Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena <usx at google.com>
Date: Thu, 4 Sep 2025 14:27:37 +0000
Subject: [PATCH] all-lvalues-have-origin
---
clang/lib/Analysis/LifetimeSafety.cpp | 178 +++++++-----
.../Sema/warn-lifetime-safety-dataflow.cpp | 260 +++++++++++-------
2 files changed, 269 insertions(+), 169 deletions(-)
diff --git a/clang/lib/Analysis/LifetimeSafety.cpp b/clang/lib/Analysis/LifetimeSafety.cpp
index dbbf7f3cc14b1..e687e5419c50a 100644
--- a/clang/lib/Analysis/LifetimeSafety.cpp
+++ b/clang/lib/Analysis/LifetimeSafety.cpp
@@ -50,6 +50,11 @@ struct Loan {
Loan(LoanID id, AccessPath path, const Expr *IssueExpr)
: ID(id), Path(path), IssueExpr(IssueExpr) {}
+
+ void dump(llvm::raw_ostream &OS) const {
+ OS << ID << " (Path: ";
+ OS << Path.D->getNameAsString() << ")";
+ }
};
/// An Origin is a symbolic identifier that represents the set of possible
@@ -120,17 +125,19 @@ class OriginManager {
// TODO: Mark this method as const once we remove the call to getOrCreate.
OriginID get(const Expr &E) {
- // Origin of DeclRefExpr is that of the declaration it refers to.
+ auto It = ExprToOriginID.find(&E);
+ if (It != ExprToOriginID.end())
+ return It->second;
+ // If the expression itself has no specific origin, and it's a reference
+ // to a declaration, its origin is that of the declaration it refers to.
+ // For pointer types, where we don't pre-emptively create an origin for the
+ // DeclRefExpr itself.
if (const auto *DRE = dyn_cast<DeclRefExpr>(&E))
return get(*DRE->getDecl());
- auto It = ExprToOriginID.find(&E);
// TODO: This should be an assert(It != ExprToOriginID.end()). The current
// implementation falls back to getOrCreate to avoid crashing on
// yet-unhandled pointer expressions, creating an empty origin for them.
- if (It == ExprToOriginID.end())
- return getOrCreate(E);
-
- return It->second;
+ return getOrCreate(E);
}
OriginID get(const ValueDecl &D) {
@@ -149,10 +156,6 @@ class OriginManager {
if (It != ExprToOriginID.end())
return It->second;
- if (const auto *DRE = dyn_cast<DeclRefExpr>(&E)) {
- // Origin of DeclRefExpr is that of the declaration it refers to.
- return getOrCreate(*DRE->getDecl());
- }
OriginID NewID = getNextOriginID();
addOrigin(NewID, E);
ExprToOriginID[&E] = NewID;
@@ -235,7 +238,8 @@ class Fact {
return nullptr;
}
- virtual void dump(llvm::raw_ostream &OS, const OriginManager &) const {
+ virtual void dump(llvm::raw_ostream &OS, const LoanManager &,
+ const OriginManager &) const {
OS << "Fact (Kind: " << static_cast<int>(K) << ")\n";
}
};
@@ -250,8 +254,11 @@ class IssueFact : public Fact {
IssueFact(LoanID LID, OriginID OID) : Fact(Kind::Issue), LID(LID), OID(OID) {}
LoanID getLoanID() const { return LID; }
OriginID getOriginID() const { return OID; }
- void dump(llvm::raw_ostream &OS, const OriginManager &OM) const override {
- OS << "Issue (LoanID: " << getLoanID() << ", ToOrigin: ";
+ void dump(llvm::raw_ostream &OS, const LoanManager &LM,
+ const OriginManager &OM) const override {
+ OS << "Issue (";
+ LM.getLoan(getLoanID()).dump(OS);
+ OS << ", ToOrigin: ";
OM.dump(getOriginID(), OS);
OS << ")\n";
}
@@ -270,8 +277,11 @@ class ExpireFact : public Fact {
LoanID getLoanID() const { return LID; }
SourceLocation getExpiryLoc() const { return ExpiryLoc; }
- void dump(llvm::raw_ostream &OS, const OriginManager &OM) const override {
- OS << "Expire (LoanID: " << getLoanID() << ")\n";
+ void dump(llvm::raw_ostream &OS, const LoanManager &LM,
+ const OriginManager &) const override {
+ OS << "Expire (";
+ LM.getLoan(getLoanID()).dump(OS);
+ OS << ")\n";
}
};
@@ -288,7 +298,8 @@ class AssignOriginFact : public Fact {
: Fact(Kind::AssignOrigin), OIDDest(OIDDest), OIDSrc(OIDSrc) {}
OriginID getDestOriginID() const { return OIDDest; }
OriginID getSrcOriginID() const { return OIDSrc; }
- void dump(llvm::raw_ostream &OS, const OriginManager &OM) const override {
+ void dump(llvm::raw_ostream &OS, const LoanManager &,
+ const OriginManager &OM) const override {
OS << "AssignOrigin (Dest: ";
OM.dump(getDestOriginID(), OS);
OS << ", Src: ";
@@ -307,7 +318,8 @@ class ReturnOfOriginFact : public Fact {
ReturnOfOriginFact(OriginID OID) : Fact(Kind::ReturnOfOrigin), OID(OID) {}
OriginID getReturnedOriginID() const { return OID; }
- void dump(llvm::raw_ostream &OS, const OriginManager &OM) const override {
+ void dump(llvm::raw_ostream &OS, const LoanManager &,
+ const OriginManager &OM) const override {
OS << "ReturnOfOrigin (";
OM.dump(getReturnedOriginID(), OS);
OS << ")\n";
@@ -333,10 +345,11 @@ class UseFact : public Fact {
void markAsWritten() { IsWritten = true; }
bool isWritten() const { return IsWritten; }
- void dump(llvm::raw_ostream &OS, const OriginManager &OM) const override {
+ void dump(llvm::raw_ostream &OS, const LoanManager &,
+ const OriginManager &OM) const override {
OS << "Use (";
OM.dump(getUsedOrigin(OM), OS);
- OS << " " << (isWritten() ? "Write" : "Read") << ")\n";
+ OS << ", " << (isWritten() ? "Write" : "Read") << ")\n";
}
};
@@ -353,7 +366,8 @@ class TestPointFact : public Fact {
StringRef getAnnotation() const { return Annotation; }
- void dump(llvm::raw_ostream &OS, const OriginManager &) const override {
+ void dump(llvm::raw_ostream &OS, const LoanManager &,
+ const OriginManager &) const override {
OS << "TestPoint (Annotation: \"" << getAnnotation() << "\")\n";
}
};
@@ -392,7 +406,7 @@ class FactManager {
if (It != BlockToFactsMap.end()) {
for (const Fact *F : It->second) {
llvm::dbgs() << " ";
- F->dump(llvm::dbgs(), OriginMgr);
+ F->dump(llvm::dbgs(), LoanMgr, OriginMgr);
}
}
llvm::dbgs() << " End of Block\n";
@@ -438,12 +452,31 @@ class FactGenerator : public ConstStmtVisitor<FactGenerator> {
void VisitDeclStmt(const DeclStmt *DS) {
for (const Decl *D : DS->decls())
if (const auto *VD = dyn_cast<VarDecl>(D))
- if (hasOrigin(VD->getType()))
+ if (hasOrigin(VD))
if (const Expr *InitExpr = VD->getInit())
addAssignOriginFact(*VD, *InitExpr);
}
- void VisitDeclRefExpr(const DeclRefExpr *DRE) { handleUse(DRE); }
+ void VisitDeclRefExpr(const DeclRefExpr *DRE) {
+ handleUse(DRE);
+ // For non-pointer/non-view types, a reference to the variable's storage
+ // is a borrow. We create a loan for it.
+ // For pointer/view types, we stick to the existing model for now and do
+ // not create an extra origin for the l-value expression itself.
+
+ // TODO: A single origin for a `DeclRefExpr` for a pointer or view type is
+ // not sufficient to model the different levels of indirection. The current
+ // single-origin model cannot distinguish between a loan to the variable's
+ // storage and a loan to what it points to. A multi-origin model would be
+ // required for this.
+ if (!isPointerType(DRE->getType())) {
+ if (const Loan *L = createLoan(DRE)) {
+ OriginID ExprOID = FactMgr.getOriginMgr().getOrCreate(*DRE);
+ CurrentBlockFacts.push_back(
+ FactMgr.createFact<IssueFact>(L->ID, ExprOID));
+ }
+ }
+ }
void VisitCXXNullPtrLiteralExpr(const CXXNullPtrLiteralExpr *N) {
/// TODO: Handle nullptr expr as a special 'null' loan. Uninitialized
@@ -452,38 +485,31 @@ class FactGenerator : public ConstStmtVisitor<FactGenerator> {
}
void VisitImplicitCastExpr(const ImplicitCastExpr *ICE) {
- if (!hasOrigin(ICE->getType()))
+ if (!hasOrigin(ICE))
return;
// An ImplicitCastExpr node itself gets an origin, which flows from the
// origin of its sub-expression (after stripping its own parens/casts).
- // TODO: Consider if this is actually useful in practice. Alternatively, we
- // could directly use the sub-expression's OriginID instead of creating a
- // new one.
addAssignOriginFact(*ICE, *ICE->getSubExpr());
}
void VisitUnaryOperator(const UnaryOperator *UO) {
if (UO->getOpcode() == UO_AddrOf) {
const Expr *SubExpr = UO->getSubExpr();
- if (const auto *DRE = dyn_cast<DeclRefExpr>(SubExpr)) {
- if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
- // Check if it's a local variable.
- if (VD->hasLocalStorage()) {
- OriginID OID = FactMgr.getOriginMgr().getOrCreate(*UO);
- AccessPath AddrOfLocalVarPath(VD);
- const Loan &L =
- FactMgr.getLoanMgr().addLoan(AddrOfLocalVarPath, UO);
- CurrentBlockFacts.push_back(
- FactMgr.createFact<IssueFact>(L.ID, OID));
- }
- }
- }
+ // Taking address of a pointer-type expression is not yet supported and
+ // will be supported in multi-origin model.
+ if (isPointerType(SubExpr->getType()))
+ return;
+ // The origin of an address-of expression (e.g., &x) is the origin of
+ // its sub-expression (x). This fact will cause the dataflow analysis
+ // to propagate any loans held by the sub-expression's origin to the
+ // origin of this UnaryOperator expression.
+ addAssignOriginFact(*UO, *SubExpr);
}
}
void VisitReturnStmt(const ReturnStmt *RS) {
if (const Expr *RetExpr = RS->getRetValue()) {
- if (hasOrigin(RetExpr->getType())) {
+ if (hasOrigin(RetExpr)) {
OriginID OID = FactMgr.getOriginMgr().getOrCreate(*RetExpr);
CurrentBlockFacts.push_back(
FactMgr.createFact<ReturnOfOriginFact>(OID));
@@ -506,20 +532,6 @@ class FactGenerator : public ConstStmtVisitor<FactGenerator> {
// expression.
if (VisitTestPoint(FCE))
return;
- // Visit as normal otherwise.
- Base::VisitCXXFunctionalCastExpr(FCE);
- }
-
-private:
- // Check if a type has an origin.
- bool hasOrigin(QualType QT) { return QT->isPointerOrReferenceType(); }
-
- template <typename Destination, typename Source>
- void addAssignOriginFact(const Destination &D, const Source &S) {
- OriginID DestOID = FactMgr.getOriginMgr().getOrCreate(D);
- OriginID SrcOID = FactMgr.getOriginMgr().get(S);
- CurrentBlockFacts.push_back(
- FactMgr.createFact<AssignOriginFact>(DestOID, SrcOID));
}
void handleDestructor(const CFGAutomaticObjDtor &DtorOpt) {
@@ -544,6 +556,41 @@ class FactGenerator : public ConstStmtVisitor<FactGenerator> {
}
}
+private:
+ static bool isPointerType(QualType QT) {
+ return QT->isPointerOrReferenceType();
+ }
+
+ // Check if a type has an origin.
+ static bool hasOrigin(const Expr *E) {
+ return E->isGLValue() || isPointerType(E->getType());
+ }
+
+ static bool hasOrigin(const VarDecl *VD) {
+ return isPointerType(VD->getType());
+ }
+
+ /// Creates a loan for the storage path of a given declaration reference.
+ /// This function should be called whenever a DeclRefExpr represents a borrow.
+ /// \param DRE The declaration reference expression that initiates the borrow.
+ /// \return The new Loan on success, nullptr otherwise.
+ const Loan *createLoan(const DeclRefExpr *DRE) {
+ if (const auto *VD = dyn_cast<ValueDecl>(DRE->getDecl())) {
+ AccessPath Path(VD);
+ // The loan is created at the location of the DeclRefExpr.
+ return &FactMgr.getLoanMgr().addLoan(Path, DRE);
+ }
+ return nullptr;
+ }
+
+ template <typename Destination, typename Source>
+ void addAssignOriginFact(const Destination &D, const Source &S) {
+ OriginID DestOID = FactMgr.getOriginMgr().getOrCreate(D);
+ OriginID SrcOID = FactMgr.getOriginMgr().get(S);
+ CurrentBlockFacts.push_back(
+ FactMgr.createFact<AssignOriginFact>(DestOID, SrcOID));
+ }
+
/// Checks if the expression is a `void("__lifetime_test_point_...")` cast.
/// If so, creates a `TestPointFact` and returns true.
bool VisitTestPoint(const CXXFunctionalCastExpr *FCE) {
@@ -566,25 +613,26 @@ class FactGenerator : public ConstStmtVisitor<FactGenerator> {
}
void handleAssignment(const Expr *LHSExpr, const Expr *RHSExpr) {
+ if (!hasOrigin(LHSExpr))
+ return;
// Find the underlying variable declaration for the left-hand side.
if (const auto *DRE_LHS =
dyn_cast<DeclRefExpr>(LHSExpr->IgnoreParenImpCasts())) {
markUseAsWrite(DRE_LHS);
if (const auto *VD_LHS = dyn_cast<ValueDecl>(DRE_LHS->getDecl()))
- if (hasOrigin(LHSExpr->getType()))
- // We are interested in assignments like `ptr1 = ptr2` or `ptr = &var`
- // LHS must be a pointer/reference type that can be an origin.
- // RHS must also represent an origin (either another pointer/ref or an
- // address-of).
- addAssignOriginFact(*VD_LHS, *RHSExpr);
+ // We are interested in assignments like `ptr1 = ptr2` or `ptr = &var`.
+ // LHS must be a pointer/reference type that can be an origin. RHS must
+ // also represent an origin (either another pointer/ref or an
+ // address-of).
+ addAssignOriginFact(*VD_LHS, *RHSExpr);
}
}
- // A DeclRefExpr is a use of the referenced decl. It is checked for
- // use-after-free unless it is being written to (e.g. on the left-hand side
- // of an assignment).
+ // A DeclRefExpr will be treated as a use of the referenced decl. It will be
+ // checked for use-after-free unless it is later marked as being written to
+ // (e.g. on the left-hand side of an assignment).
void handleUse(const DeclRefExpr *DRE) {
- if (hasOrigin(DRE->getType())) {
+ if (isPointerType(DRE->getType())) {
UseFact *UF = FactMgr.createFact<UseFact>(DRE);
CurrentBlockFacts.push_back(UF);
assert(!UseFacts.contains(DRE));
diff --git a/clang/test/Sema/warn-lifetime-safety-dataflow.cpp b/clang/test/Sema/warn-lifetime-safety-dataflow.cpp
index 11437d024b693..7dac27506fb6b 100644
--- a/clang/test/Sema/warn-lifetime-safety-dataflow.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-dataflow.cpp
@@ -10,45 +10,55 @@ struct MyObj {
// CHECK-LABEL: Function: return_local_addr
MyObj* return_local_addr() {
MyObj x {10};
- MyObj* p = &x;
// CHECK: Block B{{[0-9]+}}:
-// CHECK: Issue (LoanID: [[L_X:[0-9]+]], ToOrigin: [[O_ADDR_X:[0-9]+]] (Expr: UnaryOperator))
+// CHECK: Issue ([[L_X:[0-9]+]] (Path: x), ToOrigin: [[O_DRE_X:[0-9]+]] (Expr: DeclRefExpr))
+// CHECK: AssignOrigin (Dest: [[O_ADDR_X:[0-9]+]] (Expr: UnaryOperator), Src: [[O_DRE_X]] (Expr: DeclRefExpr))
+ MyObj* p = &x;
// CHECK: AssignOrigin (Dest: [[O_P:[0-9]+]] (Decl: p), Src: [[O_ADDR_X]] (Expr: UnaryOperator))
return p;
+// CHECK: Use ([[O_P]] (Decl: p), Read)
// CHECK: AssignOrigin (Dest: [[O_RET_VAL:[0-9]+]] (Expr: ImplicitCastExpr), Src: [[O_P]] (Decl: p))
// CHECK: ReturnOfOrigin ([[O_RET_VAL]] (Expr: ImplicitCastExpr))
-// CHECK: Expire (LoanID: [[L_X]])
+// CHECK: Expire ([[L_X]] (Path: x))
}
// Pointer Assignment and Return
// CHECK-LABEL: Function: assign_and_return_local_addr
-// CHECK-NEXT: Block B{{[0-9]+}}:
MyObj* assign_and_return_local_addr() {
MyObj y{20};
+// CHECK: Block B{{[0-9]+}}:
+// CHECK: Issue ([[L_Y:[0-9]+]] (Path: y), ToOrigin: [[O_DRE_Y:[0-9]+]] (Expr: DeclRefExpr))
+// CHECK: AssignOrigin (Dest: [[O_ADDR_Y:[0-9]+]] (Expr: UnaryOperator), Src: [[O_DRE_Y]] (Expr: DeclRefExpr))
MyObj* ptr1 = &y;
-// CHECK: Issue (LoanID: [[L_Y:[0-9]+]], ToOrigin: [[O_ADDR_Y:[0-9]+]] (Expr: UnaryOperator))
-// CHECK: AssignOrigin (Dest: [[O_PTR1:[0-9]+]] (Decl: ptr1), Src: [[O_ADDR_Y]] (Expr: UnaryOperator))
+// CHECK: AssignOrigin (Dest: [[O_PTR1:[0-9]+]] (Decl: ptr1), Src: [[O_ADDR_Y]] (Expr: UnaryOperator))
MyObj* ptr2 = ptr1;
-// CHECK: AssignOrigin (Dest: [[O_PTR1_RVAL:[0-9]+]] (Expr: ImplicitCastExpr), Src: [[O_PTR1]] (Decl: ptr1))
-// CHECK: AssignOrigin (Dest: [[O_PTR2:[0-9]+]] (Decl: ptr2), Src: [[O_PTR1_RVAL]] (Expr: ImplicitCastExpr))
+// CHECK: Use ([[O_PTR1]] (Decl: ptr1), Read)
+// CHECK: AssignOrigin (Dest: [[O_PTR1_RVAL:[0-9]+]] (Expr: ImplicitCastExpr), Src: [[O_PTR1]] (Decl: ptr1))
+// CHECK: AssignOrigin (Dest: [[O_PTR2:[0-9]+]] (Decl: ptr2), Src: [[O_PTR1_RVAL]] (Expr: ImplicitCastExpr))
ptr2 = ptr1;
-// CHECK: AssignOrigin (Dest: [[O_PTR1_RVAL_2:[0-9]+]] (Expr: ImplicitCastExpr), Src: [[O_PTR1]] (Decl: ptr1))
-// CHECK: AssignOrigin (Dest: [[O_PTR2]] (Decl: ptr2), Src: [[O_PTR1_RVAL_2]] (Expr: ImplicitCastExpr))
+// CHECK: Use ([[O_PTR1]] (Decl: ptr1), Read)
+// CHECK: AssignOrigin (Dest: [[O_PTR1_RVAL_2:[0-9]+]] (Expr: ImplicitCastExpr), Src: [[O_PTR1]] (Decl: ptr1))
+// CHECK: Use ({{[0-9]+}} (Decl: ptr2), Write)
+// CHECK: AssignOrigin (Dest: [[O_PTR2]] (Decl: ptr2), Src: [[O_PTR1_RVAL_2]] (Expr: ImplicitCastExpr))
ptr2 = ptr2; // Self assignment.
-// CHECK: AssignOrigin (Dest: [[O_PTR2_RVAL:[0-9]+]] (Expr: ImplicitCastExpr), Src: [[O_PTR2]] (Decl: ptr2))
-// CHECK: AssignOrigin (Dest: [[O_PTR2]] (Decl: ptr2), Src: [[O_PTR2_RVAL]] (Expr: ImplicitCastExpr))
+// CHECK: Use ([[O_PTR2]] (Decl: ptr2), Read)
+// CHECK: AssignOrigin (Dest: [[O_PTR2_RVAL:[0-9]+]] (Expr: ImplicitCastExpr), Src: [[O_PTR2]] (Decl: ptr2))
+// CHECK: Use ([[O_PTR2]] (Decl: ptr2), Write)
+// CHECK: AssignOrigin (Dest: [[O_PTR2]] (Decl: ptr2), Src: [[O_PTR2_RVAL]] (Expr: ImplicitCastExpr))
return ptr2;
-// CHECK: AssignOrigin (Dest: [[O_PTR2_RVAL_2:[0-9]+]] (Expr: ImplicitCastExpr), Src: [[O_PTR2]] (Decl: ptr2))
-// CHECK: ReturnOfOrigin ([[O_PTR2_RVAL_2]] (Expr: ImplicitCastExpr))
-// CHECK: Expire (LoanID: [[L_Y]])
+// CHECK: Use ([[O_PTR2]] (Decl: ptr2), Read)
+// CHECK: AssignOrigin (Dest: [[O_RET_VAL:[0-9]+]] (Expr: ImplicitCastExpr), Src: [[O_PTR2]] (Decl: ptr2))
+// CHECK: ReturnOfOrigin ([[O_RET_VAL]] (Expr: ImplicitCastExpr))
+// CHECK: Expire ([[L_Y]] (Path: y))
}
// Return of Non-Pointer Type
// CHECK-LABEL: Function: return_int_val
-// CHECK-NEXT: Block B{{[0-9]+}}:
int return_int_val() {
int x = 10;
+// CHECK: Block B{{[0-9]+}}:
+// CHECK: Issue ([[L_X:[0-9]+]] (Path: x), ToOrigin: {{[0-9]+}} (Expr: DeclRefExpr))
return x;
}
// CHECK-NEXT: End of Block
@@ -56,25 +66,27 @@ int return_int_val() {
// Loan Expiration (Automatic Variable, C++)
// CHECK-LABEL: Function: loan_expires_cpp
-// CHECK-NEXT: Block B{{[0-9]+}}:
void loan_expires_cpp() {
MyObj obj{1};
+// CHECK: Block B{{[0-9]+}}:
+// CHECK: Issue ([[L_OBJ:[0-9]+]] (Path: obj), ToOrigin: [[O_DRE_OBJ:[0-9]+]] (Expr: DeclRefExpr))
+// CHECK: AssignOrigin (Dest: [[O_ADDR_OBJ:[0-9]+]] (Expr: UnaryOperator), Src: [[O_DRE_OBJ]] (Expr: DeclRefExpr))
MyObj* pObj = &obj;
-// CHECK: Issue (LoanID: [[L_OBJ:[0-9]+]], ToOrigin: [[O_ADDR_OBJ:[0-9]+]] (Expr: UnaryOperator))
-// CHECK: AssignOrigin (Dest: [[O_POBJ:[0-9]+]] (Decl: pObj), Src: [[O_ADDR_OBJ]] (Expr: UnaryOperator))
-// CHECK: Expire (LoanID: [[L_OBJ]])
+// CHECK: AssignOrigin (Dest: {{[0-9]+}} (Decl: pObj), Src: [[O_ADDR_OBJ]] (Expr: UnaryOperator))
+// CHECK: Expire ([[L_OBJ]] (Path: obj))
}
// FIXME: No expire for Trivial Destructors
// CHECK-LABEL: Function: loan_expires_trivial
-// CHECK-NEXT: Block B{{[0-9]+}}:
void loan_expires_trivial() {
int trivial_obj = 1;
+// CHECK: Block B{{[0-9]+}}:
+// CHECK: Issue ([[L_TRIVIAL_OBJ:[0-9]+]] (Path: trivial_obj), ToOrigin: [[O_DRE_TRIVIAL:[0-9]+]] (Expr: DeclRefExpr))
+// CHECK: AssignOrigin (Dest: [[O_ADDR_TRIVIAL_OBJ:[0-9]+]] (Expr: UnaryOperator), Src: [[O_DRE_TRIVIAL]] (Expr: DeclRefExpr))
int* pTrivialObj = &trivial_obj;
-// CHECK: Issue (LoanID: [[L_TRIVIAL_OBJ:[0-9]+]], ToOrigin: [[O_ADDR_TRIVIAL_OBJ:[0-9]+]] (Expr: UnaryOperator))
-// CHECK: AssignOrigin (Dest: [[O_PTOBJ:[0-9]+]] (Decl: pTrivialObj), Src: [[O_ADDR_TRIVIAL_OBJ]] (Expr: UnaryOperator))
-// CHECK-NOT: Expire (LoanID: [[L_TRIVIAL_OBJ]])
+// CHECK: AssignOrigin (Dest: {{[0-9]+}} (Decl: pTrivialObj), Src: [[O_ADDR_TRIVIAL_OBJ]] (Expr: UnaryOperator))
+// CHECK-NOT: Expire
// CHECK-NEXT: End of Block
// FIXME: Add check for Expire once trivial destructors are handled for expiration.
}
@@ -86,16 +98,22 @@ void conditional(bool condition) {
int* p = nullptr;
if (condition)
+// CHECK: Block B{{[0-9]+}}:
+// CHECK: Issue ([[L_A:[0-9]+]] (Path: a), ToOrigin: [[O_DRE_A:[0-9]+]] (Expr: DeclRefExpr))
+// CHECK: AssignOrigin (Dest: [[O_ADDR_A:[0-9]+]] (Expr: UnaryOperator), Src: [[O_DRE_A]] (Expr: DeclRefExpr))
+// CHECK: AssignOrigin (Dest: [[O_P:[0-9]+]] (Decl: p), Src: [[O_ADDR_A]] (Expr: UnaryOperator))
p = &a;
-// CHECK: Issue (LoanID: [[L_A:[0-9]+]], ToOrigin: [[O_ADDR_A:[0-9]+]] (Expr: UnaryOperator))
-// CHECK: AssignOrigin (Dest: [[O_P:[0-9]+]] (Decl: p), Src: [[O_ADDR_A]] (Expr: UnaryOperator))
else
+// CHECK: Block B{{[0-9]+}}:
+// CHECK: Issue ([[L_B:[0-9]+]] (Path: b), ToOrigin: [[O_DRE_B:[0-9]+]] (Expr: DeclRefExpr))
+// CHECK: AssignOrigin (Dest: [[O_ADDR_B:[0-9]+]] (Expr: UnaryOperator), Src: [[O_DRE_B]] (Expr: DeclRefExpr))
+// CHECK: AssignOrigin (Dest: [[O_P:[0-9]+]] (Decl: p), Src: [[O_ADDR_B]] (Expr: UnaryOperator))
p = &b;
-// CHECK: Issue (LoanID: [[L_B:[0-9]+]], ToOrigin: [[O_ADDR_B:[0-9]+]] (Expr: UnaryOperator))
-// CHECK: AssignOrigin (Dest: [[O_P]] (Decl: p), Src: [[O_ADDR_B]] (Expr: UnaryOperator))
+// CHECK: Block B{{[0-9]+}}:
int *q = p;
-// CHECK: AssignOrigin (Dest: [[O_P_RVAL:[0-9]+]] (Expr: ImplicitCastExpr), Src: [[O_P]] (Decl: p))
-// CHECK: AssignOrigin (Dest: [[O_Q:[0-9]+]] (Decl: q), Src: [[O_P_RVAL]] (Expr: ImplicitCastExpr))
+// CHECK: Use ([[O_P]] (Decl: p), Read)
+// CHECK: AssignOrigin (Dest: [[O_P_RVAL:[0-9]+]] (Expr: ImplicitCastExpr), Src: [[O_P]] (Decl: p))
+// CHECK: AssignOrigin (Dest: [[O_Q:[0-9]+]] (Decl: q), Src: [[O_P_RVAL]] (Expr: ImplicitCastExpr))
}
@@ -109,26 +127,36 @@ void pointers_in_a_cycle(bool condition) {
MyObj* p2 = &v2;
MyObj* p3 = &v3;
// CHECK: Block B{{[0-9]+}}:
-// CHECK: Issue (LoanID: [[L_V1:[0-9]+]], ToOrigin: [[O_ADDR_V1:[0-9]+]] (Expr: UnaryOperator))
+// CHECK: Issue ([[L_V1:[0-9]+]] (Path: v1), ToOrigin: [[O_DRE_V1:[0-9]+]] (Expr: DeclRefExpr))
+// CHECK: AssignOrigin (Dest: [[O_ADDR_V1:[0-9]+]] (Expr: UnaryOperator), Src: [[O_DRE_V1]] (Expr: DeclRefExpr))
// CHECK: AssignOrigin (Dest: [[O_P1:[0-9]+]] (Decl: p1), Src: [[O_ADDR_V1]] (Expr: UnaryOperator))
-// CHECK: Issue (LoanID: [[L_V2:[0-9]+]], ToOrigin: [[O_ADDR_V2:[0-9]+]] (Expr: UnaryOperator))
+// CHECK: Issue ([[L_V2:[0-9]+]] (Path: v2), ToOrigin: [[O_DRE_V2:[0-9]+]] (Expr: DeclRefExpr))
+// CHECK: AssignOrigin (Dest: [[O_ADDR_V2:[0-9]+]] (Expr: UnaryOperator), Src: [[O_DRE_V2]] (Expr: DeclRefExpr))
// CHECK: AssignOrigin (Dest: [[O_P2:[0-9]+]] (Decl: p2), Src: [[O_ADDR_V2]] (Expr: UnaryOperator))
-// CHECK: Issue (LoanID: [[L_V3:[0-9]+]], ToOrigin: [[O_ADDR_V3:[0-9]+]] (Expr: UnaryOperator))
+// CHECK: Issue ([[L_V3:[0-9]+]] (Path: v3), ToOrigin: [[O_DRE_V3:[0-9]+]] (Expr: DeclRefExpr))
+// CHECK: AssignOrigin (Dest: [[O_ADDR_V3:[0-g]+]] (Expr: UnaryOperator), Src: [[O_DRE_V3]] (Expr: DeclRefExpr))
// CHECK: AssignOrigin (Dest: [[O_P3:[0-9]+]] (Decl: p3), Src: [[O_ADDR_V3]] (Expr: UnaryOperator))
while (condition) {
- MyObj* temp = p1;
- p1 = p2;
- p2 = p3;
- p3 = temp;
// CHECK: Block B{{[0-9]+}}:
+ MyObj* temp = p1;
+// CHECK: Use ([[O_P1]] (Decl: p1), Read)
// CHECK: AssignOrigin (Dest: [[O_P1_RVAL:[0-9]+]] (Expr: ImplicitCastExpr), Src: [[O_P1]] (Decl: p1))
// CHECK: AssignOrigin (Dest: [[O_TEMP:[0-9]+]] (Decl: temp), Src: [[O_P1_RVAL]] (Expr: ImplicitCastExpr))
+ p1 = p2;
+// CHECK: Use ([[O_P2:[0-9]+]] (Decl: p2), Read)
// CHECK: AssignOrigin (Dest: [[O_P2_RVAL:[0-9]+]] (Expr: ImplicitCastExpr), Src: [[O_P2]] (Decl: p2))
+// CHECK: Use ({{[0-9]+}} (Decl: p1), Write)
// CHECK: AssignOrigin (Dest: [[O_P1]] (Decl: p1), Src: [[O_P2_RVAL]] (Expr: ImplicitCastExpr))
+ p2 = p3;
+// CHECK: Use ([[O_P3:[0-9]+]] (Decl: p3), Read)
// CHECK: AssignOrigin (Dest: [[O_P3_RVAL:[0-9]+]] (Expr: ImplicitCastExpr), Src: [[O_P3]] (Decl: p3))
+// CHECK: Use ({{[0-9]+}} (Decl: p2), Write)
// CHECK: AssignOrigin (Dest: [[O_P2]] (Decl: p2), Src: [[O_P3_RVAL]] (Expr: ImplicitCastExpr))
+ p3 = temp;
+// CHECK: Use ([[O_TEMP]] (Decl: temp), Read)
// CHECK: AssignOrigin (Dest: [[O_TEMP_RVAL:[0-9]+]] (Expr: ImplicitCastExpr), Src: [[O_TEMP]] (Decl: temp))
+// CHECK: Use ({{[0-9]+}} (Decl: p3), Write)
// CHECK: AssignOrigin (Dest: [[O_P3]] (Decl: p3), Src: [[O_TEMP_RVAL]] (Expr: ImplicitCastExpr))
}
}
@@ -137,28 +165,33 @@ void pointers_in_a_cycle(bool condition) {
void overwrite_origin() {
MyObj s1;
MyObj s2;
- MyObj* p = &s1;
// CHECK: Block B{{[0-9]+}}:
-// CHECK: Issue (LoanID: [[L_S1:[0-9]+]], ToOrigin: [[O_ADDR_S1:[0-9]+]] (Expr: UnaryOperator))
+ MyObj* p = &s1;
+// CHECK: Issue ([[L_S1:[0-9]+]] (Path: s1), ToOrigin: [[O_DRE_S1:[0-9]+]] (Expr: DeclRefExpr))
+// CHECK: AssignOrigin (Dest: [[O_ADDR_S1:[0-9]+]] (Expr: UnaryOperator), Src: [[O_DRE_S1]] (Expr: DeclRefExpr))
// CHECK: AssignOrigin (Dest: [[O_P:[0-9]+]] (Decl: p), Src: [[O_ADDR_S1]] (Expr: UnaryOperator))
p = &s2;
-// CHECK: Issue (LoanID: [[L_S2:[0-9]+]], ToOrigin: [[O_ADDR_S2:[0-9]+]] (Expr: UnaryOperator))
+// CHECK: Issue ([[L_S2:[0-9]+]] (Path: s2), ToOrigin: [[O_DRE_S2:[0-9]+]] (Expr: DeclRefExpr))
+// CHECK: AssignOrigin (Dest: [[O_ADDR_S2:[0-9]+]] (Expr: UnaryOperator), Src: [[O_DRE_S2]] (Expr: DeclRefExpr))
+// CHECK: Use ({{[0-9]+}} (Decl: p), Write)
// CHECK: AssignOrigin (Dest: [[O_P]] (Decl: p), Src: [[O_ADDR_S2]] (Expr: UnaryOperator))
-// CHECK: Expire (LoanID: [[L_S2]])
-// CHECK: Expire (LoanID: [[L_S1]])
+// CHECK: Expire ([[L_S2]] (Path: s2))
+// CHECK: Expire ([[L_S1]] (Path: s1))
}
// CHECK-LABEL: Function: reassign_to_null
void reassign_to_null() {
MyObj s1;
- MyObj* p = &s1;
// CHECK: Block B{{[0-9]+}}:
-// CHECK: Issue (LoanID: [[L_S1:[0-9]+]], ToOrigin: [[O_ADDR_S1:[0-9]+]] (Expr: UnaryOperator))
+ MyObj* p = &s1;
+// CHECK: Issue ([[L_S1:[0-9]+]] (Path: s1), ToOrigin: [[O_DRE_S1:[0-9]+]] (Expr: DeclRefExpr))
+// CHECK: AssignOrigin (Dest: [[O_ADDR_S1:[0-9]+]] (Expr: UnaryOperator), Src: [[O_DRE_S1]] (Expr: DeclRefExpr))
// CHECK: AssignOrigin (Dest: [[O_P:[0-9]+]] (Decl: p), Src: [[O_ADDR_S1]] (Expr: UnaryOperator))
p = nullptr;
// CHECK: AssignOrigin (Dest: [[O_NULLPTR_CAST:[0-9]+]] (Expr: ImplicitCastExpr), Src: {{[0-9]+}} (Expr: CXXNullPtrLiteralExpr))
+// CHECK: Use ({{[0-9]+}} (Decl: p), Write)
// CHECK: AssignOrigin (Dest: [[O_P]] (Decl: p), Src: [[O_NULLPTR_CAST]] (Expr: ImplicitCastExpr))
-// CHECK: Expire (LoanID: [[L_S1]])
+// CHECK: Expire ([[L_S1]] (Path: s1))
}
// FIXME: Have a better representation for nullptr than just an empty origin.
// It should be a separate loan and origin kind.
@@ -170,17 +203,20 @@ void reassign_in_if(bool condition) {
MyObj s2;
MyObj* p = &s1;
// CHECK: Block B{{[0-9]+}}:
-// CHECK: Issue (LoanID: [[L_S1:[0-9]+]], ToOrigin: [[O_ADDR_S1:[0-9]+]] (Expr: UnaryOperator))
+// CHECK: Issue ([[L_S1:[0-9]+]] (Path: s1), ToOrigin: [[O_DRE_S1:[0-9]+]] (Expr: DeclRefExpr))
+// CHECK: AssignOrigin (Dest: [[O_ADDR_S1:[0-9]+]] (Expr: UnaryOperator), Src: [[O_DRE_S1]] (Expr: DeclRefExpr))
// CHECK: AssignOrigin (Dest: [[O_P:[0-9]+]] (Decl: p), Src: [[O_ADDR_S1]] (Expr: UnaryOperator))
if (condition) {
- p = &s2;
// CHECK: Block B{{[0-9]+}}:
-// CHECK: Issue (LoanID: [[L_S2:[0-9]+]], ToOrigin: [[O_ADDR_S2:[0-9]+]] (Expr: UnaryOperator))
+ p = &s2;
+// CHECK: Issue ([[L_S2:[0-9]+]] (Path: s2), ToOrigin: [[O_DRE_S2:[0-9]+]] (Expr: DeclRefExpr))
+// CHECK: AssignOrigin (Dest: [[O_ADDR_S2:[0-9]+]] (Expr: UnaryOperator), Src: [[O_DRE_S2]] (Expr: DeclRefExpr))
+// CHECK: Use ({{[0-9]+}} (Decl: p), Write)
// CHECK: AssignOrigin (Dest: [[O_P]] (Decl: p), Src: [[O_ADDR_S2]] (Expr: UnaryOperator))
}
// CHECK: Block B{{[0-9]+}}:
-// CHECK: Expire (LoanID: [[L_S2]])
-// CHECK: Expire (LoanID: [[L_S1]])
+// CHECK: Expire ([[L_S2]] (Path: s2))
+// CHECK: Expire ([[L_S1]] (Path: s1))
}
@@ -195,42 +231,51 @@ void assign_in_switch(int mode) {
// CHECK: AssignOrigin (Dest: [[O_P:[0-9]+]] (Decl: p), Src: [[O_NULLPTR_CAST]] (Expr: ImplicitCastExpr))
switch (mode) {
case 1:
+// CHECK-DAG: Block B{{[0-9]+}}:
p = &s1;
-// CHECK: Block B{{[0-9]+}}:
-// CHECK: Issue (LoanID: [[L_S1:[0-9]+]], ToOrigin: [[O_ADDR_S1:[0-9]+]] (Expr: UnaryOperator))
-// CHECK: AssignOrigin (Dest: [[O_P]] (Decl: p), Src: [[O_ADDR_S1]] (Expr: UnaryOperator))
+// CHECK-DAG: Issue ([[L_S1:[0-9]+]] (Path: s1), ToOrigin: [[O_DRE_S1:[0-9]+]] (Expr: DeclRefExpr))
+// CHECK-DAG: AssignOrigin (Dest: [[O_ADDR_S1:[0-9]+]] (Expr: UnaryOperator), Src: [[O_DRE_S1]] (Expr: DeclRefExpr))
+// CHECK-DAG: Use ({{[0-9]+}} (Decl: p), Write)
+// CHECK-DAG: AssignOrigin (Dest: [[O_P]] (Decl: p), Src: [[O_ADDR_S1]] (Expr: UnaryOperator))
break;
case 2:
+// CHECK-DAG: Block B{{[0-9]+}}:
p = &s2;
-// CHECK: Block B{{[0-9]+}}:
-// CHECK: Issue (LoanID: [[L_S2:[0-9]+]], ToOrigin: [[O_ADDR_S2:[0-9]+]] (Expr: UnaryOperator))
-// CHECK: AssignOrigin (Dest: [[O_P]] (Decl: p), Src: [[O_ADDR_S2]] (Expr: UnaryOperator))
+// CHECK-DAG: Issue ([[L_S2:[0-9]+]] (Path: s2), ToOrigin: [[O_DRE_S2:[0-9]+]] (Expr: DeclRefExpr))
+// CHECK-DAG: AssignOrigin (Dest: [[O_ADDR_S2:[0-9]+]] (Expr: UnaryOperator), Src: [[O_DRE_S2]] (Expr: DeclRefExpr))
+// CHECK-DAG: Use ({{[0-9]+}} (Decl: p), Write)
+// CHECK-DAG: AssignOrigin (Dest: [[O_P]] (Decl: p), Src: [[O_ADDR_S2]] (Expr: UnaryOperator))
break;
default:
- p = &s3;
// CHECK: Block B{{[0-9]+}}:
-// CHECK: Issue (LoanID: [[L_S3:[0-9]+]], ToOrigin: [[O_ADDR_S3:[0-9]+]] (Expr: UnaryOperator))
+ p = &s3;
+// CHECK: Issue ([[L_S3:[0-9]+]] (Path: s3), ToOrigin: [[O_DRE_S3:[0-9]+]] (Expr: DeclRefExpr))
+// CHECK: AssignOrigin (Dest: [[O_ADDR_S3:[0-9]+]] (Expr: UnaryOperator), Src: [[O_DRE_S3]] (Expr: DeclRefExpr))
+// CHECK: Use ({{[0-9]+}} (Decl: p), Write)
// CHECK: AssignOrigin (Dest: [[O_P]] (Decl: p), Src: [[O_ADDR_S3]] (Expr: UnaryOperator))
break;
}
// CHECK: Block B{{[0-9]+}}:
-// CHECK-DAG: Expire (LoanID: [[L_S3]])
-// CHECK-DAG: Expire (LoanID: [[L_S2]])
-// CHECK-DAG: Expire (LoanID: [[L_S1]])
+// CHECK-DAG: Expire ([[L_S3]] (Path: s3))
+// CHECK-DAG: Expire ([[L_S2]] (Path: s2))
+// CHECK-DAG: Expire ([[L_S1]] (Path: s1))
}
// CHECK-LABEL: Function: loan_in_loop
void loan_in_loop(bool condition) {
MyObj* p = nullptr;
- // CHECK: AssignOrigin (Dest: [[O_NULLPTR_CAST:[0-9]+]] (Expr: ImplicitCastExpr), Src: [[O_NULLPTR:[0-9]+]] (Expr: CXXNullPtrLiteralExpr))
- // CHECK: AssignOrigin (Dest: [[O_P:[0-9]+]] (Decl: p), Src: [[O_NULLPTR_CAST]] (Expr: ImplicitCastExpr))
+// CHECK: Block B{{[0-9]+}}:
+// CHECK: AssignOrigin (Dest: [[O_NULLPTR_CAST:[0-9]+]] (Expr: ImplicitCastExpr), Src: [[O_NULLPTR:[0-9]+]] (Expr: CXXNullPtrLiteralExpr))
+// CHECK: AssignOrigin (Dest: [[O_P:[0-9]+]] (Decl: p), Src: [[O_NULLPTR_CAST]] (Expr: ImplicitCastExpr))
while (condition) {
MyObj inner;
- p = &inner;
// CHECK: Block B{{[0-9]+}}:
-// CHECK: Issue (LoanID: [[L_INNER:[0-9]+]], ToOrigin: [[O_ADDR_INNER:[0-9]+]] (Expr: UnaryOperator))
+ p = &inner;
+// CHECK: Issue ([[L_INNER:[0-9]+]] (Path: inner), ToOrigin: [[O_DRE_INNER:[0-9]+]] (Expr: DeclRefExpr))
+// CHECK: AssignOrigin (Dest: [[O_ADDR_INNER:[0-9]+]] (Expr: UnaryOperator), Src: [[O_DRE_INNER]] (Expr: DeclRefExpr))
+// CHECK: Use ({{[0-9]+}} (Decl: p), Write)
// CHECK: AssignOrigin (Dest: [[O_P]] (Decl: p), Src: [[O_ADDR_INNER]] (Expr: UnaryOperator))
-// CHECK: Expire (LoanID: [[L_INNER]])
+// CHECK: Expire ([[L_INNER]] (Path: inner))
}
}
@@ -240,20 +285,23 @@ void loop_with_break(int count) {
MyObj s2;
MyObj* p = &s1;
// CHECK: Block B{{[0-9]+}}:
-// CHECK: Issue (LoanID: [[L_S1:[0-9]+]], ToOrigin: [[O_ADDR_S1:[0-9]+]] (Expr: UnaryOperator))
+// CHECK: Issue ([[L_S1:[0-9]+]] (Path: s1), ToOrigin: [[O_DRE_S1:[0-9]+]] (Expr: DeclRefExpr))
+// CHECK: AssignOrigin (Dest: [[O_ADDR_S1:[0-9]+]] (Expr: UnaryOperator), Src: [[O_DRE_S1]] (Expr: DeclRefExpr))
// CHECK: AssignOrigin (Dest: [[O_P:[0-9]+]] (Decl: p), Src: [[O_ADDR_S1]] (Expr: UnaryOperator))
for (int i = 0; i < count; ++i) {
if (i == 5) {
- p = &s2;
// CHECK: Block B{{[0-9]+}}:
-// CHECK: Issue (LoanID: [[L_S2:[0-9]+]], ToOrigin: [[O_ADDR_S2:[0-9]+]] (Expr: UnaryOperator))
+ p = &s2;
+// CHECK: Issue ([[L_S2:[0-9]+]] (Path: s2), ToOrigin: [[O_DRE_S2:[0-9]+]] (Expr: DeclRefExpr))
+// CHECK: AssignOrigin (Dest: [[O_ADDR_S2:[0-9]+]] (Expr: UnaryOperator), Src: [[O_DRE_S2]] (Expr: DeclRefExpr))
+// CHECK: Use ({{[0-9]+}} (Decl: p), Write)
// CHECK: AssignOrigin (Dest: [[O_P]] (Decl: p), Src: [[O_ADDR_S2]] (Expr: UnaryOperator))
break;
}
}
// CHECK: Block B{{[0-9]+}}:
-// CHECK: Expire (LoanID: [[L_S2]])
-// CHECK: Expire (LoanID: [[L_S1]])
+// CHECK: Expire ([[L_S2]] (Path: s2))
+// CHECK: Expire ([[L_S1]] (Path: s1))
}
// CHECK-LABEL: Function: nested_scopes
@@ -265,32 +313,36 @@ void nested_scopes() {
{
MyObj outer;
p = &outer;
-// CHECK: Issue (LoanID: [[L_OUTER:[0-9]+]], ToOrigin: [[O_ADDR_OUTER:[0-9]+]] (Expr: UnaryOperator))
+// CHECK: Issue ([[L_OUTER:[0-9]+]] (Path: outer), ToOrigin: [[O_DRE_OUTER:[0-9]+]] (Expr: DeclRefExpr))
+// CHECK: AssignOrigin (Dest: [[O_ADDR_OUTER:[0-9]+]] (Expr: UnaryOperator), Src: [[O_DRE_OUTER]] (Expr: DeclRefExpr))
+// CHECK: Use ({{[0-9]+}} (Decl: p), Write)
// CHECK: AssignOrigin (Dest: [[O_P]] (Decl: p), Src: [[O_ADDR_OUTER]] (Expr: UnaryOperator))
{
MyObj inner;
p = &inner;
-// CHECK: Issue (LoanID: [[L_INNER:[0-9]+]], ToOrigin: [[O_ADDR_INNER:[0-9]+]] (Expr: UnaryOperator))
+// CHECK: Issue ([[L_INNER:[0-9]+]] (Path: inner), ToOrigin: [[O_DRE_INNER:[0-9]+]] (Expr: DeclRefExpr))
+// CHECK: AssignOrigin (Dest: [[O_ADDR_INNER:[0-9]+]] (Expr: UnaryOperator), Src: [[O_DRE_INNER]] (Expr: DeclRefExpr))
+// CHECK: Use ({{[0-9]+}} (Decl: p), Write)
// CHECK: AssignOrigin (Dest: [[O_P]] (Decl: p), Src: [[O_ADDR_INNER]] (Expr: UnaryOperator))
}
-// CHECK: Expire (LoanID: [[L_INNER]])
+// CHECK: Expire ([[L_INNER]] (Path: inner))
}
-// CHECK: Expire (LoanID: [[L_OUTER]])
+// CHECK: Expire ([[L_OUTER]] (Path: outer))
}
// CHECK-LABEL: Function: pointer_indirection
void pointer_indirection() {
int a;
int *p = &a;
-// CHECK: Block B1:
-// CHECK: Issue (LoanID: [[L_A:[0-9]+]], ToOrigin: [[O_ADDR_A:[0-9]+]] (Expr: UnaryOperator))
+// CHECK: Block B{{[0-9]+}}:
+// CHECK: Issue ([[L_A:[0-9]+]] (Path: a), ToOrigin: [[O_DRE_A:[0-9]+]] (Expr: DeclRefExpr))
+// CHECK: AssignOrigin (Dest: [[O_ADDR_A:[0-9]+]] (Expr: UnaryOperator), Src: [[O_DRE_A]] (Expr: DeclRefExpr))
// CHECK: AssignOrigin (Dest: [[O_P:[0-9]+]] (Decl: p), Src: [[O_ADDR_A]] (Expr: UnaryOperator))
int **pp = &p;
-// CHECK: Issue (LoanID: [[L_P:[0-g]+]], ToOrigin: [[O_ADDR_P:[0-9]+]] (Expr: UnaryOperator))
-// CHECK: AssignOrigin (Dest: [[O_PP:[0-9]+]] (Decl: pp), Src: [[O_ADDR_P]] (Expr: UnaryOperator))
-
-// FIXME: The Origin for the RHS is broken
+// Note: No facts are generated for &p because the subexpression is a pointer type,
+// which is not yet supported by the origin model. This is expected.
int *q = *pp;
+// CHECK: Use ([[O_PP:[0-9]+]] (Decl: pp), Read)
// CHECK: AssignOrigin (Dest: {{[0-9]+}} (Decl: q), Src: {{[0-9]+}} (Expr: ImplicitCastExpr))
}
@@ -300,41 +352,41 @@ void ternary_operator() {
int a, b;
int *p;
p = (a > b) ? &a : &b;
- // CHECK: Block B2:
- // CHECK: Issue (LoanID: [[L_A:[0-9]+]], ToOrigin: [[O_ADDR_A:[0-9]+]] (Expr: UnaryOperator))
- // CHECK: End of Block
-
- // CHECK: Block B3:
- // CHECK: Issue (LoanID: [[L_B:[0-9]+]], ToOrigin: [[O_ADDR_A:[0-9]+]] (Expr: UnaryOperator))
- // CHECK: End of Block
-
- // CHECK: Block B1:
- // CHECK: AssignOrigin (Dest: [[O_P:[0-9]+]] (Decl: p), Src: {{[0-9]+}} (Expr: ConditionalOperator))
- // CHECK: End of Block
+// CHECK: Block B{{[0-9]+}}:
+// CHECK: Issue ([[L_A:[0-9]+]] (Path: a), ToOrigin: [[O_DRE_A:[0-9]+]] (Expr: DeclRefExpr))
+
+// CHECK: Block B{{[0-9]+}}:
+// CHECK: Issue ([[L_B:[0-9]+]] (Path: b), ToOrigin: [[O_DRE_B:[0-9]+]] (Expr: DeclRefExpr))
+
+// CHECK: Block B{{[0-9]+}}:
+// CHECK: Use ({{[0-9]+}} (Decl: p), Write)
+// CHECK: AssignOrigin (Dest: {{[0-9]+}} (Decl: p), Src: {{[0-9]+}} (Expr: ConditionalOperator))
}
// CHECK-LABEL: Function: test_use_facts
void usePointer(MyObj*);
void test_use_facts() {
- // CHECK: Block B{{[0-9]+}}:
MyObj x;
MyObj *p;
+// CHECK: Block B{{[0-9]+}}:
p = &x;
- // CHECK: Use ([[O_P:[0-9]+]] (Decl: p) Write)
+// CHECK: Issue ([[L_X:[0-9]+]] (Path: x), ToOrigin: [[O_DRE_X:[0-9]+]] (Expr: DeclRefExpr))
+// CHECK: AssignOrigin (Dest: [[O_ADDR_X:[0-9]+]] (Expr: UnaryOperator), Src: [[O_DRE_X]] (Expr: DeclRefExpr))
+// CHECK: Use ([[O_P:[0-9]+]] (Decl: p), Write)
+// CHECK: AssignOrigin (Dest: [[O_P]] (Decl: p), Src: [[O_ADDR_X]] (Expr: UnaryOperator))
(void)*p;
- // CHECK: Use ([[O_P]] (Decl: p) Read)
+// CHECK: Use ([[O_P]] (Decl: p), Read)
usePointer(p);
- // CHECK: Use ([[O_P]] (Decl: p) Read)
+// CHECK: Use ([[O_P]] (Decl: p), Read)
p->id = 1;
- // CHECK: Use ([[O_P]] (Decl: p) Read)
-
-
+// CHECK: Use ([[O_P]] (Decl: p), Read)
MyObj* q;
q = p;
- // CHECK: Use ([[O_P]] (Decl: p) Read)
- // CHECK: Use ([[O_Q:[0-9]+]] (Decl: q) Write)
+// CHECK: Use ([[O_P]] (Decl: p), Read)
+// CHECK: Use ([[O_Q:[0-9]+]] (Decl: q), Write)
usePointer(q);
- // CHECK: Use ([[O_Q]] (Decl: q) Read)
+// CHECK: Use ([[O_Q]] (Decl: q), Read)
q->id = 2;
- // CHECK: Use ([[O_Q]] (Decl: q) Read)
-}
+// CHECK: Use ([[O_Q]] (Decl: q), Read)
+// CHECK: Expire ([[L_X]] (Path: x))
+}
\ No newline at end of file
More information about the cfe-commits
mailing list