[clang] [llvm] Reapply "[clang][ssaf] Add --ssaf-extract-summaries= and --ssaf-tu-summary-file= options" (PR #186463)
Balázs Benics via cfe-commits
cfe-commits at lists.llvm.org
Fri Mar 13 10:30:30 PDT 2026
https://github.com/steakhal created https://github.com/llvm/llvm-project/pull/186463
This reverts commit 3548ec95178c00a2895a65b435945ce318396c8e and adapts the code to the new ScalableStaticAnalysisFramework/ directory layout.
Re-adds:
- TUSummaryExtractorFrontendAction and its integration into ExecuteCompilerInvocation
- --ssaf-extract-summaries= and --ssaf-tu-summary-file= CLI options
- SSAFForceLinker / SSAFBuiltinForceLinker headers and anchor symbols
- Diagnostics under -Wscalable-static-analysis-framework
- Lit tests for the CLI and unit tests for the frontend action
- Changes the Formats to be lowercase - and match their spellings in the file paths.
>From c896ca9fb5235023ec3fc4a2e9b7d65b34ba4a08 Mon Sep 17 00:00:00 2001
From: Balazs Benics <benicsbalazs at gmail.com>
Date: Thu, 12 Mar 2026 12:21:39 +0000
Subject: [PATCH] Reapply "[clang][ssaf] Add --ssaf-extract-summaries= and
--ssaf-tu-summary-file= options"
This reverts commit 3548ec95178c00a2895a65b435945ce318396c8e and
adapts the code to the new ScalableStaticAnalysisFramework/ directory
layout.
Re-adds:
- TUSummaryExtractorFrontendAction and its integration into
ExecuteCompilerInvocation
- --ssaf-extract-summaries= and --ssaf-tu-summary-file= CLI options
- SSAFForceLinker / SSAFBuiltinForceLinker headers and anchor symbols
- Diagnostics under -Wscalable-static-analysis-framework
- Lit tests for the CLI and unit tests for the frontend action
- Changes the Formats to be lowercase - and match their spellings in the
file paths.
---
.../clang/Basic/DiagnosticFrontendKinds.td | 23 ++
clang/include/clang/Basic/DiagnosticGroups.td | 3 +
.../include/clang/Frontend/FrontendOptions.h | 7 +
clang/include/clang/Options/Options.td | 20 +
.../Core/Serialization/JSONFormat.h | 4 -
.../SerializationFormatRegistry.h | 14 +-
.../Core/TUSummary/ExtractorRegistry.h | 9 +
.../TUSummaryExtractorFrontendAction.h | 33 ++
.../SSAFBuiltinForceLinker.h | 28 ++
.../SSAFForceLinker.h | 25 ++
clang/lib/Driver/ToolChains/Clang.cpp | 3 +
clang/lib/FrontendTool/CMakeLists.txt | 2 +
.../ExecuteCompilerInvocation.cpp | 6 +
.../CMakeLists.txt | 1 +
.../JSONFormat/JSONFormatImpl.cpp | 7 +-
.../Frontend/CMakeLists.txt | 14 +
.../TUSummaryExtractorFrontendAction.cpp | 181 +++++++++
.../Analysis/SSAF/command-line-interface.cpp | 22 ++
.../Analysis/SSAF/downgradable-errors.cpp | 15 +
clang/test/Analysis/SSAF/help.cpp | 7 +
.../Analysis/Scalable/ssaf-format/list.test | 2 +-
clang/tools/ssaf-format/SSAFFormat.cpp | 10 +-
clang/tools/ssaf-linker/SSAFLinker.cpp | 11 +-
.../CMakeLists.txt | 3 +
.../TUSummaryExtractorFrontendActionTest.cpp | 366 ++++++++++++++++++
.../Registries/FancyAnalysisData.cpp | 2 +
.../Registries/MockSerializationFormat.cpp | 2 +
.../Registries/MockSummaryExtractor1.cpp | 6 +-
.../Registries/MockSummaryExtractor2.cpp | 6 +-
.../SummaryExtractorRegistryTest.cpp | 1 +
.../SSAFBuiltinTestForceLinker.h | 51 +++
.../SSAFTestForceLinker.h | 23 ++
.../TestFixture.cpp | 1 +
.../secondary/clang/lib/FrontendTool/BUILD.gn | 2 +
.../Frontend/BUILD.gn | 15 +
.../ScalableStaticAnalysisFramework/BUILD.gn | 3 +
36 files changed, 898 insertions(+), 30 deletions(-)
create mode 100644 clang/include/clang/ScalableStaticAnalysisFramework/Frontend/TUSummaryExtractorFrontendAction.h
create mode 100644 clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h
create mode 100644 clang/include/clang/ScalableStaticAnalysisFramework/SSAFForceLinker.h
create mode 100644 clang/lib/ScalableStaticAnalysisFramework/Frontend/CMakeLists.txt
create mode 100644 clang/lib/ScalableStaticAnalysisFramework/Frontend/TUSummaryExtractorFrontendAction.cpp
create mode 100644 clang/test/Analysis/SSAF/command-line-interface.cpp
create mode 100644 clang/test/Analysis/SSAF/downgradable-errors.cpp
create mode 100644 clang/test/Analysis/SSAF/help.cpp
create mode 100644 clang/unittests/ScalableStaticAnalysisFramework/Frontend/TUSummaryExtractorFrontendActionTest.cpp
create mode 100644 clang/unittests/ScalableStaticAnalysisFramework/SSAFBuiltinTestForceLinker.h
create mode 100644 clang/unittests/ScalableStaticAnalysisFramework/SSAFTestForceLinker.h
create mode 100644 llvm/utils/gn/secondary/clang/lib/ScalableStaticAnalysisFramework/Frontend/BUILD.gn
diff --git a/clang/include/clang/Basic/DiagnosticFrontendKinds.td b/clang/include/clang/Basic/DiagnosticFrontendKinds.td
index 5c62bb70ebd0f..00db1e7ee5afa 100644
--- a/clang/include/clang/Basic/DiagnosticFrontendKinds.td
+++ b/clang/include/clang/Basic/DiagnosticFrontendKinds.td
@@ -379,6 +379,29 @@ def warn_profile_data_misexpect : Warning<
BackendInfo, InGroup<MisExpect>;
} // end of instrumentation issue category
+def warn_ssaf_extract_tu_summary_file_unknown_output_format :
+ Warning<"unknown output summary file format '%0' "
+ "specified by '--ssaf-tu-summary-file=%1'">,
+ InGroup<ScalableStaticAnalysisFramework>, DefaultError;
+
+def warn_ssaf_extract_tu_summary_file_unknown_format :
+ Warning<"failed to parse the value of '--ssaf-tu-summary-file=%0' "
+ "the value must follow the '<path>.<format>' pattern">,
+ InGroup<ScalableStaticAnalysisFramework>, DefaultError;
+
+def warn_ssaf_must_enable_summary_extractors :
+ Warning<"must enable some summary extractors using the "
+ "'--ssaf-extract-summaries=' option">,
+ InGroup<ScalableStaticAnalysisFramework>, DefaultError;
+
+def warn_ssaf_extract_summary_unknown_extractor_name :
+ Warning<"no summary extractor%s0 %plural{1:was|:were}0 registered with name: %1">,
+ InGroup<ScalableStaticAnalysisFramework>, DefaultError;
+
+def warn_ssaf_write_tu_summary_failed :
+ Warning<"failed to write TU summary to '%0': %1">,
+ InGroup<ScalableStaticAnalysisFramework>, DefaultError;
+
def err_extract_api_ignores_file_not_found :
Error<"file '%0' specified by '--extract-api-ignores=' not found">, DefaultFatal;
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index 5d39f12d5c00f..e440c9d2fb982 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -1907,6 +1907,9 @@ def BitIntExtension : DiagGroup<"bit-int-extension">;
// Warnings about misuse of ExtractAPI options.
def ExtractAPIMisuse : DiagGroup<"extractapi-misuse">;
+// Warnings related to the "Scalable Static Analysis Framework" - SSAF.
+def ScalableStaticAnalysisFramework : DiagGroup<"scalable-static-analysis-framework">;
+
// Warnings about using the non-standard extension having an explicit specialization
// with a storage class specifier.
def ExplicitSpecializationStorageClass : DiagGroup<"explicit-specialization-storage-class">;
diff --git a/clang/include/clang/Frontend/FrontendOptions.h b/clang/include/clang/Frontend/FrontendOptions.h
index 9e05181ac916c..0d8eb6a1b7379 100644
--- a/clang/include/clang/Frontend/FrontendOptions.h
+++ b/clang/include/clang/Frontend/FrontendOptions.h
@@ -543,6 +543,13 @@ class FrontendOptions {
/// minimization hints.
std::string DumpMinimizationHintsPath;
+ /// List of SSAF extractors to enable.
+ std::vector<std::string> SSAFExtractSummaries;
+
+ /// The TU summary output file with the file extension representing the file
+ /// format.
+ std::string SSAFTUSummaryFile;
+
public:
FrontendOptions()
: DisableFree(false), RelocatablePCH(false), ShowHelp(false),
diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td
index 8e17cd5ae15b5..692df65abb367 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -274,6 +274,10 @@ def StaticAnalyzer_Group : OptionGroup<"<Static analyzer group>">,
DocName<"Static analyzer options">, DocBrief<[{
Flags controlling the behavior of the Clang Static Analyzer.}]>;
+def SSAF_Group : OptionGroup<"<ssaf options>">,
+ DocName<"SSAF options">, DocBrief<[{
+Flags controlling the behavior of the Scalable Static Analysis Framework (SSAF).}]>;
+
// gfortran options that we recognize in the driver and pass along when
// invoking GCC to compile Fortran code.
def gfortran_Group : OptionGroup<"<gfortran group>">,
@@ -941,6 +945,22 @@ def W_Joined : Joined<["-"], "W">, Group<W_Group>,
def Xanalyzer : Separate<["-"], "Xanalyzer">,
HelpText<"Pass <arg> to the static analyzer">, MetaVarName<"<arg>">,
Group<StaticAnalyzer_Group>;
+def _ssaf_extract_summaries :
+ CommaJoined<["--"], "ssaf-extract-summaries=">,
+ MetaVarName<"<summary-names>">,
+ Group<SSAF_Group>,
+ Visibility<[ClangOption, CC1Option]>,
+ HelpText<"Comma-separated list of summary names to extract">,
+ MarshallingInfoStringVector<FrontendOpts<"SSAFExtractSummaries">>;
+def _ssaf_tu_summary_file :
+ Joined<["--"], "ssaf-tu-summary-file=">,
+ MetaVarName<"<path>.<format>">,
+ Group<SSAF_Group>,
+ Visibility<[ClangOption, CC1Option]>,
+ HelpText<
+ "The output file for the extracted summaries. "
+ "The extension selects which file format to use.">,
+ MarshallingInfoString<FrontendOpts<"SSAFTUSummaryFile">>;
def Xarch__
: JoinedAndSeparate<["-"], "Xarch_">,
Flags<[NoXarchOption]>,
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Serialization/JSONFormat.h b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Serialization/JSONFormat.h
index 6c5303c928661..47b46cbe42698 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Serialization/JSONFormat.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Serialization/JSONFormat.h
@@ -28,10 +28,6 @@ class EntityIdTable;
class EntitySummary;
class SummaryName;
-/// Call this from main() to prevent the linker from dead-stripping the
-/// JSONFormat library and its static registration objects.
-void initializeJSONFormat();
-
class JSONFormat final : public SerializationFormat {
using Array = llvm::json::Array;
using Object = llvm::json::Object;
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Serialization/SerializationFormatRegistry.h b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Serialization/SerializationFormatRegistry.h
index 9f83955b884e8..ea916dd397b79 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Serialization/SerializationFormatRegistry.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Serialization/SerializationFormatRegistry.h
@@ -24,10 +24,11 @@
//
// Insert this code to the cpp file:
//
-// LLVM_INSTANTIATE_REGISTRY(llvm::Registry<MyFormat::FormatInfo>)
-//
+// // NOLINTNEXTLINE(misc-use-internal-linkage)
+// volatile int SSAFMyFormatAnchorSource = 0;
// static SerializationFormatRegistry::Add<MyFormat>
// RegisterFormat("MyFormat", "My awesome serialization format");
+// LLVM_INSTANTIATE_REGISTRY(llvm::Registry<MyFormat::FormatInfo>)
//
// Then implement the formatter for the specific analysis and register the
// format info for it:
@@ -49,6 +50,15 @@
// "The MyFormat format info implementation for MyAnalysis"
// );
//
+// Finally, insert a use of the new anchor symbol into the force-linker header:
+// clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h:
+//
+// This anchor is used to force the linker to link the MyFormat registration.
+//
+// extern volatile int SSAFMyFormatAnchorSource;
+// [[maybe_unused]] static int SSAFMyFormatAnchorDestination =
+// SSAFMyFormatAnchorSource;
+//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_SERIALIZATION_SERIALIZATIONFORMATREGISTRY_H
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Core/TUSummary/ExtractorRegistry.h b/clang/include/clang/ScalableStaticAnalysisFramework/Core/TUSummary/ExtractorRegistry.h
index a49d0e5faeeb1..9c3f7088c62e5 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Core/TUSummary/ExtractorRegistry.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Core/TUSummary/ExtractorRegistry.h
@@ -9,9 +9,18 @@
// Registry for TUSummaryExtractors, and some helper functions.
// To register some custom extractor, insert this code:
//
+// // NOLINTNEXTLINE(misc-use-internal-linkage)
+// volatile int SSAFMyExtractorAnchorSource = 0;
// static TUSummaryExtractorRegistry::Add<MyExtractor>
// X("MyExtractor", "My awesome extractor");
//
+// Finally, insert a use of the new anchor symbol into the force-linker header:
+// clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h:
+//
+// extern volatile int SSAFMyExtractorAnchorSource;
+// [[maybe_unused]] static int SSAFMyExtractorAnchorDestination =
+// SSAFMyExtractorAnchorSource;
+//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_TUSUMMARY_EXTRACTORREGISTRY_H
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Frontend/TUSummaryExtractorFrontendAction.h b/clang/include/clang/ScalableStaticAnalysisFramework/Frontend/TUSummaryExtractorFrontendAction.h
new file mode 100644
index 0000000000000..fe5d75149914e
--- /dev/null
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Frontend/TUSummaryExtractorFrontendAction.h
@@ -0,0 +1,33 @@
+//===- TUSummaryExtractorFrontendAction.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_FRONTEND_TUSUMMARYEXTRACTORFRONTENDACTION_H
+#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_FRONTEND_TUSUMMARYEXTRACTORFRONTENDACTION_H
+
+#include "clang/Frontend/FrontendAction.h"
+#include <memory>
+
+namespace clang::ssaf {
+
+/// Wraps the existing \c FrontendAction and injects the extractor
+/// \c ASTConsumers into the pipeline after the ASTConsumers of the wrapped
+/// action.
+class TUSummaryExtractorFrontendAction final : public WrapperFrontendAction {
+public:
+ explicit TUSummaryExtractorFrontendAction(
+ std::unique_ptr<FrontendAction> WrappedAction);
+ ~TUSummaryExtractorFrontendAction();
+
+protected:
+ std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
+ StringRef InFile) override;
+};
+
+} // namespace clang::ssaf
+
+#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_FRONTEND_TUSUMMARYEXTRACTORFRONTENDACTION_H
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h b/clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h
new file mode 100644
index 0000000000000..5f201487ca1fe
--- /dev/null
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h
@@ -0,0 +1,28 @@
+//===- SSAFBuiltinForceLinker.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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file pulls in all built-in SSAF extractor and format registrations
+/// by referencing their anchor symbols, preventing the static linker from
+/// discarding the containing object files.
+///
+/// Include this header (with IWYU pragma: keep) in any translation unit that
+/// must guarantee these registrations are active — typically the entry point
+/// of a binary that uses clangScalableStaticAnalysisFrameworkCore.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_SSAFBUILTINFORCELINKER_H
+#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_SSAFBUILTINFORCELINKER_H
+
+// This anchor is used to force the linker to link the JSONFormat registration.
+extern volatile int SSAFJSONFormatAnchorSource;
+[[maybe_unused]] static int SSAFJSONFormatAnchorDestination =
+ SSAFJSONFormatAnchorSource;
+
+#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_SSAFBUILTINFORCELINKER_H
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/SSAFForceLinker.h b/clang/include/clang/ScalableStaticAnalysisFramework/SSAFForceLinker.h
new file mode 100644
index 0000000000000..204a504c36435
--- /dev/null
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/SSAFForceLinker.h
@@ -0,0 +1,25 @@
+//===- SSAFForceLinker.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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file pulls in all built-in SSAF extractor and format registrations
+/// by referencing their anchor symbols, preventing the static linker from
+/// discarding the containing object files.
+///
+/// Include this header (with IWYU pragma: keep) in any translation unit that
+/// must guarantee these registrations are active — typically the entry point
+/// of a binary that uses clangScalableStaticAnalysisFrameworkCore.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_SSAFFORCELINKER_H
+#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_SSAFFORCELINKER_H
+
+#include "SSAFBuiltinForceLinker.h" // IWYU pragma: keep
+
+#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_SSAFFORCELINKER_H
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index c5b75bdb2511f..3b852528d92c4 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -7707,6 +7707,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
Args.AddLastArg(CmdArgs, options::OPT_fmax_tokens_EQ);
+ Args.AddLastArg(CmdArgs, options::OPT__ssaf_extract_summaries);
+ Args.AddLastArg(CmdArgs, options::OPT__ssaf_tu_summary_file);
+
// Handle serialized diagnostics.
if (Arg *A = Args.getLastArg(options::OPT__serialize_diags)) {
CmdArgs.push_back("-serialize-diagnostic-file");
diff --git a/clang/lib/FrontendTool/CMakeLists.txt b/clang/lib/FrontendTool/CMakeLists.txt
index 66213f76eb968..a451eb967e904 100644
--- a/clang/lib/FrontendTool/CMakeLists.txt
+++ b/clang/lib/FrontendTool/CMakeLists.txt
@@ -4,6 +4,8 @@ set(LLVM_LINK_COMPONENTS
)
set(link_libs
+ clangScalableStaticAnalysisFrameworkCore
+ clangScalableStaticAnalysisFrameworkFrontend
clangBasic
clangCodeGen
clangDriver
diff --git a/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp b/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
index c8ad63bc989a4..e4622496758ac 100644
--- a/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
+++ b/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
@@ -23,6 +23,8 @@
#include "clang/FrontendTool/Utils.h"
#include "clang/Options/Options.h"
#include "clang/Rewrite/Frontend/FrontendActions.h"
+#include "clang/ScalableStaticAnalysisFramework/Frontend/TUSummaryExtractorFrontendAction.h"
+#include "clang/ScalableStaticAnalysisFramework/SSAFForceLinker.h" // IWYU pragma: keep
#include "clang/StaticAnalyzer/Frontend/AnalyzerHelpFlags.h"
#include "clang/StaticAnalyzer/Frontend/FrontendActions.h"
#include "llvm/Option/OptTable.h"
@@ -207,6 +209,10 @@ CreateFrontendAction(CompilerInstance &CI) {
Act = std::make_unique<ASTMergeAction>(std::move(Act),
FEOpts.ASTMergeFiles);
+ if (!FEOpts.SSAFTUSummaryFile.empty()) {
+ Act = std::make_unique<ssaf::TUSummaryExtractorFrontendAction>(
+ std::move(Act));
+ }
return Act;
}
diff --git a/clang/lib/ScalableStaticAnalysisFramework/CMakeLists.txt b/clang/lib/ScalableStaticAnalysisFramework/CMakeLists.txt
index 194a13a1af845..d3d75430233fe 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/CMakeLists.txt
+++ b/clang/lib/ScalableStaticAnalysisFramework/CMakeLists.txt
@@ -1 +1,2 @@
add_subdirectory(Core)
+add_subdirectory(Frontend)
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Core/Serialization/JSONFormat/JSONFormatImpl.cpp b/clang/lib/ScalableStaticAnalysisFramework/Core/Serialization/JSONFormat/JSONFormatImpl.cpp
index 0f1b9ccf6258e..4072532d4972c 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Core/Serialization/JSONFormat/JSONFormatImpl.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Core/Serialization/JSONFormat/JSONFormatImpl.cpp
@@ -9,18 +9,17 @@
#include "JSONFormatImpl.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Serialization/SerializationFormatRegistry.h"
-#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummary.h"
#include "llvm/Support/Registry.h"
+// NOLINTNEXTLINE(misc-use-internal-linkage)
+volatile int SSAFJSONFormatAnchorSource = 0;
LLVM_INSTANTIATE_REGISTRY(llvm::Registry<clang::ssaf::JSONFormat::FormatInfo>)
static clang::ssaf::SerializationFormatRegistry::Add<clang::ssaf::JSONFormat>
- RegisterJSONFormat("JSON", "JSON serialization format");
+ RegisterJSONFormat("json", "JSON serialization format");
namespace clang::ssaf {
-void initializeJSONFormat() {}
-
//----------------------------------------------------------------------------
// JSON Reader and Writer
//----------------------------------------------------------------------------
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Frontend/CMakeLists.txt b/clang/lib/ScalableStaticAnalysisFramework/Frontend/CMakeLists.txt
new file mode 100644
index 0000000000000..b90d9c0ded1a9
--- /dev/null
+++ b/clang/lib/ScalableStaticAnalysisFramework/Frontend/CMakeLists.txt
@@ -0,0 +1,14 @@
+set(LLVM_LINK_COMPONENTS
+ Support
+ )
+
+add_clang_library(clangScalableStaticAnalysisFrameworkFrontend
+ TUSummaryExtractorFrontendAction.cpp
+
+ LINK_LIBS
+ clangAST
+ clangBasic
+ clangFrontend
+ clangScalableStaticAnalysisFrameworkCore
+ clangSema
+ )
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Frontend/TUSummaryExtractorFrontendAction.cpp b/clang/lib/ScalableStaticAnalysisFramework/Frontend/TUSummaryExtractorFrontendAction.cpp
new file mode 100644
index 0000000000000..9a75b20fa548b
--- /dev/null
+++ b/clang/lib/ScalableStaticAnalysisFramework/Frontend/TUSummaryExtractorFrontendAction.cpp
@@ -0,0 +1,181 @@
+//===- TUSummaryExtractorFrontendAction.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/Frontend/TUSummaryExtractorFrontendAction.h"
+#include "clang/AST/ASTConsumer.h"
+#include "clang/Basic/DiagnosticFrontend.h"
+#include "clang/Frontend/MultiplexConsumer.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Serialization/SerializationFormatRegistry.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/ExtractorRegistry.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummary.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryExtractor.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Path.h"
+#include <memory>
+#include <string>
+#include <vector>
+
+using namespace clang;
+using namespace ssaf;
+
+static std::optional<std::pair<llvm::StringRef, llvm::StringRef>>
+parseOutputFileFormatAndPathOrReportError(DiagnosticsEngine &Diags,
+ StringRef SSAFTUSummaryFile) {
+
+ StringRef Ext = llvm::sys::path::extension(SSAFTUSummaryFile);
+ StringRef FilePath = SSAFTUSummaryFile.drop_back(Ext.size());
+
+ if (!Ext.consume_front(".") || FilePath.empty()) {
+ Diags.Report(diag::warn_ssaf_extract_tu_summary_file_unknown_format)
+ << SSAFTUSummaryFile;
+ return std::nullopt;
+ }
+
+ if (!isFormatRegistered(Ext)) {
+ Diags.Report(diag::warn_ssaf_extract_tu_summary_file_unknown_output_format)
+ << Ext << SSAFTUSummaryFile;
+ return std::nullopt;
+ }
+
+ return std::pair{Ext, FilePath};
+}
+
+/// Return \c true if reported unrecognized extractors.
+static bool
+reportUnrecognizedExtractorNames(DiagnosticsEngine &Diags,
+ ArrayRef<std::string> SSAFExtractSummaries) {
+ if (SSAFExtractSummaries.empty()) {
+ Diags.Report(diag::warn_ssaf_must_enable_summary_extractors);
+ return true;
+ }
+
+ std::vector<StringRef> UnrecognizedExtractorNames;
+ for (StringRef Name : SSAFExtractSummaries)
+ if (!isTUSummaryExtractorRegistered(Name))
+ UnrecognizedExtractorNames.push_back(Name);
+
+ if (!UnrecognizedExtractorNames.empty()) {
+ Diags.Report(diag::warn_ssaf_extract_summary_unknown_extractor_name)
+ << UnrecognizedExtractorNames.size()
+ << llvm::join(UnrecognizedExtractorNames, ", ");
+ return true;
+ }
+
+ return false;
+}
+
+static std::vector<std::unique_ptr<ASTConsumer>>
+makeTUSummaryExtractors(TUSummaryBuilder &Builder,
+ ArrayRef<std::string> SSAFExtractSummaries) {
+ std::vector<std::unique_ptr<ASTConsumer>> Extractors;
+ Extractors.reserve(SSAFExtractSummaries.size());
+ for (StringRef Name : SSAFExtractSummaries) {
+ assert(isTUSummaryExtractorRegistered(Name));
+ Extractors.push_back(makeTUSummaryExtractor(Name, Builder));
+ }
+ return Extractors;
+}
+
+namespace {
+
+/// Drives all extractor \c ASTConsumers and serializes the completed
+/// \c TUSummary.
+///
+/// Derives from \c MultiplexConsumer so every \c ASTConsumer virtual method is
+/// automatically forwarded to each extractor.
+class TUSummaryRunner final : public MultiplexConsumer {
+public:
+ static std::unique_ptr<TUSummaryRunner> create(CompilerInstance &CI,
+ StringRef InFile);
+
+private:
+ TUSummaryRunner(StringRef InFile, std::unique_ptr<SerializationFormat> Format,
+ const FrontendOptions &Opts);
+
+ void HandleTranslationUnit(ASTContext &Ctx) override;
+
+ TUSummary Summary;
+ TUSummaryBuilder Builder = TUSummaryBuilder(Summary);
+ std::unique_ptr<SerializationFormat> Format;
+ const FrontendOptions &Opts;
+};
+} // namespace
+
+std::unique_ptr<TUSummaryRunner> TUSummaryRunner::create(CompilerInstance &CI,
+ StringRef InFile) {
+ const FrontendOptions &Opts = CI.getFrontendOpts();
+ DiagnosticsEngine &Diags = CI.getDiagnostics();
+
+ auto MaybePair =
+ parseOutputFileFormatAndPathOrReportError(Diags, Opts.SSAFTUSummaryFile);
+ if (!MaybePair.has_value())
+ return nullptr;
+ auto [FormatName, OutputPath] = MaybePair.value();
+
+ if (reportUnrecognizedExtractorNames(Diags, Opts.SSAFExtractSummaries))
+ return nullptr;
+
+ return std::unique_ptr<TUSummaryRunner>{
+ new TUSummaryRunner{InFile, makeFormat(FormatName), Opts}};
+}
+
+TUSummaryRunner::TUSummaryRunner(StringRef InFile,
+ std::unique_ptr<SerializationFormat> Format,
+ const FrontendOptions &Opts)
+ : MultiplexConsumer(std::vector<std::unique_ptr<ASTConsumer>>{}),
+ Summary(BuildNamespace(BuildNamespaceKind::CompilationUnit, InFile)),
+ Format(std::move(Format)), Opts(Opts) {
+ assert(this->Format);
+
+ // Now the Summary and the builders are constructed, we can also construct the
+ // extractors.
+ auto Extractors = makeTUSummaryExtractors(Builder, Opts.SSAFExtractSummaries);
+ assert(!Extractors.empty());
+
+ // We must initialize the Consumers here because our extractors need a
+ // Builder that holds a reference to the TUSummary, which would be only
+ // initialized after the MultiplexConsumer ctor. This is the only way we can
+ // avoid the use of the TUSummary before it starts its lifetime.
+ MultiplexConsumer::Consumers = std::move(Extractors);
+}
+
+void TUSummaryRunner::HandleTranslationUnit(ASTContext &Ctx) {
+ // First, invoke the Summary Extractors.
+ MultiplexConsumer::HandleTranslationUnit(Ctx);
+
+ // Then serialize the result.
+ if (auto Err = Format->writeTUSummary(Summary, Opts.SSAFTUSummaryFile)) {
+ Ctx.getDiagnostics().Report(diag::warn_ssaf_write_tu_summary_failed)
+ << Opts.SSAFTUSummaryFile << llvm::toString(std::move(Err));
+ }
+}
+
+TUSummaryExtractorFrontendAction::~TUSummaryExtractorFrontendAction() = default;
+
+TUSummaryExtractorFrontendAction::TUSummaryExtractorFrontendAction(
+ std::unique_ptr<FrontendAction> WrappedAction)
+ : WrapperFrontendAction(std::move(WrappedAction)) {}
+
+std::unique_ptr<ASTConsumer>
+TUSummaryExtractorFrontendAction::CreateASTConsumer(CompilerInstance &CI,
+ StringRef InFile) {
+ auto WrappedConsumer = WrapperFrontendAction::CreateASTConsumer(CI, InFile);
+ if (!WrappedConsumer)
+ return nullptr;
+
+ if (auto Runner = TUSummaryRunner::create(CI, InFile)) {
+ std::vector<std::unique_ptr<ASTConsumer>> Consumers;
+ Consumers.reserve(2);
+ Consumers.push_back(std::move(WrappedConsumer));
+ Consumers.push_back(std::move(Runner));
+ return std::make_unique<MultiplexConsumer>(std::move(Consumers));
+ }
+ return WrappedConsumer;
+}
diff --git a/clang/test/Analysis/SSAF/command-line-interface.cpp b/clang/test/Analysis/SSAF/command-line-interface.cpp
new file mode 100644
index 0000000000000..a632f487f2bb7
--- /dev/null
+++ b/clang/test/Analysis/SSAF/command-line-interface.cpp
@@ -0,0 +1,22 @@
+// DEFINE: %{filecheck} = FileCheck %s --match-full-lines --check-prefix
+
+// The flags should behave the same way on the clang driver and also on CC1.
+
+// RUN: not %clang -fsyntax-only %s --ssaf-tu-summary-file=foobar 2>&1 | %{filecheck}=NOT-MATCHING-THE-PATTERN
+// RUN: not %clang_cc1 -fsyntax-only %s --ssaf-tu-summary-file=foobar 2>&1 | %{filecheck}=NOT-MATCHING-THE-PATTERN
+// RUN: not %clang -fsyntax-only %s --ssaf-tu-summary-file=%t.ssaf.unknownfmt 2>&1 | %{filecheck}=UNKNOWN-FILE-FORMAT
+// RUN: not %clang_cc1 -fsyntax-only %s --ssaf-tu-summary-file=%t.ssaf.unknownfmt 2>&1 | %{filecheck}=UNKNOWN-FILE-FORMAT
+// RUN: not %clang -fsyntax-only %s --ssaf-tu-summary-file=%t.ssaf.json 2>&1 | %{filecheck}=NO-EXTRACTORS-ENABLED
+// RUN: not %clang_cc1 -fsyntax-only %s --ssaf-tu-summary-file=%t.ssaf.json 2>&1 | %{filecheck}=NO-EXTRACTORS-ENABLED
+// RUN: not %clang -fsyntax-only %s --ssaf-tu-summary-file=%t.ssaf.json --ssaf-extract-summaries=extractor1 2>&1 | %{filecheck}=NO-EXTRACTOR-WITH-NAME
+// RUN: not %clang_cc1 -fsyntax-only %s --ssaf-tu-summary-file=%t.ssaf.json --ssaf-extract-summaries=extractor1 2>&1 | %{filecheck}=NO-EXTRACTOR-WITH-NAME
+// RUN: not %clang -fsyntax-only %s --ssaf-tu-summary-file=%t.ssaf.json --ssaf-extract-summaries=extractor1,extractor2 2>&1 | %{filecheck}=NO-EXTRACTORS-WITH-NAME
+// RUN: not %clang_cc1 -fsyntax-only %s --ssaf-tu-summary-file=%t.ssaf.json --ssaf-extract-summaries=extractor1,extractor2 2>&1 | %{filecheck}=NO-EXTRACTORS-WITH-NAME
+
+void empty() {}
+
+// NOT-MATCHING-THE-PATTERN: error: failed to parse the value of '--ssaf-tu-summary-file=foobar' the value must follow the '<path>.<format>' pattern [-Wscalable-static-analysis-framework]
+// UNKNOWN-FILE-FORMAT: error: unknown output summary file format 'unknownfmt' specified by '--ssaf-tu-summary-file={{.+}}.ssaf.unknownfmt' [-Wscalable-static-analysis-framework]
+// NO-EXTRACTORS-ENABLED: error: must enable some summary extractors using the '--ssaf-extract-summaries=' option [-Wscalable-static-analysis-framework]
+// NO-EXTRACTOR-WITH-NAME: error: no summary extractor was registered with name: extractor1 [-Wscalable-static-analysis-framework]
+// NO-EXTRACTORS-WITH-NAME: error: no summary extractors were registered with name: extractor1, extractor2 [-Wscalable-static-analysis-framework]
diff --git a/clang/test/Analysis/SSAF/downgradable-errors.cpp b/clang/test/Analysis/SSAF/downgradable-errors.cpp
new file mode 100644
index 0000000000000..494e3e71092ac
--- /dev/null
+++ b/clang/test/Analysis/SSAF/downgradable-errors.cpp
@@ -0,0 +1,15 @@
+// DEFINE: %{filecheck} = FileCheck %s --match-full-lines --check-prefix
+
+// RUN: not %clang -fsyntax-only %s --ssaf-tu-summary-file=foobar 2>&1 | %{filecheck}=DEFAULT-ERROR
+// RUN: %clang -fsyntax-only %s --ssaf-tu-summary-file=foobar -Wno-error=scalable-static-analysis-framework 2>&1 | %{filecheck}=DEMOTED-TO-WARNING
+// RUN: %clang -fsyntax-only %s --ssaf-tu-summary-file=foobar -Wno-scalable-static-analysis-framework 2>&1 | count 0
+
+// This test demonstrates that the "scalable-static-analysis-framework" diagnostics can be downgraded or completely silenced with the right flags.
+
+void empty() {}
+
+// DEFAULT-ERROR: error: failed to parse the value of '--ssaf-tu-summary-file=foobar' the value must follow the '<path>.<format>' pattern [-Wscalable-static-analysis-framework]
+// DEFAULT-ERROR: 1 error generated.
+
+// DEMOTED-TO-WARNING: warning: failed to parse the value of '--ssaf-tu-summary-file=foobar' the value must follow the '<path>.<format>' pattern [-Wscalable-static-analysis-framework]
+// DEMOTED-TO-WARNING: 1 warning generated.
diff --git a/clang/test/Analysis/SSAF/help.cpp b/clang/test/Analysis/SSAF/help.cpp
new file mode 100644
index 0000000000000..3755b96e3ce4f
--- /dev/null
+++ b/clang/test/Analysis/SSAF/help.cpp
@@ -0,0 +1,7 @@
+// RUN: %clang --help 2>&1 | FileCheck %s
+// RUN: %clang_cc1 --help 2>&1 | FileCheck %s
+
+// CHECK: --ssaf-extract-summaries=<summary-names>
+// CHECK-NEXT: Comma-separated list of summary names to extract
+// CHECK-NEXT: --ssaf-tu-summary-file=<path>.<format>
+// CHECK-NEXT: The output file for the extracted summaries. The extension selects which file format to use.
diff --git a/clang/test/Analysis/Scalable/ssaf-format/list.test b/clang/test/Analysis/Scalable/ssaf-format/list.test
index 47a678766aed1..4d389d78543ef 100644
--- a/clang/test/Analysis/Scalable/ssaf-format/list.test
+++ b/clang/test/Analysis/Scalable/ssaf-format/list.test
@@ -5,5 +5,5 @@
// CHECK: Registered serialization formats:
// CHECK-EMPTY:
-// CHECK-NEXT: 1. JSON JSON serialization format
+// CHECK-NEXT: 1. json JSON serialization format
// CHECK-NEXT: Analyses: (none)
diff --git a/clang/tools/ssaf-format/SSAFFormat.cpp b/clang/tools/ssaf-format/SSAFFormat.cpp
index 9b5312e0e085b..497d7437a08ae 100644
--- a/clang/tools/ssaf-format/SSAFFormat.cpp
+++ b/clang/tools/ssaf-format/SSAFFormat.cpp
@@ -15,6 +15,7 @@
#include "clang/ScalableStaticAnalysisFramework/Core/EntityLinker/TUSummaryEncoding.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Serialization/JSONFormat.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Serialization/SerializationFormatRegistry.h"
+#include "clang/ScalableStaticAnalysisFramework/SSAFForceLinker.h" // IWYU pragma: keep
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/CommandLine.h"
@@ -152,14 +153,11 @@ SerializationFormat *getFormatForExtension(llvm::StringRef Extension) {
return It->second.get();
}
- // SerializationFormats are uppercase while file extensions are lowercase.
- std::string CapitalizedExtension = Extension.upper();
-
- if (!isFormatRegistered(CapitalizedExtension)) {
+ if (!isFormatRegistered(Extension)) {
return nullptr;
}
- auto Format = makeFormat(CapitalizedExtension);
+ auto Format = makeFormat(Extension);
SerializationFormat *Result = Format.get();
assert(Result);
@@ -470,8 +468,6 @@ int main(int argc, const char **argv) {
loadPlugins();
- initializeJSONFormat();
-
if (ListFormats) {
listFormats();
} else {
diff --git a/clang/tools/ssaf-linker/SSAFLinker.cpp b/clang/tools/ssaf-linker/SSAFLinker.cpp
index e0b1cfdc02160..904cecb5d10fc 100644
--- a/clang/tools/ssaf-linker/SSAFLinker.cpp
+++ b/clang/tools/ssaf-linker/SSAFLinker.cpp
@@ -14,9 +14,9 @@
#include "clang/ScalableStaticAnalysisFramework/Core/EntityLinker/EntityLinker.h"
#include "clang/ScalableStaticAnalysisFramework/Core/EntityLinker/TUSummaryEncoding.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Model/BuildNamespace.h"
-#include "clang/ScalableStaticAnalysisFramework/Core/Serialization/JSONFormat.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Serialization/SerializationFormatRegistry.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Support/ErrorBuilder.h"
+#include "clang/ScalableStaticAnalysisFramework/SSAFForceLinker.h" // IWYU pragma: keep
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/CommandLine.h"
@@ -133,14 +133,11 @@ SerializationFormat *getFormatForExtension(llvm::StringRef Extension) {
return It->second.get();
}
- // SerializationFormats are uppercase while file extensions are lowercase.
- std::string CapitalizedExtension = Extension.upper();
-
- if (!isFormatRegistered(CapitalizedExtension)) {
+ if (!isFormatRegistered(Extension)) {
return nullptr;
}
- auto Format = makeFormat(CapitalizedExtension);
+ auto Format = makeFormat(Extension);
SerializationFormat *Result = Format.get();
assert(Result);
@@ -304,8 +301,6 @@ int main(int argc, const char **argv) {
// Parse command-line arguments and exit with an error if they are invalid.
cl::ParseCommandLineOptions(argc, argv, "SSAF Linker\n");
- initializeJSONFormat();
-
llvm::TimerGroup LinkerTimers(ToolName, "SSAF Linker");
LinkerInput LI;
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/CMakeLists.txt b/clang/unittests/ScalableStaticAnalysisFramework/CMakeLists.txt
index f7ab9c24f3723..d2d3536c198cb 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/CMakeLists.txt
+++ b/clang/unittests/ScalableStaticAnalysisFramework/CMakeLists.txt
@@ -8,6 +8,7 @@ add_distinct_clang_unittest(ClangScalableAnalysisTests
EntityLinkerTest.cpp
EntityNameTest.cpp
ErrorBuilderTest.cpp
+ Frontend/TUSummaryExtractorFrontendActionTest.cpp
ModelStringConversionsTest.cpp
Registries/FancyAnalysisData.cpp
Registries/MockSerializationFormat.cpp
@@ -26,8 +27,10 @@ add_distinct_clang_unittest(ClangScalableAnalysisTests
CLANG_LIBS
clangAST
clangASTMatchers
+ clangBasic
clangFrontend
clangScalableStaticAnalysisFrameworkCore
+ clangScalableStaticAnalysisFrameworkFrontend
clangSerialization
clangTooling
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Frontend/TUSummaryExtractorFrontendActionTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Frontend/TUSummaryExtractorFrontendActionTest.cpp
new file mode 100644
index 0000000000000..d684366ed53ce
--- /dev/null
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Frontend/TUSummaryExtractorFrontendActionTest.cpp
@@ -0,0 +1,366 @@
+//===- TUSummaryExtractorFrontendActionTest.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/Frontend/TUSummaryExtractorFrontendAction.h"
+#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/FrontendOptions.h"
+#include "clang/Frontend/TextDiagnosticBuffer.h"
+#include "clang/Lex/PreprocessorOptions.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Serialization/SerializationFormat.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Serialization/SerializationFormatRegistry.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/ExtractorRegistry.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryExtractor.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/VirtualFileSystem.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include <memory>
+#include <string>
+#include <vector>
+
+using namespace clang;
+using namespace ssaf;
+using ::testing::Contains;
+using ::testing::UnorderedElementsAre;
+
+static auto errorsMsgsOf(const TextDiagnosticBuffer &Diags) {
+ auto Errors = llvm::make_range(Diags.err_begin(), Diags.err_end());
+ return llvm::make_second_range(Errors);
+}
+namespace {
+
+/// A no-op TUSummaryExtractor suitable for use with a real TUSummaryBuilder.
+class NoOpExtractor : public TUSummaryExtractor {
+public:
+ using TUSummaryExtractor::TUSummaryExtractor;
+ void HandleTranslationUnit(ASTContext &Ctx) override {}
+};
+} // namespace
+
+// NOLINTNEXTLINE(misc-use-internal-linkage)
+volatile int SSAFNoOpExtractorAnchorSource = 0;
+static TUSummaryExtractorRegistry::Add<NoOpExtractor>
+ RegisterNoOp("NoOpExtractor", "No-op extractor for frontend action tests");
+
+namespace {
+class FailingSerializationFormat final : public SerializationFormat {
+public:
+ static llvm::Error failing(llvm::StringRef Component) {
+ return llvm::createStringError(
+ "error from always failing serialization format: " + Component);
+ }
+
+ llvm::Expected<TUSummary> readTUSummary(llvm::StringRef Path) override {
+ return failing("readTUSummary");
+ }
+
+ llvm::Error writeTUSummary(const TUSummary &Summary,
+ llvm::StringRef Path) override {
+ return failing("writeTUSummary");
+ }
+
+ llvm::Expected<TUSummaryEncoding>
+ readTUSummaryEncoding(llvm::StringRef Path) override {
+ return failing("readTUSummaryEncoding");
+ }
+
+ llvm::Error writeTUSummaryEncoding(const TUSummaryEncoding &SummaryEncoding,
+ llvm::StringRef Path) override {
+ return failing("writeTUSummaryEncoding");
+ }
+
+ llvm::Expected<LUSummary> readLUSummary(llvm::StringRef Path) override {
+ return failing("readLUSummary");
+ }
+
+ llvm::Error writeLUSummary(const LUSummary &Summary,
+ llvm::StringRef Path) override {
+ return failing("writeLUSummary");
+ }
+
+ llvm::Expected<LUSummaryEncoding>
+ readLUSummaryEncoding(llvm::StringRef Path) override {
+ return failing("readLUSummaryEncoding");
+ }
+
+ llvm::Error writeLUSummaryEncoding(const LUSummaryEncoding &SummaryEncoding,
+ llvm::StringRef Path) override {
+ return failing("writeLUSummaryEncoding");
+ }
+
+ void forEachRegisteredAnalysis(
+ llvm::function_ref<void(llvm::StringRef Name, llvm::StringRef Desc)>
+ Callback) const override {}
+};
+} // namespace
+
+// NOLINTNEXTLINE(misc-use-internal-linkage)
+volatile int SSAFFailingSerializationFormatAnchorSource = 0;
+static SerializationFormatRegistry::Add<FailingSerializationFormat>
+ RegisterFormat(
+ "FailingSerializationFormat",
+ "A serialization format that fails on every possible operation.");
+
+using EventLog = std::vector<std::string>;
+
+namespace {
+
+/// An ASTConsumer that logs callback invocations into a shared log.
+class RecordingASTConsumer : public ASTConsumer {
+public:
+ RecordingASTConsumer(EventLog &Log, std::string Tag)
+ : Log(Log), Tag(std::move(Tag)) {}
+
+ void Initialize(ASTContext &Ctx) override {
+ Log.push_back(Tag + "::Initialize");
+ }
+ bool HandleTopLevelDecl(DeclGroupRef D) override {
+ Log.push_back(Tag + "::HandleTopLevelDecl");
+ return true;
+ }
+ void HandleTranslationUnit(ASTContext &Ctx) override {
+ Log.push_back(Tag + "::HandleTranslationUnit");
+ }
+
+private:
+ EventLog &Log;
+ std::string Tag;
+};
+
+/// A FrontendAction that returns a RecordingASTConsumer with the tag "Wrapped".
+class RecordingAction : public ASTFrontendAction {
+public:
+ EventLog &getLog() { return Log; }
+ std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &,
+ StringRef) override {
+ return std::make_unique<RecordingASTConsumer>(Log, /*Tag=*/"Wrapped");
+ }
+
+private:
+ EventLog Log;
+};
+
+class FailingAction : public ASTFrontendAction {
+public:
+ std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &,
+ StringRef) override {
+ return nullptr;
+ }
+};
+
+/// Creates a CompilerInstance configured with an in-memory "test.cc" file
+/// containing "int x = 42;".
+static std::unique_ptr<CompilerInstance>
+makeCompiler(TextDiagnosticBuffer &DiagBuf) {
+ auto Invocation = std::make_shared<CompilerInvocation>();
+ Invocation->getPreprocessorOpts().addRemappedFile(
+ "test.cc", llvm::MemoryBuffer::getMemBuffer("int x = 42;").release());
+ Invocation->getFrontendOpts().Inputs.push_back(
+ FrontendInputFile("test.cc", Language::CXX));
+ Invocation->getFrontendOpts().ProgramAction = frontend::ParseSyntaxOnly;
+ Invocation->getTargetOpts().Triple = "i386-unknown-linux-gnu";
+ auto Compiler = std::make_unique<CompilerInstance>(std::move(Invocation));
+ Compiler->setVirtualFileSystem(llvm::vfs::getRealFileSystem());
+ Compiler->createDiagnostics(&DiagBuf, /*ShouldOwnClient=*/false);
+ return Compiler;
+}
+
+struct TUSummaryExtractorFrontendActionTest : testing::Test {
+ using PathString = llvm::SmallString<128>;
+ PathString TestDir;
+ TextDiagnosticBuffer DiagBuf;
+ std::unique_ptr<CompilerInstance> Compiler = makeCompiler(DiagBuf);
+
+ void SetUp() override {
+ std::error_code EC = llvm::sys::fs::createUniqueDirectory(
+ "ssaf-frontend-action-test", TestDir);
+ ASSERT_FALSE(EC) << "Failed to create temp directory: " << EC.message();
+ }
+
+ void TearDown() override { llvm::sys::fs::remove_directories(TestDir); }
+
+ std::string makePath(llvm::StringRef FileOrDirectoryName) const {
+ PathString FullPath = TestDir;
+ llvm::sys::path::append(FullPath, FileOrDirectoryName);
+ return FullPath.str().str();
+ }
+};
+
+TEST_F(TUSummaryExtractorFrontendActionTest,
+ WrappedActionFailsToCreateConsumer) {
+ // Configure valid SSAF options so the failure is purely from the wrapped
+ // action, not from runner creation.
+ std::string Output = makePath("output.MockSerializationFormat");
+ Compiler->getFrontendOpts().SSAFTUSummaryFile = Output;
+ Compiler->getFrontendOpts().SSAFExtractSummaries = {"NoOpExtractor"};
+
+ TUSummaryExtractorFrontendAction ExtractorAction(
+ std::make_unique<FailingAction>());
+ Compiler->ExecuteAction(ExtractorAction);
+
+ // If the wrapped action fails, the ExtractorAction should not output.
+ EXPECT_FALSE(llvm::sys::fs::exists(Output));
+}
+
+TEST_F(TUSummaryExtractorFrontendActionTest,
+ RunnerFailsWithInvalidFormat_WrappedConsumerStillRuns) {
+ // Use an unregistered format extension so TUSummaryRunner::create fails.
+ std::string Output = makePath("output.xyz");
+ Compiler->getFrontendOpts().SSAFTUSummaryFile = Output;
+ Compiler->getFrontendOpts().SSAFExtractSummaries = {"NoOpExtractor"};
+
+ auto Wrapped = std::make_unique<RecordingAction>();
+ const EventLog &Log = Wrapped->getLog();
+ TUSummaryExtractorFrontendAction ExtractorAction(std::move(Wrapped));
+
+ // The runner fails, so ExecuteAction should return false due to the fatal
+ // diagnostic.
+ EXPECT_FALSE(Compiler->ExecuteAction(ExtractorAction));
+
+ // The wrapped consumer should still have run.
+ EXPECT_THAT(Log, Contains("Wrapped::Initialize"));
+ EXPECT_THAT(Log, Contains("Wrapped::HandleTranslationUnit"));
+
+ // Exactly one error about the unknown format.
+ EXPECT_THAT(errorsMsgsOf(DiagBuf),
+ UnorderedElementsAre(
+ "unknown output summary file format 'xyz' specified by "
+ "'--ssaf-tu-summary-file=" +
+ Output + "'"));
+
+ // No output should have been created due to the failure.
+ EXPECT_FALSE(llvm::sys::fs::exists(Output));
+}
+
+TEST_F(TUSummaryExtractorFrontendActionTest,
+ RunnerFailsWithUnknownExtractor_WrappedConsumerStillRuns) {
+ std::string Output = makePath("output.MockSerializationFormat");
+ Compiler->getFrontendOpts().SSAFTUSummaryFile = Output;
+ Compiler->getFrontendOpts().SSAFExtractSummaries = {"NonExistentExtractor"};
+
+ auto Wrapped = std::make_unique<RecordingAction>();
+ const EventLog &Log = Wrapped->getLog();
+ TUSummaryExtractorFrontendAction ExtractorAction(std::move(Wrapped));
+ EXPECT_FALSE(Compiler->ExecuteAction(ExtractorAction));
+
+ // The wrapped consumer should still have run.
+ EXPECT_THAT(Log, Contains("Wrapped::Initialize"));
+ EXPECT_THAT(Log, Contains("Wrapped::HandleTranslationUnit"));
+
+ // Exactly one error about the unknown extractor.
+ EXPECT_THAT(errorsMsgsOf(DiagBuf),
+ UnorderedElementsAre("no summary extractor was registered with "
+ "name: NonExistentExtractor"));
+
+ // No output should have been created due to the failure.
+ EXPECT_FALSE(llvm::sys::fs::exists(Output));
+}
+
+TEST_F(TUSummaryExtractorFrontendActionTest,
+ RunnerSucceeds_ASTConsumerCallbacksPropagate) {
+ std::string Output = makePath("output.MockSerializationFormat");
+ Compiler->getFrontendOpts().SSAFTUSummaryFile = Output;
+ Compiler->getFrontendOpts().SSAFExtractSummaries = {"NoOpExtractor"};
+
+ auto Wrapped = std::make_unique<RecordingAction>();
+ const EventLog &Log = Wrapped->getLog();
+ TUSummaryExtractorFrontendAction ExtractorAction(std::move(Wrapped));
+ EXPECT_TRUE(Compiler->ExecuteAction(ExtractorAction));
+
+ // All wrapped ASTConsumer callbacks should have fired, not just
+ // HandleTranslationUnit.
+ EXPECT_THAT(Log, Contains("Wrapped::Initialize"));
+ EXPECT_THAT(Log, Contains("Wrapped::HandleTopLevelDecl"));
+ EXPECT_THAT(Log, Contains("Wrapped::HandleTranslationUnit"));
+ EXPECT_EQ(DiagBuf.getNumErrors(), 0U);
+
+ // The runner should have written output.
+ EXPECT_TRUE(llvm::sys::fs::exists(Output));
+}
+
+// Use a custom action that checks whether the output path exists during
+// HandleTranslationUnit — it should not, because the wrapped consumer runs
+// before the runner.
+struct OrderCheckingAction : public ASTFrontendAction {
+ EventLog Log;
+ std::string OutputPath;
+
+ std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
+ StringRef InFile) override {
+ struct Consumer : public ASTConsumer {
+ Consumer(EventLog &Log, std::string OutputPath)
+ : Log(Log), OutputPath(std::move(OutputPath)) {}
+ void Initialize(ASTContext &) override {
+ Log.push_back("Wrapped::Initialize");
+ }
+ bool HandleTopLevelDecl(DeclGroupRef) override {
+ Log.push_back("Wrapped::HandleTopLevelDecl");
+ return true;
+ }
+ void HandleTranslationUnit(ASTContext &) override {
+ bool Exists = llvm::sys::fs::exists(OutputPath);
+ Log.push_back(std::string("OutputExistsDuringWrappedHTU=") +
+ (Exists ? "true" : "false"));
+ Log.push_back("Wrapped::HandleTranslationUnit");
+ }
+
+ EventLog &Log;
+ std::string OutputPath;
+ };
+ return std::make_unique<Consumer>(Log, OutputPath);
+ }
+};
+TEST_F(TUSummaryExtractorFrontendActionTest,
+ RunnerSucceeds_WrappedRunsBeforeRunner) {
+ std::string Output = makePath("output.MockSerializationFormat");
+ Compiler->getFrontendOpts().SSAFTUSummaryFile = Output;
+ Compiler->getFrontendOpts().SSAFExtractSummaries = {"NoOpExtractor"};
+
+ auto Wrapped = std::make_unique<OrderCheckingAction>();
+ Wrapped->OutputPath = Output;
+ const EventLog &Log = Wrapped->Log;
+ TUSummaryExtractorFrontendAction Action(std::move(Wrapped));
+
+ EXPECT_TRUE(Compiler->ExecuteAction(Action));
+ EXPECT_EQ(DiagBuf.getNumErrors(), 0U);
+
+ // The output should NOT have existed when the wrapped consumer's
+ // HandleTranslationUnit ran (wrapped is at index 0, runner at index 1).
+ EXPECT_THAT(Log, Contains("OutputExistsDuringWrappedHTU=false"));
+
+ // After ExecuteAction, the output should exist.
+ EXPECT_TRUE(llvm::sys::fs::exists(Output));
+}
+
+TEST_F(TUSummaryExtractorFrontendActionTest, RunnerFailsToWrite) {
+ std::string Output = makePath("output.FailingSerializationFormat");
+ Compiler->getFrontendOpts().SSAFTUSummaryFile = Output;
+ Compiler->getFrontendOpts().SSAFExtractSummaries = {"NoOpExtractor"};
+
+ TUSummaryExtractorFrontendAction Action(std::make_unique<RecordingAction>());
+
+ // This should fail because the summary writing fails and emits an error
+ // diagnostic.
+ EXPECT_FALSE(Compiler->ExecuteAction(Action));
+ EXPECT_THAT(
+ errorsMsgsOf(DiagBuf),
+ UnorderedElementsAre(
+ "failed to write TU summary to '" + Output +
+ "': error from always failing serialization format: writeTUSummary"));
+
+ // No output should have been created due to the failure.
+ EXPECT_FALSE(llvm::sys::fs::exists(Output));
+}
+
+} // namespace
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Registries/FancyAnalysisData.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Registries/FancyAnalysisData.cpp
index 084835190f7bd..313c53518dfe8 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Registries/FancyAnalysisData.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Registries/FancyAnalysisData.cpp
@@ -54,6 +54,8 @@ struct FancyAnalysisFormatInfo final : FormatInfo {
};
} // namespace
+// NOLINTNEXTLINE(misc-use-internal-linkage)
+volatile int SSAFFancyAnalysisDataAnchorSource = 0;
static llvm::Registry<FormatInfo>::Add<FancyAnalysisFormatInfo>
RegisterFormatInfo("FancyAnalysisData",
"Format info for FancyAnalysisData for the "
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Registries/MockSerializationFormat.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Registries/MockSerializationFormat.cpp
index e7a3e90e0bb31..535b5fced0da6 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Registries/MockSerializationFormat.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Registries/MockSerializationFormat.cpp
@@ -160,6 +160,8 @@ llvm::Error MockSerializationFormat::writeTUSummary(const TUSummary &Summary,
return llvm::Error::success();
}
+// NOLINTNEXTLINE(misc-use-internal-linkage)
+volatile int SSAFMockSerializationFormatAnchorSource = 0;
static SerializationFormatRegistry::Add<MockSerializationFormat>
RegisterFormat("MockSerializationFormat",
"A serialization format for testing");
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Registries/MockSummaryExtractor1.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Registries/MockSummaryExtractor1.cpp
index 7f4e9a91febbb..1bce78c8b1030 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Registries/MockSummaryExtractor1.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Registries/MockSummaryExtractor1.cpp
@@ -38,7 +38,9 @@ class MockSummaryExtractor1 : public TUSummaryExtractor {
}
};
+} // namespace
+
+// NOLINTNEXTLINE(misc-use-internal-linkage)
+volatile int SSAFMockSummaryExtractor1AnchorSource = 0;
static TUSummaryExtractorRegistry::Add<MockSummaryExtractor1>
RegisterExtractor("MockSummaryExtractor1", "Mock summary extractor 1");
-
-} // namespace
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Registries/MockSummaryExtractor2.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Registries/MockSummaryExtractor2.cpp
index 640228e2b7e2c..242f427f5e346 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Registries/MockSummaryExtractor2.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Registries/MockSummaryExtractor2.cpp
@@ -38,7 +38,9 @@ class MockSummaryExtractor2 : public TUSummaryExtractor {
}
};
+} // namespace
+
+// NOLINTNEXTLINE(misc-use-internal-linkage)
+volatile int SSAFMockSummaryExtractor2AnchorSource = 0;
static TUSummaryExtractorRegistry::Add<MockSummaryExtractor2>
RegisterExtractor("MockSummaryExtractor2", "Mock summary extractor 2");
-
-} // namespace
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Registries/SummaryExtractorRegistryTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Registries/SummaryExtractorRegistryTest.cpp
index 5211319063b60..2018beebd53da 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Registries/SummaryExtractorRegistryTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Registries/SummaryExtractorRegistryTest.cpp
@@ -41,6 +41,7 @@ TEST(SummaryExtractorRegistryTest, EnumeratingRegistryEntries) {
EXPECT_EQ(ActualNames, (std::set<llvm::StringRef>{
"MockSummaryExtractor1",
"MockSummaryExtractor2",
+ "NoOpExtractor",
}));
}
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/SSAFBuiltinTestForceLinker.h b/clang/unittests/ScalableStaticAnalysisFramework/SSAFBuiltinTestForceLinker.h
new file mode 100644
index 0000000000000..05d96af80cb27
--- /dev/null
+++ b/clang/unittests/ScalableStaticAnalysisFramework/SSAFBuiltinTestForceLinker.h
@@ -0,0 +1,51 @@
+//===- SSAFBuiltinTestForceLinker.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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file pulls in all test-only SSAF mock extractor and format
+/// registrations by referencing their anchor symbols.
+///
+/// Include this header (with IWYU pragma: keep) in a translation unit that
+/// is compiled into the SSAF unittest binary.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_UNITTESTS_SCALABLESTATICANALYSISFRAMEWORK_SSAFBUILTINTESTFORCELINKER_H
+#define LLVM_CLANG_UNITTESTS_SCALABLESTATICANALYSISFRAMEWORK_SSAFBUILTINTESTFORCELINKER_H
+
+// Force the linker to link NoOpExtractor registration.
+extern volatile int SSAFNoOpExtractorAnchorSource;
+[[maybe_unused]] static int SSAFNoOpExtractorAnchorDestination =
+ SSAFNoOpExtractorAnchorSource;
+
+// Force the linker to link MockSummaryExtractor1 registration.
+extern volatile int SSAFMockSummaryExtractor1AnchorSource;
+[[maybe_unused]] static int SSAFMockSummaryExtractor1AnchorDestination =
+ SSAFMockSummaryExtractor1AnchorSource;
+
+// Force the linker to link MockSummaryExtractor2 registration.
+extern volatile int SSAFMockSummaryExtractor2AnchorSource;
+[[maybe_unused]] static int SSAFMockSummaryExtractor2AnchorDestination =
+ SSAFMockSummaryExtractor2AnchorSource;
+
+// Force the linker to link FailingSerializationFormat registration.
+extern volatile int SSAFFailingSerializationFormatAnchorSource;
+[[maybe_unused]] static int SSAFFailingSerializationFormatAnchorDestination =
+ SSAFFailingSerializationFormatAnchorSource;
+
+// Force the linker to link MockSerializationFormat registration.
+extern volatile int SSAFMockSerializationFormatAnchorSource;
+[[maybe_unused]] static int SSAFMockSerializationFormatAnchorDestination =
+ SSAFMockSerializationFormatAnchorSource;
+
+// Force the linker to link FancyAnalysisData format info registration.
+extern volatile int SSAFFancyAnalysisDataAnchorSource;
+[[maybe_unused]] static int SSAFFancyAnalysisDataAnchorDestination =
+ SSAFFancyAnalysisDataAnchorSource;
+
+#endif // LLVM_CLANG_UNITTESTS_SCALABLESTATICANALYSISFRAMEWORK_SSAFBUILTINTESTFORCELINKER_H
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/SSAFTestForceLinker.h b/clang/unittests/ScalableStaticAnalysisFramework/SSAFTestForceLinker.h
new file mode 100644
index 0000000000000..dd2077569a4eb
--- /dev/null
+++ b/clang/unittests/ScalableStaticAnalysisFramework/SSAFTestForceLinker.h
@@ -0,0 +1,23 @@
+//===- SSAFTestForceLinker.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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file pulls in all test-only SSAF mock extractor and format
+/// registrations by referencing their anchor symbols.
+///
+/// Include this header (with IWYU pragma: keep) in a translation unit that
+/// is compiled into the SSAF unittest binary.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_UNITTESTS_SCALABLESTATICANALYSISFRAMEWORK_SSAFTESTFORCELINKER_H
+#define LLVM_CLANG_UNITTESTS_SCALABLESTATICANALYSISFRAMEWORK_SSAFTESTFORCELINKER_H
+
+#include "SSAFBuiltinTestForceLinker.h" // IWYU pragma: keep
+
+#endif // LLVM_CLANG_UNITTESTS_SCALABLESTATICANALYSISFRAMEWORK_SSAFTESTFORCELINKER_H
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/TestFixture.cpp b/clang/unittests/ScalableStaticAnalysisFramework/TestFixture.cpp
index c1c41997abcf2..772eaf069a350 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/TestFixture.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/TestFixture.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "TestFixture.h"
+#include "SSAFBuiltinTestForceLinker.h" // IWYU pragma: keep
#include "clang/ScalableStaticAnalysisFramework/Core/Model/BuildNamespace.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityLinkage.h"
diff --git a/llvm/utils/gn/secondary/clang/lib/FrontendTool/BUILD.gn b/llvm/utils/gn/secondary/clang/lib/FrontendTool/BUILD.gn
index 707eabf1af70b..60157daa66d40 100644
--- a/llvm/utils/gn/secondary/clang/lib/FrontendTool/BUILD.gn
+++ b/llvm/utils/gn/secondary/clang/lib/FrontendTool/BUILD.gn
@@ -12,6 +12,8 @@ static_library("FrontendTool") {
"//clang/lib/Frontend",
"//clang/lib/Frontend/Rewrite",
"//clang/lib/Options",
+ "//clang/lib/ScalableStaticAnalysisFramework/Core",
+ "//clang/lib/ScalableStaticAnalysisFramework/Frontend",
"//llvm/lib/Option",
"//llvm/lib/Support",
]
diff --git a/llvm/utils/gn/secondary/clang/lib/ScalableStaticAnalysisFramework/Frontend/BUILD.gn b/llvm/utils/gn/secondary/clang/lib/ScalableStaticAnalysisFramework/Frontend/BUILD.gn
new file mode 100644
index 0000000000000..96da539ae24da
--- /dev/null
+++ b/llvm/utils/gn/secondary/clang/lib/ScalableStaticAnalysisFramework/Frontend/BUILD.gn
@@ -0,0 +1,15 @@
+static_library("Frontend") {
+ output_name = "clangScalableStaticAnalysisFrameworkFrontend"
+ configs += [ "//llvm/utils/gn/build:clang_code" ]
+ deps = [
+ "//clang/lib/AST",
+ "//clang/lib/Basic",
+ "//clang/lib/Frontend",
+ "//clang/lib/ScalableStaticAnalysisFramework/Core",
+ "//clang/lib/Sema",
+ "//llvm/lib/Support",
+ ]
+ sources = [
+ "TUSummaryExtractorFrontendAction.cpp",
+ ]
+}
diff --git a/llvm/utils/gn/secondary/clang/unittests/ScalableStaticAnalysisFramework/BUILD.gn b/llvm/utils/gn/secondary/clang/unittests/ScalableStaticAnalysisFramework/BUILD.gn
index 8e19ddf03dd77..9c5b5b18fe94b 100644
--- a/llvm/utils/gn/secondary/clang/unittests/ScalableStaticAnalysisFramework/BUILD.gn
+++ b/llvm/utils/gn/secondary/clang/unittests/ScalableStaticAnalysisFramework/BUILD.gn
@@ -5,8 +5,10 @@ unittest("ClangScalableAnalysisTests") {
deps = [
"//clang/lib/AST",
"//clang/lib/ASTMatchers",
+ "//clang/lib/Basic",
"//clang/lib/Frontend",
"//clang/lib/ScalableStaticAnalysisFramework/Core",
+ "//clang/lib/ScalableStaticAnalysisFramework/Frontend",
"//clang/lib/Serialization",
"//clang/lib/Tooling",
"//llvm/lib/Testing/Support",
@@ -22,6 +24,7 @@ unittest("ClangScalableAnalysisTests") {
"EntityLinkerTest.cpp",
"EntityNameTest.cpp",
"ErrorBuilderTest.cpp",
+ "Frontend/TUSummaryExtractorFrontendActionTest.cpp",
"ModelStringConversionsTest.cpp",
"Registries/FancyAnalysisData.cpp",
"Registries/MockSerializationFormat.cpp",
More information about the cfe-commits
mailing list