[clang] 3ce03c4 - [clang][dataflow] Fix 2 bugs in `MemberExpr` interpretation.
Yitzhak Mandelbaum via cfe-commits
cfe-commits at lists.llvm.org
Tue Jan 10 07:48:08 PST 2023
Author: Yitzhak Mandelbaum
Date: 2023-01-10T15:48:00Z
New Revision: 3ce03c42dbb46531968c527d80c0243c2db7bc0e
URL: https://github.com/llvm/llvm-project/commit/3ce03c42dbb46531968c527d80c0243c2db7bc0e
DIFF: https://github.com/llvm/llvm-project/commit/3ce03c42dbb46531968c527d80c0243c2db7bc0e.diff
LOG: [clang][dataflow] Fix 2 bugs in `MemberExpr` interpretation.
There were two (small) bugs causing crashes in the analysis. This patch fixes both of them.
1. An enum value was accessed as a class member. Now, the engine gracefully
ignores such member expressions.
2. Field access in `MemberExpr` of struct/class-typed global variables. Analysis
didn't interpret fields of global vars, because the vars were initialized before
the fields were added to the "allowlist". Now, the allowlist is set _before_
init of globals.
Differential Revision: https://reviews.llvm.org/D141384
Added:
Modified:
clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
clang/lib/Analysis/FlowSensitive/Transfer.cpp
clang/unittests/Analysis/FlowSensitive/DataflowEnvironmentTest.cpp
clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
Removed:
################################################################################
diff --git a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
index 7d10a75b36e3e..37d200509e8d2 100644
--- a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
+++ b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
@@ -250,11 +250,12 @@ Environment::Environment(DataflowAnalysisContext &DACtx,
}
getFieldsAndGlobalVars(*FuncDecl->getBody(), Fields, Vars);
- initVars(Vars);
- // These have to be set before the lines that follow to ensure that create*
- // work correctly for structs.
+ // These have to be added before the lines that follow to ensure that
+ // `create*` work correctly for structs.
DACtx.addModeledFields(Fields);
+ initVars(Vars);
+
for (const auto *ParamDecl : FuncDecl->parameters()) {
assert(ParamDecl != nullptr);
auto &ParamLoc = createStorageLocation(*ParamDecl);
@@ -341,9 +342,13 @@ void Environment::pushCallInternal(const FunctionDecl *FuncDecl,
}
}
getFieldsAndGlobalVars(*FuncDecl->getBody(), Fields, Vars);
- initVars(Vars);
+
+ // These have to be added before the lines that follow to ensure that
+ // `create*` work correctly for structs.
DACtx->addModeledFields(Fields);
+ initVars(Vars);
+
const auto *ParamIt = FuncDecl->param_begin();
// FIXME: Parameters don't always map to arguments 1:1; examples include
diff --git a/clang/lib/Analysis/FlowSensitive/Transfer.cpp b/clang/lib/Analysis/FlowSensitive/Transfer.cpp
index c2bf18754be51..259b82d647981 100644
--- a/clang/lib/Analysis/FlowSensitive/Transfer.cpp
+++ b/clang/lib/Analysis/FlowSensitive/Transfer.cpp
@@ -461,6 +461,10 @@ class TransferVisitor : public ConstStmtVisitor<TransferVisitor> {
if (Member->isFunctionOrFunctionTemplate())
return;
+ // FIXME: if/when we add support for modeling enums, use that support here.
+ if (isa<EnumConstantDecl>(Member))
+ return;
+
if (auto *D = dyn_cast<VarDecl>(Member)) {
if (D->hasGlobalStorage()) {
auto *VarDeclLoc = Env.getStorageLocation(*D, SkipPast::None);
diff --git a/clang/unittests/Analysis/FlowSensitive/DataflowEnvironmentTest.cpp b/clang/unittests/Analysis/FlowSensitive/DataflowEnvironmentTest.cpp
index e66ed70be4feb..dfbd4ff274154 100644
--- a/clang/unittests/Analysis/FlowSensitive/DataflowEnvironmentTest.cpp
+++ b/clang/unittests/Analysis/FlowSensitive/DataflowEnvironmentTest.cpp
@@ -118,7 +118,7 @@ TEST_F(EnvironmentTest, InitGlobalVarsFun) {
Context);
const auto *Fun = selectFirst<FunctionDecl>("target", Results);
const auto *Var = selectFirst<VarDecl>("global", Results);
- ASSERT_TRUE(Fun != nullptr);
+ ASSERT_THAT(Fun, NotNull());
ASSERT_THAT(Var, NotNull());
// Verify the global variable is populated when we analyze `Target`.
@@ -126,6 +126,53 @@ TEST_F(EnvironmentTest, InitGlobalVarsFun) {
EXPECT_THAT(Env.getValue(*Var, SkipPast::None), NotNull());
}
+TEST_F(EnvironmentTest, InitGlobalVarsFieldFun) {
+ using namespace ast_matchers;
+
+ std::string Code = R"cc(
+ struct S { int Bar; };
+ S Global = {0};
+ int Target () { return Global.Bar; }
+ )cc";
+
+ auto Unit =
+ tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++11"});
+ auto &Context = Unit->getASTContext();
+
+ ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U);
+
+ auto Results =
+ match(decl(anyOf(varDecl(hasName("Global")).bind("global"),
+ functionDecl(hasName("Target")).bind("target"))),
+ Context);
+ const auto *Fun = selectFirst<FunctionDecl>("target", Results);
+ const auto *GlobalDecl = selectFirst<VarDecl>("global", Results);
+ ASSERT_THAT(Fun, NotNull());
+ ASSERT_THAT(GlobalDecl, NotNull());
+
+ ASSERT_TRUE(GlobalDecl->getType()->isStructureType());
+ auto GlobalFields = GlobalDecl->getType()->getAsRecordDecl()->fields();
+
+ FieldDecl *BarDecl = nullptr;
+ for (FieldDecl *Field : GlobalFields) {
+ if (Field->getNameAsString() == "Bar") {
+ BarDecl = Field;
+ break;
+ }
+ FAIL() << "Unexpected field: " << Field->getNameAsString();
+ }
+ ASSERT_THAT(BarDecl, NotNull());
+
+ // Verify the global variable is populated when we analyze `Target`.
+ Environment Env(DAContext, *Fun);
+ const auto *GlobalLoc = cast<AggregateStorageLocation>(
+ Env.getStorageLocation(*GlobalDecl, SkipPast::None));
+ const auto *GlobalVal = cast<StructValue>(Env.getValue(*GlobalLoc));
+ const auto *BarVal = GlobalVal->getChild(*BarDecl);
+ ASSERT_THAT(BarVal, NotNull());
+ EXPECT_TRUE(isa<IntegerValue>(BarVal));
+}
+
TEST_F(EnvironmentTest, InitGlobalVarsConstructor) {
using namespace ast_matchers;
diff --git a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
index 6c6584cdc8921..b4bf699b22918 100644
--- a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
+++ b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
@@ -1066,6 +1066,28 @@ TEST(TransferTest, StructMember) {
});
}
+TEST(TransferTest, StructMemberEnum) {
+ std::string Code = R"(
+ struct A {
+ int Bar;
+ enum E { ONE, TWO };
+ };
+
+ void target(A Foo) {
+ A::E Baz = Foo.ONE;
+ // [[p]]
+ }
+ )";
+ // Minimal expectations -- we're just testing that it doesn't crash, since
+ // enums aren't interpreted.
+ runDataflow(
+ Code,
+ [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
+ ASTContext &ASTCtx) {
+ EXPECT_THAT(Results.keys(), UnorderedElementsAre("p"));
+ });
+}
+
TEST(TransferTest, DerivedBaseMemberClass) {
std::string Code = R"(
class A {
More information about the cfe-commits
mailing list