Skip to content

Commit cc54129

Browse files
authored
Add option to exclude headers from clang-tidy analysis (#91400)
This is a renewed attempt to land @toddlipcon's D34654. The comments on that patch indicate a broad desire for some ability to ignore headers. After considering various options, including migrating to std::regex, I believe this is the best path forward. It's intuitive to have separate regexes for including headers versus excluding them, and this approach has the added benefit of being completely opt-in. No existing configs will break, regardless of existing HeaderFilterRegex values. This functionality is useful for improving performance when analyzing a targeted subset of code, as well as in cases where some collection of headers cannot be modified (third party source, for example).
1 parent 415616d commit cc54129

File tree

13 files changed

+207
-126
lines changed

13 files changed

+207
-126
lines changed

clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp

+17-11
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,18 @@ ClangTidyDiagnosticConsumer::ClangTidyDiagnosticConsumer(
311311
: Context(Ctx), ExternalDiagEngine(ExternalDiagEngine),
312312
RemoveIncompatibleErrors(RemoveIncompatibleErrors),
313313
GetFixesFromNotes(GetFixesFromNotes),
314-
EnableNolintBlocks(EnableNolintBlocks) {}
314+
EnableNolintBlocks(EnableNolintBlocks) {
315+
316+
if (Context.getOptions().HeaderFilterRegex &&
317+
!Context.getOptions().HeaderFilterRegex->empty())
318+
HeaderFilter =
319+
std::make_unique<llvm::Regex>(*Context.getOptions().HeaderFilterRegex);
320+
321+
if (Context.getOptions().ExcludeHeaderFilterRegex &&
322+
!Context.getOptions().ExcludeHeaderFilterRegex->empty())
323+
ExcludeHeaderFilter = std::make_unique<llvm::Regex>(
324+
*Context.getOptions().ExcludeHeaderFilterRegex);
325+
}
315326

316327
void ClangTidyDiagnosticConsumer::finalizeLastError() {
317328
if (!Errors.empty()) {
@@ -562,22 +573,17 @@ void ClangTidyDiagnosticConsumer::checkFilters(SourceLocation Location,
562573
}
563574

564575
StringRef FileName(File->getName());
565-
LastErrorRelatesToUserCode = LastErrorRelatesToUserCode ||
566-
Sources.isInMainFile(Location) ||
567-
getHeaderFilter()->match(FileName);
576+
LastErrorRelatesToUserCode =
577+
LastErrorRelatesToUserCode || Sources.isInMainFile(Location) ||
578+
(HeaderFilter &&
579+
(HeaderFilter->match(FileName) &&
580+
!(ExcludeHeaderFilter && ExcludeHeaderFilter->match(FileName))));
568581

569582
unsigned LineNumber = Sources.getExpansionLineNumber(Location);
570583
LastErrorPassesLineFilter =
571584
LastErrorPassesLineFilter || passesLineFilter(FileName, LineNumber);
572585
}
573586

574-
llvm::Regex *ClangTidyDiagnosticConsumer::getHeaderFilter() {
575-
if (!HeaderFilter)
576-
HeaderFilter =
577-
std::make_unique<llvm::Regex>(*Context.getOptions().HeaderFilterRegex);
578-
return HeaderFilter.get();
579-
}
580-
581587
void ClangTidyDiagnosticConsumer::removeIncompatibleErrors() {
582588
// Each error is modelled as the set of intervals in which it applies
583589
// replacements. To detect overlapping replacements, we use a sweep line

clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.h

+1
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,7 @@ class ClangTidyDiagnosticConsumer : public DiagnosticConsumer {
313313
bool EnableNolintBlocks;
314314
std::vector<ClangTidyError> Errors;
315315
std::unique_ptr<llvm::Regex> HeaderFilter;
316+
std::unique_ptr<llvm::Regex> ExcludeHeaderFilter;
316317
bool LastErrorRelatesToUserCode = false;
317318
bool LastErrorPassesLineFilter = false;
318319
bool LastErrorWasIgnored = false;

clang-tools-extra/clang-tidy/ClangTidyOptions.cpp

+5-1
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,8 @@ template <> struct MappingTraits<ClangTidyOptions> {
170170
IO.mapOptional("ImplementationFileExtensions",
171171
Options.ImplementationFileExtensions);
172172
IO.mapOptional("HeaderFilterRegex", Options.HeaderFilterRegex);
173+
IO.mapOptional("ExcludeHeaderFilterRegex",
174+
Options.ExcludeHeaderFilterRegex);
173175
IO.mapOptional("FormatStyle", Options.FormatStyle);
174176
IO.mapOptional("User", Options.User);
175177
IO.mapOptional("CheckOptions", Options.CheckOptions);
@@ -191,7 +193,8 @@ ClangTidyOptions ClangTidyOptions::getDefaults() {
191193
Options.WarningsAsErrors = "";
192194
Options.HeaderFileExtensions = {"", "h", "hh", "hpp", "hxx"};
193195
Options.ImplementationFileExtensions = {"c", "cc", "cpp", "cxx"};
194-
Options.HeaderFilterRegex = "";
196+
Options.HeaderFilterRegex = std::nullopt;
197+
Options.ExcludeHeaderFilterRegex = std::nullopt;
195198
Options.SystemHeaders = false;
196199
Options.FormatStyle = "none";
197200
Options.User = std::nullopt;
@@ -231,6 +234,7 @@ ClangTidyOptions &ClangTidyOptions::mergeWith(const ClangTidyOptions &Other,
231234
overrideValue(ImplementationFileExtensions,
232235
Other.ImplementationFileExtensions);
233236
overrideValue(HeaderFilterRegex, Other.HeaderFilterRegex);
237+
overrideValue(ExcludeHeaderFilterRegex, Other.ExcludeHeaderFilterRegex);
234238
overrideValue(SystemHeaders, Other.SystemHeaders);
235239
overrideValue(FormatStyle, Other.FormatStyle);
236240
overrideValue(User, Other.User);

clang-tools-extra/clang-tidy/ClangTidyOptions.h

+4
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,10 @@ struct ClangTidyOptions {
8383
/// main files will always be displayed.
8484
std::optional<std::string> HeaderFilterRegex;
8585

86+
/// \brief Exclude warnings from headers matching this filter, even if they
87+
/// match \c HeaderFilterRegex.
88+
std::optional<std::string> ExcludeHeaderFilterRegex;
89+
8690
/// Output warnings from system headers matching \c HeaderFilterRegex.
8791
std::optional<bool> SystemHeaders;
8892

clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp

+18
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ Configuration files:
5353
Checks - Same as '--checks'. Additionally, the list of
5454
globs can be specified as a list instead of a
5555
string.
56+
ExcludeHeaderFilterRegex - Same as '--exclude-header-filter'.
5657
ExtraArgs - Same as '--extra-args'.
5758
ExtraArgsBefore - Same as '--extra-args-before'.
5859
FormatStyle - Same as '--format-style'.
@@ -132,6 +133,20 @@ option in .clang-tidy file, if any.
132133
cl::init(""),
133134
cl::cat(ClangTidyCategory));
134135

136+
static cl::opt<std::string> ExcludeHeaderFilter("exclude-header-filter",
137+
desc(R"(
138+
Regular expression matching the names of the
139+
headers to exclude diagnostics from. Diagnostics
140+
from the main file of each translation unit are
141+
always displayed.
142+
Must be used together with --header-filter.
143+
Can be used together with -line-filter.
144+
This option overrides the 'ExcludeHeaderFilterRegex'
145+
option in .clang-tidy file, if any.
146+
)"),
147+
cl::init(""),
148+
cl::cat(ClangTidyCategory));
149+
135150
static cl::opt<bool> SystemHeaders("system-headers", desc(R"(
136151
Display the errors from system headers.
137152
This option overrides the 'SystemHeaders' option
@@ -353,6 +368,7 @@ static std::unique_ptr<ClangTidyOptionsProvider> createOptionsProvider(
353368
DefaultOptions.Checks = DefaultChecks;
354369
DefaultOptions.WarningsAsErrors = "";
355370
DefaultOptions.HeaderFilterRegex = HeaderFilter;
371+
DefaultOptions.ExcludeHeaderFilterRegex = ExcludeHeaderFilter;
356372
DefaultOptions.SystemHeaders = SystemHeaders;
357373
DefaultOptions.FormatStyle = FormatStyle;
358374
DefaultOptions.User = llvm::sys::Process::GetEnv("USER");
@@ -367,6 +383,8 @@ static std::unique_ptr<ClangTidyOptionsProvider> createOptionsProvider(
367383
OverrideOptions.WarningsAsErrors = WarningsAsErrors;
368384
if (HeaderFilter.getNumOccurrences() > 0)
369385
OverrideOptions.HeaderFilterRegex = HeaderFilter;
386+
if (ExcludeHeaderFilter.getNumOccurrences() > 0)
387+
OverrideOptions.ExcludeHeaderFilterRegex = ExcludeHeaderFilter;
370388
if (SystemHeaders.getNumOccurrences() > 0)
371389
OverrideOptions.SystemHeaders = SystemHeaders;
372390
if (FormatStyle.getNumOccurrences() > 0)

clang-tools-extra/clang-tidy/tool/run-clang-tidy.py

+13
Original file line numberDiff line numberDiff line change
@@ -106,11 +106,14 @@ def get_tidy_invocation(
106106
use_color,
107107
plugins,
108108
warnings_as_errors,
109+
exclude_header_filter,
109110
):
110111
"""Gets a command line for clang-tidy."""
111112
start = [clang_tidy_binary]
112113
if allow_enabling_alpha_checkers:
113114
start.append("-allow-enabling-analyzer-alpha-checkers")
115+
if exclude_header_filter is not None:
116+
start.append("--exclude-header-filter=" + exclude_header_filter)
114117
if header_filter is not None:
115118
start.append("-header-filter=" + header_filter)
116119
if line_filter is not None:
@@ -228,6 +231,7 @@ def run_tidy(args, clang_tidy_binary, tmpdir, build_path, queue, lock, failed_fi
228231
args.use_color,
229232
args.plugins,
230233
args.warnings_as_errors,
234+
args.exclude_header_filter,
231235
)
232236

233237
proc = subprocess.Popen(
@@ -292,6 +296,14 @@ def main():
292296
"-config option after reading specified config file. "
293297
"Use either -config-file or -config, not both.",
294298
)
299+
parser.add_argument(
300+
"-exclude-header-filter",
301+
default=None,
302+
help="Regular expression matching the names of the "
303+
"headers to exclude diagnostics from. Diagnostics from "
304+
"the main file of each translation unit are always "
305+
"displayed.",
306+
)
295307
parser.add_argument(
296308
"-header-filter",
297309
default=None,
@@ -450,6 +462,7 @@ def main():
450462
args.use_color,
451463
args.plugins,
452464
args.warnings_as_errors,
465+
args.exclude_header_filter,
453466
)
454467
invocation.append("-list-checks")
455468
invocation.append("-")

clang-tools-extra/docs/ReleaseNotes.rst

+3
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,9 @@ Improvements to clang-tidy
115115
- Fixed `--verify-config` option not properly parsing checks when using the
116116
literal operator in the `.clang-tidy` config.
117117

118+
- Added argument `--exclude-header-filter` and config option `ExcludeHeaderFilterRegex`
119+
to exclude headers from analysis via a RegEx.
120+
118121
New checks
119122
^^^^^^^^^^
120123

0 commit comments

Comments
 (0)