[clang] 3a10c3a - [clang][SSAF] Optionally skip system-header contributors (#205446)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Jul 15 10:55:43 PDT 2026
Author: Rashmi Mudduluru
Date: 2026-07-15T10:55:39-07:00
New Revision: 3a10c3aa3c2087ca4e6f0a18e182ced84a8a7fd0
URL: https://github.com/llvm/llvm-project/commit/3a10c3aa3c2087ca4e6f0a18e182ced84a8a7fd0
DIFF: https://github.com/llvm/llvm-project/commit/3a10c3aa3c2087ca4e6f0a18e182ced84a8a7fd0.diff
LOG: [clang][SSAF] Optionally skip system-header contributors (#205446)
A new —ssaf-no-extract-from-system-headers switch that gates the
contributor finder so contributors located inside system headers are
dropped from the per-TU summary.
rdar://179151040
---------
Co-authored-by: Balázs Benics <benicsbalazs at gmail.com>
Added:
clang/test/Analysis/Scalable/PointerFlow/system-header-opt-out.cpp
Modified:
clang/include/clang/Frontend/SSAFOptions.h
clang/include/clang/Options/Options.td
clang/lib/Driver/ToolChains/Clang.cpp
clang/lib/ScalableStaticAnalysis/Analyses/PointerFlow/PointerFlowExtractor.cpp
clang/lib/ScalableStaticAnalysis/Analyses/SSAFAnalysesCommon.cpp
clang/lib/ScalableStaticAnalysis/Analyses/SSAFAnalysesCommon.h
clang/lib/ScalableStaticAnalysis/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
clang/test/Analysis/Scalable/help.cpp
clang/unittests/ScalableStaticAnalysis/Analyses/PointerFlow/PointerFlowTest.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/Frontend/SSAFOptions.h b/clang/include/clang/Frontend/SSAFOptions.h
index f760d51ab5414..737d8809ce24e 100644
--- a/clang/include/clang/Frontend/SSAFOptions.h
+++ b/clang/include/clang/Frontend/SSAFOptions.h
@@ -47,10 +47,17 @@ class SSAFOptions {
LLVM_PREFERRED_TYPE(bool)
unsigned IncludeLocalEntities : 1;
+ /// Extract from system-header declarations during SSAF contributor
+ /// enumeration. Defaults to true to preserve the original behavior.
+ /// Controlled by: --ssaf-no-extract-from-system-headers
+ LLVM_PREFERRED_TYPE(bool)
+ unsigned ExtractFromSystemHeaders : 1;
+
SSAFOptions() {
ShowExtractors = false;
ShowFormats = false;
IncludeLocalEntities = false;
+ ExtractFromSystemHeaders = true;
};
};
diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td
index e6868e6b9a4ba..bad443efb6c65 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -990,6 +990,13 @@ def _ssaf_include_local_entities :
"Include block-scope (function-local) declarations in extracted SSAF "
"summaries. By default they are omitted.">,
MarshallingInfoFlag<SSAFOpts<"IncludeLocalEntities">>;
+def _ssaf_no_extract_from_system_headers :
+ Flag<["--"], "ssaf-no-extract-from-system-headers">,
+ Group<SSAF_Group>,
+ Visibility<[ClangOption, CC1Option]>,
+ HelpText<
+ "Skip declarations in system headers during SSAF summary extraction">,
+ MarshallingInfoNegativeFlag<SSAFOpts<"ExtractFromSystemHeaders">>;
def Xarch__
: JoinedAndSeparate<["-"], "Xarch_">,
Flags<[NoXarchOption]>,
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index c2b44919c1305..bdf72f848aeff 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -8082,6 +8082,7 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
Args.AddLastArg(CmdArgs, options::OPT__ssaf_tu_summary_file);
Args.AddLastArg(CmdArgs, options::OPT__ssaf_compilation_unit_id);
Args.AddLastArg(CmdArgs, options::OPT__ssaf_include_local_entities);
+ Args.AddLastArg(CmdArgs, options::OPT__ssaf_no_extract_from_system_headers);
// Handle serialized diagnostics.
if (Arg *A = Args.getLastArg(options::OPT__serialize_diags)) {
diff --git a/clang/lib/ScalableStaticAnalysis/Analyses/PointerFlow/PointerFlowExtractor.cpp b/clang/lib/ScalableStaticAnalysis/Analyses/PointerFlow/PointerFlowExtractor.cpp
index 8961a90acaf81..42d5b5a7de041 100644
--- a/clang/lib/ScalableStaticAnalysis/Analyses/PointerFlow/PointerFlowExtractor.cpp
+++ b/clang/lib/ScalableStaticAnalysis/Analyses/PointerFlow/PointerFlowExtractor.cpp
@@ -15,6 +15,7 @@
#include "clang/AST/ExprCXX.h"
#include "clang/AST/Stmt.h"
#include "clang/AST/TypeBase.h"
+#include "clang/Frontend/SSAFOptions.h"
#include "clang/ScalableStaticAnalysis/Analyses/EntityPointerLevel/EntityPointerLevel.h"
#include "clang/ScalableStaticAnalysis/Analyses/PointerFlow/PointerFlow.h"
#include "clang/ScalableStaticAnalysis/Core/Model/EntityId.h"
diff --git a/clang/lib/ScalableStaticAnalysis/Analyses/SSAFAnalysesCommon.cpp b/clang/lib/ScalableStaticAnalysis/Analyses/SSAFAnalysesCommon.cpp
index 80cf371220298..9d7e3a7bbbea0 100644
--- a/clang/lib/ScalableStaticAnalysis/Analyses/SSAFAnalysesCommon.cpp
+++ b/clang/lib/ScalableStaticAnalysis/Analyses/SSAFAnalysesCommon.cpp
@@ -12,6 +12,7 @@
#include "clang/AST/DeclObjC.h"
#include "clang/AST/DynamicRecursiveASTVisitor.h"
#include "clang/AST/ExprCXX.h"
+#include "clang/Basic/SourceManager.h"
#include "clang/Frontend/SSAFOptions.h"
#include "llvm/ADT/SetVector.h"
@@ -37,22 +38,30 @@ class ContributorFinder : public DynamicRecursiveASTVisitor {
llvm::SetVector<const NamedDecl *> Contributors;
const SSAFOptions &Opts;
- ContributorFinder(const SSAFOptions &Opts) : Opts(Opts) {
+ ContributorFinder(ASTContext &Ctx, const SSAFOptions &Opts,
+ bool ExtractFromSystemHeaders)
+ : Opts(Opts), Ctx(Ctx),
+ ExtractFromSystemHeaders(ExtractFromSystemHeaders) {
ShouldVisitTemplateInstantiations = true;
ShouldVisitImplicitCode = false;
}
bool VisitFunctionDecl(FunctionDecl *D) override {
- Contributors.insert(D);
+ if (!skipForSystemHeader(D))
+ Contributors.insert(D);
return true;
}
bool VisitRecordDecl(RecordDecl *D) override {
+ if (skipForSystemHeader(D))
+ return true;
Contributors.insert(D);
return true;
}
bool VisitVarDecl(VarDecl *D) override {
+ if (skipForSystemHeader(D))
+ return true;
DeclContext *DC = D->getDeclContext();
// Collects Decl for global variables or static data members:
@@ -78,11 +87,19 @@ class ContributorFinder : public DynamicRecursiveASTVisitor {
}
bool VisitLambdaExpr(LambdaExpr *L) override {
- // TraverseLambdaExpr directly visits the body stmt, skipping the
- // CXXMethodDecl, which is a contributor that needs to be collected.
- VisitFunctionDecl(L->getCallOperator());
- return true;
+ return VisitFunctionDecl(L->getCallOperator());
+ }
+
+private:
+ bool skipForSystemHeader(const Decl *D) const {
+ if (ExtractFromSystemHeaders)
+ return false;
+ SourceLocation Loc = D->getLocation();
+ return Loc.isValid() && Ctx.getSourceManager().isInSystemHeader(Loc);
}
+
+ ASTContext &Ctx;
+ bool ExtractFromSystemHeaders;
};
/// An AST visitor that skips the root node's strict-descendants that are
@@ -146,8 +163,9 @@ class ContributorFactFinder : public DynamicRecursiveASTVisitor {
void ssaf::findContributors(
ASTContext &Ctx, const SSAFOptions &Options,
llvm::DenseMap<const NamedDecl *, std::vector<const NamedDecl *>>
- &Contributors) {
- ContributorFinder Finder{Options};
+ &Contributors,
+ bool ExtractFromSystemHeaders) {
+ ContributorFinder Finder{Ctx, Options, ExtractFromSystemHeaders};
Finder.TraverseAST(Ctx);
for (const NamedDecl *C : Finder.Contributors)
Contributors[cast<NamedDecl>(C->getCanonicalDecl())].push_back(C);
diff --git a/clang/lib/ScalableStaticAnalysis/Analyses/SSAFAnalysesCommon.h b/clang/lib/ScalableStaticAnalysis/Analyses/SSAFAnalysesCommon.h
index 2e602a1ad35b9..95e3411cec386 100644
--- a/clang/lib/ScalableStaticAnalysis/Analyses/SSAFAnalysesCommon.h
+++ b/clang/lib/ScalableStaticAnalysis/Analyses/SSAFAnalysesCommon.h
@@ -15,6 +15,7 @@
#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTTypeTraits.h"
#include "clang/AST/Decl.h"
+#include "clang/Frontend/SSAFOptions.h"
#include "clang/ScalableStaticAnalysis/Core/Model/EntityId.h"
#include "clang/ScalableStaticAnalysis/Core/TUSummary/TUSummaryBuilder.h"
#include "clang/ScalableStaticAnalysis/Core/TUSummary/TUSummaryExtractor.h"
@@ -87,7 +88,8 @@ inline void logWarningFromError(llvm::Error Err) {
void findContributors(
ASTContext &Ctx, const SSAFOptions &Options,
llvm::DenseMap<const NamedDecl *, std::vector<const NamedDecl *>>
- &Contributors);
+ &Contributors,
+ bool ExtractFromSystemHeaders = true);
/// Perform "MatchAction" on each Stmt and Decl belonging to the `Contributor`.
/// \param Contributor
@@ -116,7 +118,8 @@ void extractAndAddSummaries(TUSummaryExtractor &Extractor,
llvm::StringRef ExtractorName = "") {
llvm::DenseMap<const NamedDecl *, std::vector<const NamedDecl *>>
Contributors;
- findContributors(Ctx, Extractor.getOptions(), Contributors);
+ findContributors(Ctx, Extractor.getOptions(), Contributors,
+ Extractor.getOptions().ExtractFromSystemHeaders);
for (const auto &[Cano, Decls] : Contributors) {
assert(!Decls.empty() &&
"'findContributors' guarantees that 'Decls' are non-empty");
diff --git a/clang/lib/ScalableStaticAnalysis/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp b/clang/lib/ScalableStaticAnalysis/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
index 24f49ef05e653..ff9ee00c41269 100644
--- a/clang/lib/ScalableStaticAnalysis/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
+++ b/clang/lib/ScalableStaticAnalysis/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
@@ -11,6 +11,7 @@
#include "clang/AST/ASTContext.h"
#include "clang/AST/DynamicRecursiveASTVisitor.h"
#include "clang/Analysis/Analyses/UnsafeBufferUsage.h"
+#include "clang/Frontend/SSAFOptions.h"
#include "clang/ScalableStaticAnalysis/Analyses/EntityPointerLevel/EntityPointerLevel.h"
#include "clang/ScalableStaticAnalysis/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
#include "clang/ScalableStaticAnalysis/Core/TUSummary/ExtractorRegistry.h"
diff --git a/clang/test/Analysis/Scalable/PointerFlow/system-header-opt-out.cpp b/clang/test/Analysis/Scalable/PointerFlow/system-header-opt-out.cpp
new file mode 100644
index 0000000000000..357e63d9d9f5c
--- /dev/null
+++ b/clang/test/Analysis/Scalable/PointerFlow/system-header-opt-out.cpp
@@ -0,0 +1,42 @@
+// Synthesise an -isystem header containing a benign user-named
+// symbol.
+
+// REQUIRES: system-darwin || system-linux
+
+// RUN: rm -rf %t
+// RUN: mkdir -p %t
+// RUN: split-file %s %t
+
+// === Case A: flag absent (default extracts from system headers). ===
+// The extractor enumerates both sys_fn and user_fn; the TU summary's
+// IdTable contains both names.
+// RUN: %clang -c %t/test.cpp -o %t/default.o -isystem %t/sysinc \
+// RUN: --ssaf-extract-summaries=PointerFlow \
+// RUN: --ssaf-tu-summary-file=%t/default.json \
+// RUN: --ssaf-compilation-unit-id=sys-default
+// RUN: FileCheck --check-prefix=DEFAULT %s < %t/default.json
+// DEFAULT-DAG: sys_fn
+// DEFAULT-DAG: user_fn
+
+// === Case B: flag present (opt-out skips system-header decls). ===
+// The extractor skips sys_fn (system header) but keeps user_fn.
+// The TU summary's IdTable contains user_fn but NOT sys_fn.
+// RUN: %clang -c %t/test.cpp -o %t/optout.o -isystem %t/sysinc \
+// RUN: --ssaf-extract-summaries=PointerFlow \
+// RUN: --ssaf-tu-summary-file=%t/optout.json \
+// RUN: --ssaf-no-extract-from-system-headers \
+// RUN: --ssaf-compilation-unit-id=sys-optout
+// RUN: FileCheck --check-prefix=OPTOUT %s < %t/optout.json
+// OPTOUT-NOT: sys_fn
+// OPTOUT: user_fn
+
+//--- sysinc/sys.h
+#pragma clang system_header
+int *sys_gp;
+void sys_fn(int *p) { sys_gp = p; }
+
+//--- test.cpp
+#include <sys.h>
+
+int *user_gp;
+void user_fn(int *p) { user_gp = p; }
diff --git a/clang/test/Analysis/Scalable/help.cpp b/clang/test/Analysis/Scalable/help.cpp
index 3feae2dfaa456..4e210abe35b9f 100644
--- a/clang/test/Analysis/Scalable/help.cpp
+++ b/clang/test/Analysis/Scalable/help.cpp
@@ -11,6 +11,8 @@
// HELP-NEXT: Include block-scope (function-local) declarations in extracted SSAF summaries. By default they are omitted.
// HELP-NEXT: --ssaf-list-extractors Display the list of available SSAF summary extractors
// HELP-NEXT: --ssaf-list-formats Display the list of available SSAF serialization formats
+// HELP-NEXT: --ssaf-no-extract-from-system-headers
+// HELP-NEXT: Skip declarations in system headers during SSAF summary extraction
// HELP-NEXT: --ssaf-tu-summary-file=<path>.<format>
// HELP-NEXT: The output file for the extracted summaries. The extension selects which file format to use.
diff --git a/clang/unittests/ScalableStaticAnalysis/Analyses/PointerFlow/PointerFlowTest.cpp b/clang/unittests/ScalableStaticAnalysis/Analyses/PointerFlow/PointerFlowTest.cpp
index a56a9072be097..1260aad81a8bf 100644
--- a/clang/unittests/ScalableStaticAnalysis/Analyses/PointerFlow/PointerFlowTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysis/Analyses/PointerFlow/PointerFlowTest.cpp
@@ -13,6 +13,7 @@
#include "clang/AST/DynamicRecursiveASTVisitor.h"
#include "clang/AST/ExprCXX.h"
#include "clang/Frontend/ASTUnit.h"
+#include "clang/Frontend/PCHContainerOperations.h"
#include "clang/Frontend/SSAFOptions.h"
#include "clang/ScalableStaticAnalysis/Core/Model/EntityId.h"
#include "clang/ScalableStaticAnalysis/Core/TUSummary/ExtractorRegistry.h"
@@ -181,6 +182,36 @@ class PointerFlowTest : public TestFixture {
return true;
}
+ // Variant that mounts a virtual `<sys.h>` header (with
+ // `#pragma clang system_header` prepended) on an `-isystem` path,
+ // letting tests exercise the system-header contributor gate.
+ // Returns true on AST build + extractor instantiation success.
+ bool setUpTestWithSystemHeader(StringRef Code, StringRef SysHeaderCode,
+ bool ExtractFromSystemHeaders) {
+ Opts.ExtractFromSystemHeaders = ExtractFromSystemHeaders;
+ std::string SysWithPragma =
+ ("#pragma clang system_header\n" + SysHeaderCode).str();
+ tooling::FileContentMappings VirtFiles = {{"/sysinc/sys.h", SysWithPragma}};
+ AST = tooling::buildASTFromCodeWithArgs(
+ Code,
+ {"-Wno-unused-value", "-Wno-int-to-pointer-cast", "-isystem/sysinc"},
+ "input.cc", "clang-tool", std::make_shared<PCHContainerOperations>(),
+ tooling::getClangStripDependencyFileAdjuster(), VirtFiles);
+
+ for (auto &E : clang::ssaf::TUSummaryExtractorRegistry::entries()) {
+ if (E.getName() == PointerFlowEntitySummary::Name) {
+ Extractor = E.instantiate(Builder);
+ break;
+ }
+ }
+ if (!Extractor) {
+ ADD_FAILURE() << "failed to find PointerFlowTUSummaryExtractor";
+ return false;
+ }
+ Extractor->HandleTranslationUnit(AST->getASTContext());
+ return true;
+ }
+
template <typename ContributorDecl = NamedDecl>
const PointerFlowEntitySummary *getEntitySummary(FindEntityByName Name) {
const auto *ContributorDefn =
@@ -1618,4 +1649,39 @@ TEST_F(PointerFlowTest, LocalPointerReportedWhenIncluded) {
EXPECT_TRUE(getEntitySummary("foo"));
}
+
+//////////////////////////////////////////////////////////////
+// System-header contributor opt-out gate. //
+// Spec: tu-summary-extraction, //
+// "System-header contributor opt-out flag". //
+//////////////////////////////////////////////////////////////
+
+// Default: ExtractFromSystemHeaders == true. A function decl in a
+// `#pragma clang system_header`-marked included header IS enumerated
+// as a contributor and produces an EntitySummary.
+TEST_F(PointerFlowTest, ExtractFromSystemHeadersByDefault) {
+ const char *SysHeader = "int *sys_gp; void sys_fn(int *p) { sys_gp = p; }\n";
+ const char *Main = R"cpp(
+ #include <sys.h>
+ int *user_gp;
+ void user_fn(int *p) { user_gp = p; }
+ )cpp";
+ ASSERT_TRUE(setUpTestWithSystemHeader(Main, SysHeader,
+ /*ExtractFromSystemHeaders=*/true));
+ EXPECT_TRUE(getEntitySummary("sys_fn"));
+ EXPECT_TRUE(getEntitySummary("user_fn"));
+}
+
+TEST_F(PointerFlowTest, DontExtractFromSystemHeadersWhenOverridden) {
+ const char *SysHeader = "int *sys_gp; void sys_fn(int *p) { sys_gp = p; }\n";
+ const char *Main = R"cpp(
+ #include <sys.h>
+ int *user_gp;
+ void user_fn(int *p) { user_gp = p; }
+ )cpp";
+ ASSERT_TRUE(setUpTestWithSystemHeader(Main, SysHeader,
+ /*ExtractFromSystemHeaders=*/false));
+ EXPECT_FALSE(getEntitySummary("sys_fn")); // 'sys_fn' is skipped.
+ EXPECT_TRUE(getEntitySummary("user_fn")); // 'user_fn' is still present.
+}
} // namespace
More information about the cfe-commits
mailing list