diff --git a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp --- a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp +++ b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp @@ -493,6 +493,41 @@ return Val; } +// Hard code certain types to exclude from modeling. Currently, we limit to +// Google protobufs, since they can be very large, and have no value in +// modeling. +static bool isExcludedRecordType(const RecordType *Ty) { + const auto *CD = dyn_cast(Ty->getDecl()); + if (CD == nullptr || !CD->hasDefinition() || CD->getNumBases() != 1) + return false; + + QualType BQ = CD->bases_begin()->getType(); + if (BQ.isNull()) + return false; + + const RecordType *BaseTy = BQ->getAs(); + if (BaseTy == nullptr) + return false; + + const RecordDecl *RD = BaseTy->getDecl(); + if (RD->getIdentifier() == nullptr || RD->getName() != "Message") + return false; + + const auto *ND = dyn_cast(RD->getDeclContext()); + if (ND == nullptr || ND->getIdentifier() == nullptr) + return false; + if (ND->getName() == "proto2") + return true; + + // Check for `::google::protobuf`: + if (ND->getName() != "protobuf") + return false; + + ND = dyn_cast(ND->getDeclContext()); + return ND != nullptr && ND->getParent()->isTranslationUnit() && + ND->getIdentifier() != nullptr && ND->getName() == "google"; +} + Value *Environment::createValueUnlessSelfReferential( QualType Type, llvm::DenseSet &Visited, int Depth, int &CreatedValuesCount) { @@ -549,7 +584,8 @@ return &takeOwnership(std::make_unique(PointeeLoc)); } - if (Type->isStructureOrClassType()) { + if (Type->isStructureOrClassType() && + !isExcludedRecordType(Type->getAs())) { CreatedValuesCount++; // FIXME: Initialize only fields that are accessed in the context that is // being analyzed. diff --git a/clang/unittests/Analysis/FlowSensitive/DataflowEnvironmentTest.cpp b/clang/unittests/Analysis/FlowSensitive/DataflowEnvironmentTest.cpp --- a/clang/unittests/Analysis/FlowSensitive/DataflowEnvironmentTest.cpp +++ b/clang/unittests/Analysis/FlowSensitive/DataflowEnvironmentTest.cpp @@ -20,8 +20,8 @@ using namespace clang; using namespace dataflow; -using ::testing::ElementsAre; -using ::testing::Pair; +using ::testing::IsNull; +using ::testing::NotNull; class EnvironmentTest : public ::testing::Test { DataflowAnalysisContext Context; @@ -98,4 +98,70 @@ EXPECT_NE(PV, nullptr); } +TEST_F(EnvironmentTest, ExcludeProtobufTypes) { + using namespace ast_matchers; + + std::string Code = R"cc( + namespace proto2 { + struct Message {}; + } + + namespace google { + namespace protobuf { + struct Message {}; + } + } + + namespace other { + namespace google { + namespace protobuf { + struct Message {}; + } + } + } + + struct Foo : public proto2::Message { + bool Field; + }; + + struct Bar : public google::protobuf::Message { + bool Field; + }; + + // Not a protobuf, but looks like it. Verify that it is *not* excluded. + struct Zab : public other::google::protobuf::Message { + bool Field; + }; + + void target() { + Foo F; + Bar B; + Zab Z; + (void)0; + /*[[check]]*/ + } + )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(varDecl(eachOf(varDecl(hasName("F")).bind("varF"), + varDecl(hasName("B")).bind("varB"), + varDecl(hasName("Z")).bind("varZ"))), + Context); + const auto *F = selectFirst("varF", Results); + ASSERT_TRUE(F != nullptr); + const auto *B = selectFirst("varB", Results); + ASSERT_TRUE(B != nullptr); + const auto *Z = selectFirst("varZ", Results); + ASSERT_TRUE(Z != nullptr); + + EXPECT_THAT(Env.createValue(F->getType()), IsNull()); + EXPECT_THAT(Env.createValue(B->getType()), IsNull()); + EXPECT_THAT(Env.createValue(Z->getType()), NotNull()); +} + } // namespace