Author: Haojian Wu Date: 2024-05-16T12:44:47+02:00 New Revision: 239f8b9eb0725b4c6ff8342717b6abbfc9a7915a
URL: https://github.com/llvm/llvm-project/commit/239f8b9eb0725b4c6ff8342717b6abbfc9a7915a DIFF: https://github.com/llvm/llvm-project/commit/239f8b9eb0725b4c6ff8342717b6abbfc9a7915a.diff LOG: [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. Added: clang/unittests/Tooling/RecursiveASTVisitorTests/DeductionGuide.cpp Modified: clang/include/clang/AST/RecursiveASTVisitor.h Removed: ################################################################################ diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h index 782f60844506f..f5cefedb07e0e 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..274f275ea66a9 --- /dev/null +++ b/clang/unittests/Tooling/RecursiveASTVisitorTests/DeductionGuide.cpp @@ -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 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits