Skip to content

[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

Merged
merged 2 commits into from
May 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 21 additions & 7 deletions clang/include/clang/AST/RecursiveASTVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -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()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
//===- 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
Loading