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 <build>/bin/clang-tidy \ -p <build> \ -checks=<check-set> \ -header-filter=<module-header-filter> \ [--experimental-header-filter-matching=true] \ <module-source-regex> ``` 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 -- <changed C++ files> 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
