llvmorg-github-actions[bot] wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang-tools-extra

Author: Vladimir Makaev (VladimirMakaev)

<details>
<summary>Changes</summary>

## Summary

This adds an experimental `--experimental-header-filter-matching` option to 
`clang-tidy`.

Today, `--header-filter` and `--exclude-header-filter` only decide which 
diagnostics are printed after checks have already matched over the AST. With 
this option enabled, clang-tidy also uses those filters to avoid AST matcher 
traversal for out-of-filter declarations.

The target use case is a large C++ monorepo where developers run clang-tidy 
over a focused module or folder, but each TU includes a broad shared internal 
framework. Main-file declarations are still matched; the filter only affects 
included declarations.

## Performance

The most representative LLVM benchmark uses focused clang-tidy checker-module 
slices. These TUs include broad clang-tidy, Clang AST, and LLVM support 
headers, while the header filter is restricted to the focused checker module 
folder.

Check set:

```text
-*,modernize-use-override,readability-qualified-auto,performance-for-range-copy,readability-redundant-member-init,bugprone-unused-return-value,misc-use-internal-linkage
```

Command shape:

```text
run-clang-tidy.py -j8 -quiet -hide-progress -enable-check-profile \
  -clang-tidy-binary &lt;build&gt;/bin/clang-tidy \
  -p &lt;build&gt; \
  -checks=&lt;check-set&gt; \
  -header-filter=&lt;module-header-filter&gt; \
  [--experimental-header-filter-matching=true] \
  &lt;module-source-regex&gt;
```

Each slice was run three times; medians are reported. Warning lines were 
identical in all repetitions.

| Slice | TUs | Baseline median real | Matching median real | Real speedup | 
Baseline median check time | Matching median check time | Check-time speedup | 
Warnings |
|---|---:|---:|---:|---:|---:|---:|---:|---|
| `clang-tools-extra/clang-tidy/bugprone` | 109 | 66.06s | 51.03s | 1.29x | 
70.8065s | 35.2657s | 2.01x | identical, 5/5 |
| `clang-tools-extra/clang-tidy/readability` | 62 | 38.82s | 30.28s | 1.28x | 
41.1617s | 20.6990s | 1.99x | identical, 4/4 |
| `clang-tools-extra/clang-tidy/modernize` | 53 | 36.31s | 27.26s | 1.33x | 
36.9228s | 18.5518s | 1.99x | identical, 4/4 |

The ~2x improvement is in clang-tidy checker-profile time. End-to-end wall time 
improves less because parsing, preprocessing, and semantic analysis still run.

Additional one-shot LLVM slices with the same check set showed the same 
direction: `clangd` including tests improved checker time by 2.00x with 
identical 770/770 warnings, `clang/lib/StaticAnalyzer/Checkers` by 1.86x with 
identical 516/516 warnings, and `clang/lib/Sema` by 1.76x with identical 
1014/1014 warnings.

A more conservative LLVM-specific check set also improved checker time by 24.9% 
to 34.7% on `clang/lib/AST`, `clang/lib/Sema`, `llvm/lib/Transforms/Scalar`, 
`llvm/lib/Target/X86`, and `clang-tools-extra/clang-tidy/readability`, again 
with identical warning lines in those runs.

## Implementation notes

The clang-facing API is a generic `MatchFinderOptions::ShouldSkipLocation` 
callback. It does not mention clang-tidy or header filters.

clang-tidy wires that callback to a shared `HeaderFilterLocationFilter`, which 
evaluates the existing header-filter and exclude-header-filter regexes with a 
per-`FileID` cache. The diagnostic consumer now uses the same helper.

This deliberately does not use `ASTContext::setTraversalScope`. The skip 
happens in matcher traversal, so the AST context and parent map remain intact. 
The matcher still descends through an out-of-filter `DeclContext` when it 
contains an in-filter lexical child, which preserves cases such as a wrapper 
namespace header including an in-filter header.

## Semantic note

This is an experimental semantic mode, not only a diagnostic filtering mode. 
Checks that build cross-file or whole-TU state can produce different results if 
relevant declarations are outside the active header filter.

Known affected patterns include checks such as `misc-confusable-identifiers`, 
`misc-unused-using-decls`, and 
`readability-inconsistent-declaration-parameter-name`, where skipped 
declarations can affect the check's internal state. Generated headers should 
also be included in the filter when they are semantically part of the focused 
module.

## Coverage

This includes CLI, `.clang-tidy` config (`ExperimentalHeaderFilterMatching`), 
`--dump-config`, option merging/defaults, `run-clang-tidy.py`, documentation, 
release notes, and regression tests for CLI/config behavior, 
exclude-header-filter, line-filter interaction, main-file diagnostics, wrapper 
`DeclContext`s, macro expansion locations, and the generic ASTMatchers callback.

The ASTMatchers tests cover pruning, preserved parent/ancestor matcher 
behavior, and the wrapper-context case. The clang-tidy tests cover the 
header-filter integration.

## Prior art

This follows the direction from PR #<!-- -->132725 and PR #<!-- -->151035: keep 
the full AST context intact and put the skip in matcher traversal instead of 
using `setTraversalScope`. That addresses the parent/ancestor matcher concern 
that led to reverting PR #<!-- -->128150.

## Testing

```text
git diff --check
git-clang-format --diff HEAD~1 HEAD --extensions h,cpp -- &lt;changed C++ 
files&gt;
ninja -C ../llvm-build-header-filter-scope clang-tidy FileCheck 
AllClangUnitTests
../llvm-build-header-filter-scope/tools/clang/unittests/AllClangUnitTests 
--gtest_filter='MatchFinder.*ShouldSkipLocation*'
../llvm-build-header-filter-scope/bin/llvm-lit -sv \
  
clang-tools-extra/test/clang-tidy/infrastructure/experimental-header-filter-matching.cpp
 \
  
clang-tools-extra/test/clang-tidy/infrastructure/experimental-header-filter-matching-wrapper.cpp
 \
  
clang-tools-extra/test/clang-tidy/infrastructure/experimental-header-filter-matching-macros.cpp
 \
  clang-tools-extra/test/clang-tidy/infrastructure/run-clang-tidy.cpp \
  clang-tools-extra/test/clang-tidy/infrastructure/config-files.cpp \
  clang-tools-extra/test/clang-tidy/infrastructure/dump-config-filtering.cpp \
  clang-tools-extra/test/clang-tidy/infrastructure/diagnostic.cpp
```


---

Patch is 52.02 KiB, truncated to 20.00 KiB below, full version: 
https://github.com/llvm/llvm-project/pull/194370.diff


29 Files Affected:

- (modified) clang-tools-extra/clang-tidy/ClangTidy.cpp (+11) 
- (modified) clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp 
(+14-19) 
- (modified) clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.h (+5-10) 
- (modified) clang-tools-extra/clang-tidy/ClangTidyOptions.cpp (+5) 
- (modified) clang-tools-extra/clang-tidy/ClangTidyOptions.h (+7) 
- (added) clang-tools-extra/clang-tidy/HeaderFilterHelpers.h (+64) 
- (modified) clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp (+28) 
- (modified) clang-tools-extra/clang-tidy/tool/run-clang-tidy.py (+19) 
- (modified) clang-tools-extra/docs/ReleaseNotes.rst (+9) 
- (modified) clang-tools-extra/docs/clang-tidy/index.rst (+26-3) 
- (modified) 
clang-tools-extra/test/clang-tidy/infrastructure/Inputs/config-files/.clang-tidy
 (+1) 
- (modified) 
clang-tools-extra/test/clang-tidy/infrastructure/Inputs/config-files/1/.clang-tidy
 (+1) 
- (modified) 
clang-tools-extra/test/clang-tidy/infrastructure/Inputs/config-files/3/.clang-tidy
 (+1) 
- (added) 
clang-tools-extra/test/clang-tidy/infrastructure/Inputs/experimental-header-filter-matching/confusable_header.h
 (+1) 
- (added) 
clang-tools-extra/test/clang-tidy/infrastructure/Inputs/experimental-header-filter-matching/macro_def_header.h
 (+6) 
- (added) 
clang-tools-extra/test/clang-tidy/infrastructure/Inputs/experimental-header-filter-matching/macro_expansion_header.h
 (+3) 
- (added) 
clang-tools-extra/test/clang-tidy/infrastructure/Inputs/experimental-header-filter-matching/wrapper_header.h
 (+3) 
- (added) 
clang-tools-extra/test/clang-tidy/infrastructure/Inputs/experimental-header-filter-matching/wrapper_kept_header.h
 (+1) 
- (modified) clang-tools-extra/test/clang-tidy/infrastructure/config-files.cpp 
(+6-1) 
- (modified) clang-tools-extra/test/clang-tidy/infrastructure/diagnostic.cpp 
(+2) 
- (modified) 
clang-tools-extra/test/clang-tidy/infrastructure/dump-config-filtering.cpp (+2) 
- (added) 
clang-tools-extra/test/clang-tidy/infrastructure/experimental-header-filter-matching-macros.cpp
 (+18) 
- (added) 
clang-tools-extra/test/clang-tidy/infrastructure/experimental-header-filter-matching-wrapper.cpp
 (+10) 
- (added) 
clang-tools-extra/test/clang-tidy/infrastructure/experimental-header-filter-matching.cpp
 (+22) 
- (modified) 
clang-tools-extra/test/clang-tidy/infrastructure/run-clang-tidy.cpp (+15) 
- (modified) clang/docs/ReleaseNotes.rst (+2) 
- (modified) clang/include/clang/ASTMatchers/ASTMatchFinder.h (+18) 
- (modified) clang/lib/ASTMatchers/ASTMatchFinder.cpp (+62-5) 
- (modified) clang/unittests/ASTMatchers/ASTMatchersInternalTest.cpp (+182) 


``````````diff
diff --git a/clang-tools-extra/clang-tidy/ClangTidy.cpp 
b/clang-tools-extra/clang-tidy/ClangTidy.cpp
index 05c8fd02fe86a..7d81bcd2f7e55 100644
--- a/clang-tools-extra/clang-tidy/ClangTidy.cpp
+++ b/clang-tools-extra/clang-tidy/ClangTidy.cpp
@@ -20,6 +20,7 @@
 #include "ClangTidyModule.h"
 #include "ClangTidyProfiling.h"
 #include "ExpandModularHeadersPPCallbacks.h"
+#include "HeaderFilterHelpers.h"
 #include "clang-tidy-config.h"
 #include "clang/AST/ASTConsumer.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
@@ -448,6 +449,16 @@ 
ClangTidyASTConsumerFactory::createASTConsumer(CompilerInstance &Compiler,
   if (!Context.getOptions().SystemHeaders.value_or(false))
     FinderOptions.IgnoreSystemHeaders = true;
 
+  if (Context.getOptions().ExperimentalHeaderFilterMatching.value_or(false)) {
+    auto LocationFilter = std::make_shared<HeaderFilterLocationFilter>(
+        Context.getOptions().HeaderFilterRegex.value_or(""),
+        Context.getOptions().ExcludeHeaderFilterRegex.value_or(""));
+    FinderOptions.ShouldSkipLocation = [LocationFilter,
+                                        SM](SourceLocation Location) {
+      return !LocationFilter->shouldInclude(Location, *SM);
+    };
+  }
+
   auto Finder =
       std::make_unique<ast_matchers::MatchFinder>(std::move(FinderOptions));
 
diff --git a/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp 
b/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp
index f7232645a329c..d35cd954e1fd7 100644
--- a/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp
+++ b/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp
@@ -36,7 +36,6 @@
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/StringMap.h"
 #include "llvm/Support/FormatVariadic.h"
-#include "llvm/Support/Regex.h"
 #include <optional>
 #include <tuple>
 #include <utility>
@@ -372,6 +371,7 @@ void ClangTidyDiagnosticConsumer::BeginSourceFile(const 
LangOptions &LangOpts,
 
   assert(!InSourceFile);
   InSourceFile = true;
+  HeaderFilterLocation.reset();
 }
 
 void ClangTidyDiagnosticConsumer::EndSourceFile() {
@@ -568,9 +568,10 @@ void ClangTidyDiagnosticConsumer::forwardDiagnostic(const 
Diagnostic &Info) {
 
 void ClangTidyDiagnosticConsumer::checkFilters(SourceLocation Location,
                                                const SourceManager &Sources) {
-  // Invalid location may mean a diagnostic in a command line, don't skip 
these.
   if (!Location.isValid()) {
-    LastErrorRelatesToUserCode = true;
+    LastErrorRelatesToUserCode =
+        LastErrorRelatesToUserCode ||
+        getHeaderFilterLocationFilter().shouldInclude(Location, Sources);
     LastErrorPassesLineFilter = true;
     return;
   }
@@ -579,6 +580,10 @@ void 
ClangTidyDiagnosticConsumer::checkFilters(SourceLocation Location,
       (Sources.isInSystemHeader(Location) || 
Sources.isInSystemMacro(Location)))
     return;
 
+  LastErrorRelatesToUserCode =
+      LastErrorRelatesToUserCode ||
+      getHeaderFilterLocationFilter().shouldInclude(Location, Sources);
+
   // FIXME: We start with a conservative approach here, but the actual type of
   // location needed depends on the check (in particular, where this check 
wants
   // to apply fixes).
@@ -588,34 +593,24 @@ void 
ClangTidyDiagnosticConsumer::checkFilters(SourceLocation Location,
   // -DMACRO definitions on the command line have locations in a virtual buffer
   // that doesn't have a FileEntry. Don't skip these as well.
   if (!File) {
-    LastErrorRelatesToUserCode = true;
     LastErrorPassesLineFilter = true;
     return;
   }
 
   const StringRef FileName(File->getName());
-  LastErrorRelatesToUserCode = LastErrorRelatesToUserCode ||
-                               Sources.isInMainFile(Location) ||
-                               (getHeaderFilter()->match(FileName) &&
-                                !getExcludeHeaderFilter()->match(FileName));
 
   const unsigned LineNumber = Sources.getExpansionLineNumber(Location);
   LastErrorPassesLineFilter =
       LastErrorPassesLineFilter || passesLineFilter(FileName, LineNumber);
 }
 
-llvm::Regex *ClangTidyDiagnosticConsumer::getHeaderFilter() {
-  if (!HeaderFilter)
-    HeaderFilter = std::make_unique<llvm::Regex>(
-        Context.getOptions().HeaderFilterRegex.value_or(""));
-  return HeaderFilter.get();
-}
-
-llvm::Regex *ClangTidyDiagnosticConsumer::getExcludeHeaderFilter() {
-  if (!ExcludeHeaderFilter)
-    ExcludeHeaderFilter = std::make_unique<llvm::Regex>(
+HeaderFilterLocationFilter &
+ClangTidyDiagnosticConsumer::getHeaderFilterLocationFilter() {
+  if (!HeaderFilterLocation)
+    HeaderFilterLocation = std::make_unique<HeaderFilterLocationFilter>(
+        Context.getOptions().HeaderFilterRegex.value_or(""),
         Context.getOptions().ExcludeHeaderFilterRegex.value_or(""));
-  return ExcludeHeaderFilter.get();
+  return *HeaderFilterLocation;
 }
 
 void ClangTidyDiagnosticConsumer::removeIncompatibleErrors() {
diff --git a/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.h 
b/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.h
index 8de5778dfefb0..241e70f915677 100644
--- a/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.h
+++ b/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.h
@@ -12,12 +12,12 @@
 #include "ClangTidyOptions.h"
 #include "ClangTidyProfiling.h"
 #include "FileExtensionsSet.h"
+#include "HeaderFilterHelpers.h"
 #include "NoLintDirectiveHandler.h"
 #include "clang/Basic/Diagnostic.h"
 #include "clang/Tooling/Core/Diagnostic.h"
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/StringSet.h"
-#include "llvm/Support/Regex.h"
 #include <optional>
 #include <utility>
 
@@ -310,13 +310,9 @@ class ClangTidyDiagnosticConsumer : public 
DiagnosticConsumer {
   void removeIncompatibleErrors();
   void removeDuplicatedDiagnosticsOfAliasCheckers();
 
-  /// Returns the \c HeaderFilter constructed for the options set in the
-  /// context.
-  llvm::Regex *getHeaderFilter();
-
-  /// Returns the \c ExcludeHeaderFilter constructed for the options set in the
-  /// context.
-  llvm::Regex *getExcludeHeaderFilter();
+  /// Returns the cached header-filter evaluator for the current translation
+  /// unit.
+  HeaderFilterLocationFilter &getHeaderFilterLocationFilter();
 
   /// Updates \c LastErrorRelatesToUserCode and LastErrorPassesLineFilter
   /// according to the diagnostic \p Location.
@@ -331,8 +327,7 @@ class ClangTidyDiagnosticConsumer : public 
DiagnosticConsumer {
   bool GetFixesFromNotes;
   bool EnableNolintBlocks;
   std::vector<ClangTidyError> Errors;
-  std::unique_ptr<llvm::Regex> HeaderFilter;
-  std::unique_ptr<llvm::Regex> ExcludeHeaderFilter;
+  std::unique_ptr<HeaderFilterLocationFilter> HeaderFilterLocation;
   bool LastErrorRelatesToUserCode = false;
   bool LastErrorPassesLineFilter = false;
   bool LastErrorWasIgnored = false;
diff --git a/clang-tools-extra/clang-tidy/ClangTidyOptions.cpp 
b/clang-tools-extra/clang-tidy/ClangTidyOptions.cpp
index 0a0f392346f6d..291b70225eed3 100644
--- a/clang-tools-extra/clang-tidy/ClangTidyOptions.cpp
+++ b/clang-tools-extra/clang-tidy/ClangTidyOptions.cpp
@@ -242,6 +242,8 @@ template <> struct MappingTraits<ClangTidyOptions> {
     IO.mapOptional("InheritParentConfig", Options.InheritParentConfig);
     IO.mapOptional("UseColor", Options.UseColor);
     IO.mapOptional("SystemHeaders", Options.SystemHeaders);
+    IO.mapOptional("ExperimentalHeaderFilterMatching",
+                   Options.ExperimentalHeaderFilterMatching);
     IO.mapOptional("CustomChecks", Options.CustomChecks);
   }
 };
@@ -259,6 +261,7 @@ ClangTidyOptions ClangTidyOptions::getDefaults() {
   Options.HeaderFilterRegex = ".*";
   Options.ExcludeHeaderFilterRegex = "";
   Options.SystemHeaders = false;
+  Options.ExperimentalHeaderFilterMatching = false;
   Options.FormatStyle = "none";
   Options.User = std::nullopt;
   Options.RemovedArgs = std::nullopt;
@@ -300,6 +303,8 @@ ClangTidyOptions &ClangTidyOptions::mergeWith(const 
ClangTidyOptions &Other,
   overrideValue(HeaderFilterRegex, Other.HeaderFilterRegex);
   overrideValue(ExcludeHeaderFilterRegex, Other.ExcludeHeaderFilterRegex);
   overrideValue(SystemHeaders, Other.SystemHeaders);
+  overrideValue(ExperimentalHeaderFilterMatching,
+                Other.ExperimentalHeaderFilterMatching);
   overrideValue(FormatStyle, Other.FormatStyle);
   overrideValue(User, Other.User);
   overrideValue(UseColor, Other.UseColor);
diff --git a/clang-tools-extra/clang-tidy/ClangTidyOptions.h 
b/clang-tools-extra/clang-tidy/ClangTidyOptions.h
index 73fdbabd5bdba..7c4efe3c1e03d 100644
--- a/clang-tools-extra/clang-tidy/ClangTidyOptions.h
+++ b/clang-tools-extra/clang-tidy/ClangTidyOptions.h
@@ -92,6 +92,13 @@ struct ClangTidyOptions {
   /// Output warnings from system headers matching \c HeaderFilterRegex.
   std::optional<bool> SystemHeaders;
 
+  /// When set, clang-tidy experimentally skips AST matching for declarations
+  /// in headers that do not match \c HeaderFilterRegex or that match
+  /// \c ExcludeHeaderFilterRegex. This is a semantic opt-in: checks that rely
+  /// on declarations outside the filtered headers can produce different
+  /// results, including false negatives or false positives.
+  std::optional<bool> ExperimentalHeaderFilterMatching;
+
   /// Format code around applied fixes with clang-format using this
   /// style.
   ///
diff --git a/clang-tools-extra/clang-tidy/HeaderFilterHelpers.h 
b/clang-tools-extra/clang-tidy/HeaderFilterHelpers.h
new file mode 100644
index 0000000000000..23fad8b9533ad
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/HeaderFilterHelpers.h
@@ -0,0 +1,64 @@
+//===--- HeaderFilterHelpers.h - clang-tidy header filtering ----*- 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_TOOLS_EXTRA_CLANG_TIDY_HEADERFILTERHELPERS_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_HEADERFILTERHELPERS_H
+
+#include "clang/Basic/SourceManager.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Regex.h"
+
+namespace clang::tidy {
+
+/// Evaluates clang-tidy's header filters for source locations and caches the
+/// per-file results for a translation unit.
+class HeaderFilterLocationFilter {
+public:
+  HeaderFilterLocationFilter(llvm::StringRef HeaderFilterRegex,
+                             llvm::StringRef ExcludeHeaderFilterRegex)
+      : HeaderFilter(HeaderFilterRegex),
+        ExcludeHeaderFilter(ExcludeHeaderFilterRegex) {}
+
+  /// Returns true when the location should be treated as in scope for
+  /// clang-tidy's header filters.
+  ///
+  /// Main-file locations are always in scope. Invalid locations and locations
+  /// without a FileEntry (such as command-line buffers) are also treated as in
+  /// scope to match clang-tidy's diagnostic filtering behavior.
+  bool shouldInclude(SourceLocation Location, const SourceManager &Sources) {
+    if (!Location.isValid())
+      return true;
+
+    if (Sources.isInMainFile(Location))
+      return true;
+
+    const FileID FID = Sources.getDecomposedExpansionLoc(Location).first;
+    if (const auto It = Cache.find(FID); It != Cache.end())
+      return It->second;
+
+    bool Result = true;
+    if (const OptionalFileEntryRef File = Sources.getFileEntryRefForID(FID)) {
+      const llvm::StringRef FileName = File->getName();
+      Result =
+          HeaderFilter.match(FileName) && !ExcludeHeaderFilter.match(FileName);
+    }
+
+    Cache[FID] = Result;
+    return Result;
+  }
+
+private:
+  llvm::Regex HeaderFilter;
+  llvm::Regex ExcludeHeaderFilter;
+  llvm::DenseMap<FileID, bool> Cache;
+};
+
+} // namespace clang::tidy
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_HEADERFILTERHELPERS_H
diff --git a/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp 
b/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp
index f61e2f40ed03b..ac43dc4500aaf 100644
--- a/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp
+++ b/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp
@@ -63,6 +63,9 @@ Configuration files:
   CustomChecks                 - Array of user defined checks based on
                                  Clang-Query syntax.
   ExcludeHeaderFilterRegex     - Same as '--exclude-header-filter'.
+  ExperimentalHeaderFilterMatching
+                               - Same as
+                                 '--experimental-header-filter-matching'.
   ExtraArgs                    - Same as '--extra-arg'.
   ExtraArgsBefore              - Same as '--extra-arg-before'.
   FormatStyle                  - Same as '--format-style'.
@@ -95,6 +98,7 @@ Configuration files:
     HeaderFileExtensions:         ['', 'h','hh','hpp','hxx']
     ImplementationFileExtensions: ['c','cc','cpp','cxx']
     HeaderFilterRegex:            '.*'
+    ExperimentalHeaderFilterMatching: false
     FormatStyle:                  none
     InheritParentConfig:          true
     User:                         user
@@ -158,6 +162,25 @@ option in .clang-tidy file, if any.
                                                 cl::init(""),
                                                 cl::cat(ClangTidyCategory));
 
+static cl::opt<bool> ExperimentalHeaderFilterMatching(
+    "experimental-header-filter-matching", desc(R"(
+When enabled, clang-tidy experimentally skips
+AST matching for declarations in headers that
+do not match -header-filter or that match
+-exclude-header-filter.
+This can improve performance for narrow
+header-filter runs, but checks that rely on
+declarations outside the filtered headers can
+produce different results, including false
+negatives or false positives.
+This is an opt-in semantic mode, not only a
+diagnostic filtering mode.
+This option overrides the
+'ExperimentalHeaderFilterMatching' option in
+.clang-tidy file, if any.
+)"),
+    cl::init(false), cl::cat(ClangTidyCategory));
+
 static cl::opt<bool> SystemHeaders("system-headers", desc(R"(
 Display the errors from system headers.
 This option overrides the 'SystemHeaders' option
@@ -412,6 +435,8 @@ 
createOptionsProvider(llvm::IntrusiveRefCntPtr<vfs::FileSystem> FS) {
   DefaultOptions.HeaderFilterRegex = HeaderFilter;
   DefaultOptions.ExcludeHeaderFilterRegex = ExcludeHeaderFilter;
   DefaultOptions.SystemHeaders = SystemHeaders;
+  DefaultOptions.ExperimentalHeaderFilterMatching =
+      ExperimentalHeaderFilterMatching;
   DefaultOptions.FormatStyle = FormatStyle;
   DefaultOptions.User = llvm::sys::Process::GetEnv("USER");
   // USERNAME is used on Windows.
@@ -429,6 +454,9 @@ 
createOptionsProvider(llvm::IntrusiveRefCntPtr<vfs::FileSystem> FS) {
     OverrideOptions.ExcludeHeaderFilterRegex = ExcludeHeaderFilter;
   if (SystemHeaders.getNumOccurrences() > 0)
     OverrideOptions.SystemHeaders = SystemHeaders;
+  if (ExperimentalHeaderFilterMatching.getNumOccurrences() > 0)
+    OverrideOptions.ExperimentalHeaderFilterMatching =
+        ExperimentalHeaderFilterMatching;
   if (FormatStyle.getNumOccurrences() > 0)
     OverrideOptions.FormatStyle = FormatStyle;
   if (UseColor.getNumOccurrences() > 0)
diff --git a/clang-tools-extra/clang-tidy/tool/run-clang-tidy.py 
b/clang-tools-extra/clang-tidy/tool/run-clang-tidy.py
index f827ef492f01c..c1445ea40ab35 100755
--- a/clang-tools-extra/clang-tidy/tool/run-clang-tidy.py
+++ b/clang-tools-extra/clang-tidy/tool/run-clang-tidy.py
@@ -94,6 +94,7 @@ def get_tidy_invocation(
     tmpdir: Optional[str],
     build_path: str,
     header_filter: Optional[str],
+    experimental_header_filter_matching: Optional[bool],
     allow_enabling_alpha_checkers: bool,
     extra_arg: List[str],
     extra_arg_before: List[str],
@@ -117,6 +118,11 @@ def get_tidy_invocation(
         start.append(f"--exclude-header-filter={exclude_header_filter}")
     if header_filter is not None:
         start.append(f"-header-filter={header_filter}")
+    if experimental_header_filter_matching is not None:
+        if experimental_header_filter_matching:
+            start.append("--experimental-header-filter-matching")
+        else:
+            start.append("--experimental-header-filter-matching=false")
     if line_filter is not None:
         start.append(f"-line-filter={line_filter}")
     if use_color is not None:
@@ -378,6 +384,7 @@ async def run_tidy(
         tmpdir,
         build_path,
         args.header_filter,
+        args.experimental_header_filter_matching,
         args.allow_enabling_alpha_checkers,
         args.extra_arg,
         args.extra_arg_before,
@@ -478,6 +485,17 @@ async def main() -> None:
         "the main file of each translation unit are always "
         "displayed.",
     )
+    parser.add_argument(
+        "-experimental-header-filter-matching",
+        "--experimental-header-filter-matching",
+        type=strtobool,
+        nargs="?",
+        const=True,
+        default=None,
+        help="Restrict experimental AST matching to headers "
+        "that match -header-filter and do not match "
+        "-exclude-header-filter.",
+    )
     parser.add_argument(
         "-source-filter",
         default=None,
@@ -647,6 +665,7 @@ async def main() -> None:
             None,
             build_path,
             args.header_filter,
+            args.experimental_header_filter_matching,
             args.allow_enabling_alpha_checkers,
             args.extra_arg,
             args.extra_arg_before,
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst 
b/clang-tools-extra/docs/ReleaseNotes.rst
index 2419917778182..08ddec7087de8 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -111,6 +111,15 @@ Improvements to clang-tidy
 - Improved :program:`clang-tidy` ``-store-check-profile`` by generating valid
   JSON when the source file path contains characters that require JSON 
escaping.
 
+- Added an experimental :program:`clang-tidy`
+  ``--experimental-header-filter-matching`` option that skips AST matching for
+  declarations in headers that do not match ``-header-filter`` (or that match
+  ``-exclude-header-filter``). This can improve performance for narrow
+  header-filter runs, but it is an opt-in semantic mode: checks that need AST
+  visibility outside the filtered headers, such as checks that build cross-file
+  state, can produce different results including false negatives or false
+  positives.
+
 New checks
 ^^^^^^^^^^
 
diff --git a/clang-tools-extra/docs/clang-tidy/index.rst 
b/clang-tools-extra/docs/clang-tidy/index.rst
index db7f2deade9ca..d437f50f30d58 100644
--- a/clang-tools-extra/docs/clang-tidy/index.rst
+++ b/clang-tools-extra/docs/clang-tidy/index.rst
@@ -185,6 +185,22 @@ An overview of all the command-line options:
                                        Can be used together with -line-filter.
                                        This option overrides the 
'ExcludeHeaderFilterRegex'
                                        option in .clang-tidy file, if any.
+    --experimental-header-filter-matching
+                                     - When enabled, clang-tidy experimentally
+                                       skips AST matching for declarations in
+                                       headers that do not match -header-filter
+                                       or that match -exclude-header-filter.
+                                       This can improve performance for narrow
+                                       header-filter runs, but checks that rely
+                                       on declarations outside the filtered
+                                       headers can produce different results,
+                                       including false negatives or false
+                                       positives.
+                                       This is an opt-in semantic mode, not
+                                       only a diagnostic filtering mode.
+                                       This option overrides the
+                                       'ExperimentalHeaderFilterMatching' 
option in
+                                       .clang-tidy file, if any.
     --experimental-custom-checks     - Enable experimental clang-query based
                                        custom checks.
                                        see https://clang...
[truncated]

``````````

</details>


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

Reply via email to