[clang] [llvm] [clang][ssaf] Implement JSON format for CallGraph summary (PR #189681)
Balázs Benics via llvm-commits
llvm-commits at lists.llvm.org
Wed Apr 1 07:29:26 PDT 2026
https://github.com/steakhal updated https://github.com/llvm/llvm-project/pull/189681
>From 7cd85dfe9357791fda4bdc07808562c1345c8eec Mon Sep 17 00:00:00 2001
From: Balazs Benics <benicsbalazs at gmail.com>
Date: Mon, 30 Mar 2026 15:44:10 +0100
Subject: [PATCH 1/2] [clang][ssaf] Implement JSON format for CallGraph summary
rdar://170258016
---
.../Analyses/CallGraph/CallGraphSummary.h | 3 +-
.../SSAFBuiltinForceLinker.h | 5 +
.../Analyses/CMakeLists.txt | 1 +
.../Analyses/CallGraph/CallGraphExtractor.cpp | 3 +-
.../CallGraph/CallGraphJSONFormat.cpp | 142 ++++++++++++++++++
.../TUSummaryExtractorFrontendAction.cpp | 4 +
clang/test/Analysis/Scalable/call-graph.cpp | 20 +++
.../Analysis/Scalable/ssaf-format/list.test | 7 +-
.../CallGraph/CallGraphExtractorTest.cpp | 8 +-
.../Analyses/BUILD.gn | 1 +
10 files changed, 186 insertions(+), 8 deletions(-)
create mode 100644 clang/lib/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphJSONFormat.cpp
create mode 100644 clang/test/Analysis/Scalable/call-graph.cpp
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphSummary.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphSummary.h
index ad70218d01614..8056b1001a216 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphSummary.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphSummary.h
@@ -30,8 +30,9 @@ struct CallGraphSummary final : public EntitySummary {
unsigned Column;
};
+ static constexpr llvm::StringLiteral Name = "CallGraph";
SummaryName getSummaryName() const override {
- return SummaryName("CallGraph");
+ return SummaryName(Name.str());
}
/// Represents the location of the function.
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h b/clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h
index 707573ce34e46..1dc50c9c58dd8 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h
@@ -37,4 +37,9 @@ extern volatile int CallGraphExtractorAnchorSource;
[[maybe_unused]] static int CallGraphExtractorAnchorDestination =
CallGraphExtractorAnchorSource;
+// This anchor is used to force the linker to link the CallGraph JSON format.
+extern volatile int CallGraphJSONFormatAnchorSource;
+[[maybe_unused]] static int CallGraphJSONFormatAnchorDestination =
+ CallGraphJSONFormatAnchorSource;
+
#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_SSAFBUILTINFORCELINKER_H
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt b/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt
index 2dcce40f886dd..df8079a7d375d 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt
@@ -4,6 +4,7 @@ set(LLVM_LINK_COMPONENTS
add_clang_library(clangScalableStaticAnalysisFrameworkAnalyses
CallGraph/CallGraphExtractor.cpp
+ CallGraph/CallGraphJSONFormat.cpp
UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
LINK_LIBS
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphExtractor.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphExtractor.cpp
index b2cd2e40f33b5..1dbed7e0b0d8a 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphExtractor.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphExtractor.cpp
@@ -98,7 +98,8 @@ void CallGraphExtractor::handleCallGraphNode(const ASTContext &Ctx,
}
static TUSummaryExtractorRegistry::Add<CallGraphExtractor>
- RegisterExtractor("CallGraph", "Extracts static call-graph information");
+ RegisterExtractor(CallGraphSummary::Name,
+ "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.
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphJSONFormat.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphJSONFormat.cpp
new file mode 100644
index 0000000000000..33da0ab0bd205
--- /dev/null
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphJSONFormat.cpp
@@ -0,0 +1,142 @@
+//===- CallGraphJSONFormat.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 "clang/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphSummary.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Serialization/JSONFormat.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/JSON.h"
+#include <memory>
+
+using namespace llvm;
+using namespace clang;
+using namespace ssaf;
+
+static json::Object serialize(const EntitySummary &Summary,
+ JSONFormat::EntityIdToJSONFn ToJSON) {
+ const auto &S = static_cast<const CallGraphSummary &>(Summary);
+
+ json::Array DirectCalleesArray;
+ DirectCalleesArray.reserve(S.DirectCallees.size());
+ append_range(DirectCalleesArray, map_range(S.DirectCallees, ToJSON));
+
+ json::Array VirtualCalleesArray;
+ VirtualCalleesArray.reserve(S.VirtualCallees.size());
+ append_range(VirtualCalleesArray, map_range(S.VirtualCallees, ToJSON));
+
+ return json::Object{
+ {"pretty_name", json::Value(S.PrettyName)},
+ {"direct_callees", std::move(DirectCalleesArray)},
+ {"virtual_callees", std::move(VirtualCalleesArray)},
+ {"def",
+ json::Object{
+ {"file", json::Value(S.Definition.File)},
+ {"line", json::Value(S.Definition.Line)},
+ {"col", json::Value(S.Definition.Column)},
+ }},
+ };
+}
+
+static Expected<std::unique_ptr<EntitySummary>>
+deserialize(const json::Object &Obj, EntityIdTable &IdTable,
+ JSONFormat::EntityIdFromJSONFn FromJSON) {
+ auto Result = std::make_unique<CallGraphSummary>();
+
+ auto PrettyName = Obj.getString("pretty_name");
+ if (!PrettyName) {
+ return createStringError(inconvertibleErrorCode(),
+ "missing or invalid field 'pretty_name'");
+ }
+ Result->PrettyName = PrettyName->str();
+
+ const json::Array *CalleesArray = Obj.getArray("direct_callees");
+ if (!CalleesArray) {
+ return createStringError(inconvertibleErrorCode(),
+ "missing or invalid field 'direct_callees'");
+ }
+ for (const auto &[Index, Value] : llvm::enumerate(*CalleesArray)) {
+ const json::Object *CalleeObj = Value.getAsObject();
+ if (!CalleeObj) {
+ return createStringError(
+ inconvertibleErrorCode(),
+ "direct_callees element at index %zu is not a JSON object", Index);
+ }
+ auto ExpectedId = FromJSON(*CalleeObj);
+ if (!ExpectedId) {
+ return createStringError(
+ inconvertibleErrorCode(),
+ "invalid entity id in direct_callees at index %zu: %s", Index,
+ toString(ExpectedId.takeError()).c_str());
+ }
+ Result->DirectCallees.insert(*ExpectedId);
+ }
+
+ const json::Array *VirtualCalleesArray = Obj.getArray("virtual_callees");
+ if (!VirtualCalleesArray) {
+ return createStringError(inconvertibleErrorCode(),
+ "missing or invalid field 'virtual_callees'");
+ }
+ for (const auto &[Index, Value] : llvm::enumerate(*VirtualCalleesArray)) {
+ const json::Object *CalleeObj = Value.getAsObject();
+ if (!CalleeObj) {
+ return createStringError(
+ inconvertibleErrorCode(),
+ "virtual_callees element at index %zu is not a JSON object", Index);
+ }
+ auto ExpectedId = FromJSON(*CalleeObj);
+ if (!ExpectedId) {
+ return createStringError(
+ inconvertibleErrorCode(),
+ "invalid entity id in virtual_callees at index %zu: %s", Index,
+ toString(ExpectedId.takeError()).c_str());
+ }
+ Result->VirtualCallees.insert(*ExpectedId);
+ }
+
+ const json::Object *DefObj = Obj.getObject("def");
+ if (!DefObj) {
+ return createStringError(inconvertibleErrorCode(),
+ "missing or invalid field 'def'");
+ }
+ auto File = DefObj->getString("file");
+ if (!File) {
+ return createStringError(inconvertibleErrorCode(),
+ "missing or invalid field 'def.file'");
+ }
+ auto Line = DefObj->getInteger("line");
+ if (!Line) {
+ return createStringError(inconvertibleErrorCode(),
+ "missing or invalid field 'def.line'");
+ }
+ auto Col = DefObj->getInteger("col");
+ if (!Col) {
+ return createStringError(inconvertibleErrorCode(),
+ "missing or invalid field 'def.col'");
+ }
+ Result->Definition = {File->str(), static_cast<unsigned>(*Line),
+ static_cast<unsigned>(*Col)};
+
+ return std::move(Result);
+}
+
+namespace {
+struct CallGraphJSONFormatInfo final : JSONFormat::FormatInfo {
+ CallGraphJSONFormatInfo()
+ : JSONFormat::FormatInfo(SummaryName(CallGraphSummary::Name.str()),
+ serialize, deserialize) {}
+};
+} // namespace
+
+static llvm::Registry<JSONFormat::FormatInfo>::Add<CallGraphJSONFormatInfo>
+ RegisterFormatInfo(CallGraphSummary::Name,
+ "JSON Format info for CallGraph summary");
+
+// This anchor is used to force the linker to link in the generated object file
+// and thus register the JSON format for CallGraphSummary.
+// NOLINTNEXTLINE(misc-use-internal-linkage)
+volatile int CallGraphJSONFormatAnchorSource = 0;
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Frontend/TUSummaryExtractorFrontendAction.cpp b/clang/lib/ScalableStaticAnalysisFramework/Frontend/TUSummaryExtractorFrontendAction.cpp
index 9a75b20fa548b..d4bf8d4a63fa4 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Frontend/TUSummaryExtractorFrontendAction.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Frontend/TUSummaryExtractorFrontendAction.cpp
@@ -17,6 +17,7 @@
#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryExtractor.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/IOSandbox.h"
#include "llvm/Support/Path.h"
#include <memory>
#include <string>
@@ -150,6 +151,9 @@ void TUSummaryRunner::HandleTranslationUnit(ASTContext &Ctx) {
// First, invoke the Summary Extractors.
MultiplexConsumer::HandleTranslationUnit(Ctx);
+ // FIXME(sandboxing): Remove this by adopting `llvm::vfs::OutputBackend`.
+ llvm::sys::sandbox::ScopedSetting Guard = llvm::sys::sandbox::scopedDisable();
+
// Then serialize the result.
if (auto Err = Format->writeTUSummary(Summary, Opts.SSAFTUSummaryFile)) {
Ctx.getDiagnostics().Report(diag::warn_ssaf_write_tu_summary_failed)
diff --git a/clang/test/Analysis/Scalable/call-graph.cpp b/clang/test/Analysis/Scalable/call-graph.cpp
new file mode 100644
index 0000000000000..b6e7ce91ee8c7
--- /dev/null
+++ b/clang/test/Analysis/Scalable/call-graph.cpp
@@ -0,0 +1,20 @@
+// RUN: rm -rf %t.summary.json
+// RUN: %clang_cc1 -fsyntax-only %s \
+// RUN: --ssaf-extract-summaries=CallGraph \
+// RUN: --ssaf-tu-summary-file=%t.summary.json
+
+// Check that the JSON validation passes.
+// TODO: Enable the next line once the LinkageTable is populated.
+// R U N: clang-ssaf-format --type=tu %t.summary.json
+
+// Check that the JSON has plausible content irrespective of the order of the fields.
+// RUN: FileCheck %s --match-full-lines --input-file=%t.summary.json
+// CHECK-DAG: "direct_callees": [
+// CHECK-DAG: "pretty_name": "example()",
+// CHECK-DAG: "virtual_callees": []
+// CHECK-DAG: "summary_name": "CallGraph"
+
+void no_body();
+void example() {
+ no_body();
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/list.test b/clang/test/Analysis/Scalable/ssaf-format/list.test
index a21d1543915f7..ffc1624c5872a 100644
--- a/clang/test/Analysis/Scalable/ssaf-format/list.test
+++ b/clang/test/Analysis/Scalable/ssaf-format/list.test
@@ -1,9 +1,10 @@
// Test clang-ssaf-format --list output without any loaded plugins.
// RUN: clang-ssaf-format --list \
-// RUN: | FileCheck %s
+// RUN: | FileCheck %s --match-full-lines
// CHECK: Registered serialization formats:
// CHECK-EMPTY:
-// CHECK-NEXT: 1. json - JSON serialization format
-// CHECK-NEXT: Analyses: (none)
+// CHECK-DAG: [[NthFormat:[0-9]+]]. json - JSON serialization format
+// CHECK-DAG: Analyses:
+// CHECK-DAG: [[NthFormat]].[[MthSummary:[0-9]+]]. CallGraph - JSON Format info for CallGraph summary
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphExtractorTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphExtractorTest.cpp
index 9e0b9e6e256a4..2557c7a62479e 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphExtractorTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphExtractorTest.cpp
@@ -120,6 +120,8 @@ template <typename... Matchers> auto hasSummaryThat(const Matchers &...Ms) {
// Test fixture
// ============================================================================
+static const SummaryName CallGraphName{CallGraphSummary::Name.str()};
+
struct CallGraphExtractorTest : ssaf::TestFixture {
TUSummary Summary =
BuildNamespace(BuildNamespaceKind::CompilationUnit, "Mock.cpp");
@@ -129,7 +131,7 @@ struct CallGraphExtractorTest : ssaf::TestFixture {
/// This will update the \c AST \c Builder and \c Summary data members.
void runExtractor(StringRef Code, ArrayRef<std::string> Args = {}) {
AST = tooling::buildASTFromCodeWithArgs(Code, Args);
- auto Consumer = makeTUSummaryExtractor("CallGraph", Builder);
+ auto Consumer = makeTUSummaryExtractor(CallGraphName.str(), Builder);
Consumer->HandleTranslationUnit(AST->getASTContext());
}
@@ -207,7 +209,7 @@ CallGraphExtractorTest::findSummary(llvm::StringRef FnName) const {
}
EntityId ID = It->second;
auto &Data = getData(Summary);
- auto SummaryIt = Data.find(SummaryName("CallGraph"));
+ auto SummaryIt = Data.find(CallGraphName);
if (SummaryIt == Data.end())
return llvm::createStringError("There is no 'CallGraph' summary");
auto EntityIt = SummaryIt->second.find(ID);
@@ -343,7 +345,7 @@ TEST_F(CallGraphExtractorTest, DeclarationsOnlyNoSummary) {
)cpp");
// No summary for functions without definitions.
- EXPECT_FALSE(llvm::is_contained(getData(Summary), SummaryName("CallGraph")));
+ EXPECT_FALSE(llvm::is_contained(getData(Summary), CallGraphName));
}
TEST_F(CallGraphExtractorTest, DuplicateCallees) {
diff --git a/llvm/utils/gn/secondary/clang/lib/ScalableStaticAnalysisFramework/Analyses/BUILD.gn b/llvm/utils/gn/secondary/clang/lib/ScalableStaticAnalysisFramework/Analyses/BUILD.gn
index ac62574ad8534..1450d8866b71f 100644
--- a/llvm/utils/gn/secondary/clang/lib/ScalableStaticAnalysisFramework/Analyses/BUILD.gn
+++ b/llvm/utils/gn/secondary/clang/lib/ScalableStaticAnalysisFramework/Analyses/BUILD.gn
@@ -10,6 +10,7 @@ static_library("Analyses") {
]
sources = [
"CallGraph/CallGraphExtractor.cpp",
+ "CallGraph/CallGraphJSONFormat.cpp",
"UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp",
]
}
>From d0b7ef88b0650fa47c3d7f3bb270f72f1097ac84 Mon Sep 17 00:00:00 2001
From: Balazs Benics <benicsbalazs at gmail.com>
Date: Wed, 1 Apr 2026 14:42:15 +0100
Subject: [PATCH 2/2] Use ErrorBuilder, also add tests for covering the error
conditions
---
.../CallGraph/CallGraphJSONFormat.cpp | 92 ++++++++++++------
clang/test/Analysis/Scalable/call-graph.cpp | 44 ++++++---
.../invalid-direct-callee-element.json | 46 +++++++++
.../CallGraph/invalid-direct-callee-id.json | 48 +++++++++
.../Inputs/CallGraph/missing-def-col.json | 48 +++++++++
.../Inputs/CallGraph/missing-def-file.json | 45 +++++++++
.../Inputs/CallGraph/missing-def-line.json | 47 +++++++++
.../Inputs/CallGraph/missing-def.json | 44 +++++++++
.../CallGraph/missing-direct-callees.json | 42 ++++++++
.../Inputs/CallGraph/missing-pretty-name.json | 40 ++++++++
.../CallGraph/missing-virtual-callees.json | 43 ++++++++
.../call-graph-invalid-json-format.test | 97 +++++++++++++++++++
12 files changed, 594 insertions(+), 42 deletions(-)
create mode 100644 clang/test/Analysis/Scalable/ssaf-format/Inputs/CallGraph/invalid-direct-callee-element.json
create mode 100644 clang/test/Analysis/Scalable/ssaf-format/Inputs/CallGraph/invalid-direct-callee-id.json
create mode 100644 clang/test/Analysis/Scalable/ssaf-format/Inputs/CallGraph/missing-def-col.json
create mode 100644 clang/test/Analysis/Scalable/ssaf-format/Inputs/CallGraph/missing-def-file.json
create mode 100644 clang/test/Analysis/Scalable/ssaf-format/Inputs/CallGraph/missing-def-line.json
create mode 100644 clang/test/Analysis/Scalable/ssaf-format/Inputs/CallGraph/missing-def.json
create mode 100644 clang/test/Analysis/Scalable/ssaf-format/Inputs/CallGraph/missing-direct-callees.json
create mode 100644 clang/test/Analysis/Scalable/ssaf-format/Inputs/CallGraph/missing-pretty-name.json
create mode 100644 clang/test/Analysis/Scalable/ssaf-format/Inputs/CallGraph/missing-virtual-callees.json
create mode 100644 clang/test/Analysis/Scalable/ssaf-format/call-graph-invalid-json-format.test
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphJSONFormat.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphJSONFormat.cpp
index 33da0ab0bd205..860e26417eb55 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphJSONFormat.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphJSONFormat.cpp
@@ -9,6 +9,7 @@
#include "clang/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphSummary.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Serialization/JSONFormat.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Support/ErrorBuilder.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/JSON.h"
#include <memory>
@@ -17,6 +18,13 @@ using namespace llvm;
using namespace clang;
using namespace ssaf;
+static const char *FailedToReadObjectAtField =
+ "failed to read {0} from field '{1}': expected JSON {2}";
+static const char *FailedToReadObjectAtIndex =
+ "failed to read {0} from index '{1}': expected JSON {2}";
+static const char *ReadingFromField = "reading {0} from field '{1}'";
+static const char *ReadingFromIndex = "reading {0} from index '{1}'";
+
static json::Object serialize(const EntitySummary &Summary,
JSONFormat::EntityIdToJSONFn ToJSON) {
const auto &S = static_cast<const CallGraphSummary &>(Summary);
@@ -49,77 +57,101 @@ deserialize(const json::Object &Obj, EntityIdTable &IdTable,
auto PrettyName = Obj.getString("pretty_name");
if (!PrettyName) {
- return createStringError(inconvertibleErrorCode(),
- "missing or invalid field 'pretty_name'");
+ return ErrorBuilder::create(std::errc::invalid_argument,
+ FailedToReadObjectAtField, "PrettyName",
+ "pretty_name", "string")
+ .build();
}
Result->PrettyName = PrettyName->str();
const json::Array *CalleesArray = Obj.getArray("direct_callees");
if (!CalleesArray) {
- return createStringError(inconvertibleErrorCode(),
- "missing or invalid field 'direct_callees'");
+ return ErrorBuilder::create(std::errc::invalid_argument,
+ FailedToReadObjectAtField, "DirectCallees",
+ "direct_callees", "array")
+ .build();
}
for (const auto &[Index, Value] : llvm::enumerate(*CalleesArray)) {
const json::Object *CalleeObj = Value.getAsObject();
if (!CalleeObj) {
- return createStringError(
- inconvertibleErrorCode(),
- "direct_callees element at index %zu is not a JSON object", Index);
+ return ErrorBuilder::create(std::errc::invalid_argument,
+ FailedToReadObjectAtIndex, "EntityId", Index,
+ "object")
+ .context(ReadingFromField, "DirectCallees", "direct_callees")
+ .build();
}
auto ExpectedId = FromJSON(*CalleeObj);
if (!ExpectedId) {
- return createStringError(
- inconvertibleErrorCode(),
- "invalid entity id in direct_callees at index %zu: %s", Index,
- toString(ExpectedId.takeError()).c_str());
+ return ErrorBuilder::wrap(ExpectedId.takeError())
+ .context(ReadingFromIndex, "EntityId", Index)
+ .context(ReadingFromField, "DirectCallees", "direct_callees")
+ .build();
}
Result->DirectCallees.insert(*ExpectedId);
}
const json::Array *VirtualCalleesArray = Obj.getArray("virtual_callees");
if (!VirtualCalleesArray) {
- return createStringError(inconvertibleErrorCode(),
- "missing or invalid field 'virtual_callees'");
+ return ErrorBuilder::create(std::errc::invalid_argument,
+ FailedToReadObjectAtField, "VirtualCallees",
+ "virtual_callees", "array")
+ .build();
}
for (const auto &[Index, Value] : llvm::enumerate(*VirtualCalleesArray)) {
const json::Object *CalleeObj = Value.getAsObject();
if (!CalleeObj) {
- return createStringError(
- inconvertibleErrorCode(),
- "virtual_callees element at index %zu is not a JSON object", Index);
+ return ErrorBuilder::create(std::errc::invalid_argument,
+ FailedToReadObjectAtIndex, "EntityId", Index,
+ "object")
+ .context(ReadingFromField, "VirtualCallees", "virtual_callees")
+ .build();
}
auto ExpectedId = FromJSON(*CalleeObj);
if (!ExpectedId) {
- return createStringError(
- inconvertibleErrorCode(),
- "invalid entity id in virtual_callees at index %zu: %s", Index,
- toString(ExpectedId.takeError()).c_str());
+ return ErrorBuilder::wrap(ExpectedId.takeError())
+ .context(ReadingFromIndex, "EntityId", Index)
+ .context(ReadingFromField, "VirtualCallees", "virtual_callees")
+ .build();
}
Result->VirtualCallees.insert(*ExpectedId);
}
const json::Object *DefObj = Obj.getObject("def");
if (!DefObj) {
- return createStringError(inconvertibleErrorCode(),
- "missing or invalid field 'def'");
+ return ErrorBuilder::create(std::errc::invalid_argument,
+ FailedToReadObjectAtField, "SourceLocation",
+ "def", "object")
+ .build();
}
auto File = DefObj->getString("file");
if (!File) {
- return createStringError(inconvertibleErrorCode(),
- "missing or invalid field 'def.file'");
+ return ErrorBuilder::create(std::errc::invalid_argument,
+ FailedToReadObjectAtField, "File", "file",
+ "string")
+ .context(ReadingFromField, "SourceLocation", "def")
+ .build();
}
auto Line = DefObj->getInteger("line");
if (!Line) {
- return createStringError(inconvertibleErrorCode(),
- "missing or invalid field 'def.line'");
+ return ErrorBuilder::create(std::errc::invalid_argument,
+ FailedToReadObjectAtField, "Line", "line",
+ "number")
+ .context(ReadingFromField, "SourceLocation", "def")
+ .build();
}
auto Col = DefObj->getInteger("col");
if (!Col) {
- return createStringError(inconvertibleErrorCode(),
- "missing or invalid field 'def.col'");
+ return ErrorBuilder::create(std::errc::invalid_argument,
+ FailedToReadObjectAtField, "Column", "col",
+ "number")
+ .context(ReadingFromField, "SourceLocation", "def")
+ .build();
}
- Result->Definition = {File->str(), static_cast<unsigned>(*Line),
- static_cast<unsigned>(*Col)};
+ Result->Definition = {
+ File->str(),
+ static_cast<unsigned>(*Line),
+ static_cast<unsigned>(*Col),
+ };
return std::move(Result);
}
diff --git a/clang/test/Analysis/Scalable/call-graph.cpp b/clang/test/Analysis/Scalable/call-graph.cpp
index b6e7ce91ee8c7..8ff0a7ee53c72 100644
--- a/clang/test/Analysis/Scalable/call-graph.cpp
+++ b/clang/test/Analysis/Scalable/call-graph.cpp
@@ -2,19 +2,39 @@
// RUN: %clang_cc1 -fsyntax-only %s \
// RUN: --ssaf-extract-summaries=CallGraph \
// RUN: --ssaf-tu-summary-file=%t.summary.json
+// RUN: FileCheck %s --match-full-lines --input-file=%t.summary.json
-// Check that the JSON validation passes.
-// TODO: Enable the next line once the LinkageTable is populated.
-// R U N: clang-ssaf-format --type=tu %t.summary.json
+// caller() has a direct callee and no virtual callees.
+// CHECK-LABEL: "entity_summary": {
+// CHECK-DAG: "def": {
+// CHECK-DAG: "col": {{[0-9]+}},
+// CHECK-DAG: "file": "{{.+}}",
+// CHECK-DAG: "line": {{[0-9]+}}
+// CHECK-DAG: "pretty_name": "caller()",
+// CHECK-DAG: "direct_callees": [
+// CHECK-DAG: "virtual_callees": []
-// Check that the JSON has plausible content irrespective of the order of the fields.
-// RUN: FileCheck %s --match-full-lines --input-file=%t.summary.json
-// CHECK-DAG: "direct_callees": [
-// CHECK-DAG: "pretty_name": "example()",
-// CHECK-DAG: "virtual_callees": []
-// CHECK-DAG: "summary_name": "CallGraph"
+// polymorphic() has a virtual callee and no direct callees.
+// CHECK-LABEL: "entity_summary": {
+// CHECK-DAG: "def": {
+// CHECK-DAG: "col": {{[0-9]+}},
+// CHECK-DAG: "file": "{{.+}}",
+// CHECK-DAG: "line": {{[0-9]+}}
+// CHECK-DAG: "pretty_name": "polymorphic(Base &)",
+// CHECK-DAG: "direct_callees": [],
+// CHECK-DAG: "virtual_callees": [
+
+struct Base {
+ virtual ~Base();
+ virtual void vmethod();
+};
+
+void callee();
+
+void caller() {
+ callee();
+}
-void no_body();
-void example() {
- no_body();
+void polymorphic(Base &b) {
+ b.vmethod();
}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/Inputs/CallGraph/invalid-direct-callee-element.json b/clang/test/Analysis/Scalable/ssaf-format/Inputs/CallGraph/invalid-direct-callee-element.json
new file mode 100644
index 0000000000000..e1490cb50af4d
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/Inputs/CallGraph/invalid-direct-callee-element.json
@@ -0,0 +1,46 @@
+{
+ "tu_namespace": {
+ "kind": "CompilationUnit",
+ "name": "test.cpp"
+ },
+ "id_table": [
+ {
+ "id": 0,
+ "name": {
+ "namespace": [
+ {
+ "kind": "CompilationUnit",
+ "name": "test.cpp"
+ }
+ ],
+ "suffix": "",
+ "usr": "c:@F at foo#"
+ }
+ }
+ ],
+ "linkage_table": [
+ {
+ "id": 0,
+ "linkage": {
+ "type": "External"
+ }
+ }
+ ],
+ "data": [
+ {
+ "summary_name": "CallGraph",
+ "summary_data": [
+ {
+ "entity_id": 0,
+ "entity_summary": {
+ "pretty_name": "foo()",
+ "direct_callees": [
+ 42
+ ],
+ "virtual_callees": []
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/Inputs/CallGraph/invalid-direct-callee-id.json b/clang/test/Analysis/Scalable/ssaf-format/Inputs/CallGraph/invalid-direct-callee-id.json
new file mode 100644
index 0000000000000..cee8fb2d78635
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/Inputs/CallGraph/invalid-direct-callee-id.json
@@ -0,0 +1,48 @@
+{
+ "tu_namespace": {
+ "kind": "CompilationUnit",
+ "name": "test.cpp"
+ },
+ "id_table": [
+ {
+ "id": 0,
+ "name": {
+ "namespace": [
+ {
+ "kind": "CompilationUnit",
+ "name": "test.cpp"
+ }
+ ],
+ "suffix": "",
+ "usr": "c:@F at foo#"
+ }
+ }
+ ],
+ "linkage_table": [
+ {
+ "id": 0,
+ "linkage": {
+ "type": "External"
+ }
+ }
+ ],
+ "data": [
+ {
+ "summary_name": "CallGraph",
+ "summary_data": [
+ {
+ "entity_id": 0,
+ "entity_summary": {
+ "pretty_name": "foo()",
+ "direct_callees": [
+ {
+ "@": "bad"
+ }
+ ],
+ "virtual_callees": []
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/Inputs/CallGraph/missing-def-col.json b/clang/test/Analysis/Scalable/ssaf-format/Inputs/CallGraph/missing-def-col.json
new file mode 100644
index 0000000000000..71ed3e937e24e
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/Inputs/CallGraph/missing-def-col.json
@@ -0,0 +1,48 @@
+{
+ "tu_namespace": {
+ "kind": "CompilationUnit",
+ "name": "test.cpp"
+ },
+ "id_table": [
+ {
+ "id": 0,
+ "name": {
+ "namespace": [
+ {
+ "kind": "CompilationUnit",
+ "name": "test.cpp"
+ }
+ ],
+ "suffix": "",
+ "usr": "c:@F at foo#"
+ }
+ }
+ ],
+ "linkage_table": [
+ {
+ "id": 0,
+ "linkage": {
+ "type": "External"
+ }
+ }
+ ],
+ "data": [
+ {
+ "summary_name": "CallGraph",
+ "summary_data": [
+ {
+ "entity_id": 0,
+ "entity_summary": {
+ "pretty_name": "foo()",
+ "direct_callees": [],
+ "virtual_callees": [],
+ "def": {
+ "file": "t.cpp",
+ "line": 1
+ }
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/Inputs/CallGraph/missing-def-file.json b/clang/test/Analysis/Scalable/ssaf-format/Inputs/CallGraph/missing-def-file.json
new file mode 100644
index 0000000000000..de1e2ac704613
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/Inputs/CallGraph/missing-def-file.json
@@ -0,0 +1,45 @@
+{
+ "tu_namespace": {
+ "kind": "CompilationUnit",
+ "name": "test.cpp"
+ },
+ "id_table": [
+ {
+ "id": 0,
+ "name": {
+ "namespace": [
+ {
+ "kind": "CompilationUnit",
+ "name": "test.cpp"
+ }
+ ],
+ "suffix": "",
+ "usr": "c:@F at foo#"
+ }
+ }
+ ],
+ "linkage_table": [
+ {
+ "id": 0,
+ "linkage": {
+ "type": "External"
+ }
+ }
+ ],
+ "data": [
+ {
+ "summary_name": "CallGraph",
+ "summary_data": [
+ {
+ "entity_id": 0,
+ "entity_summary": {
+ "pretty_name": "foo()",
+ "direct_callees": [],
+ "virtual_callees": [],
+ "def": {}
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/Inputs/CallGraph/missing-def-line.json b/clang/test/Analysis/Scalable/ssaf-format/Inputs/CallGraph/missing-def-line.json
new file mode 100644
index 0000000000000..2b23d0f55498f
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/Inputs/CallGraph/missing-def-line.json
@@ -0,0 +1,47 @@
+{
+ "tu_namespace": {
+ "kind": "CompilationUnit",
+ "name": "test.cpp"
+ },
+ "id_table": [
+ {
+ "id": 0,
+ "name": {
+ "namespace": [
+ {
+ "kind": "CompilationUnit",
+ "name": "test.cpp"
+ }
+ ],
+ "suffix": "",
+ "usr": "c:@F at foo#"
+ }
+ }
+ ],
+ "linkage_table": [
+ {
+ "id": 0,
+ "linkage": {
+ "type": "External"
+ }
+ }
+ ],
+ "data": [
+ {
+ "summary_name": "CallGraph",
+ "summary_data": [
+ {
+ "entity_id": 0,
+ "entity_summary": {
+ "pretty_name": "foo()",
+ "direct_callees": [],
+ "virtual_callees": [],
+ "def": {
+ "file": "t.cpp"
+ }
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/Inputs/CallGraph/missing-def.json b/clang/test/Analysis/Scalable/ssaf-format/Inputs/CallGraph/missing-def.json
new file mode 100644
index 0000000000000..bf13b1cf96543
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/Inputs/CallGraph/missing-def.json
@@ -0,0 +1,44 @@
+{
+ "tu_namespace": {
+ "kind": "CompilationUnit",
+ "name": "test.cpp"
+ },
+ "id_table": [
+ {
+ "id": 0,
+ "name": {
+ "namespace": [
+ {
+ "kind": "CompilationUnit",
+ "name": "test.cpp"
+ }
+ ],
+ "suffix": "",
+ "usr": "c:@F at foo#"
+ }
+ }
+ ],
+ "linkage_table": [
+ {
+ "id": 0,
+ "linkage": {
+ "type": "External"
+ }
+ }
+ ],
+ "data": [
+ {
+ "summary_name": "CallGraph",
+ "summary_data": [
+ {
+ "entity_id": 0,
+ "entity_summary": {
+ "pretty_name": "foo()",
+ "direct_callees": [],
+ "virtual_callees": []
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/Inputs/CallGraph/missing-direct-callees.json b/clang/test/Analysis/Scalable/ssaf-format/Inputs/CallGraph/missing-direct-callees.json
new file mode 100644
index 0000000000000..522c291191c0c
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/Inputs/CallGraph/missing-direct-callees.json
@@ -0,0 +1,42 @@
+{
+ "tu_namespace": {
+ "kind": "CompilationUnit",
+ "name": "test.cpp"
+ },
+ "id_table": [
+ {
+ "id": 0,
+ "name": {
+ "namespace": [
+ {
+ "kind": "CompilationUnit",
+ "name": "test.cpp"
+ }
+ ],
+ "suffix": "",
+ "usr": "c:@F at foo#"
+ }
+ }
+ ],
+ "linkage_table": [
+ {
+ "id": 0,
+ "linkage": {
+ "type": "External"
+ }
+ }
+ ],
+ "data": [
+ {
+ "summary_name": "CallGraph",
+ "summary_data": [
+ {
+ "entity_id": 0,
+ "entity_summary": {
+ "pretty_name": "foo()"
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/Inputs/CallGraph/missing-pretty-name.json b/clang/test/Analysis/Scalable/ssaf-format/Inputs/CallGraph/missing-pretty-name.json
new file mode 100644
index 0000000000000..598361ee8c89d
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/Inputs/CallGraph/missing-pretty-name.json
@@ -0,0 +1,40 @@
+{
+ "tu_namespace": {
+ "kind": "CompilationUnit",
+ "name": "test.cpp"
+ },
+ "id_table": [
+ {
+ "id": 0,
+ "name": {
+ "namespace": [
+ {
+ "kind": "CompilationUnit",
+ "name": "test.cpp"
+ }
+ ],
+ "suffix": "",
+ "usr": "c:@F at foo#"
+ }
+ }
+ ],
+ "linkage_table": [
+ {
+ "id": 0,
+ "linkage": {
+ "type": "External"
+ }
+ }
+ ],
+ "data": [
+ {
+ "summary_name": "CallGraph",
+ "summary_data": [
+ {
+ "entity_id": 0,
+ "entity_summary": {}
+ }
+ ]
+ }
+ ]
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/Inputs/CallGraph/missing-virtual-callees.json b/clang/test/Analysis/Scalable/ssaf-format/Inputs/CallGraph/missing-virtual-callees.json
new file mode 100644
index 0000000000000..9552ff43f840b
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/Inputs/CallGraph/missing-virtual-callees.json
@@ -0,0 +1,43 @@
+{
+ "tu_namespace": {
+ "kind": "CompilationUnit",
+ "name": "test.cpp"
+ },
+ "id_table": [
+ {
+ "id": 0,
+ "name": {
+ "namespace": [
+ {
+ "kind": "CompilationUnit",
+ "name": "test.cpp"
+ }
+ ],
+ "suffix": "",
+ "usr": "c:@F at foo#"
+ }
+ }
+ ],
+ "linkage_table": [
+ {
+ "id": 0,
+ "linkage": {
+ "type": "External"
+ }
+ }
+ ],
+ "data": [
+ {
+ "summary_name": "CallGraph",
+ "summary_data": [
+ {
+ "entity_id": 0,
+ "entity_summary": {
+ "pretty_name": "foo()",
+ "direct_callees": []
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/call-graph-invalid-json-format.test b/clang/test/Analysis/Scalable/ssaf-format/call-graph-invalid-json-format.test
new file mode 100644
index 0000000000000..9cd3323603a44
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/call-graph-invalid-json-format.test
@@ -0,0 +1,97 @@
+// Tests for CallGraph JSON deserialization error conditions.
+
+// RUN: not clang-ssaf-format --type=tu %S/Inputs/CallGraph/missing-pretty-name.json 2>&1 \
+// RUN: | FileCheck %s --match-full-lines --check-prefix=PRETTY-NAME
+// PRETTY-NAME: clang-ssaf-format: error: reading TUSummary from file '{{.*}}missing-pretty-name.json'
+// PRETTY-NAME-NEXT: reading SummaryData entries from field 'data'
+// PRETTY-NAME-NEXT: reading SummaryData entry from index '0'
+// PRETTY-NAME-NEXT: reading EntitySummary entries from field 'summary_data'
+// PRETTY-NAME-NEXT: reading EntitySummary entry from index '0'
+// PRETTY-NAME-NEXT: reading EntitySummary from field 'entity_summary'
+// PRETTY-NAME-NEXT: failed to read PrettyName from field 'pretty_name': expected JSON string
+
+// RUN: not clang-ssaf-format --type=tu %S/Inputs/CallGraph/missing-direct-callees.json 2>&1 \
+// RUN: | FileCheck %s --match-full-lines --check-prefix=DIRECT-CALLEES
+// DIRECT-CALLEES: clang-ssaf-format: error: reading TUSummary from file '{{.*}}missing-direct-callees.json'
+// DIRECT-CALLEES-NEXT: reading SummaryData entries from field 'data'
+// DIRECT-CALLEES-NEXT: reading SummaryData entry from index '0'
+// DIRECT-CALLEES-NEXT: reading EntitySummary entries from field 'summary_data'
+// DIRECT-CALLEES-NEXT: reading EntitySummary entry from index '0'
+// DIRECT-CALLEES-NEXT: reading EntitySummary from field 'entity_summary'
+// DIRECT-CALLEES-NEXT: failed to read DirectCallees from field 'direct_callees': expected JSON array
+
+// RUN: not clang-ssaf-format --type=tu %S/Inputs/CallGraph/invalid-direct-callee-element.json 2>&1 \
+// RUN: | FileCheck %s --match-full-lines --check-prefix=CALLEE-ELEMENT
+// CALLEE-ELEMENT: clang-ssaf-format: error: reading TUSummary from file '{{.*}}invalid-direct-callee-element.json'
+// CALLEE-ELEMENT-NEXT: reading SummaryData entries from field 'data'
+// CALLEE-ELEMENT-NEXT: reading SummaryData entry from index '0'
+// CALLEE-ELEMENT-NEXT: reading EntitySummary entries from field 'summary_data'
+// CALLEE-ELEMENT-NEXT: reading EntitySummary entry from index '0'
+// CALLEE-ELEMENT-NEXT: reading EntitySummary from field 'entity_summary'
+// CALLEE-ELEMENT-NEXT: reading DirectCallees from field 'direct_callees'
+// CALLEE-ELEMENT-NEXT: failed to read EntityId from index '0': expected JSON object
+
+// RUN: not clang-ssaf-format --type=tu %S/Inputs/CallGraph/invalid-direct-callee-id.json 2>&1 \
+// RUN: | FileCheck %s --match-full-lines --check-prefix=CALLEE-ID
+// CALLEE-ID: clang-ssaf-format: error: reading TUSummary from file '{{.*}}invalid-direct-callee-id.json'
+// CALLEE-ID-NEXT: reading SummaryData entries from field 'data'
+// CALLEE-ID-NEXT: reading SummaryData entry from index '0'
+// CALLEE-ID-NEXT: reading EntitySummary entries from field 'summary_data'
+// CALLEE-ID-NEXT: reading EntitySummary entry from index '0'
+// CALLEE-ID-NEXT: reading EntitySummary from field 'entity_summary'
+// CALLEE-ID-NEXT: reading DirectCallees from field 'direct_callees'
+// CALLEE-ID-NEXT: reading EntityId from index '0'
+// CALLEE-ID-NEXT: failed to read EntityId: expected JSON object with a single '@' key mapped to a number (unsigned 64-bit integer)
+
+// RUN: not clang-ssaf-format --type=tu %S/Inputs/CallGraph/missing-virtual-callees.json 2>&1 \
+// RUN: | FileCheck %s --match-full-lines --check-prefix=VIRTUAL-CALLEES
+// VIRTUAL-CALLEES: clang-ssaf-format: error: reading TUSummary from file '{{.*}}missing-virtual-callees.json'
+// VIRTUAL-CALLEES-NEXT: reading SummaryData entries from field 'data'
+// VIRTUAL-CALLEES-NEXT: reading SummaryData entry from index '0'
+// VIRTUAL-CALLEES-NEXT: reading EntitySummary entries from field 'summary_data'
+// VIRTUAL-CALLEES-NEXT: reading EntitySummary entry from index '0'
+// VIRTUAL-CALLEES-NEXT: reading EntitySummary from field 'entity_summary'
+// VIRTUAL-CALLEES-NEXT: failed to read VirtualCallees from field 'virtual_callees': expected JSON array
+
+// RUN: not clang-ssaf-format --type=tu %S/Inputs/CallGraph/missing-def.json 2>&1 \
+// RUN: | FileCheck %s --match-full-lines --check-prefix=DEF
+// DEF: clang-ssaf-format: error: reading TUSummary from file '{{.*}}missing-def.json'
+// DEF-NEXT: reading SummaryData entries from field 'data'
+// DEF-NEXT: reading SummaryData entry from index '0'
+// DEF-NEXT: reading EntitySummary entries from field 'summary_data'
+// DEF-NEXT: reading EntitySummary entry from index '0'
+// DEF-NEXT: reading EntitySummary from field 'entity_summary'
+// DEF-NEXT: failed to read SourceLocation from field 'def': expected JSON object
+
+// RUN: not clang-ssaf-format --type=tu %S/Inputs/CallGraph/missing-def-file.json 2>&1 \
+// RUN: | FileCheck %s --match-full-lines --check-prefix=DEF-FILE
+// DEF-FILE: clang-ssaf-format: error: reading TUSummary from file '{{.*}}missing-def-file.json'
+// DEF-FILE-NEXT: reading SummaryData entries from field 'data'
+// DEF-FILE-NEXT: reading SummaryData entry from index '0'
+// DEF-FILE-NEXT: reading EntitySummary entries from field 'summary_data'
+// DEF-FILE-NEXT: reading EntitySummary entry from index '0'
+// DEF-FILE-NEXT: reading EntitySummary from field 'entity_summary'
+// DEF-FILE-NEXT: reading SourceLocation from field 'def'
+// DEF-FILE-NEXT: failed to read File from field 'file': expected JSON string
+
+// RUN: not clang-ssaf-format --type=tu %S/Inputs/CallGraph/missing-def-line.json 2>&1 \
+// RUN: | FileCheck %s --match-full-lines --check-prefix=DEF-LINE
+// DEF-LINE: clang-ssaf-format: error: reading TUSummary from file '{{.*}}missing-def-line.json'
+// DEF-LINE-NEXT: reading SummaryData entries from field 'data'
+// DEF-LINE-NEXT: reading SummaryData entry from index '0'
+// DEF-LINE-NEXT: reading EntitySummary entries from field 'summary_data'
+// DEF-LINE-NEXT: reading EntitySummary entry from index '0'
+// DEF-LINE-NEXT: reading EntitySummary from field 'entity_summary'
+// DEF-LINE-NEXT: reading SourceLocation from field 'def'
+// DEF-LINE-NEXT: failed to read Line from field 'line': expected JSON number
+
+// RUN: not clang-ssaf-format --type=tu %S/Inputs/CallGraph/missing-def-col.json 2>&1 \
+// RUN: | FileCheck %s --match-full-lines --check-prefix=DEF-COL
+// DEF-COL: clang-ssaf-format: error: reading TUSummary from file '{{.*}}missing-def-col.json'
+// DEF-COL-NEXT: reading SummaryData entries from field 'data'
+// DEF-COL-NEXT: reading SummaryData entry from index '0'
+// DEF-COL-NEXT: reading EntitySummary entries from field 'summary_data'
+// DEF-COL-NEXT: reading EntitySummary entry from index '0'
+// DEF-COL-NEXT: reading EntitySummary from field 'entity_summary'
+// DEF-COL-NEXT: reading SourceLocation from field 'def'
+// DEF-COL-NEXT: failed to read Column from field 'col': expected JSON number
More information about the llvm-commits
mailing list