Skip to content

Conversation

@ilovepi
Copy link
Contributor

@ilovepi ilovepi commented Dec 3, 2025

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.

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.
@llvmbot
Copy link
Member

llvmbot commented Dec 3, 2025

@llvm/pr-subscribers-clang-tools-extra

Author: Paul Kirth (ilovepi)

Changes

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.


Full diff: https://github.com/llvm/llvm-project/pull/170540.diff

1 Files Affected:

  • (modified) clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.cpp (+7-3)
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;

@llvmbot
Copy link
Member

llvmbot commented Dec 3, 2025

@llvm/pr-subscribers-clang-tidy

Author: Paul Kirth (ilovepi)

Changes

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.


Full diff: https://github.com/llvm/llvm-project/pull/170540.diff

1 Files Affected:

  • (modified) clang-tools-extra/clang-tidy/readability/RedundantTypenameCheck.cpp (+7-3)
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;

Comment on lines +21 to +27
Finder->addMatcher(
typeLoc(loc(TypeMatcher(anyOf(typedefType(), tagType(),
deducedTemplateSpecializationType(),
templateSpecializationType()))),
unless(hasAncestor(decl(isInstantiated()))))
.bind("nonDependentTypeLoc"),
this);
Copy link
Contributor

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

Copy link
Contributor

@zwuis zwuis Dec 4, 2025

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.

Copy link
Contributor Author

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.

Copy link
Contributor

@localspook localspook Dec 5, 2025

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:

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?

Copy link
Contributor

@localspook localspook Dec 5, 2025

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:

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?!
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(),
Copy link
Contributor

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(...)))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants