Skip to content

Commit bd77e9a

Browse files
carlosgalvezpCarlos Gálvez
andauthored
[clang-tidy] Avoid matching nodes in system headers (#151035)
This commit is a re-do of e4a8969, which got reverted, with the same goal: dramatically speed-up clang-tidy by avoiding doing work in system headers (which is wasteful as warnings are later discarded). This proposal was already discussed here with favorable feedback: #132725 The novelty of this patch is: - It's less aggressive: it does not fiddle with AST traversal. This solves the issue with the previous patch, which impacted the ability to inspect parents of a given node. - Instead, what we optimize for is exitting early in each `Traverse*` function of `MatchASTVisitor` if the node is in a system header, thus avoiding calling the `match()` function with its corresponding callback (when there is a match). - It does not cause any failing tests. - It does not move `MatchFinderOptions` - instead we add a user-defined default constructor which solves the same problem. - It introduces a function `shouldSkipNode` which can be extended for adding more conditions. For example there's a PR open about skipping modules in clang-tidy where this could come handy: #145630 As a benchmark, I ran clang-tidy with all checks activated, on a single .cpp file which #includes all the standard C++ headers, then measure the time as well as found warnings. On trunk: ``` Suppressed 75413 warnings (75413 in non-user code). real 0m12.418s user 0m12.270s sys 0m0.129s ``` With this patch: ``` Suppressed 11448 warnings (11448 in non-user code). Use -header-filter=.* to display errors from all non-system headers. Use -system-headers to display errors from system headers as well. real 0m1.666s user 0m1.538s sys 0m0.129s ``` With the original patch that got reverted: ``` Suppressed 11428 warnings (11428 in non-user code). real 0m1.193s user 0m1.096s sys 0m0.096s ``` We therefore get a dramatic reduction in number of warnings and runtime, with no change in functionality. The remaining warnings are due to `PPCallbacks` - implementing a similar system-header exclusion mechanism there can lead to almost no warnings left in system headers. This does not bring the runtime down as much, though, so it's probably not worth the effort. Fixes #52959 Co-authored-by: Carlos Gálvez <[email protected]>
1 parent 5ae8a9b commit bd77e9a

File tree

7 files changed

+74
-12
lines changed

7 files changed

+74
-12
lines changed

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,10 @@ ClangTidyASTConsumerFactory::createASTConsumer(
424424
FinderOptions.CheckProfiling.emplace(Profiling->Records);
425425
}
426426

427+
// Avoid processing system headers, unless the user explicitly requests it
428+
if (!Context.getOptions().SystemHeaders.value_or(false))
429+
FinderOptions.IgnoreSystemHeaders = true;
430+
427431
std::unique_ptr<ast_matchers::MatchFinder> Finder(
428432
new ast_matchers::MatchFinder(std::move(FinderOptions)));
429433

clang-tools-extra/docs/ReleaseNotes.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,10 @@ Improvements to clang-query
104104
Improvements to clang-tidy
105105
--------------------------
106106

107+
- :program:`clang-tidy` no longer attemps to analyze code from system headers
108+
by default, greatly improving performance. This behavior is disabled if the
109+
`SystemHeaders` option is enabled.
110+
107111
- The :program:`run-clang-tidy.py` and :program:`clang-tidy-diff.py` scripts
108112
now run checks in parallel by default using all available hardware threads.
109113
Both scripts display the number of threads being used in their output.

clang-tools-extra/test/clang-tidy/infrastructure/file-filter.cpp

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,19 +66,14 @@ class A { A(int); };
6666
// CHECK4-NOT: warning:
6767
// CHECK4-QUIET-NOT: warning:
6868

69-
// CHECK: Suppressed 3 warnings (3 in non-user code)
7069
// CHECK: Use -header-filter=.* to display errors from all non-system headers.
7170
// CHECK-QUIET-NOT: Suppressed
72-
// CHECK2: Suppressed 1 warnings (1 in non-user code)
73-
// CHECK2: Use -header-filter=.* {{.*}}
7471
// CHECK2-QUIET-NOT: Suppressed
75-
// CHECK3: Suppressed 2 warnings (2 in non-user code)
7672
// CHECK3: Use -header-filter=.* {{.*}}
7773
// CHECK3-QUIET-NOT: Suppressed
7874
// CHECK4-NOT: Suppressed {{.*}} warnings
7975
// CHECK4-NOT: Use -header-filter=.* {{.*}}
8076
// CHECK4-QUIET-NOT: Suppressed
81-
// CHECK6: Suppressed 2 warnings (2 in non-user code)
8277
// CHECK6: Use -header-filter=.* {{.*}}
8378

8479
int x = 123;

clang-tools-extra/test/clang-tidy/infrastructure/system-headers.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@
1111
// RUN: clang-tidy -help | FileCheck -check-prefix=CHECK-OPT-PRESENT %s
1212

1313
// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='.*' -system-headers=true %s -- -isystem %S/Inputs/system-headers 2>&1 | FileCheck -check-prefix=CHECK-SYSTEM-HEADERS %s
14-
// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='.*' -system-headers=false %s -- -isystem %S/Inputs/system-headers 2>&1 | FileCheck -check-prefix=CHECK-NO-SYSTEM-HEADERS %s
14+
// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='.*' -system-headers=false %s -- -isystem %S/Inputs/system-headers 2>&1 | FileCheck -check-prefix=CHECK-NO-SYSTEM-HEADERS --allow-empty %s
1515
// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='.*' -config='SystemHeaders: true' %s -- -isystem %S/Inputs/system-headers 2>&1 | FileCheck -check-prefix=CHECK-SYSTEM-HEADERS %s
16-
// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='.*' -config='SystemHeaders: false' %s -- -isystem %S/Inputs/system-headers 2>&1 | FileCheck -check-prefix=CHECK-NO-SYSTEM-HEADERS %s
16+
// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='.*' -config='SystemHeaders: false' %s -- -isystem %S/Inputs/system-headers 2>&1 | FileCheck -check-prefix=CHECK-NO-SYSTEM-HEADERS --allow-empty %s
1717

1818
#include <system_header.h>
1919
// CHECK-SYSTEM-HEADERS: system_header.h:1:13: warning: single-argument constructors must be marked explicit

clang/docs/ReleaseNotes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,9 @@ AST Matchers
308308
- Ensure ``hasBitWidth`` doesn't crash on bit widths that are dependent on template
309309
parameters.
310310

311+
- Add a boolean member ``IgnoreSystemHeaders`` to ``MatchFinderOptions``. This
312+
allows it to ignore nodes in system headers when traversing the AST.
313+
311314
clang-format
312315
------------
313316

clang/include/clang/ASTMatchers/ASTMatchFinder.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,10 +135,15 @@ class MatchFinder {
135135
llvm::StringMap<llvm::TimeRecord> &Records;
136136
};
137137

138+
MatchFinderOptions() {}
139+
138140
/// Enables per-check timers.
139141
///
140142
/// It prints a report after match.
141143
std::optional<Profiling> CheckProfiling;
144+
145+
/// Avoids matching declarations in system headers.
146+
bool IgnoreSystemHeaders{false};
142147
};
143148

144149
MatchFinder(MatchFinderOptions Options = MatchFinderOptions());

clang/lib/ASTMatchers/ASTMatchFinder.cpp

Lines changed: 56 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1344,6 +1344,41 @@ class MatchASTVisitor : public RecursiveASTVisitor<MatchASTVisitor>,
13441344
return false;
13451345
}
13461346

1347+
template <typename T> static SourceLocation getNodeLocation(const T &Node) {
1348+
return Node.getBeginLoc();
1349+
}
1350+
1351+
static SourceLocation getNodeLocation(const CXXCtorInitializer &Node) {
1352+
return Node.getSourceLocation();
1353+
}
1354+
1355+
static SourceLocation getNodeLocation(const TemplateArgumentLoc &Node) {
1356+
return Node.getLocation();
1357+
}
1358+
1359+
static SourceLocation getNodeLocation(const Attr &Node) {
1360+
return Node.getLocation();
1361+
}
1362+
1363+
bool isInSystemHeader(SourceLocation Loc) {
1364+
const SourceManager &SM = getASTContext().getSourceManager();
1365+
return SM.isInSystemHeader(Loc);
1366+
}
1367+
1368+
template <typename T> bool shouldSkipNode(T &Node) {
1369+
if (Options.IgnoreSystemHeaders && isInSystemHeader(getNodeLocation(Node)))
1370+
return true;
1371+
return false;
1372+
}
1373+
1374+
template <typename T> bool shouldSkipNode(T *Node) {
1375+
return (Node == nullptr) || shouldSkipNode(*Node);
1376+
}
1377+
1378+
bool shouldSkipNode(QualType &) { return false; }
1379+
1380+
bool shouldSkipNode(NestedNameSpecifier &) { return false; }
1381+
13471382
/// Bucket to record map.
13481383
///
13491384
/// Used to get the appropriate bucket for each matcher.
@@ -1473,9 +1508,8 @@ bool MatchASTVisitor::objcClassIsDerivedFrom(
14731508
}
14741509

14751510
bool MatchASTVisitor::TraverseDecl(Decl *DeclNode) {
1476-
if (!DeclNode) {
1511+
if (shouldSkipNode(DeclNode))
14771512
return true;
1478-
}
14791513

14801514
bool ScopedTraversal =
14811515
TraversingASTNodeNotSpelledInSource || DeclNode->isImplicit();
@@ -1503,9 +1537,9 @@ bool MatchASTVisitor::TraverseDecl(Decl *DeclNode) {
15031537
}
15041538

15051539
bool MatchASTVisitor::TraverseStmt(Stmt *StmtNode, DataRecursionQueue *Queue) {
1506-
if (!StmtNode) {
1540+
if (shouldSkipNode(StmtNode))
15071541
return true;
1508-
}
1542+
15091543
bool ScopedTraversal = TraversingASTNodeNotSpelledInSource ||
15101544
TraversingASTChildrenNotSpelledInSource;
15111545

@@ -1515,13 +1549,18 @@ bool MatchASTVisitor::TraverseStmt(Stmt *StmtNode, DataRecursionQueue *Queue) {
15151549
}
15161550

15171551
bool MatchASTVisitor::TraverseType(QualType TypeNode, bool TraverseQualifier) {
1552+
if (shouldSkipNode(TypeNode))
1553+
return true;
1554+
15181555
match(TypeNode);
15191556
return RecursiveASTVisitor<MatchASTVisitor>::TraverseType(TypeNode,
15201557
TraverseQualifier);
15211558
}
15221559

15231560
bool MatchASTVisitor::TraverseTypeLoc(TypeLoc TypeLocNode,
15241561
bool TraverseQualifier) {
1562+
if (shouldSkipNode(TypeLocNode))
1563+
return true;
15251564
// The RecursiveASTVisitor only visits types if they're not within TypeLocs.
15261565
// We still want to find those types via matchers, so we match them here. Note
15271566
// that the TypeLocs are structurally a shadow-hierarchy to the expressed
@@ -1534,6 +1573,9 @@ bool MatchASTVisitor::TraverseTypeLoc(TypeLoc TypeLocNode,
15341573
}
15351574

15361575
bool MatchASTVisitor::TraverseNestedNameSpecifier(NestedNameSpecifier NNS) {
1576+
if (shouldSkipNode(NNS))
1577+
return true;
1578+
15371579
match(NNS);
15381580
return RecursiveASTVisitor<MatchASTVisitor>::TraverseNestedNameSpecifier(NNS);
15391581
}
@@ -1543,6 +1585,9 @@ bool MatchASTVisitor::TraverseNestedNameSpecifierLoc(
15431585
if (!NNS)
15441586
return true;
15451587

1588+
if (shouldSkipNode(NNS))
1589+
return true;
1590+
15461591
match(NNS);
15471592

15481593
// We only match the nested name specifier here (as opposed to traversing it)
@@ -1555,7 +1600,7 @@ bool MatchASTVisitor::TraverseNestedNameSpecifierLoc(
15551600

15561601
bool MatchASTVisitor::TraverseConstructorInitializer(
15571602
CXXCtorInitializer *CtorInit) {
1558-
if (!CtorInit)
1603+
if (shouldSkipNode(CtorInit))
15591604
return true;
15601605

15611606
bool ScopedTraversal = TraversingASTNodeNotSpelledInSource ||
@@ -1573,11 +1618,17 @@ bool MatchASTVisitor::TraverseConstructorInitializer(
15731618
}
15741619

15751620
bool MatchASTVisitor::TraverseTemplateArgumentLoc(TemplateArgumentLoc Loc) {
1621+
if (shouldSkipNode(Loc))
1622+
return true;
1623+
15761624
match(Loc);
15771625
return RecursiveASTVisitor<MatchASTVisitor>::TraverseTemplateArgumentLoc(Loc);
15781626
}
15791627

15801628
bool MatchASTVisitor::TraverseAttr(Attr *AttrNode) {
1629+
if (shouldSkipNode(AttrNode))
1630+
return true;
1631+
15811632
match(*AttrNode);
15821633
return RecursiveASTVisitor<MatchASTVisitor>::TraverseAttr(AttrNode);
15831634
}

0 commit comments

Comments
 (0)