https://github.com/HighCommander4 created https://github.com/llvm/llvm-project/pull/156283
Fixes https://github.com/clangd/clangd/issues/2431 >From 45bcf1eee94b6c13d81b6a1049881882c098db2a Mon Sep 17 00:00:00 2001 From: Nathan Ridge <zeratul...@hotmail.com> Date: Wed, 27 Aug 2025 00:50:30 -0400 Subject: [PATCH] [clangd] Use HeuristicResolver to try to resolve dependent 'auto' --- clang-tools-extra/clangd/AST.cpp | 21 +++++++++++++++---- clang-tools-extra/clangd/AST.h | 4 +++- clang-tools-extra/clangd/Hover.cpp | 4 +++- clang-tools-extra/clangd/XRefs.cpp | 11 ++++++---- .../refactor/tweaks/ExpandDeducedType.cpp | 3 ++- .../clangd/unittests/ASTTests.cpp | 3 ++- .../clangd/unittests/HoverTests.cpp | 4 ++-- .../clangd/unittests/XRefsTests.cpp | 10 ++++++++- .../tweaks/ExpandDeducedTypeTests.cpp | 4 ++-- 9 files changed, 47 insertions(+), 17 deletions(-) diff --git a/clang-tools-extra/clangd/AST.cpp b/clang-tools-extra/clangd/AST.cpp index 2f46ecc92576c..b96a84519e78c 100644 --- a/clang-tools-extra/clangd/AST.cpp +++ b/clang-tools-extra/clangd/AST.cpp @@ -29,6 +29,7 @@ #include "clang/Basic/SourceManager.h" #include "clang/Basic/Specifiers.h" #include "clang/Index/USRGeneration.h" +#include "clang/Sema/HeuristicResolver.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallSet.h" @@ -479,10 +480,12 @@ namespace { /// a deduced type set. The AST should be improved to simplify this scenario. class DeducedTypeVisitor : public RecursiveASTVisitor<DeducedTypeVisitor> { SourceLocation SearchedLocation; + const HeuristicResolver *Resolver; public: - DeducedTypeVisitor(SourceLocation SearchedLocation) - : SearchedLocation(SearchedLocation) {} + DeducedTypeVisitor(SourceLocation SearchedLocation, + const HeuristicResolver *Resolver) + : SearchedLocation(SearchedLocation), Resolver(Resolver) {} // Handle auto initializers: //- auto i = 1; @@ -499,6 +502,14 @@ class DeducedTypeVisitor : public RecursiveASTVisitor<DeducedTypeVisitor> { return true; if (auto *AT = D->getType()->getContainedAutoType()) { + if (AT->isUndeducedAutoType()) { + if (const auto *VD = dyn_cast<VarDecl>(D)) { + if (Resolver && VD->hasInit()) { + DeducedType = Resolver->resolveExprToType(VD->getInit()); + return true; + } + } + } DeducedType = AT->desugar(); } return true; @@ -608,10 +619,12 @@ class DeducedTypeVisitor : public RecursiveASTVisitor<DeducedTypeVisitor> { }; } // namespace -std::optional<QualType> getDeducedType(ASTContext &ASTCtx, SourceLocation Loc) { +std::optional<QualType> getDeducedType(ASTContext &ASTCtx, + const HeuristicResolver *Resolver, + SourceLocation Loc) { if (!Loc.isValid()) return {}; - DeducedTypeVisitor V(Loc); + DeducedTypeVisitor V(Loc, Resolver); V.TraverseAST(ASTCtx); if (V.DeducedType.isNull()) return std::nullopt; diff --git a/clang-tools-extra/clangd/AST.h b/clang-tools-extra/clangd/AST.h index 1538d12172593..2b83595e5b8e9 100644 --- a/clang-tools-extra/clangd/AST.h +++ b/clang-tools-extra/clangd/AST.h @@ -31,6 +31,7 @@ namespace clang { class SourceManager; class Decl; class DynTypedNode; +class HeuristicResolver; namespace clangd { @@ -167,7 +168,8 @@ QualType declaredType(const TypeDecl *D); /// Retrieves the deduced type at a given location (auto, decltype). /// It will return the underlying type. /// If the type is an undeduced auto, returns the type itself. -std::optional<QualType> getDeducedType(ASTContext &, SourceLocation Loc); +std::optional<QualType> getDeducedType(ASTContext &, const HeuristicResolver *, + SourceLocation Loc); // Find the abbreviated-function-template `auto` within a type, or returns null. // Similar to getContainedAutoTypeLoc, but these `auto`s are diff --git a/clang-tools-extra/clangd/Hover.cpp b/clang-tools-extra/clangd/Hover.cpp index 9eec322fe5963..138544dea99a1 100644 --- a/clang-tools-extra/clangd/Hover.cpp +++ b/clang-tools-extra/clangd/Hover.cpp @@ -1309,7 +1309,9 @@ std::optional<HoverInfo> getHover(ParsedAST &AST, Position Pos, } } else if (Tok.kind() == tok::kw_auto || Tok.kind() == tok::kw_decltype) { HoverCountMetric.record(1, "keyword"); - if (auto Deduced = getDeducedType(AST.getASTContext(), Tok.location())) { + if (auto Deduced = + getDeducedType(AST.getASTContext(), AST.getHeuristicResolver(), + Tok.location())) { HI = getDeducedTypeHoverContents(*Deduced, Tok, AST.getASTContext(), PP, Index); HighlightRange = Tok.range(SM).toCharRange(SM); diff --git a/clang-tools-extra/clangd/XRefs.cpp b/clang-tools-extra/clangd/XRefs.cpp index a253a630a48cc..18bc29426df29 100644 --- a/clang-tools-extra/clangd/XRefs.cpp +++ b/clang-tools-extra/clangd/XRefs.cpp @@ -806,7 +806,9 @@ std::vector<LocatedSymbol> locateSymbolAt(ParsedAST &AST, Position Pos, if (Tok.kind() == tok::kw_auto || Tok.kind() == tok::kw_decltype) { // go-to-definition on auto should find the definition of the deduced // type, if possible - if (auto Deduced = getDeducedType(AST.getASTContext(), Tok.location())) { + if (auto Deduced = + getDeducedType(AST.getASTContext(), AST.getHeuristicResolver(), + Tok.location())) { auto LocSym = locateSymbolForType(AST, *Deduced, Index); if (!LocSym.empty()) return LocSym; @@ -1965,7 +1967,7 @@ std::vector<const CXXRecordDecl *> findRecordTypeAt(ParsedAST &AST, // Return the type most associated with an AST node. // This isn't precisely defined: we want "go to type" to do something useful. -static QualType typeForNode(const ASTContext &Ctx, +static QualType typeForNode(const ASTContext &Ctx, const HeuristicResolver *H, const SelectionTree::Node *N) { // If we're looking at a namespace qualifier, walk up to what it's qualifying. // (If we're pointing at a *class* inside a NNS, N will be a TypeLoc). @@ -1978,7 +1980,7 @@ static QualType typeForNode(const ASTContext &Ctx, if (const TypeLoc *TL = N->ASTNode.get<TypeLoc>()) { if (llvm::isa<DeducedType>(TL->getTypePtr())) if (auto Deduced = getDeducedType( - N->getDeclContext().getParentASTContext(), TL->getBeginLoc())) + N->getDeclContext().getParentASTContext(), H, TL->getBeginLoc())) return *Deduced; // Exception: an alias => underlying type. if (llvm::isa<TypedefType>(TL->getTypePtr())) @@ -2161,7 +2163,8 @@ std::vector<LocatedSymbol> findType(ParsedAST &AST, Position Pos, // information about the type you may have not known before // (since unique_ptr<unique_ptr<T>> != unique_ptr<T>). for (const QualType &Type : unwrapFindType( - typeForNode(AST.getASTContext(), N), AST.getHeuristicResolver())) + typeForNode(AST.getASTContext(), AST.getHeuristicResolver(), N), + AST.getHeuristicResolver())) llvm::copy(locateSymbolForType(AST, Type, Index), std::back_inserter(LocatedSymbols)); diff --git a/clang-tools-extra/clangd/refactor/tweaks/ExpandDeducedType.cpp b/clang-tools-extra/clangd/refactor/tweaks/ExpandDeducedType.cpp index fec5f5797cb62..52afda56a5028 100644 --- a/clang-tools-extra/clangd/refactor/tweaks/ExpandDeducedType.cpp +++ b/clang-tools-extra/clangd/refactor/tweaks/ExpandDeducedType.cpp @@ -133,7 +133,8 @@ Expected<Tweak::Effect> ExpandDeducedType::apply(const Selection &Inputs) { auto &SrcMgr = Inputs.AST->getSourceManager(); std::optional<clang::QualType> DeducedType = - getDeducedType(Inputs.AST->getASTContext(), Range.getBegin()); + getDeducedType(Inputs.AST->getASTContext(), + Inputs.AST->getHeuristicResolver(), Range.getBegin()); // if we can't resolve the type, return an error message if (DeducedType == std::nullopt || (*DeducedType)->isUndeducedAutoType()) diff --git a/clang-tools-extra/clangd/unittests/ASTTests.cpp b/clang-tools-extra/clangd/unittests/ASTTests.cpp index 76d46bad82224..91ae727d8c944 100644 --- a/clang-tools-extra/clangd/unittests/ASTTests.cpp +++ b/clang-tools-extra/clangd/unittests/ASTTests.cpp @@ -244,7 +244,8 @@ TEST(GetDeducedType, KwAutoKwDecltypeExpansion) { for (Position Pos : File.points()) { auto Location = sourceLocationInMainFile(SM.get(), Pos); ASSERT_TRUE(!!Location) << llvm::toString(Location.takeError()); - auto DeducedType = getDeducedType(AST.getASTContext(), *Location); + auto DeducedType = getDeducedType(AST.getASTContext(), + AST.getHeuristicResolver(), *Location); if (T.DeducedType == nullptr) { EXPECT_FALSE(DeducedType); } else { diff --git a/clang-tools-extra/clangd/unittests/HoverTests.cpp b/clang-tools-extra/clangd/unittests/HoverTests.cpp index 743c0dc0d0187..e9abf71e6d1b6 100644 --- a/clang-tools-extra/clangd/unittests/HoverTests.cpp +++ b/clang-tools-extra/clangd/unittests/HoverTests.cpp @@ -481,7 +481,7 @@ class Foo final {})cpp"; [](HoverInfo &HI) { HI.Name = "auto"; HI.Kind = index::SymbolKind::TypeAlias; - HI.Definition = "/* not deduced */"; + HI.Definition = "T"; }}, // constrained auto {R"cpp( @@ -2657,7 +2657,7 @@ TEST(Hover, All) { [](HoverInfo &HI) { HI.Name = "auto"; HI.Kind = index::SymbolKind::TypeAlias; - HI.Definition = "/* not deduced */"; + HI.Definition = "T"; }}, { R"cpp(// Undeduced auto return type diff --git a/clang-tools-extra/clangd/unittests/XRefsTests.cpp b/clang-tools-extra/clangd/unittests/XRefsTests.cpp index 17204a47ba3bc..7ed08d7cce3d3 100644 --- a/clang-tools-extra/clangd/unittests/XRefsTests.cpp +++ b/clang-tools-extra/clangd/unittests/XRefsTests.cpp @@ -924,11 +924,19 @@ TEST(LocateSymbol, All) { } )cpp", + R"cpp(// auto with dependent type + template <typename> + struct [[A]] {}; + template <typename T> + void foo(A<T> a) { + ^auto copy = a; + } + )cpp", + R"cpp(// Override specifier jumps to overridden method class Y { virtual void $decl[[a]]() = 0; }; class X : Y { void a() ^override {} }; )cpp", - R"cpp(// Final specifier jumps to overridden method class Y { virtual void $decl[[a]]() = 0; }; class X : Y { void a() ^final {} }; diff --git a/clang-tools-extra/clangd/unittests/tweaks/ExpandDeducedTypeTests.cpp b/clang-tools-extra/clangd/unittests/tweaks/ExpandDeducedTypeTests.cpp index 8da394d74b54d..3a53c11839c20 100644 --- a/clang-tools-extra/clangd/unittests/tweaks/ExpandDeducedTypeTests.cpp +++ b/clang-tools-extra/clangd/unittests/tweaks/ExpandDeducedTypeTests.cpp @@ -47,7 +47,7 @@ TEST_F(ExpandDeducedTypeTest, Test) { "namespace ns { void f() { Class C = Class(); } }"); // undefined functions should not be replaced EXPECT_THAT(apply("au^to x = doesnt_exist(); // error-ok"), - StartsWith("fail: Could not deduce type for 'auto' type")); + StartsWith("fail: Could not expand a dependent type")); // function pointers should not be replaced EXPECT_THAT(apply("au^to x = &ns::Func;"), StartsWith("fail: Could not expand type")); @@ -91,7 +91,7 @@ TEST_F(ExpandDeducedTypeTest, Test) { // unknown types in a template should not be replaced EXPECT_THAT(apply("template <typename T> void x() { ^auto y = T::z(); }"), - StartsWith("fail: Could not deduce type for 'auto' type")); + StartsWith("fail: Could not expand a dependent type")); // check primitive type EXPECT_EQ(apply("decl^type(0) i;"), "int i;"); _______________________________________________ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits