llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang-ssaf Author: Balázs Benics (steakhal) <details> <summary>Changes</summary> rdar://170258016 --- Patch is 26.44 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/188753.diff 15 Files Affected: - (added) clang/include/clang/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphSummary.h (+51) - (modified) clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h (+7) - (modified) clang/lib/Driver/CMakeLists.txt (+1) - (modified) clang/lib/FrontendTool/CMakeLists.txt (+1) - (modified) clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt (+1) - (added) clang/lib/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphExtractor.cpp (+110) - (modified) clang/tools/clang-ssaf-format/CMakeLists.txt (+1) - (modified) clang/tools/clang-ssaf-linker/CMakeLists.txt (+1) - (added) clang/unittests/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphExtractorTest.cpp (+375) - (modified) clang/unittests/ScalableStaticAnalysisFramework/CMakeLists.txt (+1) - (modified) clang/unittests/ScalableStaticAnalysisFramework/Registries/SummaryExtractorRegistryTest.cpp (+3-5) - (modified) llvm/utils/gn/secondary/clang/lib/Driver/BUILD.gn (+1) - (modified) llvm/utils/gn/secondary/clang/lib/FrontendTool/BUILD.gn (+1) - (modified) llvm/utils/gn/secondary/clang/lib/ScalableStaticAnalysisFramework/Analyses/BUILD.gn (+4-1) - (modified) llvm/utils/gn/secondary/clang/unittests/ScalableStaticAnalysisFramework/BUILD.gn (+2-1) ``````````diff diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphSummary.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphSummary.h new file mode 100644 index 0000000000000..03bac3c6dd8e0 --- /dev/null +++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphSummary.h @@ -0,0 +1,51 @@ +//===- CallGraphSummary.h ---------------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_CALLGRAPH_CALLGRAPHSUMMARY_H +#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_CALLGRAPH_CALLGRAPHSUMMARY_H + +#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h" +#include "clang/ScalableStaticAnalysisFramework/Core/Model/SummaryName.h" +#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/EntitySummary.h" +#include <set> + +namespace clang::ssaf { + +/// Summary of direct call-graph edges for a single function entity. +/// +/// Represents a function definition, and information about its callees. +struct CallGraphSummary final : public EntitySummary { + struct Location { + std::string File; + unsigned Line; + unsigned Column; + }; + + SummaryName getSummaryName() const override { + return SummaryName("CallGraph"); + } + + /// Represents the location of the function. + Location Definition = {}; + + /// The set of direct callees of this function. + std::set<EntityId> DirectCallees; + + /// A human-readable name of the function. + /// This is not guaranteed to be accurate or unique. + std::string PrettyName; + + /// Whether this function contains calls that could not be resolved to a + /// direct callee. + /// E.g. virtual method calls, or calls through function pointers. + bool HasIndirectCalls = false; +}; + +} // namespace clang::ssaf + +#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_CALLGRAPH_CALLGRAPHSUMMARY_H diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h b/clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h index 2f144b92a1a94..707573ce34e46 100644 --- a/clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h +++ b/clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h @@ -20,6 +20,8 @@ #ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_SSAFBUILTINFORCELINKER_H #define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_SSAFBUILTINFORCELINKER_H +// TODO: Move these to the `clang::ssaf` namespace. + // This anchor is used to force the linker to link the JSONFormat registration. extern volatile int SSAFJSONFormatAnchorSource; [[maybe_unused]] static int SSAFJSONFormatAnchorDestination = @@ -30,4 +32,9 @@ extern volatile int SSAFAnalysisRegistryAnchorSource; [[maybe_unused]] static int SSAFAnalysisRegistryAnchorDestination = SSAFAnalysisRegistryAnchorSource; +// This anchor is used to force the linker to link the CallGraphExtractor. +extern volatile int CallGraphExtractorAnchorSource; +[[maybe_unused]] static int CallGraphExtractorAnchorDestination = + CallGraphExtractorAnchorSource; + #endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_SSAFBUILTINFORCELINKER_H diff --git a/clang/lib/Driver/CMakeLists.txt b/clang/lib/Driver/CMakeLists.txt index 8a2ffb303b4cb..92554712fffd8 100644 --- a/clang/lib/Driver/CMakeLists.txt +++ b/clang/lib/Driver/CMakeLists.txt @@ -110,6 +110,7 @@ add_clang_library(clangDriver clangBasic clangDependencyScanning clangFrontend + clangScalableStaticAnalysisFrameworkAnalyses clangScalableStaticAnalysisFrameworkCore clangScalableStaticAnalysisFrameworkFrontend clangSerialization diff --git a/clang/lib/FrontendTool/CMakeLists.txt b/clang/lib/FrontendTool/CMakeLists.txt index a451eb967e904..24623303e6bdb 100644 --- a/clang/lib/FrontendTool/CMakeLists.txt +++ b/clang/lib/FrontendTool/CMakeLists.txt @@ -4,6 +4,7 @@ set(LLVM_LINK_COMPONENTS ) set(link_libs + clangScalableStaticAnalysisFrameworkAnalyses clangScalableStaticAnalysisFrameworkCore clangScalableStaticAnalysisFrameworkFrontend clangBasic diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt b/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt index 34c6dd9b61203..2dcce40f886dd 100644 --- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt +++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt @@ -3,6 +3,7 @@ set(LLVM_LINK_COMPONENTS ) add_clang_library(clangScalableStaticAnalysisFrameworkAnalyses + CallGraph/CallGraphExtractor.cpp UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp LINK_LIBS diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphExtractor.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphExtractor.cpp new file mode 100644 index 0000000000000..09b237ccf8e3d --- /dev/null +++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphExtractor.cpp @@ -0,0 +1,110 @@ +//===- CallGraphExtractor.cpp - Call Graph Summary Extractor --------------===// +// +// 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 "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclObjC.h" +#include "clang/Analysis/AnalysisDeclContext.h" +#include "clang/Analysis/CallGraph.h" +#include "clang/Basic/SourceManager.h" +#include "clang/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphSummary.h" +#include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h" +#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/ExtractorRegistry.h" +#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.h" +#include "llvm/ADT/STLExtras.h" +#include <memory> + +using namespace clang; +using namespace ssaf; + +namespace { +class CallGraphExtractor final : public TUSummaryExtractor { +public: + using TUSummaryExtractor::TUSummaryExtractor; + +private: + void HandleTranslationUnit(ASTContext &Ctx) override; + + void handleCallGraphNode(const ASTContext &Ctx, const CallGraphNode *N); +}; +} // namespace + +void CallGraphExtractor::HandleTranslationUnit(ASTContext &Ctx) { + CallGraph CG; + CG.addToCallGraph( + const_cast<TranslationUnitDecl *>(Ctx.getTranslationUnitDecl())); + + for (const auto &N : llvm::make_second_range(CG)) { + if (N && N->getDecl() && N->getDefinition()) + handleCallGraphNode(Ctx, N.get()); + } +} + +void CallGraphExtractor::handleCallGraphNode(const ASTContext &Ctx, + const CallGraphNode *N) { + const FunctionDecl *Definition = N->getDefinition(); + + // Ignore templates for now. + if (Definition->isTemplated()) + return; + + auto CallerName = getEntityName(Definition); + if (!CallerName) + return; + + auto FnSummary = std::make_unique<CallGraphSummary>(); + + PresumedLoc Loc = + Ctx.getSourceManager().getPresumedLoc(Definition->getLocation()); + FnSummary->Definition.File = Loc.getFilename(); + FnSummary->Definition.Line = Loc.getLine(); + FnSummary->Definition.Column = Loc.getColumn(); + FnSummary->PrettyName = AnalysisDeclContext::getFunctionName(Definition); + + for (const auto &Record : N->callees()) { + const Decl *CalleeDecl = Record.Callee->getDecl(); + if (!CalleeDecl) { + FnSummary->HasIndirectCalls = true; + continue; + } + assert(!CalleeDecl->isTemplated()); + + // Objective-C methods might be replaced at runtime, so they are effectively + // indirect calls. + if (isa<ObjCMethodDecl>(CalleeDecl)) { + FnSummary->HasIndirectCalls = true; + continue; + } + + // Treat virtual functions as indirect calls for now. + if (const auto *MD = dyn_cast_or_null<CXXMethodDecl>(CalleeDecl); + MD && MD->isVirtual()) { + FnSummary->HasIndirectCalls = true; + continue; + } + + auto CalleeName = getEntityName(CalleeDecl); + if (!CalleeName) + continue; + + EntityId CalleeId = SummaryBuilder.addEntity(*CalleeName); + FnSummary->DirectCallees.insert(CalleeId); + } + + EntityId CallerId = SummaryBuilder.addEntity(*CallerName); + SummaryBuilder.addSummary(CallerId, std::move(FnSummary)); +} + +static TUSummaryExtractorRegistry::Add<CallGraphExtractor> + RegisterExtractor("CallGraph", "Extracts static call-graph information"); + +// This anchor is used to force the linker to link in the generated object file +// and thus register the CallGraphExtractor. +// NOLINTNEXTLINE(misc-use-internal-linkage) +volatile int CallGraphExtractorAnchorSource = 0; diff --git a/clang/tools/clang-ssaf-format/CMakeLists.txt b/clang/tools/clang-ssaf-format/CMakeLists.txt index 864fe5bc27aaa..c76756ad4770c 100644 --- a/clang/tools/clang-ssaf-format/CMakeLists.txt +++ b/clang/tools/clang-ssaf-format/CMakeLists.txt @@ -10,6 +10,7 @@ add_clang_tool(clang-ssaf-format clang_target_link_libraries(clang-ssaf-format PRIVATE clangBasic + clangScalableStaticAnalysisFrameworkAnalyses clangScalableStaticAnalysisFrameworkCore clangScalableStaticAnalysisFrameworkTool ) diff --git a/clang/tools/clang-ssaf-linker/CMakeLists.txt b/clang/tools/clang-ssaf-linker/CMakeLists.txt index 95db5ad2d3d08..af65aaa3b1aeb 100644 --- a/clang/tools/clang-ssaf-linker/CMakeLists.txt +++ b/clang/tools/clang-ssaf-linker/CMakeLists.txt @@ -10,6 +10,7 @@ add_clang_tool(clang-ssaf-linker clang_target_link_libraries(clang-ssaf-linker PRIVATE clangBasic + clangScalableStaticAnalysisFrameworkAnalyses clangScalableStaticAnalysisFrameworkCore clangScalableStaticAnalysisFrameworkTool ) diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphExtractorTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphExtractorTest.cpp new file mode 100644 index 0000000000000..e108542a70b45 --- /dev/null +++ b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphExtractorTest.cpp @@ -0,0 +1,375 @@ +//===- CallGraphExtractorTest.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 "TestFixture.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphSummary.h" +#include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h" +#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/ExtractorRegistry.h" +#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummary.h" +#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.h" +#include "clang/Tooling/Tooling.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/Error.h" +#include "llvm/Testing/Support/Error.h" +#include "gtest/gtest.h" +#include <cassert> + +using namespace clang; +using namespace ssaf; + +using llvm::Succeeded; + +namespace { +AST_MATCHER(FunctionDecl, isPrimaryTemplate) { + return Node.getDescribedFunctionTemplate() != nullptr; +} +} // namespace + +static llvm::Expected<const FunctionDecl *> findFn(ASTContext &Ctx, + StringRef FnName) { + using namespace ast_matchers; + auto Matcher = + functionDecl(hasName(FnName), unless(isPrimaryTemplate())).bind("decl"); + auto Matches = match(Matcher, Ctx); + if (Matches.empty()) + return llvm::createStringError( + "No FunctionDecl definition was found with name '" + FnName + "'"); + auto *FD = Matches[0].getNodeAs<FunctionDecl>("decl"); + assert(FD); + return FD->getCanonicalDecl(); +} + +namespace { + +struct CallGraphExtractorTest : ssaf::TestFixture { + TUSummary Summary = + BuildNamespace(BuildNamespaceKind::CompilationUnit, "Mock.cpp"); + TUSummaryBuilder Builder = TUSummaryBuilder(Summary); + + /// Creates the AST and extractor, then extracts the summaries from the AST. + /// This will update the \c AST \c Builder and \c Summary data members. + void runExtractor(StringRef Code) { + AST = tooling::buildASTFromCode(Code); + auto Consumer = makeTUSummaryExtractor("CallGraph", Builder); + Consumer->HandleTranslationUnit(AST->getASTContext()); + } + + /// Tries to find the \c CallGraphSummary for the \p FnName function. + llvm::Expected<const CallGraphSummary *> + findSummary(llvm::StringRef FnName) const; + + /// Collects the USRs of all direct callees in CallGraphSummary \p S. + std::set<std::string> getDirectCalleeUSRs(const CallGraphSummary *S) const; + + /// Looks up the Decls for \p FnNames, and then transforms those into USRs. + llvm::Expected<std::set<std::string>> + asUSRs(llvm::ArrayRef<StringRef> FnNames); + + /// Creates a GTest matcher selecting the direct callees of summary \p S. + auto matchCalleeUSRs(const CallGraphSummary *S) const { + return llvm::HasValue(testing::Eq(getDirectCalleeUSRs(S))); + } + +private: + std::unique_ptr<ASTUnit> AST; +}; + +llvm::Expected<const CallGraphSummary *> +CallGraphExtractorTest::findSummary(llvm::StringRef FnName) const { + auto MaybeFD = findFn(AST->getASTContext(), FnName); + if (!MaybeFD) + return MaybeFD.takeError(); + + std::optional<EntityName> EntName = getEntityName(*MaybeFD); + if (!EntName.has_value()) { + return llvm::createStringError("Failed to create an entity name for '" + + FnName + "'"); + } + + const auto &EntitiesTable = getEntities(getIdTable(Summary)); + auto It = EntitiesTable.find(EntName.value()); + if (It == EntitiesTable.end()) { + return llvm::createStringError( + "No entity ID was present in the entity table for '" + FnName + "'"); + } + EntityId ID = It->second; + auto &Data = getData(Summary); + auto SummaryIt = Data.find(SummaryName("CallGraph")); + if (SummaryIt == Data.end()) + return llvm::createStringError("There is no 'CallGraph' summary"); + auto EntityIt = SummaryIt->second.find(ID); + if (EntityIt == SummaryIt->second.end()) { + return llvm::createStringError( + "There is no 'CallGraph' summary for entity ID " + + std::to_string(getIndex(ID)) + " aka. '" + FnName + "'"); + } + return static_cast<const CallGraphSummary *>(EntityIt->second.get()); +} + +std::set<std::string> +CallGraphExtractorTest::getDirectCalleeUSRs(const CallGraphSummary *S) const { + const std::set<EntityId> &DirectCallees = S->DirectCallees; + std::set<std::string> USRs; + + auto GatherCalleeUSRs = [&](const EntityName &Name, EntityId Id) { + if (llvm::is_contained(DirectCallees, Id)) + USRs.insert(TestFixture::getUSR(Name)); + }; + TestFixture::getIdTable(Summary).forEach(GatherCalleeUSRs); + assert(DirectCallees.size() == USRs.size()); + return USRs; +} + +llvm::Expected<std::set<std::string>> +CallGraphExtractorTest::asUSRs(llvm::ArrayRef<StringRef> FnNames) { + std::set<std::string> USRs; + ASTContext &Ctx = AST->getASTContext(); + for (StringRef FnName : FnNames) { + auto MaybeFD = findFn(Ctx, FnName); + if (!MaybeFD) + return MaybeFD.takeError(); + std::optional<EntityName> Name = getEntityName(MaybeFD.get()); + if (!Name.has_value()) { + return llvm::createStringError("Failed to get the USR of '" + FnName + + "'"); + } + USRs.insert(getUSR(Name.value())); + } + assert(USRs.size() == FnNames.size()); + return USRs; +} + +TEST_F(CallGraphExtractorTest, SimpleFunctionCalls) { + runExtractor(R"cpp( + void a(); + void b(); + void calls_a_and_b(bool coin) { + if (coin) + a(); + else + b(); + } + )cpp"); + + const CallGraphSummary *S; + ASSERT_THAT_ERROR(findSummary("calls_a_and_b").moveInto(S), Succeeded()); + EXPECT_FALSE(S->HasIndirectCalls); + EXPECT_THAT_EXPECTED(asUSRs({"a", "b"}), matchCalleeUSRs(S)); +} + +TEST_F(CallGraphExtractorTest, NoCallees) { + runExtractor(R"cpp( + void leaf() {} + )cpp"); + + const CallGraphSummary *S; + ASSERT_THAT_ERROR(findSummary("leaf").moveInto(S), Succeeded()); + EXPECT_FALSE(S->HasIndirectCalls); + EXPECT_TRUE(S->DirectCallees.empty()); +} + +TEST_F(CallGraphExtractorTest, TransitiveCalls) { + runExtractor(R"cpp( + void c() { /*empty*/ } + void b() { c(); } + void a() { b(); } + )cpp"); + + { // a calls b (not c — we only record direct callees). + const CallGraphSummary *SA; + ASSERT_THAT_ERROR(findSummary("a").moveInto(SA), Succeeded()); + EXPECT_FALSE(SA->HasIndirectCalls); + EXPECT_THAT_EXPECTED(asUSRs({"b"}), matchCalleeUSRs(SA)); + } + + { // b calls c. + const CallGraphSummary *SB; + ASSERT_THAT_ERROR(findSummary("b").moveInto(SB), Succeeded()); + EXPECT_FALSE(SB->HasIndirectCalls); + EXPECT_THAT_EXPECTED(asUSRs({"c"}), matchCalleeUSRs(SB)); + } + + { // c calls nothing. + const CallGraphSummary *SC; + ASSERT_THAT_ERROR(findSummary("c").moveInto(SC), Succeeded()); + EXPECT_FALSE(SC->HasIndirectCalls); + EXPECT_TRUE(SC->DirectCallees.empty()); + } +} + +TEST_F(CallGraphExtractorTest, VirtualCallsAreImprecise) { + runExtractor(R"cpp( + struct Base { + virtual void virt(); + }; + struct Derived : Base { + void virt() override; + }; + void caller(Base &Obj) { + Obj.virt(); + } + )cpp"); + const CallGraphSummary *S; + ASSERT_THAT_ERROR(findSummary("caller").moveInto(S), Succeeded()); + + // Virtual calls are treated as indirect calls. + EXPECT_TRUE(S->HasIndirectCalls); + + // Virtual calls should not appear in DirectCallees. + EXPECT_THAT_EXPECTED(asUSRs({}), matchCalleeUSRs(S)); +} + +TEST_F(CallGraphExtractorTest, MixedDirectAndVirtualCalls) { + runExtractor(R"cpp( + void direct_target(); + struct Base { + virtual void virt(); + }; + void caller(Base &Obj) { + direct_target(); + Obj.virt(); + } + )cpp"); + + const CallGraphSummary *S; + ASSERT_THAT_ERROR(findSummary("caller").moveInto(S), Succeeded()); + EXPECT_TRUE(S->HasIndirectCalls); + EXPECT_THAT_EXPECTED(asUSRs({"direct_target"}), matchCalleeUSRs(S)); +} + +TEST_F(CallGraphExtractorTest, DeclarationsOnlyNoSummary) { + runExtractor(R"cpp( + void declared_only(); + )cpp"); + + // No summary for functions without definitions. + EXPECT_FALSE(llvm::is_contained(getData(Summary), SummaryName("CallGraph"))); +} + +TEST_F(CallGraphExtractorTest, DuplicateCallees) { + runExtractor(R"cpp( + void target(); + void caller() { + target(); + target(); + target(); + } + )cpp"); + + const CallGraphSummary *S; + ASSERT_THAT_ERROR(findSummary("caller").moveInto(S), Succeeded()); + EXPECT_FALSE(S->HasIndirectCalls); + + // Despite three calls, there's only one unique callee. + EXPECT_THAT_EXPECTED(asUSRs({"target"}), matchCalleeUSRs(S)); +} + +TEST_F(CallGraphExtractorTest, NonVirtualMethodCalls) { + runExtractor(R"cpp( + struct S { + void method(); + }; + void caller() { + S s; + s.method(); + } + )cpp"); + + const CallGraphSummary *S; + ASSERT_THAT_ERROR(findSummary("caller").moveInto(S), Succeeded()); + EXPECT_FALSE(S->HasIndirectCalls); + EXPECT_THAT_EXPECTED(asUSRs({"method"}), matchCalleeUSRs(S)); +} + +TEST_F(CallGraphExtractorTest, StaticMethodCalls) { + runExtractor(R"cpp( + struct S { + static void staticMethod(); + }; + void caller() { + S::staticMethod(); + } + )cpp"); + + const CallGraphSummary *S; + ASSERT_THAT_ERROR(findSummary("caller").moveInto(S), Succeeded()); + EXPECT_FALSE(S->HasIndirectCalls); + EXPECT... [truncated] `````````` </details> https://github.com/llvm/llvm-project/pull/188753 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
