-
Notifications
You must be signed in to change notification settings - Fork 15.4k
[clang-tidy] Avoid expensive AST traversal in RedundantTypenameCheck #170540
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
In Fuchsia, we have several files that take over 2 hours for this check to run, where as it only takes 8 seconds to finish without the RedundantTypenameCheck. We can avoid this exponential behavior by limiting the use of hasAncestor to typeLocs for the types that are actually used in the checking logic. From the wall time for the check with --enable-profile goes from 6724 seconds (about 2 hours) to down to a reasonable 0.1753 seconds.
|
@llvm/pr-subscribers-clang-tools-extra Author: Paul Kirth (ilovepi) ChangesIn Fuchsia, we have several files that take over 2 hours for this check to run, where as it only takes 8 seconds to finish without the RedundantTypenameCheck. We can avoid this exponential behavior by limiting the use of hasAncestor to typeLocs for the types that are actually used in the checking logic. With this patch, the wall time for the check with --enable-profile goes from 6724 seconds (about 2 hours) to down to a reasonable 0.1753 seconds. Full diff: https://github.com/llvm/llvm-project/pull/170540.diff 1 Files Affected:
diff --git a/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.cpp b/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.cpp
index 5f2519ce9d5c3..f8e576e2a14d7 100644
--- a/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.cpp
@@ -18,9 +18,13 @@ using namespace clang::ast_matchers;
namespace clang::tidy::readability {
void RedundantTypenameCheck::registerMatchers(MatchFinder *Finder) {
- Finder->addMatcher(typeLoc(unless(hasAncestor(decl(isInstantiated()))))
- .bind("nonDependentTypeLoc"),
- this);
+ Finder->addMatcher(
+ typeLoc(loc(TypeMatcher(anyOf(typedefType(), tagType(),
+ deducedTemplateSpecializationType(),
+ templateSpecializationType()))),
+ unless(hasAncestor(decl(isInstantiated()))))
+ .bind("nonDependentTypeLoc"),
+ this);
if (!getLangOpts().CPlusPlus20)
return;
|
|
@llvm/pr-subscribers-clang-tidy Author: Paul Kirth (ilovepi) ChangesIn Fuchsia, we have several files that take over 2 hours for this check to run, where as it only takes 8 seconds to finish without the RedundantTypenameCheck. We can avoid this exponential behavior by limiting the use of hasAncestor to typeLocs for the types that are actually used in the checking logic. With this patch, the wall time for the check with --enable-profile goes from 6724 seconds (about 2 hours) to down to a reasonable 0.1753 seconds. Full diff: https://github.com/llvm/llvm-project/pull/170540.diff 1 Files Affected:
diff --git a/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.cpp b/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.cpp
index 5f2519ce9d5c3..f8e576e2a14d7 100644
--- a/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.cpp
@@ -18,9 +18,13 @@ using namespace clang::ast_matchers;
namespace clang::tidy::readability {
void RedundantTypenameCheck::registerMatchers(MatchFinder *Finder) {
- Finder->addMatcher(typeLoc(unless(hasAncestor(decl(isInstantiated()))))
- .bind("nonDependentTypeLoc"),
- this);
+ Finder->addMatcher(
+ typeLoc(loc(TypeMatcher(anyOf(typedefType(), tagType(),
+ deducedTemplateSpecializationType(),
+ templateSpecializationType()))),
+ unless(hasAncestor(decl(isInstantiated()))))
+ .bind("nonDependentTypeLoc"),
+ this);
if (!getLangOpts().CPlusPlus20)
return;
|
| Finder->addMatcher( | ||
| typeLoc(loc(TypeMatcher(anyOf(typedefType(), tagType(), | ||
| deducedTemplateSpecializationType(), | ||
| templateSpecializationType()))), | ||
| unless(hasAncestor(decl(isInstantiated())))) | ||
| .bind("nonDependentTypeLoc"), | ||
| this); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't we skip the hasAncestor part (I'd keep the anyOf you added anyways since we are producing less matches that way, which also improves perf)
typeLoc(loc(qualType(unless(dependentNameType())))?
Then we are not checking a type that is dependent, which eliminates the need for the hasAncestor and also the
if (NonDependentTypeLoc->getType()->isDependentType())
return SourceLocation();later
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems that we can remove unless(...). The traverse mode of this checker is TK_IgnoreUnlessSpelledInSource, so decl(isInstantiated()) doesn't matches anything IIUC.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Those are great suggestions. Thanks 😃
I’ll try to clean those up and see how our test case works after.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems that we can remove unless(...). The traverse mode of this checker is TK_IgnoreUnlessSpelledInSource, so decl(isInstantiated()) doesn't matches anything IIUC.
I originally added the unless to make this test case pass:
llvm-project/clang-tools-extra/test/clang-tidy/checkers/readability/redundant-typename.cpp
Lines 167 to 170 in 5911754
| template <typename T> | |
| void n(typename T::R *) {} | |
| template void n<NotDependent>(NotDependent::R *); |
Without the
unless, we get a false positive; the traversal seems to descend into the instantiation despite the traversal mode being TK_IgnoreUnlessSpelledInSource. Experimenting just now, I tried setting the traversal mode on the matcher explicitly:
Finder->addMatcher(traverse(TK_IgnoreUnlessSpelledInSource, typeLoc()
.bind("nonDependentTypeLoc")),
this);And now it does work! Is there maybe an issue causing the traversal mode to not get propagated?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, bingo. There are a bunch of addMatcher overloads. The ones for DeclarationMatcher and StatementMatcher respect getCheckTraversalKind:
llvm-project/clang/lib/ASTMatchers/ASTMatchFinder.cpp
Lines 1670 to 1680 in 7220268
| void MatchFinder::addMatcher(const DeclarationMatcher &NodeMatch, | |
| MatchCallback *Action) { | |
| std::optional<TraversalKind> TK; | |
| if (Action) | |
| TK = Action->getCheckTraversalKind(); | |
| if (TK) | |
| Matchers.DeclOrStmt.emplace_back(traverse(*TK, NodeMatch), Action); | |
| else | |
| Matchers.DeclOrStmt.emplace_back(NodeMatch, Action); | |
| Matchers.AllCallbacks.insert(Action); | |
| } |
And all the rest (including the one we're calling) just... don't?!
llvm-project/clang/lib/ASTMatchers/ASTMatchFinder.cpp
Lines 1712 to 1716 in 7220268
| void MatchFinder::addMatcher(const TypeLocMatcher &NodeMatch, | |
| MatchCallback *Action) { | |
| Matchers.TypeLoc.emplace_back(NodeMatch, Action); | |
| Matchers.AllCallbacks.insert(Action); | |
| } |
| .bind("nonDependentTypeLoc"), | ||
| this); | ||
| Finder->addMatcher( | ||
| typeLoc(loc(TypeMatcher(anyOf(typedefType(), tagType(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we remove TypeMatcher?
-typeLoc(loc(TypeMatcher(anyOf(...))))
+typeLoc(loc(anyOf(...)))
In Fuchsia, we have several files that take over 2 hours for this check to run, where as it only takes 8 seconds to finish without the RedundantTypenameCheck. We can avoid this exponential behavior by limiting the use of hasAncestor to typeLocs for the types that are actually used in the checking logic.
With this patch, the wall time for the check with --enable-profile goes from 6724 seconds (about 2 hours) to down to a reasonable 0.1753 seconds under c++17, and 347 seconds under C++20.