-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[AST] RecursiveASTVisitor: Don't traverse the alias deduction guides in the default mode. #91454
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
Conversation
@llvm/pr-subscribers-clang Author: Haojian Wu (hokein) ChangesBy default ( This is usually done by checking the implicit bit of the Decl. However, this doesn't work deduction guides that are synthesized from explicit user-defined deduction guides, as we must maintain the explicit bit to ensure correct overload resolution. Full diff: https://github.com/llvm/llvm-project/pull/91454.diff 2 Files Affected:
diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h
index f9b145b4e86a5..2517189c95300 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -736,13 +736,27 @@ bool RecursiveASTVisitor<Derived>::TraverseDecl(Decl *D) {
// As a syntax visitor, by default we want to ignore declarations for
// implicit declarations (ones not typed explicitly by the user).
- if (!getDerived().shouldVisitImplicitCode() && D->isImplicit()) {
- // For an implicit template type parameter, its type constraints are not
- // implicit and are not represented anywhere else. We still need to visit
- // them.
- if (auto *TTPD = dyn_cast<TemplateTypeParmDecl>(D))
- return TraverseTemplateTypeParamDeclConstraints(TTPD);
- return true;
+ if (!getDerived().shouldVisitImplicitCode()) {
+ if (D->isImplicit()) {
+ // For an implicit template type parameter, its type constraints are not
+ // implicit and are not represented anywhere else. We still need to visit
+ // them.
+ if (auto *TTPD = dyn_cast<TemplateTypeParmDecl>(D))
+ return TraverseTemplateTypeParamDeclConstraints(TTPD);
+ return true;
+ }
+
+ // Deduction guides for alias templates are always synthesized, so they
+ // should not be traversed unless shouldVisitImplicitCode() returns true.
+ //
+ // It's important to note that checking the implicit bit is not efficient
+ // for the alias case. For deduction guides synthesized from explicit
+ // user-defined deduction guides, we must maintain the explicit bit to
+ // ensure correct overload resolution.
+ if (auto *FTD = dyn_cast<FunctionTemplateDecl>(D))
+ if (llvm::isa_and_present<TypeAliasTemplateDecl>(
+ FTD->getDeclName().getCXXDeductionGuideTemplate()))
+ return true;
}
switch (D->getKind()) {
diff --git a/clang/unittests/Tooling/RecursiveASTVisitorTests/DeductionGuide.cpp b/clang/unittests/Tooling/RecursiveASTVisitorTests/DeductionGuide.cpp
new file mode 100644
index 0000000000000..abfdbaea4a615
--- /dev/null
+++ b/clang/unittests/Tooling/RecursiveASTVisitorTests/DeductionGuide.cpp
@@ -0,0 +1,89 @@
+//===- unittest/Tooling/RecursiveASTVisitorTests/DeductionGuide.cpp -------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "TestVisitor.h"
+#include <string>
+
+using namespace clang;
+
+namespace {
+
+class DeductionGuideVisitor
+ : public ExpectedLocationVisitor<DeductionGuideVisitor> {
+public:
+ DeductionGuideVisitor(bool ShouldVisitImplicitCode)
+ : ShouldVisitImplicitCode(ShouldVisitImplicitCode) {}
+ bool VisitCXXDeductionGuideDecl(CXXDeductionGuideDecl *D) {
+ std::string Storage;
+ llvm::raw_string_ostream Stream(Storage);
+ D->print(Stream);
+ Match(Stream.str(),D->getLocation());
+ return true;
+ }
+
+ bool shouldVisitTemplateInstantiations() const {
+ return false;
+ }
+
+ bool shouldVisitImplicitCode() const {
+ return ShouldVisitImplicitCode;
+ }
+ bool ShouldVisitImplicitCode;
+};
+
+TEST(RecursiveASTVisitor, DeductionGuideNonImplicitMode) {
+ DeductionGuideVisitor Visitor(/*ShouldVisitImplicitCode*/ false);
+ // Verify that the synthezied deduction guide for alias is not visited in
+ // RAV's implicit mode.
+ Visitor.ExpectMatch("Foo(T) -> Foo<int>", 11, 1);
+ Visitor.DisallowMatch("Bar(type-parameter-0-0) -> Foo<int>", 14, 1);
+ EXPECT_TRUE(Visitor.runOver(
+ R"cpp(
+template <typename T>
+concept False = true;
+
+template <typename T>
+struct Foo {
+ Foo(T);
+};
+
+template<typename T> requires False<T>
+Foo(T) -> Foo<int>;
+
+template <typename U>
+using Bar = Foo<U>;
+Bar s(1);
+ )cpp"
+ , DeductionGuideVisitor::Lang_CXX2a));
+}
+
+TEST(RecursiveASTVisitor, DeductionGuideImplicitMode) {
+ DeductionGuideVisitor Visitor(/*ShouldVisitImplicitCode*/ true);
+ Visitor.ExpectMatch("Foo(T) -> Foo<int>", 11, 1);
+ Visitor.ExpectMatch("Bar(type-parameter-0-0) -> Foo<int>", 14, 1);
+ EXPECT_TRUE(Visitor.runOver(
+ R"cpp(
+template <typename T>
+concept False = true;
+
+template <typename T>
+struct Foo {
+ Foo(T);
+};
+
+template<typename T> requires False<T>
+Foo(T) -> Foo<int>;
+
+template <typename U>
+using Bar = Foo<U>;
+Bar s(1);
+ )cpp"
+ , DeductionGuideVisitor::Lang_CXX2a));
+}
+
+} // end anonymous namespace
|
✅ With the latest revision this PR passed the C/C++ code formatter. |
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.
LGTM
@hokein looks like this test wasn't added to any CMakeLists.txt so it doesn't actually run? |
I think you want it here?
|
I think the tests broke in #99840 |
By default (
shouldVisitImplicitCode()
returnsfalse
), RAV should not traverse AST nodes that are not spelled in the source code. Deduction guides for alias templates are always synthesized, so they should not be traversed.This is usually done by checking the implicit bit of the Decl. However, this doesn't work deduction guides that are synthesized from explicit user-defined deduction guides, as we must maintain the explicit bit to ensure correct overload resolution.