================ @@ -0,0 +1,228 @@ +//===- VirtualMethodFamilyExtractorTest.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 "VirtualMethodFamilyTestSupport.h" +#include "clang/ScalableStaticAnalysis/Analyses/VirtualMethodFamily/VirtualMethodFamily.h" +#include "clang/ScalableStaticAnalysis/Core/Model/EntityId.h" +#include "clang/ScalableStaticAnalysis/Core/TUSummary/ExtractorRegistry.h" +#include "gtest/gtest.h" + +#include <set> + +using namespace clang; +using namespace ssaf; + +namespace { + +using VirtualMethodFamilyExtractorTest = VirtualMethodFamilyTestBase; + +TEST_F(VirtualMethodFamilyExtractorTest, Registers) { + EXPECT_TRUE(isTUSummaryExtractorRegistered(VirtualMethodSummary::Name)); +} + +using VirtualMethodFamilyExtractorBasicFieldPopulationTest = + VirtualMethodFamilyExtractorTest; + +TEST_F(VirtualMethodFamilyExtractorBasicFieldPopulationTest, BaseVirtual) { + ASSERT_TRUE(runVirtualMethodExtractor(R"cpp( + class Base { + public: + virtual void foo(int *p); + }; + )cpp")); + + const auto *S = getMethodSummary(AST.fn("Base::foo")); + ASSERT_TRUE(S); + EXPECT_TRUE(S->ReturnEntity.has_value()); + EXPECT_EQ(S->ParamEntities.size(), 1u); + // A root virtual method overrides nothing. + EXPECT_TRUE(S->OverriddenMethods.empty()); +} + +TEST_F(VirtualMethodFamilyExtractorBasicFieldPopulationTest, PureVirtual) { + ASSERT_TRUE(runVirtualMethodExtractor(R"cpp( + class Interface { + public: + virtual void foo(int *p) = 0; + }; + )cpp")); + + // A pure-virtual method is still virtual, so a summary is produced for it. + ASSERT_TRUE(getMethodSummary(AST.fn("Interface::foo"))); +} + +TEST_F(VirtualMethodFamilyExtractorBasicFieldPopulationTest, + NonVirtualMethodSkipped) { + ASSERT_TRUE(runVirtualMethodExtractor(R"cpp( + class C { + public: + virtual void v(); + void nv(); + }; + )cpp")); + + // Only the virtual method has a summary; non-virtual is skipped. + EXPECT_EQ(methodSummaryCount(), 1u); + EXPECT_TRUE(getMethodSummary(AST.fn("C::v"))); +} + +TEST_F(VirtualMethodFamilyExtractorBasicFieldPopulationTest, + OverrideWithoutVirtualKeywordExtracted) { + ASSERT_TRUE(runVirtualMethodExtractor(R"cpp( + class Base { + public: + virtual void foo(int *p); + }; + class Derived : public Base { + public: + void foo(int *p) override; + }; + )cpp")); + + EXPECT_EQ(methodSummaryCount(), 2u); + EXPECT_TRUE(getMethodSummary(AST.fn("Base::foo"))); + EXPECT_TRUE(getMethodSummary(AST.fn("Derived::foo"))); +} + +using VirtualMethodFamilyExtractorOverriddenMethodTest = + VirtualMethodFamilyExtractorTest; + +TEST_F(VirtualMethodFamilyExtractorOverriddenMethodTest, + EdgeBaseDerivedOverride) { + ASSERT_TRUE(runVirtualMethodExtractor(R"cpp( + struct Base { + virtual void f(int *p); + }; + struct Derived : Base { + void f(int *p) override; + }; + )cpp")); + const auto *D = getMethodSummary(AST.fn("Derived::f")); + const auto *B = getMethodSummary(AST.fn("Base::f")); + ASSERT_TRUE(D); + ASSERT_TRUE(B); + auto BId = entityIdOf(AST.fn("Base::f")); + ASSERT_TRUE(BId.has_value()); + ASSERT_EQ(D->OverriddenMethods.size(), 1u); + EXPECT_EQ(D->OverriddenMethods[0], *BId); + EXPECT_TRUE(B->OverriddenMethods.empty()); +} + +TEST_F(VirtualMethodFamilyExtractorOverriddenMethodTest, + EdgePureVirtualReabstractsOverride) { + // Tricky: a pure-virtual method that OVERRIDES a concrete virtual. Its edge + // set must be non-empty despite being pure. + ASSERT_TRUE(runVirtualMethodExtractor(R"cpp( + struct A { + virtual void f(int *p); + }; + struct B : A { + void f(int *p) = 0; + }; + )cpp")); + const auto *Bf = getMethodSummary(AST.fn("B::f")); + ASSERT_TRUE(Bf); + auto Af = entityIdOf(AST.fn("A::f")); + ASSERT_TRUE(Af.has_value()); + ASSERT_EQ(Bf->OverriddenMethods.size(), 1u); + EXPECT_EQ(Bf->OverriddenMethods[0], *Af); +} + +TEST_F(VirtualMethodFamilyExtractorOverriddenMethodTest, + EdgeMultipleInheritanceTwoEdges) { + // Tricky: one override occupies two independent base slots. + ASSERT_TRUE(runVirtualMethodExtractor(R"cpp( + struct A { + virtual void f(int *p); + }; + struct B { + virtual void f(int *p); + }; + struct D : A, B { + void f(int *p) override; + }; + )cpp")); + const auto *Df = getMethodSummary(AST.fn("D::f")); + ASSERT_TRUE(Df); + auto Af = entityIdOf(AST.fn("A::f")); + auto Bf = entityIdOf(AST.fn("B::f")); + ASSERT_TRUE(Af.has_value() && Bf.has_value()); + ASSERT_EQ(Df->OverriddenMethods.size(), 2u); + std::set<EntityId> Edges(Df->OverriddenMethods.begin(), + Df->OverriddenMethods.end()); + EXPECT_EQ(Edges.count(*Af), 1u); + EXPECT_EQ(Edges.count(*Bf), 1u); +} + +TEST_F(VirtualMethodFamilyExtractorOverriddenMethodTest, + EdgeOverrideLinksMatchingOverloadOnly) { + // Tricky: overloads must not be conflated. B::f(int*) overrides only the + // f(int*) base overload, never f(char*). + ASSERT_TRUE(runVirtualMethodExtractor(R"cpp( + struct A { + virtual void f(int *p); + virtual void f(char *p); + }; + struct B : A { + void f(int *p) override; + }; + )cpp")); + const auto *Bf = getMethodSummary(AST.fn("B::f")); + ASSERT_TRUE(Bf); + auto AfInt = entityIdOf(AST.fn("A::f(int *)")); + auto AfChar = entityIdOf(AST.fn("A::f(char *)")); + ASSERT_TRUE(AfInt.has_value() && AfChar.has_value()); + ASSERT_EQ(Bf->OverriddenMethods.size(), 1u); + EXPECT_EQ(Bf->OverriddenMethods[0], *AfInt); + EXPECT_NE(Bf->OverriddenMethods[0], *AfChar); +} + +TEST_F(VirtualMethodFamilyExtractorOverriddenMethodTest, + EdgeCovariantReturnOverride) { + ASSERT_TRUE(runVirtualMethodExtractor(R"cpp( + struct Base { + virtual Base *clone(); + }; + struct Deriv : Base { + Deriv *clone() override; + }; + )cpp")); + const auto *Dc = getMethodSummary(AST.fn("Deriv::clone")); + ASSERT_TRUE(Dc); + auto Bc = entityIdOf(AST.fn("Base::clone")); + ASSERT_TRUE(Bc.has_value()); + ASSERT_EQ(Dc->OverriddenMethods.size(), 1u); + EXPECT_EQ(Dc->OverriddenMethods[0], *Bc); + EXPECT_TRUE(Dc->ReturnEntity.has_value()); +} + +TEST_F(VirtualMethodFamilyExtractorOverriddenMethodTest, + EdgeDependentBaseTemplatePatternNoCrash) { + // Tricky: the primary template pattern has a dependent base; overridden_ + // methods is unresolved there. Must not crash; the instantiation carries the + // edge. ---------------- ziqingluo-90 wrote:
any plan for this 🤔? https://github.com/llvm/llvm-project/pull/213316 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
