Skip to content

Commit 38fb173

Browse files
author
Carlos Gálvez
committed
[clang-tidy] Avoid matching nodes in system headers
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 outside - 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 213314 warnings (213314 in non-user code). real 0m14.311s user 0m14.126s sys 0m0.185s With this patch: Suppressed 149399 warnings (149399 in non-user code). real 0m3.583s user 0m3.454s sys 0m0.128s With the original patch that got reverted: Suppressed 8050 warnings (8050 in non-user code). Runtime: around 1 second. A lot of warnings remain and the runtime is sligthly higher, but we still got a dramatic reduction with no change in functionality. Further investigation has shown that all of 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. However this may not be as straightforward or wanted, it may even need to be done on a per-check basis (there's about 10 checks or so that would need to explicitly ignore system headers). I will leave that for another patch, it's low priority and does not improve the runtime much (it just prints better statistics). Fixes #52959
1 parent c93d166 commit 38fb173

File tree

7 files changed

+78
-12
lines changed

7 files changed

+78
-12
lines changed

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

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

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

clang-tools-extra/docs/ReleaseNotes.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,10 @@ Improvements to clang-tidy
105105
now run checks in parallel by default using all available hardware threads.
106106
Both scripts display the number of threads being used in their output.
107107

108+
- :program:`clang-tidy` no longer attemps to analyze code from system headers
109+
by default, greatly improving performance. This behavior is disabled if the
110+
`SystemHeaders` option is enabled.
111+
108112
New checks
109113
^^^^^^^^^^
110114

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
@@ -221,6 +221,9 @@ AST Matchers
221221
- Ensure ``hasBitWidth`` doesn't crash on bit widths that are dependent on template
222222
parameters.
223223

224+
- Add a boolean member ``IgnoreSystemHeaders`` to ``MatchFinderOptions``. This
225+
allows it to ignore nodes in system headers when traversing the AST.
226+
224227
clang-format
225228
------------
226229

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: 60 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1336,6 +1336,44 @@ class MatchASTVisitor : public RecursiveASTVisitor<MatchASTVisitor>,
13361336
return false;
13371337
}
13381338

1339+
template <typename T> static SourceLocation getNodeLocation(const T &Node) {
1340+
return Node.getBeginLoc();
1341+
}
1342+
1343+
static SourceLocation getNodeLocation(const QualType &Node) { return {}; }
1344+
1345+
static SourceLocation getNodeLocation(const NestedNameSpecifier &Node) {
1346+
return {};
1347+
}
1348+
1349+
static SourceLocation getNodeLocation(const CXXCtorInitializer &Node) {
1350+
return Node.getSourceLocation();
1351+
}
1352+
1353+
static SourceLocation getNodeLocation(const TemplateArgumentLoc &Node) {
1354+
return Node.getLocation();
1355+
}
1356+
1357+
static SourceLocation getNodeLocation(const Attr &Node) {
1358+
return Node.getLocation();
1359+
}
1360+
1361+
bool isInSystemHeader(const SourceLocation &Loc) {
1362+
const SourceManager &SM = getASTContext().getSourceManager();
1363+
return SM.isInSystemHeader(Loc);
1364+
}
1365+
1366+
template <typename T> bool shouldSkipNode(const T &Node) {
1367+
if constexpr (std::is_pointer_v<T>)
1368+
return (Node == nullptr) || shouldSkipNode(*Node);
1369+
else {
1370+
if (Options.IgnoreSystemHeaders &&
1371+
isInSystemHeader(getNodeLocation(Node)))
1372+
return true;
1373+
return false;
1374+
}
1375+
}
1376+
13391377
/// Bucket to record map.
13401378
///
13411379
/// Used to get the appropriate bucket for each matcher.
@@ -1465,9 +1503,8 @@ bool MatchASTVisitor::objcClassIsDerivedFrom(
14651503
}
14661504

14671505
bool MatchASTVisitor::TraverseDecl(Decl *DeclNode) {
1468-
if (!DeclNode) {
1506+
if (shouldSkipNode(DeclNode))
14691507
return true;
1470-
}
14711508

14721509
bool ScopedTraversal =
14731510
TraversingASTNodeNotSpelledInSource || DeclNode->isImplicit();
@@ -1495,9 +1532,9 @@ bool MatchASTVisitor::TraverseDecl(Decl *DeclNode) {
14951532
}
14961533

14971534
bool MatchASTVisitor::TraverseStmt(Stmt *StmtNode, DataRecursionQueue *Queue) {
1498-
if (!StmtNode) {
1535+
if (shouldSkipNode(StmtNode))
14991536
return true;
1500-
}
1537+
15011538
bool ScopedTraversal = TraversingASTNodeNotSpelledInSource ||
15021539
TraversingASTChildrenNotSpelledInSource;
15031540

@@ -1507,11 +1544,17 @@ bool MatchASTVisitor::TraverseStmt(Stmt *StmtNode, DataRecursionQueue *Queue) {
15071544
}
15081545

15091546
bool MatchASTVisitor::TraverseType(QualType TypeNode) {
1547+
if (shouldSkipNode(TypeNode))
1548+
return true;
1549+
15101550
match(TypeNode);
15111551
return RecursiveASTVisitor<MatchASTVisitor>::TraverseType(TypeNode);
15121552
}
15131553

15141554
bool MatchASTVisitor::TraverseTypeLoc(TypeLoc TypeLocNode) {
1555+
if (shouldSkipNode(TypeLocNode))
1556+
return true;
1557+
15151558
// The RecursiveASTVisitor only visits types if they're not within TypeLocs.
15161559
// We still want to find those types via matchers, so we match them here. Note
15171560
// that the TypeLocs are structurally a shadow-hierarchy to the expressed
@@ -1523,6 +1566,9 @@ bool MatchASTVisitor::TraverseTypeLoc(TypeLoc TypeLocNode) {
15231566
}
15241567

15251568
bool MatchASTVisitor::TraverseNestedNameSpecifier(NestedNameSpecifier *NNS) {
1569+
if (shouldSkipNode(NNS))
1570+
return true;
1571+
15261572
match(*NNS);
15271573
return RecursiveASTVisitor<MatchASTVisitor>::TraverseNestedNameSpecifier(NNS);
15281574
}
@@ -1532,6 +1578,9 @@ bool MatchASTVisitor::TraverseNestedNameSpecifierLoc(
15321578
if (!NNS)
15331579
return true;
15341580

1581+
if (shouldSkipNode(NNS))
1582+
return true;
1583+
15351584
match(NNS);
15361585

15371586
// We only match the nested name specifier here (as opposed to traversing it)
@@ -1544,7 +1593,7 @@ bool MatchASTVisitor::TraverseNestedNameSpecifierLoc(
15441593

15451594
bool MatchASTVisitor::TraverseConstructorInitializer(
15461595
CXXCtorInitializer *CtorInit) {
1547-
if (!CtorInit)
1596+
if (shouldSkipNode(CtorInit))
15481597
return true;
15491598

15501599
bool ScopedTraversal = TraversingASTNodeNotSpelledInSource ||
@@ -1562,11 +1611,17 @@ bool MatchASTVisitor::TraverseConstructorInitializer(
15621611
}
15631612

15641613
bool MatchASTVisitor::TraverseTemplateArgumentLoc(TemplateArgumentLoc Loc) {
1614+
if (shouldSkipNode(Loc))
1615+
return true;
1616+
15651617
match(Loc);
15661618
return RecursiveASTVisitor<MatchASTVisitor>::TraverseTemplateArgumentLoc(Loc);
15671619
}
15681620

15691621
bool MatchASTVisitor::TraverseAttr(Attr *AttrNode) {
1622+
if (shouldSkipNode(AttrNode))
1623+
return true;
1624+
15701625
match(*AttrNode);
15711626
return RecursiveASTVisitor<MatchASTVisitor>::TraverseAttr(AttrNode);
15721627
}

0 commit comments

Comments
 (0)