diff --git a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningService.h b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningService.h index a4a03b88b2051..dcdf1c171f6d7 100644 --- a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningService.h +++ b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningService.h @@ -51,8 +51,11 @@ enum class ScanningOptimizations { /// Remove unused header search paths including header maps. HeaderSearch = 1, - LLVM_MARK_AS_BITMASK_ENUM(HeaderSearch), - All = HeaderSearch, + /// Remove warnings from system modules. + SystemWarnings = 2, + + LLVM_MARK_AS_BITMASK_ENUM(SystemWarnings), + All = HeaderSearch | SystemWarnings, Default = All }; diff --git a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp index 5d95bb305bc38..9099c18391e4d 100644 --- a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp +++ b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp @@ -52,6 +52,28 @@ static void optimizeHeaderSearchOpts(HeaderSearchOptions &Opts, Opts.UserEntries.push_back(Entries[Idx]); } +static void optimizeDiagnosticOpts(DiagnosticOptions &Opts, + bool IsSystemModule) { + // If this is not a system module or -Wsystem-headers was passed, don't + // optimize. + if (!IsSystemModule) + return; + bool Wsystem_headers = false; + for (StringRef Opt : Opts.Warnings) { + bool isPositive = !Opt.consume_front("no-"); + if (Opt == "system-headers") + Wsystem_headers = isPositive; + } + if (Wsystem_headers) + return; + + // Remove all warning flags. System modules suppress most, but not all, + // warnings. + Opts.Warnings.clear(); + Opts.UndefPrefixes.clear(); + Opts.Remarks.clear(); +} + static std::vector splitString(std::string S, char Separator) { SmallVector Segments; StringRef(S).split(Segments, Separator, /*MaxSplit=*/-1, /*KeepEmpty=*/false); @@ -532,6 +554,10 @@ ModuleDepCollectorPP::handleTopLevelModule(const Module *M) { if (any(MDC.OptimizeArgs & ScanningOptimizations::HeaderSearch)) optimizeHeaderSearchOpts(BuildInvocation.getMutHeaderSearchOpts(), *MDC.ScanInstance.getASTReader(), *MF); + if (any(MDC.OptimizeArgs & ScanningOptimizations::SystemWarnings)) + optimizeDiagnosticOpts( + BuildInvocation.getMutDiagnosticOpts(), + BuildInvocation.getFrontendOpts().IsSystemModule); }); MDC.associateWithContextHash(CI, MD); diff --git a/clang/test/ClangScanDeps/optimize-system-warnings.m b/clang/test/ClangScanDeps/optimize-system-warnings.m new file mode 100644 index 0000000000000..0c6e544cc5229 --- /dev/null +++ b/clang/test/ClangScanDeps/optimize-system-warnings.m @@ -0,0 +1,123 @@ +// This test verifies that system module variants are mergable despite having +// different warning flags, as most warnings are disabled in system modules. +// This checks for system modules marked as such both via `-isystem` and +// `[system]`. + +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: sed -e "s|DIR|%/t|g" %t/build/compile-commands.json.in > %t/build/compile-commands.json +// RUN: clang-scan-deps -compilation-database %t/build/compile-commands.json \ +// RUN: -j 1 -format experimental-full -optimize-args=system-warnings > %t/deps.db +// RUN: cat %t/deps.db | sed 's:\\\\\?:/:g' | FileCheck %s -DPREFIX=%/t + +// CHECK: { +// CHECK-NEXT: "modules": [ +// CHECK-NEXT: { +// CHECK-NEXT: "clang-module-deps": [], +// CHECK-NEXT: "clang-modulemap-file": +// CHECK-NEXT: "command-line": [ +// CHECK-NOT: "-W +// CHECK: ], +// CHECK-NEXT: "context-hash": "{{.*}}", +// CHECK-NEXT: "file-deps": [ +// CHECK: ], +// CHECK-NEXT: "name": "A" +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "clang-module-deps": [], +// CHECK-NEXT: "clang-modulemap-file": +// CHECK-NEXT: "command-line": [ +// CHECK-NOT: "-W +// CHECK: ], +// CHECK-NEXT: "context-hash": "{{.*}}", +// CHECK-NEXT: "file-deps": [ +// CHECK: ], +// CHECK-NEXT: "name": "B" +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "clang-module-deps": [], +// CHECK-NEXT: "clang-modulemap-file": +// CHECK-NEXT: "command-line": [ +// CHECK: "-Wmaybe-unused +// CHECK: ], +// CHECK-NEXT: "context-hash": "{{.*}}", +// CHECK-NEXT: "file-deps": [ +// CHECK: ], +// CHECK-NEXT: "name": "C" +// CHECK-NEXT: } +// CHECK-NEXT: ], +// CHECK-NEXT: "translation-units": [ +// CHECK: ] +// CHECK: } + +// A.m and B.m verify that system modules with different warning flags get +// merged. C.m verifies that -Wsystem-headers disables the optimization. +//--- build/compile-commands.json.in + +[ +{ + "directory": "DIR", + "command": "clang -c DIR/A.m -isystem modules/A -I modules/B -fmodules -fmodules-cache-path=DIR/module-cache -fimplicit-module-maps", + "file": "DIR/A.m" +}, +{ + "directory": "DIR", + "command": "clang -c DIR/B.m -isystem modules/A -I modules/B -fmodules -fmodules-cache-path=DIR/module-cache -fimplicit-module-maps -Wmaybe-unused", + "file": "DIR/B.m" +}, +{ + "directory": "DIR", + "command": "clang -c DIR/C.m -isystem modules/C -fmodules -fmodules-cache-path=DIR/module-cache -fimplicit-module-maps -Wmaybe-unused -Wsystem-headers", + "file": "DIR/C.m" +} +] + +//--- modules/A/module.modulemap + +module A { + umbrella header "A.h" +} + +//--- modules/A/A.h + +typedef int A_t; + +//--- modules/B/module.modulemap + +module B [system] { + umbrella header "B.h" +} + +//--- modules/B/B.h + +typedef int B_t; + +//--- modules/C/module.modulemap + +module C [system] { + umbrella header "C.h" +} + +//--- modules/C/C.h + +typedef int C_t; + +//--- A.m + +#include +#include + +A_t a = 0; + +//--- B.m + +#include +#include + +A_t b = 0; + +//--- C.m + +#include + +C_t c = 0; diff --git a/clang/tools/clang-scan-deps/ClangScanDeps.cpp b/clang/tools/clang-scan-deps/ClangScanDeps.cpp index 50631fdf0bc3b..f11c933d95765 100644 --- a/clang/tools/clang-scan-deps/ClangScanDeps.cpp +++ b/clang/tools/clang-scan-deps/ClangScanDeps.cpp @@ -156,6 +156,7 @@ static void ParseArgs(int argc, char **argv) { llvm::StringSwitch>(Arg) .Case("none", ScanningOptimizations::None) .Case("header-search", ScanningOptimizations::HeaderSearch) + .Case("system-warnings", ScanningOptimizations::SystemWarnings) .Case("all", ScanningOptimizations::All) .Default(std::nullopt); if (!Optimization) {