https://github.com/steakhal updated https://github.com/llvm/llvm-project/pull/213318
From afbd1b935e0bba8578f53bd918e23f21b1f7fcfb Mon Sep 17 00:00:00 2001 From: Balazs Benics <[email protected]> Date: Fri, 31 Jul 2026 16:19:02 +0100 Subject: [PATCH] [SSAF] Serialize virtual method summaries and families MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per-TU summaries and whole-program results cross process boundaries, and the JSON layer refuses to write a summary kind it has no format for. Register both sides so --ssaf-extract-summaries=VirtualMethod becomes usable and the family result survives a round trip. Deserialization tolerates a missing override list, since a root virtual method legitimately has none. §3 of rdar://179151603 --- .../VirtualMethodFamily/VirtualMethodFamily.h | 5 + .../BuiltinAnchorSources.def | 1 + .../Analyses/CMakeLists.txt | 1 + .../VirtualMethodFamilyAnalysis.cpp | 22 + .../VirtualMethodFamilyFormat.cpp | 186 +++++++++ .../ScalableStaticAnalysis/CMakeLists.txt | 1 + .../VirtualMethodFamilyFormatTest.cpp | 379 ++++++++++++++++++ 7 files changed, 595 insertions(+) create mode 100644 clang/lib/ScalableStaticAnalysis/Analyses/VirtualMethodFamily/VirtualMethodFamilyFormat.cpp create mode 100644 clang/unittests/ScalableStaticAnalysis/Serialization/JSONFormatTest/VirtualMethodFamilyFormatTest.cpp diff --git a/clang/include/clang/ScalableStaticAnalysis/Analyses/VirtualMethodFamily/VirtualMethodFamily.h b/clang/include/clang/ScalableStaticAnalysis/Analyses/VirtualMethodFamily/VirtualMethodFamily.h index ce1978d82b6a7..0f891abf362f5 100644 --- a/clang/include/clang/ScalableStaticAnalysis/Analyses/VirtualMethodFamily/VirtualMethodFamily.h +++ b/clang/include/clang/ScalableStaticAnalysis/Analyses/VirtualMethodFamily/VirtualMethodFamily.h @@ -50,6 +50,11 @@ struct VirtualMethodSummary final : public EntitySummary { } }; +/// Prints \p S as +/// "VirtualMethodSummary { params=[...], return=..., overridden=[...] }". +llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, + const VirtualMethodSummary &S); + struct VirtualMethodFamilyAnalysisResult final : AnalysisResult { static AnalysisName analysisName() { return AnalysisName("VirtualMethodFamilyAnalysisResult"); diff --git a/clang/include/clang/ScalableStaticAnalysis/BuiltinAnchorSources.def b/clang/include/clang/ScalableStaticAnalysis/BuiltinAnchorSources.def index 138dceb56dfc4..bfb13a957e9be 100644 --- a/clang/include/clang/ScalableStaticAnalysis/BuiltinAnchorSources.def +++ b/clang/include/clang/ScalableStaticAnalysis/BuiltinAnchorSources.def @@ -33,5 +33,6 @@ ANCHOR(UnsafeBufferUsageExtractorAnchorSource) ANCHOR(UnsafeBufferUsageJSONFormatAnchorSource) ANCHOR(VirtualMethodEntityExtractorAnchorSource) ANCHOR(VirtualMethodFamilyAnalysisAnchorSource) +ANCHOR(VirtualMethodFamilyJSONFormatAnchorSource) #undef ANCHOR diff --git a/clang/lib/ScalableStaticAnalysis/Analyses/CMakeLists.txt b/clang/lib/ScalableStaticAnalysis/Analyses/CMakeLists.txt index e83a84739d6a9..82ea646249fc0 100644 --- a/clang/lib/ScalableStaticAnalysis/Analyses/CMakeLists.txt +++ b/clang/lib/ScalableStaticAnalysis/Analyses/CMakeLists.txt @@ -22,6 +22,7 @@ add_clang_library(clangScalableStaticAnalysisAnalyses UnsafeBufferUsage/UnsafeBufferUsageFormat.cpp VirtualMethodFamily/VirtualMethodEntityExtractor.cpp VirtualMethodFamily/VirtualMethodFamilyAnalysis.cpp + VirtualMethodFamily/VirtualMethodFamilyFormat.cpp LINK_LIBS clangAST diff --git a/clang/lib/ScalableStaticAnalysis/Analyses/VirtualMethodFamily/VirtualMethodFamilyAnalysis.cpp b/clang/lib/ScalableStaticAnalysis/Analyses/VirtualMethodFamily/VirtualMethodFamilyAnalysis.cpp index 1d890da33a3bf..d9851ba274ffd 100644 --- a/clang/lib/ScalableStaticAnalysis/Analyses/VirtualMethodFamily/VirtualMethodFamilyAnalysis.cpp +++ b/clang/lib/ScalableStaticAnalysis/Analyses/VirtualMethodFamily/VirtualMethodFamilyAnalysis.cpp @@ -10,6 +10,7 @@ #include "clang/ScalableStaticAnalysis/Core/Model/EntityId.h" #include "clang/ScalableStaticAnalysis/Core/WholeProgramAnalysis/AnalysisRegistry.h" #include "clang/ScalableStaticAnalysis/Core/WholeProgramAnalysis/SummaryAnalysis.h" +#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" @@ -124,8 +125,29 @@ static AnalysisRegistry::Add<VirtualMethodFamilyAnalysis> // Printing //===----------------------------------------------------------------------===// +static void printEntityIds(llvm::raw_ostream &OS, + llvm::ArrayRef<EntityId> Ids) { + OS << "["; + llvm::interleaveComma(Ids, OS, [&](EntityId Id) { OS << Id; }); + OS << "]"; +} + namespace clang::ssaf { +llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, + const VirtualMethodSummary &S) { + OS << "VirtualMethodSummary { params="; + printEntityIds(OS, S.ParamEntities); + OS << ", return="; + if (S.ReturnEntity) + OS << *S.ReturnEntity; + else + OS << "<none>"; + OS << ", overridden="; + printEntityIds(OS, S.OverriddenMethods); + return OS << " }"; +} + llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const VirtualMethodFamilyAnalysisResult &R) { OS << "VirtualMethodFamilyAnalysisResult with " << R.RetAndParamData.size() diff --git a/clang/lib/ScalableStaticAnalysis/Analyses/VirtualMethodFamily/VirtualMethodFamilyFormat.cpp b/clang/lib/ScalableStaticAnalysis/Analyses/VirtualMethodFamily/VirtualMethodFamilyFormat.cpp new file mode 100644 index 0000000000000..cb63ebc73b447 --- /dev/null +++ b/clang/lib/ScalableStaticAnalysis/Analyses/VirtualMethodFamily/VirtualMethodFamilyFormat.cpp @@ -0,0 +1,186 @@ +//===- VirtualMethodFamilyFormat.cpp --------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "SSAFAnalysesCommon.h" +#include "clang/ScalableStaticAnalysis/Analyses/VirtualMethodFamily/VirtualMethodFamily.h" +#include "clang/ScalableStaticAnalysis/Core/Serialization/JSONFormat.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/JSON.h" +#include "llvm/Support/Registry.h" +#include <memory> +#include <utility> +#include <vector> + +using namespace clang; +using namespace ssaf; + +using llvm::Expected; +using Object = llvm::json::Object; +using Array = llvm::json::Array; + +namespace { +constexpr llvm::StringLiteral KeyParamEntities = "param_entities"; +constexpr llvm::StringLiteral KeyReturnEntity = "return_entity"; +constexpr llvm::StringLiteral KeyOverriddenMethods = "overridden_methods"; + +constexpr llvm::StringLiteral KeyRetAndParamFamilyIds = "families"; +constexpr llvm::StringLiteral KeyParamId = "pid"; +constexpr llvm::StringLiteral KeyFamilyId = "fid"; +} // namespace + +static Array entityIdVectorToJSON(const std::vector<EntityId> &Ids, + JSONFormat::EntityIdToJSONFn IdToJSON) { + Array Result; + Result.reserve(Ids.size()); + for (EntityId Id : Ids) + Result.push_back(IdToJSON(Id)); + return Result; +} + +static Expected<std::vector<EntityId>> +entityIdVectorFromJSON(const Array &Arr, + JSONFormat::EntityIdFromJSONFn IdFromJSON) { + std::vector<EntityId> Result; + Result.reserve(Arr.size()); + for (const auto &V : Arr) { + const Object *Obj = V.getAsObject(); + if (!Obj) + return makeSawButExpectedError(V, "an object representing EntityId"); + auto Id = IdFromJSON(*Obj); + if (!Id) + return Id.takeError(); + Result.push_back(*Id); + } + return Result; +} + +//===----------------------------------------------------------------------===// +// VirtualMethodSummary <-> JSON +//===----------------------------------------------------------------------===// + +static Object +serializeVirtualMethodSummary(const EntitySummary &ES, + JSONFormat::EntityIdToJSONFn IdToJSON) { + const auto &S = static_cast<const VirtualMethodSummary &>(ES); + Object Out; + Out[KeyParamEntities] = entityIdVectorToJSON(S.ParamEntities, IdToJSON); + if (S.ReturnEntity.has_value()) + Out[KeyReturnEntity] = IdToJSON(*S.ReturnEntity); + Out[KeyOverriddenMethods] = + entityIdVectorToJSON(S.OverriddenMethods, IdToJSON); + return Out; +} + +static Expected<std::unique_ptr<EntitySummary>> +deserializeVirtualMethodSummary(const Object &Obj, EntityIdTable &, + JSONFormat::EntityIdFromJSONFn IdFromJSON) { + auto Result = std::make_unique<VirtualMethodSummary>(); + + const Array *ParamArr = Obj.getArray(KeyParamEntities); + if (!ParamArr) + return makeSawButExpectedError(Obj, "an object with an array field '%s'", + KeyParamEntities.data()); + auto Params = entityIdVectorFromJSON(*ParamArr, IdFromJSON); + if (!Params) + return Params.takeError(); + Result->ParamEntities = std::move(*Params); + + if (const Object *RE = Obj.getObject(KeyReturnEntity)) { + auto Id = IdFromJSON(*RE); + if (!Id) + return Id.takeError(); + Result->ReturnEntity = *Id; + } + + // Tolerant: absent key means no override edges (a root virtual method). + if (const Array *OMArr = Obj.getArray(KeyOverriddenMethods)) { + auto OM = entityIdVectorFromJSON(*OMArr, IdFromJSON); + if (!OM) + return OM.takeError(); + Result->OverriddenMethods = std::move(*OM); + } + + return std::move(Result); +} + +//===----------------------------------------------------------------------===// +// VirtualMethodFamilyAnalysisResult <-> JSON +//===----------------------------------------------------------------------===// + +static Object serializeVirtualMethodFamilyAnalysisResult( + const VirtualMethodFamilyAnalysisResult &R, + JSONFormat::EntityIdToJSONFn IdToJSON) { + Array FamilyDataArr; + for (const auto &[ParamId, FamilyId] : R.RetAndParamData) { + Object Item; + Item[KeyParamId] = IdToJSON(ParamId); + Item[KeyFamilyId] = IdToJSON(FamilyId); + FamilyDataArr.push_back(std::move(Item)); + } + + Object Out; + Out[KeyRetAndParamFamilyIds] = std::move(FamilyDataArr); + return Out; +} + +static Expected<std::unique_ptr<AnalysisResult>> +deserializeVirtualMethodFamilyAnalysisResult( + const Object &Obj, JSONFormat::EntityIdFromJSONFn IdFromJSON) { + const Array *FamilyDataArr = Obj.getArray(KeyRetAndParamFamilyIds); + if (!FamilyDataArr) + return makeSawButExpectedError(Obj, "an object with an array field '%s'", + KeyRetAndParamFamilyIds.data()); + + auto Result = std::make_unique<VirtualMethodFamilyAnalysisResult>(); + + for (const auto &V : *FamilyDataArr) { + const Object *Item = V.getAsObject(); + if (!Item) + return makeSawButExpectedError(V, "an object {pid, fid}"); + const Object *ParamObj = Item->getObject(KeyParamId); + const Object *FamilyObj = Item->getObject(KeyFamilyId); + if (!ParamObj || !FamilyObj) + return makeSawButExpectedError(*Item, + "an object with fields {'%s', '%s'}", + KeyParamId.data(), KeyFamilyId.data()); + auto ParamId = IdFromJSON(*ParamObj); + if (!ParamId) + return ParamId.takeError(); + auto FamilyId = IdFromJSON(*FamilyObj); + if (!FamilyId) + return FamilyId.takeError(); + Result->RetAndParamData.insert({*ParamId, *FamilyId}); + } + return std::move(Result); +} + +namespace { + +struct VirtualMethodSummaryJSONFormatInfo final : JSONFormat::FormatInfo { + VirtualMethodSummaryJSONFormatInfo() + : JSONFormat::FormatInfo(VirtualMethodSummary::summaryName(), + serializeVirtualMethodSummary, + deserializeVirtualMethodSummary) {} +}; +} // namespace + +static llvm::Registry<JSONFormat::FormatInfo>::Add< + VirtualMethodSummaryJSONFormatInfo> + RegisterJSONFormat(VirtualMethodSummary::Name, + "JSON Format info for VirtualMethodSummary"); + +static JSONFormat::AnalysisResultRegistry::Add< + VirtualMethodFamilyAnalysisResult> + RegisterResultJSONFormat(serializeVirtualMethodFamilyAnalysisResult, + deserializeVirtualMethodFamilyAnalysisResult); + +namespace clang::ssaf { +// NOLINTNEXTLINE(misc-use-internal-linkage) +volatile int VirtualMethodFamilyJSONFormatAnchorSource = 0; +} // namespace clang::ssaf diff --git a/clang/unittests/ScalableStaticAnalysis/CMakeLists.txt b/clang/unittests/ScalableStaticAnalysis/CMakeLists.txt index 6223528d29776..a8195cedefd14 100644 --- a/clang/unittests/ScalableStaticAnalysis/CMakeLists.txt +++ b/clang/unittests/ScalableStaticAnalysis/CMakeLists.txt @@ -30,6 +30,7 @@ add_distinct_clang_unittest(ClangScalableAnalysisTests Serialization/JSONFormatTest/LUSummaryTest.cpp Serialization/JSONFormatTest/SharedLexicalRepresentationFormatTest.cpp Serialization/JSONFormatTest/TUSummaryTest.cpp + Serialization/JSONFormatTest/VirtualMethodFamilyFormatTest.cpp SourceTransformation/CppBoundedBuffersTest.cpp SourceTransformation/EmitterTest.cpp SourceTransformation/RegistryTest.cpp diff --git a/clang/unittests/ScalableStaticAnalysis/Serialization/JSONFormatTest/VirtualMethodFamilyFormatTest.cpp b/clang/unittests/ScalableStaticAnalysis/Serialization/JSONFormatTest/VirtualMethodFamilyFormatTest.cpp new file mode 100644 index 0000000000000..3c7432dcb5706 --- /dev/null +++ b/clang/unittests/ScalableStaticAnalysis/Serialization/JSONFormatTest/VirtualMethodFamilyFormatTest.cpp @@ -0,0 +1,379 @@ +//===- VirtualMethodFamilyFormatTest.cpp ----------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "JSONFormatTest.h" +#include "ParsedAST.h" +#include "clang/AST/Decl.h" +#include "clang/ScalableStaticAnalysis/Analyses/VirtualMethodFamily/VirtualMethodFamily.h" +#include "clang/ScalableStaticAnalysis/Core/ASTEntityMapping.h" +#include "clang/ScalableStaticAnalysis/Core/EntityLinker/LUSummary.h" +#include "clang/ScalableStaticAnalysis/Core/Model/BuildNamespace.h" +#include "clang/ScalableStaticAnalysis/Core/Model/EntityId.h" +#include "clang/ScalableStaticAnalysis/Core/Model/EntityIdTable.h" +#include "clang/ScalableStaticAnalysis/Core/Model/EntityLinkage.h" +#include "clang/ScalableStaticAnalysis/Core/Model/EntityName.h" +#include "clang/ScalableStaticAnalysis/Core/Serialization/JSONFormat.h" +#include "clang/ScalableStaticAnalysis/Core/WholeProgramAnalysis/WPASuite.h" +#include "llvm/TargetParser/Triple.h" +#include "llvm/Testing/Support/Error.h" +#include "gtest/gtest.h" +#include <memory> +#include <optional> +#include <ostream> +#include <string> +#include <utility> +#include <vector> + +using namespace clang; +using namespace ssaf; + +namespace clang::ssaf { +// NOLINTNEXTLINE(misc-use-internal-linkage) +void PrintTo(const VirtualMethodSummary &S, std::ostream *OS) { + std::string Str; + llvm::raw_string_ostream(Str) << S; + *OS << Str; +} +} // namespace clang::ssaf + +namespace { + +class VirtualMethodFamilyFormatTest : public JSONFormatTest { +protected: + static constexpr EntityLinkage ExternalLinkage{EntityLinkageType::External}; + + ParsedAST AST; + + std::unique_ptr<LUSummary> makeLUSummary() { + return std::make_unique<LUSummary>(llvm::Triple("arm64-apple-macosx"), + linkUnitNamespace()); + } + + /// Create an LU-scope EntityId for \p ND and mark it externally visible. + EntityId addEntity(LUSummary &LU, const NamedDecl *ND) { + return declare(LU, addEntity(getIdTable(LU), ND)); + } + + /// Create an LU-scope EntityId for the return slot of \p FD. + EntityId addReturnEntity(LUSummary &LU, const FunctionDecl *FD) { + return declare(LU, addReturnEntity(getIdTable(LU), FD)); + } + + /// Create an EntityId for \p ND directly in \p Ids. Used for the WPASuite + /// tests, which carry a bare EntityIdTable rather than an LUSummary. + EntityId addEntity(EntityIdTable &Ids, const NamedDecl *ND) { + return getOrCreateId(Ids, ND ? getEntityName(ND) : std::nullopt, ND); + } + + /// Create an EntityId for the return slot of \p FD directly in \p Ids. + EntityId addReturnEntity(EntityIdTable &Ids, const FunctionDecl *FD) { + return getOrCreateId(Ids, FD ? getEntityNameForReturn(FD) : std::nullopt, + FD); + } + + /// Insert a single entity summary (by SummaryName, EntityId). + template <typename SummaryT> + void insertSummary(LUSummary &LU, EntityId Id, SummaryT Sum) { + getData(LU)[SummaryT::summaryName()][Id] = + std::make_unique<SummaryT>(std::move(Sum)); + } + + /// Looks up the \c SummaryT stored for \p Id, or nullptr (with a failure + /// recorded) if there is none. + template <typename SummaryT> + const SummaryT *getSummary(const LUSummary &LU, EntityId Id) { + const auto &Data = getData(LU); + auto SumIt = Data.find(SummaryT::summaryName()); + if (SumIt == Data.end()) { + ADD_FAILURE() << "no summaries of kind " << SummaryT::summaryName(); + return nullptr; + } + auto EIt = SumIt->second.find(Id); + if (EIt == SumIt->second.end()) { + ADD_FAILURE() << "no " << SummaryT::summaryName() << " for " << Id; + return nullptr; + } + return static_cast<const SummaryT *>(EIt->second.get()); + } + + /// Number of \c SummaryT entries in \p LU. + template <typename SummaryT> size_t summaryCount(const LUSummary &LU) { + const auto &Data = getData(LU); + auto It = Data.find(SummaryT::summaryName()); + return It == Data.end() ? 0 : It->second.size(); + } + + /// Round-trips an LUSummary through JSON write -> read and returns the + /// resulting LUSummary on success. + llvm::Expected<LUSummary> roundTripLU(const LUSummary &LU) { + PathString InPath = makePath("vmf-lu.json"); + if (auto Err = JSONFormat().writeLUSummary(LU, InPath)) + return std::move(Err); + return JSONFormat().readLUSummary(InPath); + } + + /// Build a WPASuite with one entry of every given AnalysisResult type. The + /// test fixture is a friend of WPASuite so it can construct one and reach + /// into private members. + WPASuite makeSuite( + EntityIdTable IdTable, + std::vector<std::pair<AnalysisName, std::unique_ptr<AnalysisResult>>> + Entries) { + WPASuite Suite = makeWPASuite(); + getIdTable(Suite) = std::move(IdTable); + for (auto &[Name, R] : Entries) + getData(Suite).emplace(Name, std::move(R)); + return Suite; + } + + /// Round-trips a WPASuite through JSON write -> read. + llvm::Expected<WPASuite> roundTripSuite(const WPASuite &Suite) { + PathString Path = makePath("vmf-suite.json"); + if (auto Err = JSONFormat().writeWPASuite(Suite, Path)) + return std::move(Err); + return JSONFormat().readWPASuite(Path); + } + +private: + static NestedBuildNamespace linkUnitNamespace() { + constexpr auto LinkUnitKind = BuildNamespaceKind::LinkUnit; + return NestedBuildNamespace{BuildNamespace(LinkUnitKind, "TestLU")}; + } + + /// Entity names coming out of ASTEntityMapping have no namespace; qualify + /// them with the link unit the way EntityLinker does, so the ids in these + /// tests look like the ones a real LUSummary carries. + EntityId getOrCreateId(EntityIdTable &Ids, std::optional<EntityName> Name, + const NamedDecl *ND) { + if (Name) + return Ids.getId(Name->makeQualified(linkUnitNamespace())); + ADD_FAILURE() << "no entity name for " + << (ND ? ND->getQualifiedNameAsString() : "<null decl>"); + // EntityId has no default constructor; the failure above already fails + // the test, so any id will do. + return Ids.getId(EntityName("<missing>", "", linkUnitNamespace())); + } + + static EntityId declare(LUSummary &LU, EntityId Id) { + getLinkageTable(LU).insert({Id, ExternalLinkage}); + return Id; + } +}; + +//===----------------------------------------------------------------------===// +// VirtualMethodSummary round-trips +//===----------------------------------------------------------------------===// + +static VirtualMethodSummary +createSummary(llvm::ArrayRef<EntityId> Params, std::optional<EntityId> Return, + llvm::ArrayRef<EntityId> Overridden = {}) { + VirtualMethodSummary S; + S.ParamEntities.assign(Params.begin(), Params.end()); + S.ReturnEntity = Return; + S.OverriddenMethods.assign(Overridden.begin(), Overridden.end()); + return S; +} + +TEST_F(VirtualMethodFamilyFormatTest, VirtualMethodSummaryEmpty) { + ASSERT_TRUE(AST.parse(R"cpp( + struct Foo { + virtual void m(); + }; + )cpp")); + + auto LU = makeLUSummary(); + EntityId M = addEntity(*LU, AST.fn("Foo::m")); + + // ParamEntities and OverriddenMethods intentionally empty. + const VirtualMethodSummary Expected = + createSummary({}, addReturnEntity(*LU, AST.fn("Foo::m"))); + insertSummary(*LU, M, Expected); + + auto Round = roundTripLU(*LU); + ASSERT_THAT_EXPECTED(Round, llvm::Succeeded()); + + EXPECT_EQ(summaryCount<VirtualMethodSummary>(*Round), 1u); + const auto *Out = getSummary<VirtualMethodSummary>(*Round, M); + ASSERT_TRUE(Out); + EXPECT_EQ(*Out, Expected); +} + +TEST_F(VirtualMethodFamilyFormatTest, VirtualMethodSummarySingleParam) { + ASSERT_TRUE(AST.parse(R"cpp( + struct BarBase { + virtual int *foo(int *p); + }; + struct Bar : BarBase { + int *foo(int *p) override; + }; + )cpp")); + + auto LU = makeLUSummary(); + EntityId M = addEntity(*LU, AST.fn("Bar::foo")); + EntityId Overridden = addEntity(*LU, AST.fn("BarBase::foo")); + EntityId P = addEntity(*LU, AST.findParam("Bar::foo", 0)); + EntityId R = addReturnEntity(*LU, AST.fn("Bar::foo")); + + const VirtualMethodSummary Expected = + createSummary({P}, R, /*Overridden=*/{Overridden}); + insertSummary(*LU, M, Expected); + + auto Round = roundTripLU(*LU); + ASSERT_THAT_EXPECTED(Round, llvm::Succeeded()); + + EXPECT_EQ(summaryCount<VirtualMethodSummary>(*Round), 1u); + const auto *Out = getSummary<VirtualMethodSummary>(*Round, M); + ASSERT_TRUE(Out); + EXPECT_EQ(*Out, Expected); +} + +TEST_F(VirtualMethodFamilyFormatTest, VirtualMethodSummaryMultiParam) { + ASSERT_TRUE(AST.parse(R"cpp( + struct Base { + virtual char *foo(int *p1, char *p2); + }; + struct Derived : Base { + char *foo(int *p1, char *p2) override; + }; + )cpp")); + + auto LU = makeLUSummary(); + EntityId M1 = addEntity(*LU, AST.fn("Base::foo")); + EntityId M2 = addEntity(*LU, AST.fn("Derived::foo")); + EntityId P1a = addEntity(*LU, AST.findParam("Base::foo", 0)); + EntityId P1b = addEntity(*LU, AST.findParam("Base::foo", 1)); + EntityId P2a = addEntity(*LU, AST.findParam("Derived::foo", 0)); + EntityId P2b = addEntity(*LU, AST.findParam("Derived::foo", 1)); + EntityId R1 = addReturnEntity(*LU, AST.fn("Base::foo")); + EntityId R2 = addReturnEntity(*LU, AST.fn("Derived::foo")); + + const VirtualMethodSummary E1 = createSummary({P1a, P1b}, R1); + // Derived::foo overrides Base::foo. + const VirtualMethodSummary E2 = + createSummary({P2a, P2b}, R2, /*Overridden=*/{M1}); + insertSummary(*LU, M1, E1); + insertSummary(*LU, M2, E2); + + auto Round = roundTripLU(*LU); + ASSERT_THAT_EXPECTED(Round, llvm::Succeeded()); + + EXPECT_EQ(summaryCount<VirtualMethodSummary>(*Round), 2u); + const auto *Out1 = getSummary<VirtualMethodSummary>(*Round, M1); + ASSERT_TRUE(Out1); + EXPECT_EQ(*Out1, E1); + const auto *Out2 = getSummary<VirtualMethodSummary>(*Round, M2); + ASSERT_TRUE(Out2); + EXPECT_EQ(*Out2, E2); +} + +//===----------------------------------------------------------------------===// +// VirtualMethodFamilyAnalysisResult round-trips (via WPASuite). +//===----------------------------------------------------------------------===// + +class VirtualMethodFamilyAnalysisRoundTrip + : public VirtualMethodFamilyFormatTest { +protected: + EntityIdTable IdTable; +}; + +TEST_F(VirtualMethodFamilyAnalysisRoundTrip, EmptyResultRoundTrips) { + auto R = std::make_unique<VirtualMethodFamilyAnalysisResult>(); + std::vector<std::pair<AnalysisName, std::unique_ptr<AnalysisResult>>> Entries; + Entries.emplace_back(VirtualMethodFamilyAnalysisResult::analysisName(), + std::move(R)); + WPASuite Suite = makeSuite(std::move(IdTable), std::move(Entries)); + + auto Round = roundTripSuite(Suite); + ASSERT_THAT_EXPECTED(Round, llvm::Succeeded()); + + auto Got = Round->get<VirtualMethodFamilyAnalysisResult>(); + ASSERT_THAT_EXPECTED(Got, llvm::Succeeded()); + EXPECT_TRUE(Got->RetAndParamData.empty()); +} + +static VirtualMethodFamilyAnalysisResult +createResult(llvm::ArrayRef<std::pair<EntityId, EntityId>> Entries) { + VirtualMethodFamilyAnalysisResult Res; + Res.RetAndParamData.insert(Entries.begin(), Entries.end()); + return Res; +} + +TEST_F(VirtualMethodFamilyAnalysisRoundTrip, SingleFamilyRoundTrips) { + ASSERT_TRUE(AST.parse(R"cpp( + struct Base { + virtual void foo(int *p); + }; + struct Derived : Base { + void foo(int *p) override; + }; + )cpp")); + + EntityId P1 = addEntity(IdTable, AST.findParam("Base::foo", 0)); + EntityId P2 = addEntity(IdTable, AST.findParam("Derived::foo", 0)); + + const VirtualMethodFamilyAnalysisResult Expected = createResult({ + {P1, /*FamilyId=*/P1}, + {P2, /*FamilyId=*/P1}, + }); + + std::vector<std::pair<AnalysisName, std::unique_ptr<AnalysisResult>>> Entries; + Entries.emplace_back( + VirtualMethodFamilyAnalysisResult::analysisName(), + std::make_unique<VirtualMethodFamilyAnalysisResult>(Expected)); + WPASuite Suite = makeSuite(std::move(IdTable), std::move(Entries)); + + auto Round = roundTripSuite(Suite); + ASSERT_THAT_EXPECTED(Round, llvm::Succeeded()); + + auto Got = Round->get<VirtualMethodFamilyAnalysisResult>(); + ASSERT_THAT_EXPECTED(Got, llvm::Succeeded()); + EXPECT_EQ(*Got, Expected); +} + +TEST_F(VirtualMethodFamilyAnalysisRoundTrip, + MultiFamilyAndReturnSlotRoundTrips) { + ASSERT_TRUE(AST.parse(R"cpp( + struct A { + virtual void m1(int *p); + virtual int *m2(); + }; + struct B : A { + void m1(int *p) override; + int *m2() override; + }; + )cpp")); + + EntityId P1 = addEntity(IdTable, AST.findParam("A::m1", 0)); + EntityId P2 = addEntity(IdTable, AST.findParam("B::m1", 0)); + EntityId R1 = addReturnEntity(IdTable, AST.fn("A::m2")); + EntityId R2 = addReturnEntity(IdTable, AST.fn("B::m2")); + + const VirtualMethodFamilyAnalysisResult Expected = createResult({ + // Family #1: parameter slot + {P1, /*FamilyId=*/P1}, + {P2, /*FamilyId=*/P1}, + // Family #2: return slot + {R1, /*FamilyId=*/R1}, + {R2, /*FamilyId=*/R1}, + }); + + std::vector<std::pair<AnalysisName, std::unique_ptr<AnalysisResult>>> Entries; + Entries.emplace_back( + VirtualMethodFamilyAnalysisResult::analysisName(), + std::make_unique<VirtualMethodFamilyAnalysisResult>(Expected)); + WPASuite Suite = makeSuite(std::move(IdTable), std::move(Entries)); + + auto Round = roundTripSuite(Suite); + ASSERT_THAT_EXPECTED(Round, llvm::Succeeded()); + + auto Got = Round->get<VirtualMethodFamilyAnalysisResult>(); + ASSERT_THAT_EXPECTED(Got, llvm::Succeeded()); + EXPECT_EQ(*Got, Expected); +} + +} // namespace _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
