ymandel updated this revision to Diff 450353.
ymandel added a comment.
add comment
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D131280/new/
https://reviews.llvm.org/D131280
Files:
clang/include/clang/Analysis/FlowSensitive/ControlFlowContext.h
clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
clang/lib/Analysis/FlowSensitive/ControlFlowContext.cpp
clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
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
@@ -11,12 +11,15 @@
#include "clang/AST/Decl.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Analysis/FlowSensitive/ControlFlowContext.h"
#include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
#include "clang/Analysis/FlowSensitive/NoopAnalysis.h"
#include "clang/Analysis/FlowSensitive/StorageLocation.h"
#include "clang/Analysis/FlowSensitive/Value.h"
#include "clang/Basic/LangStandard.h"
+#include "clang/Tooling/Tooling.h"
#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Casting.h"
#include "llvm/Testing/Support/Error.h"
@@ -40,11 +43,39 @@
template <typename Matcher>
void runDataflow(llvm::StringRef Code, Matcher Match,
DataflowAnalysisOptions Options,
+ llvm::StringRef ModeledFunctionsCode = {},
LangStandard::Kind Std = LangStandard::lang_cxx17,
llvm::StringRef TargetFun = "target") {
+ const std::vector<std::string> Args = {
+ "-fsyntax-only", "-fno-delayed-template-parsing",
+ "-std=" +
+ std::string(LangStandard::getLangStandardForKind(Std).getName())};
+
+ auto MainASTUnit = tooling::buildASTFromCodeWithArgs(Code, Args);
+ ASSERT_NE(MainASTUnit, nullptr);
+
+ // The unique_ptr has to live outside the if-body since the lifetime of the
+ // function map is linked to the lifetime of the ASTUnit. The raw pointer
+ // allows us to select between the main TU and the (optional) model TU.
+ std::unique_ptr<ASTUnit> ModelASTUnit = nullptr;
+ ASTUnit *UnitPtr = nullptr;
+ if (ModeledFunctionsCode.empty()) {
+ UnitPtr = MainASTUnit.get();
+ } else {
+ ModelASTUnit =
+ tooling::buildASTFromCodeWithArgs(ModeledFunctionsCode, Args);
+ ASSERT_NE(ModelASTUnit, nullptr);
+ UnitPtr = ModelASTUnit.get();
+ }
+
+ llvm::Expected<llvm::StringMap<ControlFlowContext>> Result =
+ buildFunctionMapFromAST(*UnitPtr);
+ ASSERT_TRUE((bool)Result);
+ llvm::StringMap<ControlFlowContext> AnalyzableFunctions = std::move(*Result);
+
ASSERT_THAT_ERROR(
test::checkDataflow<NoopAnalysis>(
- Code, TargetFun,
+ Code, std::move(MainASTUnit), ast_matchers::hasName(TargetFun),
[Options](ASTContext &C, Environment &) {
return NoopAnalysis(C, Options);
},
@@ -53,18 +84,18 @@
std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
Results,
ASTContext &ASTCtx) { Match(Results, ASTCtx); },
- {"-fsyntax-only", "-fno-delayed-template-parsing",
- "-std=" + std::string(
- LangStandard::getLangStandardForKind(Std).getName())}),
+ std::move(AnalyzableFunctions)),
llvm::Succeeded());
}
+// FIXME: inline this overload and remove it.
template <typename Matcher>
void runDataflow(llvm::StringRef Code, Matcher Match,
LangStandard::Kind Std = LangStandard::lang_cxx17,
bool ApplyBuiltinTransfer = true,
llvm::StringRef TargetFun = "target") {
- runDataflow(Code, Match, {ApplyBuiltinTransfer, {}}, Std, TargetFun);
+ runDataflow(Code, Match, /*Options=*/{ApplyBuiltinTransfer, {}},
+ /*AnalyzableFunctions=*/{}, Std, TargetFun);
}
TEST(TransferTest, IntVarDeclNotTrackedWhenTransferDisabled) {
@@ -1320,9 +1351,19 @@
}
};
)";
+
+ auto ASTUnit = tooling::buildASTFromCodeWithArgs(
+ Code,
+ /*Args=*/
+ {"-fsyntax-only", "-fno-delayed-template-parsing",
+ "-std=" + std::string(LangStandard::getLangStandardForKind(
+ LangStandard::lang_cxx17)
+ .getName())},
+ "input.cc", "clang-dataflow-test");
+
ASSERT_THAT_ERROR(
test::checkDataflow<NoopAnalysis>(
- Code, cxxConstructorDecl(ofClass(hasName("B"))),
+ Code, std::move(ASTUnit), cxxConstructorDecl(ofClass(hasName("B"))),
[](ASTContext &C, Environment &) {
return NoopAnalysis(C, /*ApplyBuiltinTransfer=*/true);
},
@@ -1335,11 +1376,7 @@
// the future, we can expand this test to check more specific
// properties.
EXPECT_THAT(Results, ElementsAre(Pair("p", _)));
- },
- {"-fsyntax-only", "-fno-delayed-template-parsing",
- "-std=" + std::string(LangStandard::getLangStandardForKind(
- LangStandard::lang_cxx17)
- .getName())}),
+ }),
llvm::Succeeded());
}
@@ -3880,30 +3917,34 @@
// [[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;
+ 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 ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
+ ASSERT_THAT(FooDecl, NotNull());
- auto &FooVal =
- *cast<BoolValue>(Env.getValue(*FooDecl, SkipPast::None));
- EXPECT_FALSE(Env.flowConditionImplies(FooVal));
- EXPECT_FALSE(Env.flowConditionImplies(Env.makeNot(FooVal)));
- },
- {/*.ApplyBuiltinTransfer=*/true,
- /*.BuiltinTransferOptions=*/{/*.ContextSensitive=*/false}});
+ auto &FooVal = *cast<BoolValue>(Env.getValue(*FooDecl, SkipPast::None));
+ EXPECT_FALSE(Env.flowConditionImplies(FooVal));
+ EXPECT_FALSE(Env.flowConditionImplies(Env.makeNot(FooVal)));
+ },
+ {/*.ApplyBuiltinTransfer=*/true,
+ /*.BuiltinTransferOptions=*/{/*.ContextSensitive=*/false}},
+ /*ModeledFunctionsCode=*/"");
}
TEST(TransferTest, ContextSensitiveSetTrue) {
+ std::string Functions = R"(
+ void SetBool(bool &Var) { Var = true; }
+ )";
std::string Code = R"(
bool GiveBool();
- void SetBool(bool &Var) { Var = true; }
+ void SetBool(bool &Var);
void target() {
bool Foo = GiveBool();
@@ -3927,13 +3968,15 @@
EXPECT_TRUE(Env.flowConditionImplies(FooVal));
},
{/*.ApplyBuiltinTransfer=*/true,
- /*.BuiltinTransferOptions=*/{/*.ContextSensitive=*/true}});
+ /*.BuiltinTransferOptions=*/{/*.ContextSensitive=*/true}},
+ Functions);
}
TEST(TransferTest, ContextSensitiveSetFalse) {
+ std::string Functions = R"(void SetBool(bool &Var) { Var = false; })";
std::string Code = R"(
bool GiveBool();
- void SetBool(bool &Var) { Var = false; }
+ void SetBool(bool &Var);
void target() {
bool Foo = GiveBool();
@@ -3941,29 +3984,33 @@
// [[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;
+ 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 ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
+ ASSERT_THAT(FooDecl, NotNull());
- auto &FooVal =
- *cast<BoolValue>(Env.getValue(*FooDecl, SkipPast::None));
- EXPECT_TRUE(Env.flowConditionImplies(Env.makeNot(FooVal)));
- },
- {/*.ApplyBuiltinTransfer=*/true,
- /*.BuiltinTransferOptions=*/{/*.ContextSensitive=*/true}});
+ auto &FooVal = *cast<BoolValue>(Env.getValue(*FooDecl, SkipPast::None));
+ EXPECT_TRUE(Env.flowConditionImplies(Env.makeNot(FooVal)));
+ },
+ {/*.ApplyBuiltinTransfer=*/true,
+ /*.BuiltinTransferOptions=*/{/*.ContextSensitive=*/true}},
+ Functions);
}
TEST(TransferTest, ContextSensitiveSetBothTrueAndFalse) {
+ std::string Functions = R"(
+ void SetBool(bool &Var, bool Val) { Var = Val; }
+ )";
std::string Code = R"(
bool GiveBool();
- void SetBool(bool &Var, bool Val) { Var = Val; }
+ void SetBool(bool &Var, bool Val);
void target() {
bool Foo = GiveBool();
@@ -3973,39 +4020,42 @@
// [[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;
+ 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 ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
+ ASSERT_THAT(FooDecl, NotNull());
- const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
- ASSERT_THAT(BarDecl, NotNull());
+ const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
+ ASSERT_THAT(BarDecl, NotNull());
- auto &FooVal =
- *cast<BoolValue>(Env.getValue(*FooDecl, SkipPast::None));
- EXPECT_TRUE(Env.flowConditionImplies(FooVal));
- EXPECT_FALSE(Env.flowConditionImplies(Env.makeNot(FooVal)));
+ auto &FooVal = *cast<BoolValue>(Env.getValue(*FooDecl, SkipPast::None));
+ EXPECT_TRUE(Env.flowConditionImplies(FooVal));
+ EXPECT_FALSE(Env.flowConditionImplies(Env.makeNot(FooVal)));
- auto &BarVal =
- *cast<BoolValue>(Env.getValue(*BarDecl, SkipPast::None));
- EXPECT_FALSE(Env.flowConditionImplies(BarVal));
- EXPECT_TRUE(Env.flowConditionImplies(Env.makeNot(BarVal)));
- },
- {/*.ApplyBuiltinTransfer=*/true,
- /*.BuiltinTransferOptions=*/{/*.ContextSensitive=*/true}});
+ auto &BarVal = *cast<BoolValue>(Env.getValue(*BarDecl, SkipPast::None));
+ EXPECT_FALSE(Env.flowConditionImplies(BarVal));
+ EXPECT_TRUE(Env.flowConditionImplies(Env.makeNot(BarVal)));
+ },
+ {/*.ApplyBuiltinTransfer=*/true,
+ /*.BuiltinTransferOptions=*/{/*.ContextSensitive=*/true}},
+ Functions);
}
TEST(TransferTest, ContextSensitiveSetTwoLayers) {
- std::string Code = R"(
- bool GiveBool();
+ std::string Functions = R"(
void SetBool1(bool &Var) { Var = true; }
void SetBool2(bool &Var) { SetBool1(Var); }
+ )";
+ std::string Code = R"(
+ bool GiveBool();
+ void SetBool2(bool &Var);
void target() {
bool Foo = GiveBool();
@@ -4013,32 +4063,36 @@
// [[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;
+ 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 ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
+ ASSERT_THAT(FooDecl, NotNull());
- auto &FooVal =
- *cast<BoolValue>(Env.getValue(*FooDecl, SkipPast::None));
- EXPECT_FALSE(Env.flowConditionImplies(FooVal));
- EXPECT_FALSE(Env.flowConditionImplies(Env.makeNot(FooVal)));
- },
- {/*.ApplyBuiltinTransfer=*/true,
- /*.BuiltinTransferOptions=*/{/*.ContextSensitive=*/true}});
+ auto &FooVal = *cast<BoolValue>(Env.getValue(*FooDecl, SkipPast::None));
+ EXPECT_FALSE(Env.flowConditionImplies(FooVal));
+ EXPECT_FALSE(Env.flowConditionImplies(Env.makeNot(FooVal)));
+ },
+ {/*.ApplyBuiltinTransfer=*/true,
+ /*.BuiltinTransferOptions=*/{/*.ContextSensitive=*/true}},
+ Functions);
}
TEST(TransferTest, ContextSensitiveSetMultipleLines) {
- std::string Code = R"(
+ std::string Functions = R"(
void SetBools(bool &Var1, bool &Var2) {
Var1 = true;
Var2 = false;
}
+ )";
+ std::string Code = R"(
+ void SetBools(bool &Var1, bool &Var2);
void target() {
bool Foo = false;
@@ -4047,36 +4101,36 @@
// [[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;
+ 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 ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
+ ASSERT_THAT(FooDecl, NotNull());
- const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
- ASSERT_THAT(BarDecl, NotNull());
+ const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
+ ASSERT_THAT(BarDecl, NotNull());
- auto &FooVal =
- *cast<BoolValue>(Env.getValue(*FooDecl, SkipPast::None));
- EXPECT_TRUE(Env.flowConditionImplies(FooVal));
- EXPECT_FALSE(Env.flowConditionImplies(Env.makeNot(FooVal)));
+ auto &FooVal = *cast<BoolValue>(Env.getValue(*FooDecl, SkipPast::None));
+ EXPECT_TRUE(Env.flowConditionImplies(FooVal));
+ EXPECT_FALSE(Env.flowConditionImplies(Env.makeNot(FooVal)));
- auto &BarVal =
- *cast<BoolValue>(Env.getValue(*BarDecl, SkipPast::None));
- EXPECT_FALSE(Env.flowConditionImplies(BarVal));
- EXPECT_TRUE(Env.flowConditionImplies(Env.makeNot(BarVal)));
- },
- {/*.ApplyBuiltinTransfer=*/true,
- /*.BuiltinTransferOptions=*/{/*.ContextSensitive=*/true}});
+ auto &BarVal = *cast<BoolValue>(Env.getValue(*BarDecl, SkipPast::None));
+ EXPECT_FALSE(Env.flowConditionImplies(BarVal));
+ EXPECT_TRUE(Env.flowConditionImplies(Env.makeNot(BarVal)));
+ },
+ {/*.ApplyBuiltinTransfer=*/true,
+ /*.BuiltinTransferOptions=*/{/*.ContextSensitive=*/true}},
+ Functions);
}
TEST(TransferTest, ContextSensitiveSetMultipleBlocks) {
- std::string Code = R"(
+ std::string Functions = R"(
void IfCond(bool Cond, bool &Then, bool &Else) {
if (Cond) {
Then = true;
@@ -4084,6 +4138,9 @@
Else = true;
}
}
+ )";
+ std::string Code = R"(
+ void IfCond(bool Cond, bool &Then, bool &Else);
void target() {
bool Foo = false;
@@ -4093,34 +4150,41 @@
// [[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;
+ 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 *BarDecl = findValueDecl(ASTCtx, "Bar");
- ASSERT_THAT(BarDecl, NotNull());
+ const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
+ ASSERT_THAT(BarDecl, NotNull());
- const ValueDecl *BazDecl = findValueDecl(ASTCtx, "Baz");
- ASSERT_THAT(BazDecl, NotNull());
+ const ValueDecl *BazDecl = findValueDecl(ASTCtx, "Baz");
+ ASSERT_THAT(BazDecl, NotNull());
- auto &BarVal =
- *cast<BoolValue>(Env.getValue(*BarDecl, SkipPast::None));
- EXPECT_FALSE(Env.flowConditionImplies(BarVal));
- EXPECT_TRUE(Env.flowConditionImplies(Env.makeNot(BarVal)));
+ auto &BarVal = *cast<BoolValue>(Env.getValue(*BarDecl, SkipPast::None));
+ EXPECT_FALSE(Env.flowConditionImplies(BarVal));
+ EXPECT_TRUE(Env.flowConditionImplies(Env.makeNot(BarVal)));
- auto &BazVal =
- *cast<BoolValue>(Env.getValue(*BazDecl, SkipPast::None));
- EXPECT_TRUE(Env.flowConditionImplies(BazVal));
- EXPECT_FALSE(Env.flowConditionImplies(Env.makeNot(BazVal)));
- },
- {/*.ApplyBuiltinTransfer=*/true,
- /*.BuiltinTransferOptions=*/{/*.ContextSensitive=*/true}});
+ auto &BazVal = *cast<BoolValue>(Env.getValue(*BazDecl, SkipPast::None));
+ EXPECT_TRUE(Env.flowConditionImplies(BazVal));
+ EXPECT_FALSE(Env.flowConditionImplies(Env.makeNot(BazVal)));
+ },
+ {/*.ApplyBuiltinTransfer=*/true,
+ /*.BuiltinTransferOptions=*/{/*.ContextSensitive=*/true}},
+ Functions);
}
+llvm::StringLiteral Model = R"(
+ void Noop() { return; }
+ bool GiveTrue() { return true; }
+ bool GiveFalse() { return false; }
+ bool GiveBack(bool Arg) { return Arg; }
+)";
+
TEST(TransferTest, ContextSensitiveReturnVoid) {
std::string Code = R"(
void Noop() { return; }
@@ -4130,24 +4194,26 @@
// [[p]]
}
)";
- runDataflow(Code,
- [](llvm::ArrayRef<
- std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
- Results,
- ASTContext &ASTCtx) {
- ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
- // This just tests that the analysis doesn't crash.
- },
- {/*.ApplyBuiltinTransfer=*/true,
- /*.BuiltinTransferOptions=*/{/*.ContextSensitive=*/true}});
+ runDataflow(
+ Code,
+ [](llvm::ArrayRef<
+ std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
+ Results,
+ ASTContext &ASTCtx) {
+ ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
+ // This just tests that the analysis doesn't crash.
+ },
+ {/*.ApplyBuiltinTransfer=*/true,
+ /*.BuiltinTransferOptions=*/{/*.ContextSensitive=*/true}},
+ Model);
}
TEST(TransferTest, ContextSensitiveReturnTrue) {
std::string Code = R"(
- bool GiveBool() { return true; }
+ bool GiveTrue();
void target() {
- bool Foo = GiveBool();
+ bool Foo = GiveTrue();
// [[p]]
}
)";
@@ -4167,15 +4233,15 @@
EXPECT_TRUE(Env.flowConditionImplies(FooVal));
},
{/*.ApplyBuiltinTransfer=*/true,
- /*.BuiltinTransferOptions=*/{/*.ContextSensitive=*/true}});
+ /*.BuiltinTransferOptions=*/{/*.ContextSensitive=*/true}}, Model);
}
TEST(TransferTest, ContextSensitiveReturnFalse) {
std::string Code = R"(
- bool GiveBool() { return false; }
+ bool GiveFalse();
void target() {
- bool Foo = GiveBool();
+ bool Foo = GiveFalse();
// [[p]]
}
)";
@@ -4195,13 +4261,13 @@
EXPECT_TRUE(Env.flowConditionImplies(Env.makeNot(FooVal)));
},
{/*.ApplyBuiltinTransfer=*/true,
- /*.BuiltinTransferOptions=*/{/*.ContextSensitive=*/true}});
+ /*.BuiltinTransferOptions=*/{/*.ContextSensitive=*/true}}, Model);
}
TEST(TransferTest, ContextSensitiveReturnArg) {
std::string Code = R"(
bool GiveBool();
- bool GiveBack(bool Arg) { return Arg; }
+ bool GiveBack(bool Arg);
void target() {
bool Foo = GiveBool();
@@ -4226,7 +4292,7 @@
EXPECT_TRUE(Env.flowConditionImplies(BazVal));
},
{/*.ApplyBuiltinTransfer=*/true,
- /*.BuiltinTransferOptions=*/{/*.ContextSensitive=*/true}});
+ /*.BuiltinTransferOptions=*/{/*.ContextSensitive=*/true}}, Model);
}
TEST(TransferTest, ContextSensitiveMethodLiteral) {
Index: clang/unittests/Analysis/FlowSensitive/TestingSupport.h
===================================================================
--- clang/unittests/Analysis/FlowSensitive/TestingSupport.h
+++ clang/unittests/Analysis/FlowSensitive/TestingSupport.h
@@ -71,22 +71,19 @@
std::vector<llvm::Optional<TypeErasedDataflowAnalysisState>> &BlockStates;
};
+/// `Code` must be the source code from `ASTUnit` was built.
template <typename AnalysisT>
llvm::Error checkDataflow(
- llvm::StringRef Code,
+ llvm::StringRef Code, std::unique_ptr<ASTUnit> ASTUnit,
ast_matchers::internal::Matcher<FunctionDecl> TargetFuncMatcher,
std::function<AnalysisT(ASTContext &, Environment &)> MakeAnalysis,
std::function<void(ASTContext &, const CFGStmt &,
const TypeErasedDataflowAnalysisState &)>
PostVisitStmt,
- std::function<void(AnalysisData)> VerifyResults, ArrayRef<std::string> Args,
- const tooling::FileContentMappings &VirtualMappedFiles = {}) {
- llvm::Annotations AnnotatedCode(Code);
- auto Unit = tooling::buildASTFromCodeWithArgs(
- AnnotatedCode.code(), Args, "input.cc", "clang-dataflow-test",
- std::make_shared<PCHContainerOperations>(),
- tooling::getClangStripDependencyFileAdjuster(), VirtualMappedFiles);
- auto &Context = Unit->getASTContext();
+ std::function<void(AnalysisData)> VerifyResults,
+ llvm::StringMap<ControlFlowContext> AnalyzableFunctions = {}) {
+ const llvm::Annotations AnnotatedCode(Code);
+ auto &Context = ASTUnit->getASTContext();
if (Context.getDiagnostics().getClient()->getNumErrors() != 0) {
return llvm::make_error<llvm::StringError>(
@@ -104,11 +101,12 @@
return llvm::make_error<llvm::StringError>(
llvm::errc::invalid_argument, "Could not find target function.");
- auto CFCtx = ControlFlowContext::build(F, F->getBody(), &F->getASTContext());
+ auto CFCtx = ControlFlowContext::build(F, *F->getBody(), F->getASTContext());
if (!CFCtx)
return CFCtx.takeError();
- DataflowAnalysisContext DACtx(std::make_unique<WatchedLiteralsSolver>());
+ DataflowAnalysisContext DACtx(std::make_unique<WatchedLiteralsSolver>(),
+ std::move(AnalyzableFunctions));
Environment Env(DACtx, *F);
auto Analysis = MakeAnalysis(Context, Env);
@@ -141,24 +139,50 @@
return llvm::Error::success();
}
-// Runs dataflow on the body of the function that matches `TargetFuncMatcher` in
-// code snippet `Code`. Requires: `AnalysisT` contains a type `Lattice`.
+/// `AnalyzableFunctions` is passed to the `DataflowAnalysisContext`
+/// constructor.
+// FIXME: This now called in one place. Inline and remove.
template <typename AnalysisT>
llvm::Error checkDataflow(
llvm::StringRef Code,
ast_matchers::internal::Matcher<FunctionDecl> TargetFuncMatcher,
std::function<AnalysisT(ASTContext &, Environment &)> MakeAnalysis,
+ std::function<void(ASTContext &, const CFGStmt &,
+ const TypeErasedDataflowAnalysisState &)>
+ PostVisitStmt,
+ std::function<void(AnalysisData)> VerifyResults, ArrayRef<std::string> Args,
+ const tooling::FileContentMappings &VirtualMappedFiles = {},
+ llvm::StringMap<ControlFlowContext> AnalyzableFunctions = {}) {
+ auto Unit = tooling::buildASTFromCodeWithArgs(
+ Code, Args, "input.cc", "clang-dataflow-test",
+ std::make_shared<PCHContainerOperations>(),
+ tooling::getClangStripDependencyFileAdjuster(), VirtualMappedFiles);
+ return checkDataflow(Code, std::move(Unit), std::move(TargetFuncMatcher),
+ std::move(MakeAnalysis), std::move(PostVisitStmt),
+ std::move(VerifyResults),
+ std::move(AnalyzableFunctions));
+}
+
+/// Runs dataflow on the body of the function that matches `TargetFuncMatcher`
+/// in code snippet `Code`. `AnalyzableFunctions` is passed to the
+/// `DataflowAnalysisContext` constructor. Requires: `AnalysisT` contains a type
+/// `Lattice`.
+template <typename AnalysisT>
+llvm::Error checkDataflow(
+ llvm::StringRef Code, std::unique_ptr<ASTUnit> ASTUnit,
+ ast_matchers::internal::Matcher<FunctionDecl> TargetFuncMatcher,
+ std::function<AnalysisT(ASTContext &, Environment &)> MakeAnalysis,
std::function<void(
llvm::ArrayRef<std::pair<
std::string, DataflowAnalysisState<typename AnalysisT::Lattice>>>,
ASTContext &)>
VerifyResults,
- ArrayRef<std::string> Args,
- const tooling::FileContentMappings &VirtualMappedFiles = {}) {
+ llvm::StringMap<ControlFlowContext> AnalyzableFunctions = {}) {
using StateT = DataflowAnalysisState<typename AnalysisT::Lattice>;
return checkDataflow(
- Code, std::move(TargetFuncMatcher), std::move(MakeAnalysis),
+ Code, std::move(ASTUnit), std::move(TargetFuncMatcher),
+ std::move(MakeAnalysis),
/*PostVisitStmt=*/nullptr,
[&VerifyResults](AnalysisData AnalysisData) {
if (AnalysisData.BlockStates.empty()) {
@@ -192,11 +216,12 @@
}
VerifyResults(Results, AnalysisData.ASTCtx);
},
- Args, VirtualMappedFiles);
+ std::move(AnalyzableFunctions));
}
-// Runs dataflow on the body of the function named `target_fun` in code snippet
-// `code`.
+/// Runs dataflow on the body of the function named `target_fun` in code snippet
+/// `code`. `AnalyzableFunctions` is passed to the `DataflowAnalysisContext`
+/// constructor.
template <typename AnalysisT>
llvm::Error checkDataflow(
llvm::StringRef Code, llvm::StringRef TargetFun,
@@ -207,10 +232,17 @@
ASTContext &)>
VerifyResults,
ArrayRef<std::string> Args,
- const tooling::FileContentMappings &VirtualMappedFiles = {}) {
- return checkDataflow(Code, ast_matchers::hasName(TargetFun),
- std::move(MakeAnalysis), std::move(VerifyResults), Args,
- VirtualMappedFiles);
+ const tooling::FileContentMappings &VirtualMappedFiles = {},
+ llvm::StringMap<ControlFlowContext> AnalyzableFunctions = {}) {
+ auto ASTUnit = tooling::buildASTFromCodeWithArgs(
+ Code, Args, "input.cc", "clang-dataflow-test",
+ std::make_shared<PCHContainerOperations>(),
+ tooling::getClangStripDependencyFileAdjuster(), VirtualMappedFiles);
+
+ return checkDataflow(Code, std::move(ASTUnit),
+ ast_matchers::hasName(TargetFun),
+ std::move(MakeAnalysis), std::move(VerifyResults),
+ std::move(AnalyzableFunctions));
}
/// Returns the `ValueDecl` for the given identifier.
Index: clang/lib/Analysis/FlowSensitive/Transfer.cpp
===================================================================
--- clang/lib/Analysis/FlowSensitive/Transfer.cpp
+++ clang/lib/Analysis/FlowSensitive/Transfer.cpp
@@ -530,6 +530,12 @@
if (!Options.ContextSensitive)
return;
+ // Note that it is important for the storage location of `S` to be set
+ // before `pushCall`, because the latter uses it to set the storage
+ // location for `return`.
+ auto &ReturnLoc = Env.createStorageLocation(*S);
+ Env.setStorageLocation(*S, ReturnLoc);
+
const ControlFlowContext *CFCtx = Env.getControlFlowContext(F);
if (!CFCtx)
return;
@@ -540,19 +546,14 @@
auto ExitBlock = CFCtx->getCFG().getExit().getBlockID();
- // Note that it is important for the storage location of `S` to be set
- // before `pushCall`, because the latter uses it to set the storage
- // location for `return`.
- auto &ReturnLoc = Env.createStorageLocation(*S);
- Env.setStorageLocation(*S, ReturnLoc);
- auto CalleeEnv = Env.pushCall(S);
+ const FunctionDecl *FuncDecl = CFCtx->getDecl()->getAsFunction();
+ assert(FuncDecl != nullptr && "ControlFlowContexts in the environment "
+ "should always carry a FunctionDecl");
+ auto CalleeEnv = Env.pushCall(S, *FuncDecl);
// FIXME: Use the same analysis as the caller for the callee. Note,
// though, that doing so would require support for changing the analysis's
// ASTContext.
- assert(
- CFCtx->getDecl() != nullptr &&
- "ControlFlowContexts in the environment should always carry a decl");
auto Analysis = NoopAnalysis(CFCtx->getDecl()->getASTContext(),
DataflowAnalysisOptions());
Index: clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
===================================================================
--- clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
+++ clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
@@ -203,13 +203,12 @@
}
}
-Environment Environment::pushCall(const CallExpr *Call) const {
+Environment Environment::pushCall(const CallExpr *Call,
+ const FunctionDecl &FuncDecl) const {
Environment Env(*this);
// FIXME: Support references here.
Env.ReturnLoc = Env.getStorageLocation(*Call, SkipPast::Reference);
- const auto *FuncDecl = Call->getDirectCallee();
- assert(FuncDecl != nullptr);
// FIXME: In order to allow the callee to reference globals, we probably need
// to call `initGlobalVars` here in some way.
@@ -219,14 +218,14 @@
}
}
- auto ParamIt = FuncDecl->param_begin();
+ auto ParamIt = FuncDecl.param_begin();
auto ArgIt = Call->arg_begin();
auto ArgEnd = Call->arg_end();
// FIXME: Parameters don't always map to arguments 1:1; examples include
// overloaded operators implemented as member functions, and parameter packs.
for (; ArgIt != ArgEnd; ++ParamIt, ++ArgIt) {
- assert(ParamIt != FuncDecl->param_end());
+ assert(ParamIt != FuncDecl.param_end());
const Expr *Arg = *ArgIt;
auto *ArgLoc = Env.getStorageLocation(*Arg, SkipPast::Reference);
Index: clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
===================================================================
--- clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
+++ clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
@@ -14,6 +14,7 @@
#include "clang/Analysis/FlowSensitive/DataflowAnalysisContext.h"
#include "clang/AST/ExprCXX.h"
+#include "clang/Analysis/FlowSensitive/ControlFlowContext.h"
#include "clang/Analysis/FlowSensitive/DebugSupport.h"
#include "clang/Analysis/FlowSensitive/Value.h"
#include "llvm/Support/Debug.h"
@@ -336,23 +337,22 @@
const ControlFlowContext *
DataflowAnalysisContext::getControlFlowContext(const FunctionDecl *F) {
- // Canonicalize the key:
- F = F->getDefinition();
- if (F == nullptr)
- return nullptr;
+ // Canonicalize the key, if possible.
+ if (auto *C = F->getCanonicalDecl())
+ F = C;
auto It = FunctionContexts.find(F);
if (It != FunctionContexts.end())
- return &It->second;
-
- if (Stmt *Body = F->getBody()) {
- auto CFCtx = ControlFlowContext::build(F, *Body, F->getASTContext());
- // FIXME: Handle errors.
- assert(CFCtx);
- auto Result = FunctionContexts.insert({F, std::move(*CFCtx)});
- return &Result.first->second;
- }
-
- return nullptr;
+ return It->second;
+
+ const ControlFlowContext *CFCtx = nullptr;
+ std::string QualName;
+ llvm::raw_string_ostream OS(QualName);
+ F->printQualifiedName(OS);
+ auto It2 = AnalyzableFunctions.find(QualName);
+ if (It2 != AnalyzableFunctions.end())
+ CFCtx = &It2->second;
+ FunctionContexts.insert({F, CFCtx});
+ return CFCtx;
}
} // namespace dataflow
Index: clang/lib/Analysis/FlowSensitive/ControlFlowContext.cpp
===================================================================
--- clang/lib/Analysis/FlowSensitive/ControlFlowContext.cpp
+++ clang/lib/Analysis/FlowSensitive/ControlFlowContext.cpp
@@ -16,6 +16,7 @@
#include "clang/AST/Decl.h"
#include "clang/AST/Stmt.h"
#include "clang/Analysis/CFG.h"
+#include "clang/Frontend/ASTUnit.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/Support/Error.h"
#include <utility>
@@ -74,5 +75,33 @@
return build(D, *S, *C);
}
+static void InsertFunction(FunctionDecl *F,
+ llvm::StringMap<ControlFlowContext> &Functions) {
+ auto *Body = F->getBody();
+ if (Body == nullptr)
+ return;
+ auto CFCtx = ControlFlowContext::build(F, *Body, F->getASTContext());
+ assert(CFCtx &&
+ "All functions in model should translate to CFG successfully");
+ std::string QualName;
+ llvm::raw_string_ostream OS(QualName);
+ F->printQualifiedName(OS);
+ Functions.insert({std::move(QualName), std::move(*CFCtx)});
+}
+
+llvm::Expected<llvm::StringMap<ControlFlowContext>>
+buildFunctionMapFromAST(ASTUnit &Unit) {
+ llvm::StringMap<ControlFlowContext> Functions;
+ for (auto It = Unit.top_level_begin(); It != Unit.top_level_end(); ++It) {
+ if (auto *C = dyn_cast<CXXRecordDecl>(*It)) {
+ for (auto *M : C->methods())
+ InsertFunction(M, Functions);
+ } else if (auto *F = dyn_cast<FunctionDecl>(*It)) {
+ InsertFunction(F, Functions);
+ }
+ }
+ return Functions;
+}
+
} // namespace dataflow
} // namespace clang
Index: clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
===================================================================
--- clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
+++ clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
@@ -129,20 +129,19 @@
/// with a symbolic representation of the `this` pointee.
Environment(DataflowAnalysisContext &DACtx, const DeclContext &DeclCtx);
- /// Creates and returns an environment to use for an inline analysis of the
- /// callee. Uses the storage location from each argument in the `Call` as the
- /// storage location for the corresponding parameter in the callee.
+ /// Creates and returns an environment to use for an inline analysis of the
+ /// callee. `FuncDecl` provides the definition of the function to be analyzed.
+ /// Uses the storage location from each argument in the `Call` as the storage
+ /// location for the corresponding parameter in the callee.
///
/// Requirements:
///
- /// The callee of `Call` must be a `FunctionDecl` with a body.
+ /// The body of `FuncDecl` must not reference globals.
///
- /// The body of the callee must not reference globals.
- ///
- /// The arguments of `Call` must map 1:1 to the callee's parameters.
+ /// The arguments of `Call` must map 1:1 to `FuncDecl`'s parameters.
///
/// Each argument of `Call` must already have a `StorageLocation`.
- Environment pushCall(const CallExpr *Call) const;
+ Environment pushCall(const CallExpr *Call, const FunctionDecl &FuncDecl) const;
/// Moves gathered information back into `this` from a `CalleeEnv` created via
/// `pushCall`.
Index: clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
===================================================================
--- clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
+++ clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
@@ -24,6 +24,7 @@
#include "clang/Analysis/FlowSensitive/Value.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/StringSet.h"
#include "llvm/Support/Compiler.h"
#include <cassert>
#include <memory>
@@ -56,12 +57,22 @@
public:
/// Constructs a dataflow analysis context.
///
+ /// Paramter `AnalyzableFunctions` is a map of analyzable function bodies
+ /// (represented as `ControlFlowContext`s), keyed by the fully-qualified
+ /// function name. The analysis will *only* inline calls to the listed
+ /// functions and will use the provided implementation for that purpose. The
+ /// actual function definition, even if available in the current TU, is
+ /// ignored.
+ ///
/// Requirements:
///
/// `S` must not be null.
- DataflowAnalysisContext(std::unique_ptr<Solver> S)
+ DataflowAnalysisContext(
+ std::unique_ptr<Solver> S,
+ llvm::StringMap<ControlFlowContext> AnalyzableFunctions = {})
: S(std::move(S)), TrueVal(createAtomicBoolValue()),
- FalseVal(createAtomicBoolValue()) {
+ FalseVal(createAtomicBoolValue()),
+ AnalyzableFunctions(std::move(AnalyzableFunctions)) {
assert(this->S != nullptr);
}
@@ -346,7 +357,16 @@
FlowConditionDeps;
llvm::DenseMap<AtomicBoolValue *, BoolValue *> FlowConditionConstraints;
- llvm::DenseMap<const FunctionDecl *, ControlFlowContext> FunctionContexts;
+ // Analyzable function definitions, keyed by fully qualified names. See
+ // constructor for detailed explanation.
+ llvm::StringMap<ControlFlowContext> AnalyzableFunctions;
+
+ // Cache mapping function decls directly to `ControlFlowContext`s. If present
+ // in the map, the function decl has already been checked against
+ // `AnalyzableFunctions` (null if not a member of `AnalyzableFunctions`). If
+ // absent, the decl has not yet been checked.
+ llvm::DenseMap<const FunctionDecl *, const ControlFlowContext*>
+ FunctionContexts;
};
} // namespace dataflow
Index: clang/include/clang/Analysis/FlowSensitive/ControlFlowContext.h
===================================================================
--- clang/include/clang/Analysis/FlowSensitive/ControlFlowContext.h
+++ clang/include/clang/Analysis/FlowSensitive/ControlFlowContext.h
@@ -18,6 +18,7 @@
#include "clang/AST/Decl.h"
#include "clang/AST/Stmt.h"
#include "clang/Analysis/CFG.h"
+#include "clang/Frontend/ASTUnit.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/Support/Error.h"
#include <memory>
@@ -35,7 +36,8 @@
static llvm::Expected<ControlFlowContext> build(const Decl *D, Stmt &S,
ASTContext &C);
- // DEPRECATED. Use overload above.
+ // FIXME: fix known callers and uncomment the annotation.
+ // [[deprecated("Use overload above.")]]
static llvm::Expected<ControlFlowContext> build(const Decl *D, Stmt *S,
ASTContext *C);
@@ -65,6 +67,22 @@
llvm::DenseMap<const Stmt *, const CFGBlock *> StmtToBlock;
};
+/// Builds a map of all declared functions in the given AST, mapping from fully
+/// qualified function name to the `ControlFlowContext` that corresponds to that
+/// function. Fully qualified names are used so that we can related same-named
+/// functions from two different ASTs. This is critical for providing
+/// implementations of functions that are suitable for inlining during analysis
+/// of their callsites.
+///
+/// N.B. The return value, if not empty, will point into `Unit`, so `Unit` must
+/// outlive the return value.
+///
+/// FIXME: add support for constructors and declarations nested in namespaces.
+/// FIXME: fully qualified names are not enough to uniquely identify members of
+/// overload sets. change the key to support uniqueness.
+llvm::Expected<llvm::StringMap<ControlFlowContext>>
+buildFunctionMapFromAST(ASTUnit &Unit);
+
} // namespace dataflow
} // namespace clang
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits