sgatev created this revision.
sgatev added reviewers: ymandel, xazax.hun, gribozavr2.
Herald added a subscriber: rnkovacs.
sgatev requested review of this revision.
Herald added a project: clang.
This is part of the implementation of the dataflow analysis framework.
See "[RFC] A dataflow analysis framework for Clang AST" on cfe-dev.
Repository:
rG LLVM Github Monorepo
https://reviews.llvm.org/D117012
Files:
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
Index: clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
===================================================================
--- clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
@@ -67,7 +67,7 @@
TEST_F(TransferTest, IntVarDecl) {
std::string Code = R"(
void target() {
- int foo;
+ int Foo;
// [[p]]
}
)";
@@ -79,7 +79,7 @@
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 =
@@ -93,12 +93,12 @@
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 @@
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());
@@ -139,12 +139,12 @@
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 @@
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());
@@ -185,12 +185,12 @@
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 @@
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 =
@@ -221,25 +221,25 @@
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 @@
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());
@@ -330,12 +330,12 @@
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 @@
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 =
@@ -365,25 +365,25 @@
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 @@
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());
@@ -487,14 +487,14 @@
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,13 +507,13 @@
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;
@@ -543,9 +543,9 @@
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 @@
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, VarDeclInitAssign) {
std::string Code = R"(
void target() {
- int foo;
- int bar = foo;
+ int Foo;
+ int Bar = Foo;
// [[p]]
}
)";
@@ -586,13 +586,13 @@
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, 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 @@
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, 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 @@
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, 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 @@
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,386 @@
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);
+ ASSERT_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));
+ ASSERT_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 =
+ cast<ReferenceValue>(Env.getValue(*FooLoc));
+ const StorageLocation &FooPointeeLoc = FooVal->getPointeeLoc();
+ ASSERT_TRUE(isa<AggregateStorageLocation>(&FooPointeeLoc));
+
+ const Value *FooPointeeVal = Env.getValue(FooPointeeLoc);
+ ASSERT_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();
+ ASSERT_TRUE(isa<AggregateStorageLocation>(&FooPointeeLoc));
+
+ const Value *FooPointeeVal = Env.getValue(FooPointeeLoc);
+ ASSERT_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;
+
+ void target() {
+ int 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 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);
+ });
+}
+
+TEST_F(TransferTest, ClassThisMember) {
+ std::string Code = R"(
+ class A {
+ int Bar;
+
+ void target() {
+ int 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 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);
+ });
+}
+
} // namespace
Index: clang/unittests/Analysis/FlowSensitive/TestingSupport.h
===================================================================
--- clang/unittests/Analysis/FlowSensitive/TestingSupport.h
+++ clang/unittests/Analysis/FlowSensitive/TestingSupport.h
@@ -99,7 +99,7 @@
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>>
Index: clang/lib/Analysis/FlowSensitive/Transfer.cpp
===================================================================
--- clang/lib/Analysis/FlowSensitive/Transfer.cpp
+++ clang/lib/Analysis/FlowSensitive/Transfer.cpp
@@ -123,6 +123,43 @@
// 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);
+
+ if (Member->isFunctionOrFunctionTemplate())
+ return;
+
+ 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 +168,7 @@
// - CXXFunctionalCastExpr
// - CXXOperatorCallExpr
// - CXXStaticCastExpr
- // - CXXThisExpr
// - MaterializeTemporaryExpr
- // - MemberExpr
private:
void visitVarDecl(const VarDecl &D) {
Index: clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
===================================================================
--- clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
+++ 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 @@
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 @@
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;
}
@@ -242,6 +272,11 @@
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");
}
Index: clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
===================================================================
--- clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
+++ 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,12 +40,27 @@
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;
@@ -92,6 +108,11 @@
/// 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
Index: clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
===================================================================
--- clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
+++ clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
@@ -88,6 +88,22 @@
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 @@
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.
};
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits