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 <[email protected]>

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


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to