Skip to content

Commit 239f8b9

Browse files
authored
[AST] RecursiveASTVisitor: Don't traverse the alias deduction guides in the default mode. (#91454)
By default (`shouldVisitImplicitCode()` returns `false`), 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.
1 parent ba2e4fe commit 239f8b9

File tree

2 files changed

+106
-7
lines changed

2 files changed

+106
-7
lines changed

clang/include/clang/AST/RecursiveASTVisitor.h

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -736,13 +736,27 @@ bool RecursiveASTVisitor<Derived>::TraverseDecl(Decl *D) {
736736

737737
// As a syntax visitor, by default we want to ignore declarations for
738738
// implicit declarations (ones not typed explicitly by the user).
739-
if (!getDerived().shouldVisitImplicitCode() && D->isImplicit()) {
740-
// For an implicit template type parameter, its type constraints are not
741-
// implicit and are not represented anywhere else. We still need to visit
742-
// them.
743-
if (auto *TTPD = dyn_cast<TemplateTypeParmDecl>(D))
744-
return TraverseTemplateTypeParamDeclConstraints(TTPD);
745-
return true;
739+
if (!getDerived().shouldVisitImplicitCode()) {
740+
if (D->isImplicit()) {
741+
// For an implicit template type parameter, its type constraints are not
742+
// implicit and are not represented anywhere else. We still need to visit
743+
// them.
744+
if (auto *TTPD = dyn_cast<TemplateTypeParmDecl>(D))
745+
return TraverseTemplateTypeParamDeclConstraints(TTPD);
746+
return true;
747+
}
748+
749+
// Deduction guides for alias templates are always synthesized, so they
750+
// should not be traversed unless shouldVisitImplicitCode() returns true.
751+
//
752+
// It's important to note that checking the implicit bit is not efficient
753+
// for the alias case. For deduction guides synthesized from explicit
754+
// user-defined deduction guides, we must maintain the explicit bit to
755+
// ensure correct overload resolution.
756+
if (auto *FTD = dyn_cast<FunctionTemplateDecl>(D))
757+
if (llvm::isa_and_present<TypeAliasTemplateDecl>(
758+
FTD->getDeclName().getCXXDeductionGuideTemplate()))
759+
return true;
746760
}
747761

748762
switch (D->getKind()) {
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
//===- unittest/Tooling/RecursiveASTVisitorTests/DeductionGuide.cpp -------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "TestVisitor.h"
10+
#include <string>
11+
12+
using namespace clang;
13+
14+
namespace {
15+
16+
class DeductionGuideVisitor
17+
: public ExpectedLocationVisitor<DeductionGuideVisitor> {
18+
public:
19+
DeductionGuideVisitor(bool ShouldVisitImplicitCode)
20+
: ShouldVisitImplicitCode(ShouldVisitImplicitCode) {}
21+
bool VisitCXXDeductionGuideDecl(CXXDeductionGuideDecl *D) {
22+
std::string Storage;
23+
llvm::raw_string_ostream Stream(Storage);
24+
D->print(Stream);
25+
Match(Stream.str(), D->getLocation());
26+
return true;
27+
}
28+
29+
bool shouldVisitTemplateInstantiations() const { return false; }
30+
31+
bool shouldVisitImplicitCode() const { return ShouldVisitImplicitCode; }
32+
bool ShouldVisitImplicitCode;
33+
};
34+
35+
TEST(RecursiveASTVisitor, DeductionGuideNonImplicitMode) {
36+
DeductionGuideVisitor Visitor(/*ShouldVisitImplicitCode*/ false);
37+
// Verify that the synthezied deduction guide for alias is not visited in
38+
// RAV's implicit mode.
39+
Visitor.ExpectMatch("Foo(T) -> Foo<int>", 11, 1);
40+
Visitor.DisallowMatch("Bar(type-parameter-0-0) -> Foo<int>", 14, 1);
41+
EXPECT_TRUE(Visitor.runOver(
42+
R"cpp(
43+
template <typename T>
44+
concept False = true;
45+
46+
template <typename T>
47+
struct Foo {
48+
Foo(T);
49+
};
50+
51+
template<typename T> requires False<T>
52+
Foo(T) -> Foo<int>;
53+
54+
template <typename U>
55+
using Bar = Foo<U>;
56+
Bar s(1);
57+
)cpp",
58+
DeductionGuideVisitor::Lang_CXX2a));
59+
}
60+
61+
TEST(RecursiveASTVisitor, DeductionGuideImplicitMode) {
62+
DeductionGuideVisitor Visitor(/*ShouldVisitImplicitCode*/ true);
63+
Visitor.ExpectMatch("Foo(T) -> Foo<int>", 11, 1);
64+
Visitor.ExpectMatch("Bar(type-parameter-0-0) -> Foo<int>", 14, 1);
65+
EXPECT_TRUE(Visitor.runOver(
66+
R"cpp(
67+
template <typename T>
68+
concept False = true;
69+
70+
template <typename T>
71+
struct Foo {
72+
Foo(T);
73+
};
74+
75+
template<typename T> requires False<T>
76+
Foo(T) -> Foo<int>;
77+
78+
template <typename U>
79+
using Bar = Foo<U>;
80+
Bar s(1);
81+
)cpp",
82+
DeductionGuideVisitor::Lang_CXX2a));
83+
}
84+
85+
} // end anonymous namespace

0 commit comments

Comments
 (0)