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
