[clang] 99f7d55 - [clang][dataflow] Add transfer functions for data members and this pointers
Stanislav Gatev via cfe-commits
cfe-commits at lists.llvm.org
Tue Jan 11 23:44:09 PST 2022
Author: Stanislav Gatev
Date: 2022-01-12T07:38:26Z
New Revision: 99f7d55eeeecd56566d23ee222e272729e05535c
URL: https://github.com/llvm/llvm-project/commit/99f7d55eeeecd56566d23ee222e272729e05535c
DIFF: https://github.com/llvm/llvm-project/commit/99f7d55eeeecd56566d23ee222e272729e05535c.diff
LOG: [clang][dataflow] Add transfer functions for data members and this pointers
This is part of the implementation of the dataflow analysis framework.
See "[RFC] A dataflow analysis framework for Clang AST" on cfe-dev.
Reviewed-by: ymandel, xazax.hun
Differential Revision: https://reviews.llvm.org/D117012
Added:
Modified:
clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
clang/lib/Analysis/FlowSensitive/Transfer.cpp
clang/unittests/Analysis/FlowSensitive/TestingSupport.h
clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h b/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
index 8a836713e3392..d1a06f7ef8d3a 100644
--- a/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
+++ b/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
@@ -88,6 +88,22 @@ class DataflowAnalysisContext {
return It == ExprToLoc.end() ? nullptr : It->second;
}
+ /// Assigns `Loc` as the storage location of the `this` pointee.
+ ///
+ /// Requirements:
+ ///
+ /// The `this` pointee must not be assigned a storage location.
+ void setThisPointeeStorageLocation(StorageLocation &Loc) {
+ assert(ThisPointeeLoc == nullptr);
+ ThisPointeeLoc = &Loc;
+ }
+
+ /// Returns the storage location assigned to the `this` pointee or null if the
+ /// `this` pointee has no assigned storage location.
+ StorageLocation *getThisPointeeStorageLocation() const {
+ return ThisPointeeLoc;
+ }
+
private:
// Storage for the state of a program.
std::vector<std::unique_ptr<StorageLocation>> Locs;
@@ -101,7 +117,7 @@ class DataflowAnalysisContext {
llvm::DenseMap<const ValueDecl *, StorageLocation *> DeclToLoc;
llvm::DenseMap<const Expr *, StorageLocation *> ExprToLoc;
- // FIXME: Add `StorageLocation` for `this`.
+ StorageLocation *ThisPointeeLoc = nullptr;
// FIXME: Add support for boolean expressions.
};
diff --git a/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h b/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
index 2138e0fb6da63..9613b2921c3f2 100644
--- a/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
+++ b/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
@@ -16,6 +16,7 @@
#define LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_DATAFLOWENVIRONMENT_H
#include "clang/AST/Decl.h"
+#include "clang/AST/DeclBase.h"
#include "clang/AST/Expr.h"
#include "clang/AST/Type.h"
#include "clang/AST/TypeOrdering.h"
@@ -39,17 +40,35 @@ enum class SkipPast {
None,
/// An optional reference should be skipped past.
Reference,
+ /// An optional reference should be skipped past, then an optional pointer
+ /// should be skipped past.
+ ReferenceThenPointer,
};
/// Holds the state of the program (store and heap) at a given program point.
class Environment {
public:
- Environment(DataflowAnalysisContext &DACtx) : DACtx(&DACtx) {}
+ /// Creates an environment that uses `DACtx` to store objects that encompass
+ /// the state of a program.
+ explicit Environment(DataflowAnalysisContext &DACtx) : DACtx(&DACtx) {}
+
+ /// Creates an environment that uses `DACtx` to store objects that encompass
+ /// the state of a program.
+ ///
+ /// If `DeclCtx` is a function, initializes the environment with symbolic
+ /// representations of the function parameters.
+ ///
+ /// If `DeclCtx` is a non-static member function, initializes the environment
+ /// with a symbolic representation of the `this` pointee.
+ Environment(DataflowAnalysisContext &DACtx, const DeclContext &DeclCtx);
bool operator==(const Environment &) const;
LatticeJoinEffect join(const Environment &);
+ // FIXME: Rename `createOrGetStorageLocation` to `getOrCreateStorageLocation`,
+ // `getStableStorageLocation`, or something more appropriate.
+
/// Creates a storage location appropriate for `Type`. Does not assign a value
/// to the returned storage location in the environment.
///
@@ -92,6 +111,11 @@ class Environment {
/// assigned a storage location in the environment.
StorageLocation *getStorageLocation(const Expr &E, SkipPast SP) const;
+ /// Returns the storage location assigned to the `this` pointee in the
+ /// environment or null if the `this` pointee has no assigned storage location
+ /// in the environment.
+ StorageLocation *getThisPointeeStorageLocation() const;
+
/// Creates a value appropriate for `Type`, assigns it to `Loc`, and returns
/// it, if `Type` is supported, otherwise return null. If `Type` is a pointer
/// or reference type, creates all the necessary storage locations and values
diff --git a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
index d1e7b2a4b38c5..5824bc13ce7b6 100644
--- a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
+++ b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
@@ -14,6 +14,7 @@
#include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
#include "clang/AST/Type.h"
#include "clang/Analysis/FlowSensitive/DataflowLattice.h"
#include "clang/Analysis/FlowSensitive/StorageLocation.h"
@@ -40,6 +41,31 @@ llvm::DenseMap<K, V> intersectDenseMaps(const llvm::DenseMap<K, V> &Map1,
return Result;
}
+Environment::Environment(DataflowAnalysisContext &DACtx,
+ const DeclContext &DeclCtx)
+ : Environment(DACtx) {
+ if (const auto *FuncDecl = dyn_cast<FunctionDecl>(&DeclCtx)) {
+ for (const auto *ParamDecl : FuncDecl->parameters()) {
+ assert(ParamDecl != nullptr);
+ auto &ParamLoc = createStorageLocation(*ParamDecl);
+ setStorageLocation(*ParamDecl, ParamLoc);
+ initValueInStorageLocation(ParamLoc, ParamDecl->getType());
+ }
+ }
+
+ if (const auto *MethodDecl = dyn_cast<CXXMethodDecl>(&DeclCtx)) {
+ if (!MethodDecl->isStatic()) {
+ QualType ThisPointeeType = MethodDecl->getThisObjectType();
+ // FIXME: Add support for union types.
+ if (!ThisPointeeType->isUnionType()) {
+ auto &ThisPointeeLoc = createStorageLocation(ThisPointeeType);
+ DACtx.setThisPointeeStorageLocation(ThisPointeeLoc);
+ initValueInStorageLocation(ThisPointeeLoc, ThisPointeeType);
+ }
+ }
+ }
+}
+
bool Environment::operator==(const Environment &Other) const {
assert(DACtx == Other.DACtx);
return DeclToLoc == Other.DeclToLoc && LocToVal == Other.LocToVal;
@@ -124,6 +150,10 @@ StorageLocation *Environment::getStorageLocation(const Expr &E,
return It == ExprToLoc.end() ? nullptr : &skip(*It->second, SP);
}
+StorageLocation *Environment::getThisPointeeStorageLocation() const {
+ return DACtx->getThisPointeeStorageLocation();
+}
+
void Environment::setValue(const StorageLocation &Loc, Value &Value) {
LocToVal[&Loc] = &Value;
}
@@ -199,6 +229,8 @@ Value *Environment::initValueInStorageLocationUnlessSelfReferential(
if (Type->isStructureOrClassType()) {
auto *AggregateLoc = cast<AggregateStorageLocation>(&Loc);
+ // FIXME: Initialize only fields that are accessed in the context that is
+ // being analyzed.
llvm::DenseMap<const ValueDecl *, Value *> FieldValues;
for (const FieldDecl *Field : Type->getAsRecordDecl()->fields()) {
assert(Field != nullptr);
@@ -242,6 +274,11 @@ StorageLocation &Environment::skip(StorageLocation &Loc, SkipPast SP) const {
if (auto *Val = dyn_cast_or_null<ReferenceValue>(getValue(Loc)))
return Val->getPointeeLoc();
return Loc;
+ case SkipPast::ReferenceThenPointer:
+ StorageLocation &LocPastRef = skip(Loc, SkipPast::Reference);
+ if (auto *Val = dyn_cast_or_null<PointerValue>(getValue(LocPastRef)))
+ return Val->getPointeeLoc();
+ return LocPastRef;
}
llvm_unreachable("bad SkipPast kind");
}
diff --git a/clang/lib/Analysis/FlowSensitive/Transfer.cpp b/clang/lib/Analysis/FlowSensitive/Transfer.cpp
index bae162ae3cd14..f28271101b9c1 100644
--- a/clang/lib/Analysis/FlowSensitive/Transfer.cpp
+++ b/clang/lib/Analysis/FlowSensitive/Transfer.cpp
@@ -15,6 +15,7 @@
#include "clang/AST/Decl.h"
#include "clang/AST/DeclBase.h"
#include "clang/AST/Expr.h"
+#include "clang/AST/ExprCXX.h"
#include "clang/AST/OperationKinds.h"
#include "clang/AST/Stmt.h"
#include "clang/AST/StmtVisitor.h"
@@ -123,6 +124,46 @@ class TransferVisitor : public ConstStmtVisitor<TransferVisitor> {
// FIXME: Add support for UO_AddrOf, UO_LNot.
}
+ void VisitCXXThisExpr(const CXXThisExpr *S) {
+ auto *ThisPointeeLoc = Env.getThisPointeeStorageLocation();
+ assert(ThisPointeeLoc != nullptr);
+
+ auto &Loc = Env.createStorageLocation(*S);
+ Env.setStorageLocation(*S, Loc);
+ Env.setValue(Loc, Env.takeOwnership(
+ std::make_unique<PointerValue>(*ThisPointeeLoc)));
+ }
+
+ void VisitMemberExpr(const MemberExpr *S) {
+ ValueDecl *Member = S->getMemberDecl();
+ assert(Member != nullptr);
+
+ // FIXME: Consider assigning pointer values to function member expressions.
+ if (Member->isFunctionOrFunctionTemplate())
+ return;
+
+ // The receiver can be either a value or a pointer to a value. Skip past the
+ // indirection to handle both cases.
+ auto *BaseLoc = cast_or_null<AggregateStorageLocation>(
+ Env.getStorageLocation(*S->getBase(), SkipPast::ReferenceThenPointer));
+ if (BaseLoc == nullptr)
+ return;
+
+ // FIXME: Add support for union types.
+ if (BaseLoc->getType()->isUnionType())
+ return;
+
+ auto &MemberLoc = BaseLoc->getChild(*Member);
+ if (MemberLoc.getType()->isReferenceType()) {
+ Env.setStorageLocation(*S, MemberLoc);
+ } else {
+ auto &Loc = Env.createStorageLocation(*S);
+ Env.setStorageLocation(*S, Loc);
+ Env.setValue(
+ Loc, Env.takeOwnership(std::make_unique<ReferenceValue>(MemberLoc)));
+ }
+ }
+
// FIXME: Add support for:
// - CallExpr
// - CXXBindTemporaryExpr
@@ -131,9 +172,7 @@ class TransferVisitor : public ConstStmtVisitor<TransferVisitor> {
// - CXXFunctionalCastExpr
// - CXXOperatorCallExpr
// - CXXStaticCastExpr
- // - CXXThisExpr
// - MaterializeTemporaryExpr
- // - MemberExpr
private:
void visitVarDecl(const VarDecl &D) {
diff --git a/clang/unittests/Analysis/FlowSensitive/TestingSupport.h b/clang/unittests/Analysis/FlowSensitive/TestingSupport.h
index f5137959cfa98..fe955207170ef 100644
--- a/clang/unittests/Analysis/FlowSensitive/TestingSupport.h
+++ b/clang/unittests/Analysis/FlowSensitive/TestingSupport.h
@@ -99,7 +99,7 @@ void checkDataflow(
ASSERT_TRUE((bool)CFCtx) << "Could not build ControlFlowContext.";
DataflowAnalysisContext DACtx;
- Environment Env(DACtx);
+ Environment Env(DACtx, *F);
auto Analysis = MakeAnalysis(Context, Env);
llvm::Expected<llvm::DenseMap<const clang::Stmt *, std::string>>
diff --git a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
index a54dceb5c5b60..55e15e28ce5eb 100644
--- a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
+++ b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
@@ -67,7 +67,7 @@ static const ValueDecl *findValueDecl(ASTContext &ASTCtx,
TEST_F(TransferTest, IntVarDecl) {
std::string Code = R"(
void target() {
- int foo;
+ int Foo;
// [[p]]
}
)";
@@ -79,7 +79,7 @@ TEST_F(TransferTest, IntVarDecl) {
ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
const Environment &Env = Results[0].second.Env;
- const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo");
+ const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
ASSERT_THAT(FooDecl, NotNull());
const StorageLocation *FooLoc =
@@ -87,18 +87,18 @@ TEST_F(TransferTest, IntVarDecl) {
ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(FooLoc));
const Value *FooVal = Env.getValue(*FooLoc);
- ASSERT_TRUE(isa_and_nonnull<IntegerValue>(FooVal));
+ EXPECT_TRUE(isa_and_nonnull<IntegerValue>(FooVal));
});
}
TEST_F(TransferTest, StructVarDecl) {
std::string Code = R"(
- struct Foo {
+ struct A {
int Bar;
};
void target() {
- Foo foo;
+ A Foo;
// [[p]]
}
)";
@@ -110,7 +110,7 @@ TEST_F(TransferTest, StructVarDecl) {
ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
const Environment &Env = Results[0].second.Env;
- const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo");
+ const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
ASSERT_THAT(FooDecl, NotNull());
ASSERT_TRUE(FooDecl->getType()->isStructureType());
@@ -133,18 +133,18 @@ TEST_F(TransferTest, StructVarDecl) {
const auto *FooVal = cast<StructValue>(Env.getValue(*FooLoc));
const auto *BarVal = cast<IntegerValue>(&FooVal->getChild(*BarDecl));
- ASSERT_EQ(Env.getValue(*BarLoc), BarVal);
+ EXPECT_EQ(Env.getValue(*BarLoc), BarVal);
});
}
TEST_F(TransferTest, ClassVarDecl) {
std::string Code = R"(
- class Foo {
+ class A {
int Bar;
};
void target() {
- Foo foo;
+ A Foo;
// [[p]]
}
)";
@@ -156,7 +156,7 @@ TEST_F(TransferTest, ClassVarDecl) {
ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
const Environment &Env = Results[0].second.Env;
- const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo");
+ const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
ASSERT_THAT(FooDecl, NotNull());
ASSERT_TRUE(FooDecl->getType()->isClassType());
@@ -179,18 +179,18 @@ TEST_F(TransferTest, ClassVarDecl) {
const auto *FooVal = cast<StructValue>(Env.getValue(*FooLoc));
const auto *BarVal = cast<IntegerValue>(&FooVal->getChild(*BarDecl));
- ASSERT_EQ(Env.getValue(*BarLoc), BarVal);
+ EXPECT_EQ(Env.getValue(*BarLoc), BarVal);
});
}
TEST_F(TransferTest, ReferenceVarDecl) {
std::string Code = R"(
- struct Foo {};
+ struct A {};
- Foo& getFoo();
+ A &getA();
void target() {
- Foo& foo = getFoo();
+ A &Foo = getA();
// [[p]]
}
)";
@@ -202,7 +202,7 @@ TEST_F(TransferTest, ReferenceVarDecl) {
ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
const Environment &Env = Results[0].second.Env;
- const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo");
+ const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
ASSERT_THAT(FooDecl, NotNull());
const StorageLocation *FooLoc =
@@ -212,34 +212,34 @@ TEST_F(TransferTest, ReferenceVarDecl) {
const ReferenceValue *FooVal =
cast<ReferenceValue>(Env.getValue(*FooLoc));
const StorageLocation &FooPointeeLoc = FooVal->getPointeeLoc();
- ASSERT_TRUE(isa<AggregateStorageLocation>(&FooPointeeLoc));
+ EXPECT_TRUE(isa<AggregateStorageLocation>(&FooPointeeLoc));
const Value *FooPointeeVal = Env.getValue(FooPointeeLoc);
- ASSERT_TRUE(isa_and_nonnull<StructValue>(FooPointeeVal));
+ EXPECT_TRUE(isa_and_nonnull<StructValue>(FooPointeeVal));
});
}
TEST_F(TransferTest, SelfReferentialReferenceVarDecl) {
std::string Code = R"(
- struct Foo;
+ struct A;
- struct Baz {};
+ struct B {};
- struct Bar {
- Foo& FooRef;
- Foo* FooPtr;
- Baz& BazRef;
- Baz* BazPtr;
+ struct C {
+ A &FooRef;
+ A *FooPtr;
+ B &BazRef;
+ B *BazPtr;
};
- struct Foo {
- Bar& Bar;
+ struct A {
+ C &Bar;
};
- Foo& getFoo();
+ A &getA();
void target() {
- Foo& foo = getFoo();
+ A &Foo = getA();
// [[p]]
}
)";
@@ -250,7 +250,7 @@ TEST_F(TransferTest, SelfReferentialReferenceVarDecl) {
ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
const Environment &Env = Results[0].second.Env;
- const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo");
+ const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
ASSERT_THAT(FooDecl, NotNull());
ASSERT_TRUE(FooDecl->getType()->isReferenceType());
@@ -309,33 +309,33 @@ TEST_F(TransferTest, SelfReferentialReferenceVarDecl) {
const auto *FooRefVal =
cast<ReferenceValue>(&BarPointeeVal->getChild(*FooRefDecl));
const StorageLocation &FooRefPointeeLoc = FooRefVal->getPointeeLoc();
- ASSERT_THAT(Env.getValue(FooRefPointeeLoc), IsNull());
+ EXPECT_THAT(Env.getValue(FooRefPointeeLoc), IsNull());
const auto *FooPtrVal =
cast<PointerValue>(&BarPointeeVal->getChild(*FooPtrDecl));
const StorageLocation &FooPtrPointeeLoc = FooPtrVal->getPointeeLoc();
- ASSERT_THAT(Env.getValue(FooPtrPointeeLoc), IsNull());
+ EXPECT_THAT(Env.getValue(FooPtrPointeeLoc), IsNull());
const auto *BazRefVal =
cast<ReferenceValue>(&BarPointeeVal->getChild(*BazRefDecl));
const StorageLocation &BazRefPointeeLoc = BazRefVal->getPointeeLoc();
- ASSERT_THAT(Env.getValue(BazRefPointeeLoc), NotNull());
+ EXPECT_THAT(Env.getValue(BazRefPointeeLoc), NotNull());
const auto *BazPtrVal =
cast<PointerValue>(&BarPointeeVal->getChild(*BazPtrDecl));
const StorageLocation &BazPtrPointeeLoc = BazPtrVal->getPointeeLoc();
- ASSERT_THAT(Env.getValue(BazPtrPointeeLoc), NotNull());
+ EXPECT_THAT(Env.getValue(BazPtrPointeeLoc), NotNull());
});
}
TEST_F(TransferTest, PointerVarDecl) {
std::string Code = R"(
- struct Foo {};
+ struct A {};
- Foo* getFoo();
+ A *getA();
void target() {
- Foo* foo = getFoo();
+ A *Foo = getA();
// [[p]]
}
)";
@@ -347,7 +347,7 @@ TEST_F(TransferTest, PointerVarDecl) {
ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
const Environment &Env = Results[0].second.Env;
- const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo");
+ const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
ASSERT_THAT(FooDecl, NotNull());
const StorageLocation *FooLoc =
@@ -356,34 +356,34 @@ TEST_F(TransferTest, PointerVarDecl) {
const PointerValue *FooVal = cast<PointerValue>(Env.getValue(*FooLoc));
const StorageLocation &FooPointeeLoc = FooVal->getPointeeLoc();
- ASSERT_TRUE(isa<AggregateStorageLocation>(&FooPointeeLoc));
+ EXPECT_TRUE(isa<AggregateStorageLocation>(&FooPointeeLoc));
const Value *FooPointeeVal = Env.getValue(FooPointeeLoc);
- ASSERT_TRUE(isa_and_nonnull<StructValue>(FooPointeeVal));
+ EXPECT_TRUE(isa_and_nonnull<StructValue>(FooPointeeVal));
});
}
TEST_F(TransferTest, SelfReferentialPointerVarDecl) {
std::string Code = R"(
- struct Foo;
+ struct A;
- struct Baz {};
+ struct B {};
- struct Bar {
- Foo& FooRef;
- Foo* FooPtr;
- Baz& BazRef;
- Baz* BazPtr;
+ struct C {
+ A &FooRef;
+ A *FooPtr;
+ B &BazRef;
+ B *BazPtr;
};
- struct Foo {
- Bar* Bar;
+ struct A {
+ C *Bar;
};
- Foo* getFoo();
+ A *getA();
void target() {
- Foo* foo = getFoo();
+ A *Foo = getA();
// [[p]]
}
)";
@@ -395,7 +395,7 @@ TEST_F(TransferTest, SelfReferentialPointerVarDecl) {
ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
const Environment &Env = Results[0].second.Env;
- const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo");
+ const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
ASSERT_THAT(FooDecl, NotNull());
ASSERT_TRUE(FooDecl->getType()->isPointerType());
@@ -466,35 +466,35 @@ TEST_F(TransferTest, SelfReferentialPointerVarDecl) {
const auto *FooRefVal =
cast<ReferenceValue>(&BarPointeeVal->getChild(*FooRefDecl));
const StorageLocation &FooRefPointeeLoc = FooRefVal->getPointeeLoc();
- ASSERT_THAT(Env.getValue(FooRefPointeeLoc), IsNull());
+ EXPECT_THAT(Env.getValue(FooRefPointeeLoc), IsNull());
const auto *FooPtrVal =
cast<PointerValue>(&BarPointeeVal->getChild(*FooPtrDecl));
const StorageLocation &FooPtrPointeeLoc = FooPtrVal->getPointeeLoc();
- ASSERT_THAT(Env.getValue(FooPtrPointeeLoc), IsNull());
+ EXPECT_THAT(Env.getValue(FooPtrPointeeLoc), IsNull());
const auto *BazRefVal =
cast<ReferenceValue>(&BarPointeeVal->getChild(*BazRefDecl));
const StorageLocation &BazRefPointeeLoc = BazRefVal->getPointeeLoc();
- ASSERT_THAT(Env.getValue(BazRefPointeeLoc), NotNull());
+ EXPECT_THAT(Env.getValue(BazRefPointeeLoc), NotNull());
const auto *BazPtrVal =
cast<PointerValue>(&BarPointeeVal->getChild(*BazPtrDecl));
const StorageLocation &BazPtrPointeeLoc = BazPtrVal->getPointeeLoc();
- ASSERT_THAT(Env.getValue(BazPtrPointeeLoc), NotNull());
+ EXPECT_THAT(Env.getValue(BazPtrPointeeLoc), NotNull());
});
}
TEST_F(TransferTest, JoinVarDecl) {
std::string Code = R"(
- void target(bool b) {
- int foo;
+ void target(bool B) {
+ int Foo;
// [[p1]]
- if (b) {
- int bar;
+ if (B) {
+ int Bar;
// [[p2]]
} else {
- int baz;
+ int Baz;
// [[p3]]
}
(void)0;
@@ -507,45 +507,45 @@ TEST_F(TransferTest, JoinVarDecl) {
ASTContext &ASTCtx) {
ASSERT_THAT(Results, ElementsAre(Pair("p4", _), Pair("p3", _),
Pair("p2", _), Pair("p1", _)));
- const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo");
+ const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
ASSERT_THAT(FooDecl, NotNull());
- const ValueDecl *BarDecl = findValueDecl(ASTCtx, "bar");
+ const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
ASSERT_THAT(BarDecl, NotNull());
- const ValueDecl *BazDecl = findValueDecl(ASTCtx, "baz");
+ const ValueDecl *BazDecl = findValueDecl(ASTCtx, "Baz");
ASSERT_THAT(BazDecl, NotNull());
const Environment &Env1 = Results[3].second.Env;
const StorageLocation *FooLoc =
Env1.getStorageLocation(*FooDecl, SkipPast::None);
- ASSERT_THAT(FooLoc, NotNull());
- ASSERT_THAT(Env1.getStorageLocation(*BarDecl, SkipPast::None), IsNull());
- ASSERT_THAT(Env1.getStorageLocation(*BazDecl, SkipPast::None), IsNull());
+ EXPECT_THAT(FooLoc, NotNull());
+ EXPECT_THAT(Env1.getStorageLocation(*BarDecl, SkipPast::None), IsNull());
+ EXPECT_THAT(Env1.getStorageLocation(*BazDecl, SkipPast::None), IsNull());
const Environment &Env2 = Results[2].second.Env;
- ASSERT_EQ(Env2.getStorageLocation(*FooDecl, SkipPast::None), FooLoc);
- ASSERT_THAT(Env2.getStorageLocation(*BarDecl, SkipPast::None), NotNull());
- ASSERT_THAT(Env2.getStorageLocation(*BazDecl, SkipPast::None), IsNull());
+ EXPECT_EQ(Env2.getStorageLocation(*FooDecl, SkipPast::None), FooLoc);
+ EXPECT_THAT(Env2.getStorageLocation(*BarDecl, SkipPast::None), NotNull());
+ EXPECT_THAT(Env2.getStorageLocation(*BazDecl, SkipPast::None), IsNull());
const Environment &Env3 = Results[1].second.Env;
- ASSERT_EQ(Env3.getStorageLocation(*FooDecl, SkipPast::None), FooLoc);
- ASSERT_THAT(Env3.getStorageLocation(*BarDecl, SkipPast::None), IsNull());
- ASSERT_THAT(Env3.getStorageLocation(*BazDecl, SkipPast::None), NotNull());
+ EXPECT_EQ(Env3.getStorageLocation(*FooDecl, SkipPast::None), FooLoc);
+ EXPECT_THAT(Env3.getStorageLocation(*BarDecl, SkipPast::None), IsNull());
+ EXPECT_THAT(Env3.getStorageLocation(*BazDecl, SkipPast::None), NotNull());
const Environment &Env4 = Results[0].second.Env;
- ASSERT_EQ(Env4.getStorageLocation(*FooDecl, SkipPast::None), FooLoc);
- ASSERT_THAT(Env4.getStorageLocation(*BarDecl, SkipPast::None), IsNull());
- ASSERT_THAT(Env4.getStorageLocation(*BazDecl, SkipPast::None), IsNull());
+ EXPECT_EQ(Env4.getStorageLocation(*FooDecl, SkipPast::None), FooLoc);
+ EXPECT_THAT(Env4.getStorageLocation(*BarDecl, SkipPast::None), IsNull());
+ EXPECT_THAT(Env4.getStorageLocation(*BazDecl, SkipPast::None), IsNull());
});
}
TEST_F(TransferTest, BinaryOperatorAssign) {
std::string Code = R"(
void target() {
- int foo;
- int bar;
- (bar) = (foo);
+ int Foo;
+ int Bar;
+ (Bar) = (Foo);
// [[p]]
}
)";
@@ -557,13 +557,13 @@ TEST_F(TransferTest, BinaryOperatorAssign) {
ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
const Environment &Env = Results[0].second.Env;
- const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo");
+ const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
ASSERT_THAT(FooDecl, NotNull());
const Value *FooVal = Env.getValue(*FooDecl, SkipPast::None);
ASSERT_TRUE(isa_and_nonnull<IntegerValue>(FooVal));
- const ValueDecl *BarDecl = findValueDecl(ASTCtx, "bar");
+ const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
ASSERT_THAT(BarDecl, NotNull());
EXPECT_EQ(Env.getValue(*BarDecl, SkipPast::None), FooVal);
@@ -573,8 +573,8 @@ TEST_F(TransferTest, BinaryOperatorAssign) {
TEST_F(TransferTest, VarDeclInitAssign) {
std::string Code = R"(
void target() {
- int foo;
- int bar = foo;
+ int Foo;
+ int Bar = Foo;
// [[p]]
}
)";
@@ -586,13 +586,13 @@ TEST_F(TransferTest, VarDeclInitAssign) {
ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
const Environment &Env = Results[0].second.Env;
- const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo");
+ const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
ASSERT_THAT(FooDecl, NotNull());
const Value *FooVal = Env.getValue(*FooDecl, SkipPast::None);
ASSERT_TRUE(isa_and_nonnull<IntegerValue>(FooVal));
- const ValueDecl *BarDecl = findValueDecl(ASTCtx, "bar");
+ const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
ASSERT_THAT(BarDecl, NotNull());
EXPECT_EQ(Env.getValue(*BarDecl, SkipPast::None), FooVal);
@@ -602,9 +602,9 @@ TEST_F(TransferTest, VarDeclInitAssign) {
TEST_F(TransferTest, VarDeclInitAssignChained) {
std::string Code = R"(
void target() {
- int foo;
- int bar;
- int baz = (bar = foo);
+ int Foo;
+ int Bar;
+ int Baz = (Bar = Foo);
// [[p]]
}
)";
@@ -616,16 +616,16 @@ TEST_F(TransferTest, VarDeclInitAssignChained) {
ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
const Environment &Env = Results[0].second.Env;
- const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo");
+ const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
ASSERT_THAT(FooDecl, NotNull());
const Value *FooVal = Env.getValue(*FooDecl, SkipPast::None);
ASSERT_TRUE(isa_and_nonnull<IntegerValue>(FooVal));
- const ValueDecl *BarDecl = findValueDecl(ASTCtx, "bar");
+ const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
ASSERT_THAT(BarDecl, NotNull());
- const ValueDecl *BazDecl = findValueDecl(ASTCtx, "baz");
+ const ValueDecl *BazDecl = findValueDecl(ASTCtx, "Baz");
ASSERT_THAT(BazDecl, NotNull());
EXPECT_EQ(Env.getValue(*BarDecl, SkipPast::None), FooVal);
@@ -636,10 +636,10 @@ TEST_F(TransferTest, VarDeclInitAssignChained) {
TEST_F(TransferTest, VarDeclInitAssignPtrDeref) {
std::string Code = R"(
void target() {
- int foo;
- int *bar;
- *(bar) = foo;
- int baz = *(bar);
+ int Foo;
+ int *Bar;
+ *(Bar) = Foo;
+ int Baz = *(Bar);
// [[p]]
}
)";
@@ -651,20 +651,20 @@ TEST_F(TransferTest, VarDeclInitAssignPtrDeref) {
ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
const Environment &Env = Results[0].second.Env;
- const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo");
+ const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
ASSERT_THAT(FooDecl, NotNull());
const Value *FooVal = Env.getValue(*FooDecl, SkipPast::None);
ASSERT_TRUE(isa_and_nonnull<IntegerValue>(FooVal));
- const ValueDecl *BarDecl = findValueDecl(ASTCtx, "bar");
+ const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
ASSERT_THAT(BarDecl, NotNull());
const auto *BarVal =
cast<PointerValue>(Env.getValue(*BarDecl, SkipPast::None));
EXPECT_EQ(Env.getValue(BarVal->getPointeeLoc()), FooVal);
- const ValueDecl *BazDecl = findValueDecl(ASTCtx, "baz");
+ const ValueDecl *BazDecl = findValueDecl(ASTCtx, "Baz");
ASSERT_THAT(BazDecl, NotNull());
EXPECT_EQ(Env.getValue(*BazDecl, SkipPast::None), FooVal);
@@ -674,13 +674,13 @@ TEST_F(TransferTest, VarDeclInitAssignPtrDeref) {
TEST_F(TransferTest, AssignToAndFromReference) {
std::string Code = R"(
void target() {
- int foo;
- int bar;
- int& baz = foo;
+ int Foo;
+ int Bar;
+ int &Baz = Foo;
// [[p1]]
- baz = bar;
- int qux = baz;
- int& quux = baz;
+ Baz = Bar;
+ int Qux = Baz;
+ int &Quux = Baz;
// [[p2]]
}
)";
@@ -693,19 +693,19 @@ TEST_F(TransferTest, AssignToAndFromReference) {
const Environment &Env1 = Results[0].second.Env;
const Environment &Env2 = Results[1].second.Env;
- const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo");
+ const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
ASSERT_THAT(FooDecl, NotNull());
const Value *FooVal = Env1.getValue(*FooDecl, SkipPast::None);
ASSERT_TRUE(isa_and_nonnull<IntegerValue>(FooVal));
- const ValueDecl *BarDecl = findValueDecl(ASTCtx, "bar");
+ const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
ASSERT_THAT(BarDecl, NotNull());
const Value *BarVal = Env1.getValue(*BarDecl, SkipPast::None);
ASSERT_TRUE(isa_and_nonnull<IntegerValue>(BarVal));
- const ValueDecl *BazDecl = findValueDecl(ASTCtx, "baz");
+ const ValueDecl *BazDecl = findValueDecl(ASTCtx, "Baz");
ASSERT_THAT(BazDecl, NotNull());
EXPECT_EQ(Env1.getValue(*BazDecl, SkipPast::Reference), FooVal);
@@ -713,14 +713,462 @@ TEST_F(TransferTest, AssignToAndFromReference) {
EXPECT_EQ(Env2.getValue(*BazDecl, SkipPast::Reference), BarVal);
EXPECT_EQ(Env2.getValue(*FooDecl, SkipPast::None), BarVal);
- const ValueDecl *QuxDecl = findValueDecl(ASTCtx, "qux");
+ const ValueDecl *QuxDecl = findValueDecl(ASTCtx, "Qux");
ASSERT_THAT(QuxDecl, NotNull());
EXPECT_EQ(Env2.getValue(*QuxDecl, SkipPast::None), BarVal);
- const ValueDecl *QuuxDecl = findValueDecl(ASTCtx, "quux");
+ const ValueDecl *QuuxDecl = findValueDecl(ASTCtx, "Quux");
ASSERT_THAT(QuuxDecl, NotNull());
EXPECT_EQ(Env2.getValue(*QuuxDecl, SkipPast::Reference), BarVal);
});
}
+TEST_F(TransferTest, MultipleParamDecls) {
+ std::string Code = R"(
+ void target(int Foo, int Bar) {
+ (void)0;
+ // [[p]]
+ }
+ )";
+ runDataflow(Code,
+ [](llvm::ArrayRef<
+ std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
+ Results,
+ ASTContext &ASTCtx) {
+ ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
+ const Environment &Env = Results[0].second.Env;
+
+ const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
+ ASSERT_THAT(FooDecl, NotNull());
+
+ const StorageLocation *FooLoc =
+ Env.getStorageLocation(*FooDecl, SkipPast::None);
+ ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(FooLoc));
+
+ const Value *FooVal = Env.getValue(*FooLoc);
+ ASSERT_TRUE(isa_and_nonnull<IntegerValue>(FooVal));
+
+ const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
+ ASSERT_THAT(BarDecl, NotNull());
+
+ const StorageLocation *BarLoc =
+ Env.getStorageLocation(*BarDecl, SkipPast::None);
+ ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(BarLoc));
+
+ const Value *BarVal = Env.getValue(*BarLoc);
+ EXPECT_TRUE(isa_and_nonnull<IntegerValue>(BarVal));
+ });
+}
+
+TEST_F(TransferTest, StructParamDecl) {
+ std::string Code = R"(
+ struct A {
+ int Bar;
+ };
+
+ void target(A Foo) {
+ (void)0;
+ // [[p]]
+ }
+ )";
+ runDataflow(
+ Code, [](llvm::ArrayRef<
+ std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
+ Results,
+ ASTContext &ASTCtx) {
+ ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
+ const Environment &Env = Results[0].second.Env;
+
+ const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
+ ASSERT_THAT(FooDecl, NotNull());
+
+ ASSERT_TRUE(FooDecl->getType()->isStructureType());
+ auto FooFields = FooDecl->getType()->getAsRecordDecl()->fields();
+
+ FieldDecl *BarDecl = nullptr;
+ for (FieldDecl *Field : FooFields) {
+ if (Field->getNameAsString() == "Bar") {
+ BarDecl = Field;
+ } else {
+ FAIL() << "Unexpected field: " << Field->getNameAsString();
+ }
+ }
+ ASSERT_THAT(BarDecl, NotNull());
+
+ const auto *FooLoc = cast<AggregateStorageLocation>(
+ Env.getStorageLocation(*FooDecl, SkipPast::None));
+ const auto *BarLoc =
+ cast<ScalarStorageLocation>(&FooLoc->getChild(*BarDecl));
+
+ const auto *FooVal = cast<StructValue>(Env.getValue(*FooLoc));
+ const auto *BarVal = cast<IntegerValue>(&FooVal->getChild(*BarDecl));
+ EXPECT_EQ(Env.getValue(*BarLoc), BarVal);
+ });
+}
+
+TEST_F(TransferTest, ReferenceParamDecl) {
+ std::string Code = R"(
+ struct A {};
+
+ void target(A &Foo) {
+ (void)0;
+ // [[p]]
+ }
+ )";
+ runDataflow(Code,
+ [](llvm::ArrayRef<
+ std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
+ Results,
+ ASTContext &ASTCtx) {
+ ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
+ const Environment &Env = Results[0].second.Env;
+
+ const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
+ ASSERT_THAT(FooDecl, NotNull());
+
+ const StorageLocation *FooLoc =
+ Env.getStorageLocation(*FooDecl, SkipPast::None);
+ ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(FooLoc));
+
+ const ReferenceValue *FooVal =
+ dyn_cast<ReferenceValue>(Env.getValue(*FooLoc));
+ ASSERT_THAT(FooVal, NotNull());
+
+ const StorageLocation &FooPointeeLoc = FooVal->getPointeeLoc();
+ EXPECT_TRUE(isa<AggregateStorageLocation>(&FooPointeeLoc));
+
+ const Value *FooPointeeVal = Env.getValue(FooPointeeLoc);
+ EXPECT_TRUE(isa_and_nonnull<StructValue>(FooPointeeVal));
+ });
+}
+
+TEST_F(TransferTest, PointerParamDecl) {
+ std::string Code = R"(
+ struct A {};
+
+ void target(A *Foo) {
+ (void)0;
+ // [[p]]
+ }
+ )";
+ runDataflow(
+ Code, [](llvm::ArrayRef<
+ std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
+ Results,
+ ASTContext &ASTCtx) {
+ ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
+ const Environment &Env = Results[0].second.Env;
+
+ const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
+ ASSERT_THAT(FooDecl, NotNull());
+
+ const StorageLocation *FooLoc =
+ Env.getStorageLocation(*FooDecl, SkipPast::None);
+ ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(FooLoc));
+
+ const PointerValue *FooVal = cast<PointerValue>(Env.getValue(*FooLoc));
+ const StorageLocation &FooPointeeLoc = FooVal->getPointeeLoc();
+ EXPECT_TRUE(isa<AggregateStorageLocation>(&FooPointeeLoc));
+
+ const Value *FooPointeeVal = Env.getValue(FooPointeeLoc);
+ EXPECT_TRUE(isa_and_nonnull<StructValue>(FooPointeeVal));
+ });
+}
+
+TEST_F(TransferTest, StructMember) {
+ std::string Code = R"(
+ struct A {
+ int Bar;
+ };
+
+ void target(A Foo) {
+ int Baz = Foo.Bar;
+ // [[p]]
+ }
+ )";
+ runDataflow(
+ Code, [](llvm::ArrayRef<
+ std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
+ Results,
+ ASTContext &ASTCtx) {
+ ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
+ const Environment &Env = Results[0].second.Env;
+
+ const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
+ ASSERT_THAT(FooDecl, NotNull());
+
+ ASSERT_TRUE(FooDecl->getType()->isStructureType());
+ auto FooFields = FooDecl->getType()->getAsRecordDecl()->fields();
+
+ FieldDecl *BarDecl = nullptr;
+ for (FieldDecl *Field : FooFields) {
+ if (Field->getNameAsString() == "Bar") {
+ BarDecl = Field;
+ } else {
+ FAIL() << "Unexpected field: " << Field->getNameAsString();
+ }
+ }
+ ASSERT_THAT(BarDecl, NotNull());
+
+ const auto *FooLoc = cast<AggregateStorageLocation>(
+ Env.getStorageLocation(*FooDecl, SkipPast::None));
+ const auto *FooVal = cast<StructValue>(Env.getValue(*FooLoc));
+ const auto *BarVal = cast<IntegerValue>(&FooVal->getChild(*BarDecl));
+
+ const ValueDecl *BazDecl = findValueDecl(ASTCtx, "Baz");
+ ASSERT_THAT(BazDecl, NotNull());
+
+ EXPECT_EQ(Env.getValue(*BazDecl, SkipPast::None), BarVal);
+ });
+}
+
+TEST_F(TransferTest, ClassMember) {
+ std::string Code = R"(
+ class A {
+ public:
+ int Bar;
+ };
+
+ void target(A Foo) {
+ int Baz = Foo.Bar;
+ // [[p]]
+ }
+ )";
+ runDataflow(
+ Code, [](llvm::ArrayRef<
+ std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
+ Results,
+ ASTContext &ASTCtx) {
+ ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
+ const Environment &Env = Results[0].second.Env;
+
+ const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
+ ASSERT_THAT(FooDecl, NotNull());
+
+ ASSERT_TRUE(FooDecl->getType()->isClassType());
+ auto FooFields = FooDecl->getType()->getAsRecordDecl()->fields();
+
+ FieldDecl *BarDecl = nullptr;
+ for (FieldDecl *Field : FooFields) {
+ if (Field->getNameAsString() == "Bar") {
+ BarDecl = Field;
+ } else {
+ FAIL() << "Unexpected field: " << Field->getNameAsString();
+ }
+ }
+ ASSERT_THAT(BarDecl, NotNull());
+
+ const auto *FooLoc = cast<AggregateStorageLocation>(
+ Env.getStorageLocation(*FooDecl, SkipPast::None));
+ const auto *FooVal = cast<StructValue>(Env.getValue(*FooLoc));
+ const auto *BarVal = cast<IntegerValue>(&FooVal->getChild(*BarDecl));
+
+ const ValueDecl *BazDecl = findValueDecl(ASTCtx, "Baz");
+ ASSERT_THAT(BazDecl, NotNull());
+
+ EXPECT_EQ(Env.getValue(*BazDecl, SkipPast::None), BarVal);
+ });
+}
+
+TEST_F(TransferTest, ReferenceMember) {
+ std::string Code = R"(
+ struct A {
+ int &Bar;
+ };
+
+ void target(A Foo) {
+ int Baz = Foo.Bar;
+ // [[p]]
+ }
+ )";
+ runDataflow(
+ Code, [](llvm::ArrayRef<
+ std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
+ Results,
+ ASTContext &ASTCtx) {
+ ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
+ const Environment &Env = Results[0].second.Env;
+
+ const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
+ ASSERT_THAT(FooDecl, NotNull());
+
+ ASSERT_TRUE(FooDecl->getType()->isStructureType());
+ auto FooFields = FooDecl->getType()->getAsRecordDecl()->fields();
+
+ FieldDecl *BarDecl = nullptr;
+ for (FieldDecl *Field : FooFields) {
+ if (Field->getNameAsString() == "Bar") {
+ BarDecl = Field;
+ } else {
+ FAIL() << "Unexpected field: " << Field->getNameAsString();
+ }
+ }
+ ASSERT_THAT(BarDecl, NotNull());
+
+ const auto *FooLoc = cast<AggregateStorageLocation>(
+ Env.getStorageLocation(*FooDecl, SkipPast::None));
+ const auto *FooVal = cast<StructValue>(Env.getValue(*FooLoc));
+ const auto *BarVal = cast<ReferenceValue>(&FooVal->getChild(*BarDecl));
+ const auto *BarPointeeVal =
+ cast<IntegerValue>(Env.getValue(BarVal->getPointeeLoc()));
+
+ const ValueDecl *BazDecl = findValueDecl(ASTCtx, "Baz");
+ ASSERT_THAT(BazDecl, NotNull());
+
+ EXPECT_EQ(Env.getValue(*BazDecl, SkipPast::None), BarPointeeVal);
+ });
+}
+
+TEST_F(TransferTest, StructThisMember) {
+ std::string Code = R"(
+ struct A {
+ int Bar;
+
+ struct B {
+ int Baz;
+ };
+
+ B Qux;
+
+ void target() {
+ int Foo = Bar;
+ int Quux = Qux.Baz;
+ // [[p]]
+ }
+ };
+ )";
+ runDataflow(
+ Code, [](llvm::ArrayRef<
+ std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
+ Results,
+ ASTContext &ASTCtx) {
+ ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
+ const Environment &Env = Results[0].second.Env;
+
+ const auto *ThisLoc = dyn_cast<AggregateStorageLocation>(
+ Env.getThisPointeeStorageLocation());
+ ASSERT_THAT(ThisLoc, NotNull());
+
+ const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
+ ASSERT_THAT(BarDecl, NotNull());
+
+ const auto *BarLoc =
+ cast<ScalarStorageLocation>(&ThisLoc->getChild(*BarDecl));
+ ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(BarLoc));
+
+ const Value *BarVal = Env.getValue(*BarLoc);
+ ASSERT_TRUE(isa_and_nonnull<IntegerValue>(BarVal));
+
+ const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
+ ASSERT_THAT(FooDecl, NotNull());
+ EXPECT_EQ(Env.getValue(*FooDecl, SkipPast::None), BarVal);
+
+ const ValueDecl *QuxDecl = findValueDecl(ASTCtx, "Qux");
+ ASSERT_THAT(QuxDecl, NotNull());
+
+ ASSERT_TRUE(QuxDecl->getType()->isStructureType());
+ auto QuxFields = QuxDecl->getType()->getAsRecordDecl()->fields();
+
+ FieldDecl *BazDecl = nullptr;
+ for (FieldDecl *Field : QuxFields) {
+ if (Field->getNameAsString() == "Baz") {
+ BazDecl = Field;
+ } else {
+ FAIL() << "Unexpected field: " << Field->getNameAsString();
+ }
+ }
+ ASSERT_THAT(BazDecl, NotNull());
+
+ const auto *QuxLoc =
+ cast<AggregateStorageLocation>(&ThisLoc->getChild(*QuxDecl));
+ const auto *QuxVal = dyn_cast<StructValue>(Env.getValue(*QuxLoc));
+ ASSERT_THAT(QuxVal, NotNull());
+
+ const auto *BazLoc =
+ cast<ScalarStorageLocation>(&QuxLoc->getChild(*BazDecl));
+ const auto *BazVal = cast<IntegerValue>(&QuxVal->getChild(*BazDecl));
+ EXPECT_EQ(Env.getValue(*BazLoc), BazVal);
+
+ const ValueDecl *QuuxDecl = findValueDecl(ASTCtx, "Quux");
+ ASSERT_THAT(QuuxDecl, NotNull());
+ EXPECT_EQ(Env.getValue(*QuuxDecl, SkipPast::None), BazVal);
+ });
+}
+
+TEST_F(TransferTest, ClassThisMember) {
+ std::string Code = R"(
+ class A {
+ int Bar;
+
+ class B {
+ public:
+ int Baz;
+ };
+
+ B Qux;
+
+ void target() {
+ int Foo = Bar;
+ int Quux = Qux.Baz;
+ // [[p]]
+ }
+ };
+ )";
+ runDataflow(
+ Code, [](llvm::ArrayRef<
+ std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
+ Results,
+ ASTContext &ASTCtx) {
+ ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
+ const Environment &Env = Results[0].second.Env;
+
+ const auto *ThisLoc =
+ cast<AggregateStorageLocation>(Env.getThisPointeeStorageLocation());
+
+ const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
+ ASSERT_THAT(BarDecl, NotNull());
+
+ const auto *BarLoc =
+ cast<ScalarStorageLocation>(&ThisLoc->getChild(*BarDecl));
+ ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(BarLoc));
+
+ const Value *BarVal = Env.getValue(*BarLoc);
+ ASSERT_TRUE(isa_and_nonnull<IntegerValue>(BarVal));
+
+ const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
+ ASSERT_THAT(FooDecl, NotNull());
+ EXPECT_EQ(Env.getValue(*FooDecl, SkipPast::None), BarVal);
+
+ const ValueDecl *QuxDecl = findValueDecl(ASTCtx, "Qux");
+ ASSERT_THAT(QuxDecl, NotNull());
+
+ ASSERT_TRUE(QuxDecl->getType()->isClassType());
+ auto QuxFields = QuxDecl->getType()->getAsRecordDecl()->fields();
+
+ FieldDecl *BazDecl = nullptr;
+ for (FieldDecl *Field : QuxFields) {
+ if (Field->getNameAsString() == "Baz") {
+ BazDecl = Field;
+ } else {
+ FAIL() << "Unexpected field: " << Field->getNameAsString();
+ }
+ }
+ ASSERT_THAT(BazDecl, NotNull());
+
+ const auto *QuxLoc =
+ cast<AggregateStorageLocation>(&ThisLoc->getChild(*QuxDecl));
+ const auto *QuxVal = dyn_cast<StructValue>(Env.getValue(*QuxLoc));
+ ASSERT_THAT(QuxVal, NotNull());
+
+ const auto *BazLoc =
+ cast<ScalarStorageLocation>(&QuxLoc->getChild(*BazDecl));
+ const auto *BazVal = cast<IntegerValue>(&QuxVal->getChild(*BazDecl));
+ EXPECT_EQ(Env.getValue(*BazLoc), BazVal);
+
+ const ValueDecl *QuuxDecl = findValueDecl(ASTCtx, "Quux");
+ ASSERT_THAT(QuuxDecl, NotNull());
+ EXPECT_EQ(Env.getValue(*QuuxDecl, SkipPast::None), BazVal);
+ });
+}
+
} // namespace
More information about the cfe-commits
mailing list