From 82f95113d5133c8d8579f6445981ad20dce1881a Mon Sep 17 00:00:00 2001 From: Nathan Ridge Date: Sun, 24 Aug 2025 23:03:40 -0400 Subject: [PATCH 1/3] [clang][Sema] Implement additional heuristics from SemaCodeComplete's getApproximateType() in HeuristicResolver After this change, HeuristicResolver should be able to do everything that SemaCodeComplete's getApproximateType() can do (and more). --- clang/include/clang/Sema/HeuristicResolver.h | 6 +- clang/lib/Sema/HeuristicResolver.cpp | 93 +++++++++++++----- .../unittests/Sema/HeuristicResolverTest.cpp | 98 ++++++++++++++++++- 3 files changed, 167 insertions(+), 30 deletions(-) diff --git a/clang/include/clang/Sema/HeuristicResolver.h b/clang/include/clang/Sema/HeuristicResolver.h index 71588bee92d16..93738ab9ebb3d 100644 --- a/clang/include/clang/Sema/HeuristicResolver.h +++ b/clang/include/clang/Sema/HeuristicResolver.h @@ -54,8 +54,6 @@ class HeuristicResolver { std::vector resolveDeclRefExpr(const DependentScopeDeclRefExpr *RE) const; std::vector - resolveTypeOfCallExpr(const CallExpr *CE) const; - std::vector resolveCalleeOfCallExpr(const CallExpr *CE) const; std::vector resolveUsingValueDecl(const UnresolvedUsingValueDecl *UUVD) const; @@ -93,6 +91,10 @@ class HeuristicResolver { // during simplification, and the operation fails if no pointer type is found. QualType simplifyType(QualType Type, const Expr *E, bool UnwrapPointer); + // Try to heuristically resolve the type of a possibly-dependent expression + // `E`. + QualType resolveExprToType(const Expr *E) const; + // Given an expression `Fn` representing the callee in a function call, // if the call is through a function pointer, try to find the declaration of // the corresponding function pointer type, so that we can recover argument diff --git a/clang/lib/Sema/HeuristicResolver.cpp b/clang/lib/Sema/HeuristicResolver.cpp index 933841beeac3d..e8eb723a9572e 100644 --- a/clang/lib/Sema/HeuristicResolver.cpp +++ b/clang/lib/Sema/HeuristicResolver.cpp @@ -36,7 +36,6 @@ class HeuristicResolverImpl { resolveMemberExpr(const CXXDependentScopeMemberExpr *ME); std::vector resolveDeclRefExpr(const DependentScopeDeclRefExpr *RE); - std::vector resolveTypeOfCallExpr(const CallExpr *CE); std::vector resolveCalleeOfCallExpr(const CallExpr *CE); std::vector resolveUsingValueDecl(const UnresolvedUsingValueDecl *UUVD); @@ -51,6 +50,7 @@ class HeuristicResolverImpl { llvm::function_ref Filter); TagDecl *resolveTypeToTagDecl(QualType T); QualType simplifyType(QualType Type, const Expr *E, bool UnwrapPointer); + QualType resolveExprToType(const Expr *E); FunctionProtoTypeLoc getFunctionProtoTypeLoc(const Expr *Fn); private: @@ -72,10 +72,8 @@ class HeuristicResolverImpl { resolveDependentMember(QualType T, DeclarationName Name, llvm::function_ref Filter); - // Try to heuristically resolve the type of a possibly-dependent expression - // `E`. - QualType resolveExprToType(const Expr *E); std::vector resolveExprToDecls(const Expr *E); + QualType resolveTypeOfCallExpr(const CallExpr *CE); bool findOrdinaryMemberInDependentClasses(const CXXBaseSpecifier *Specifier, CXXBasePath &Path, @@ -97,18 +95,25 @@ const auto TemplateFilter = [](const NamedDecl *D) { return isa(D); }; -QualType resolveDeclsToType(const std::vector &Decls, - ASTContext &Ctx) { - if (Decls.size() != 1) // Names an overload set -- just bail. - return QualType(); - if (const auto *TD = dyn_cast(Decls[0])) +QualType resolveDeclToType(const NamedDecl *D, ASTContext &Ctx) { + if (const auto *TempD = dyn_cast(D)) { + D = TempD->getTemplatedDecl(); + } + if (const auto *TD = dyn_cast(D)) return Ctx.getCanonicalTypeDeclType(TD); - if (const auto *VD = dyn_cast(Decls[0])) { + if (const auto *VD = dyn_cast(D)) { return VD->getType(); } return QualType(); } +QualType resolveDeclsToType(const std::vector &Decls, + ASTContext &Ctx) { + if (Decls.size() != 1) // Names an overload set -- just bail. + return QualType(); + return resolveDeclToType(Decls[0], Ctx); +} + TemplateName getReferencedTemplateName(const Type *T) { if (const auto *TST = T->getAs()) { return TST->getTemplateName(); @@ -315,19 +320,29 @@ HeuristicResolverImpl::resolveDeclRefExpr(const DependentScopeDeclRefExpr *RE) { return resolveDependentMember(Qualifier, RE->getDeclName(), StaticFilter); } -std::vector -HeuristicResolverImpl::resolveTypeOfCallExpr(const CallExpr *CE) { - QualType CalleeType = resolveExprToType(CE->getCallee()); - if (CalleeType.isNull()) - return {}; - if (const auto *FnTypePtr = CalleeType->getAs()) - CalleeType = FnTypePtr->getPointeeType(); - if (const FunctionType *FnType = CalleeType->getAs()) { - if (const auto *D = resolveTypeToTagDecl(FnType->getReturnType())) { - return {D}; +QualType HeuristicResolverImpl::resolveTypeOfCallExpr(const CallExpr *CE) { + // resolveExprToType(CE->getCallee()) would bail in the case of multiple + // overloads, as it can't produce a single type for them. We can be more + // permissive here, and allow multiple overloads with a common return type. + std::vector CalleeDecls = + resolveExprToDecls(CE->getCallee()); + QualType CommonReturnType; + for (const NamedDecl *CalleeDecl : CalleeDecls) { + QualType CalleeType = resolveDeclToType(CalleeDecl, Ctx); + if (CalleeType.isNull()) + continue; + if (const auto *FnTypePtr = CalleeType->getAs()) + CalleeType = FnTypePtr->getPointeeType(); + if (const FunctionType *FnType = CalleeType->getAs()) { + QualType ReturnType = + simplifyType(FnType->getReturnType(), nullptr, false); + if (!CommonReturnType.isNull() && CommonReturnType != ReturnType) { + return {}; // conflicting return types + } + CommonReturnType = ReturnType; } } - return {}; + return CommonReturnType; } std::vector @@ -378,15 +393,41 @@ HeuristicResolverImpl::resolveExprToDecls(const Expr *E) { return {OE->decls_begin(), OE->decls_end()}; } if (const auto *CE = dyn_cast(E)) { - return resolveTypeOfCallExpr(CE); + QualType T = resolveTypeOfCallExpr(CE); + if (const auto *D = resolveTypeToTagDecl(T)) { + return {D}; + } + return {}; } if (const auto *ME = dyn_cast(E)) return {ME->getMemberDecl()}; + if (const auto *DRE = dyn_cast(E)) + return {DRE->getDecl()}; return {}; } QualType HeuristicResolverImpl::resolveExprToType(const Expr *E) { + // resolveExprToDecls on a CallExpr only succeeds if the return type is + // a TagDecl, but we may want the type of a call in other cases as well. + // (FIXME: There are probably other cases where we can do something more + // flexible than resoveExprToDecls + resolveDeclsToType, e.g. in the case + // of OverloadExpr we can probably accept overloads with a common type). + if (const auto *CE = dyn_cast(E)) { + if (QualType Resolved = resolveTypeOfCallExpr(CE); !Resolved.isNull()) + return Resolved; + } + // Similarly, unwrapping a unary dereference operation does not work via + // resolveExprToDecls. + if (const auto *UO = dyn_cast(E->IgnoreParenCasts())) { + if (UO->getOpcode() == UnaryOperatorKind::UO_Deref) { + if (auto Pointee = getPointeeType(resolveExprToType(UO->getSubExpr())); + !Pointee.isNull()) { + return Pointee; + } + } + } + std::vector Decls = resolveExprToDecls(E); if (!Decls.empty()) return resolveDeclsToType(Decls, Ctx); @@ -565,10 +606,6 @@ std::vector HeuristicResolver::resolveDeclRefExpr( return HeuristicResolverImpl(Ctx).resolveDeclRefExpr(RE); } std::vector -HeuristicResolver::resolveTypeOfCallExpr(const CallExpr *CE) const { - return HeuristicResolverImpl(Ctx).resolveTypeOfCallExpr(CE); -} -std::vector HeuristicResolver::resolveCalleeOfCallExpr(const CallExpr *CE) const { return HeuristicResolverImpl(Ctx).resolveCalleeOfCallExpr(CE); } @@ -604,7 +641,9 @@ QualType HeuristicResolver::simplifyType(QualType Type, const Expr *E, bool UnwrapPointer) { return HeuristicResolverImpl(Ctx).simplifyType(Type, E, UnwrapPointer); } - +QualType HeuristicResolver::resolveExprToType(const Expr *E) const { + return HeuristicResolverImpl(Ctx).resolveExprToType(E); +} FunctionProtoTypeLoc HeuristicResolver::getFunctionProtoTypeLoc(const Expr *Fn) const { return HeuristicResolverImpl(Ctx).getFunctionProtoTypeLoc(Fn); diff --git a/clang/unittests/Sema/HeuristicResolverTest.cpp b/clang/unittests/Sema/HeuristicResolverTest.cpp index 7df25e01e66d4..e8e97793f0e2a 100644 --- a/clang/unittests/Sema/HeuristicResolverTest.cpp +++ b/clang/unittests/Sema/HeuristicResolverTest.cpp @@ -203,7 +203,6 @@ TEST(HeuristicResolver, MemberExpr_AutoTypeDeduction2) { struct B { int waldo; }; - template struct A { B b; @@ -238,6 +237,103 @@ TEST(HeuristicResolver, MemberExpr_Chained) { cxxMethodDecl(hasName("foo")).bind("output")); } +TEST(HeuristicResolver, MemberExpr_Chained_ReferenceType) { + std::string Code = R"cpp( + struct B { + int waldo; + }; + template + struct A { + B &foo(); + }; + template + void bar(A a) { + a.foo().waldo; + } + )cpp"; + // Test resolution of "waldo" in "a.foo().waldo" + expectResolution( + Code, &HeuristicResolver::resolveMemberExpr, + cxxDependentScopeMemberExpr(hasMemberName("waldo")).bind("input"), + fieldDecl(hasName("waldo")).bind("output")); +} + +TEST(HeuristicResolver, MemberExpr_Chained_PointerArrow) { + std::string Code = R"cpp( + struct B { + int waldo; + }; + template + B* foo(T); + template + void bar(T t) { + foo(t)->waldo; + } + )cpp"; + // Test resolution of "waldo" in "foo(t)->waldo" + expectResolution( + Code, &HeuristicResolver::resolveMemberExpr, + cxxDependentScopeMemberExpr(hasMemberName("waldo")).bind("input"), + fieldDecl(hasName("waldo")).bind("output")); +} + +TEST(HeuristicResolver, MemberExpr_Chained_PointerDeref) { + std::string Code = R"cpp( + struct B { + int waldo; + }; + template + B* foo(T); + template + void bar(T t) { + (*foo(t)).waldo; + } + )cpp"; + // Test resolution of "waldo" in "foo(t)->waldo" + expectResolution( + Code, &HeuristicResolver::resolveMemberExpr, + cxxDependentScopeMemberExpr(hasMemberName("waldo")).bind("input"), + fieldDecl(hasName("waldo")).bind("output")); +} + +TEST(HeuristicResolver, MemberExpr_Chained_Overload) { + std::string Code = R"cpp( + struct B { + int waldo; + }; + B overloaded(int); + B overloaded(double); + template + void foo(T t) { + overloaded(t).waldo; + } + )cpp"; + // Test resolution of "waldo" in "overloaded(t).waldo" + expectResolution( + Code, &HeuristicResolver::resolveMemberExpr, + cxxDependentScopeMemberExpr(hasMemberName("waldo")).bind("input"), + fieldDecl(hasName("waldo")).bind("output")); +} + +TEST(HeuristicResolver, MemberExpr_CallToFunctionTemplate) { + std::string Code = R"cpp( + struct B { + int waldo; + }; + template + B bar(T); + template + void foo(T t) { + bar(t).waldo; + } + )cpp"; + // Test resolution of "waldo" in "bar(t).waldo" + expectResolution( + Code, &HeuristicResolver::resolveMemberExpr, + cxxDependentScopeMemberExpr(hasMemberName("waldo")).bind("input"), + fieldDecl(hasName("waldo")).bind("output")); +} + TEST(HeuristicResolver, MemberExpr_ReferenceType) { std::string Code = R"cpp( struct B { From 7b78f65cba28c3489b94bb7e0bc79c48f05303e0 Mon Sep 17 00:00:00 2001 From: Nathan Ridge Date: Sun, 24 Aug 2025 23:05:28 -0400 Subject: [PATCH 2/3] [clang][Sema] Replace most of SemaCodeComplete's getApproximateType() with calls to HeuristicResolver --- clang/lib/Sema/SemaCodeComplete.cpp | 97 +++-------------------------- 1 file changed, 7 insertions(+), 90 deletions(-) diff --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp index c6adead5a65f5..fc5bb15ec0f2c 100644 --- a/clang/lib/Sema/SemaCodeComplete.cpp +++ b/clang/lib/Sema/SemaCodeComplete.cpp @@ -5830,96 +5830,13 @@ class ConceptInfo { // We accept some lossiness (like dropping parameters). // We only try to handle common expressions on the LHS of MemberExpr. QualType getApproximateType(const Expr *E, HeuristicResolver &Resolver) { - if (E->getType().isNull()) - return QualType(); - // Don't drop implicit cast if it's an array decay. - if (auto *ICE = dyn_cast(E); - !ICE || ICE->getCastKind() != CK_ArrayToPointerDecay) - E = E->IgnoreParenImpCasts(); - QualType Unresolved = E->getType(); - // Resolve DependentNameType - if (const auto *DNT = Unresolved->getAs()) { - if (auto Decls = Resolver.resolveDependentNameType(DNT); - Decls.size() == 1) { - if (const auto *TD = dyn_cast(Decls[0])) - return TD->getASTContext().getTypeDeclType(TD); - } - } - // We only resolve DependentTy, or undeduced autos (including auto* etc). - if (!Unresolved->isSpecificBuiltinType(BuiltinType::Dependent)) { - AutoType *Auto = Unresolved->getContainedAutoType(); - if (!Auto || !Auto->isUndeducedAutoType()) - return Unresolved; - } - // A call: approximate-resolve callee to a function type, get its return type - if (const CallExpr *CE = llvm::dyn_cast(E)) { - QualType Callee = getApproximateType(CE->getCallee(), Resolver); - if (Callee.isNull() || - Callee->isSpecificPlaceholderType(BuiltinType::BoundMember)) - Callee = Expr::findBoundMemberType(CE->getCallee()); - if (Callee.isNull()) - return Unresolved; - - if (const auto *FnTypePtr = Callee->getAs()) { - Callee = FnTypePtr->getPointeeType(); - } else if (const auto *BPT = Callee->getAs()) { - Callee = BPT->getPointeeType(); - } - if (const FunctionType *FnType = Callee->getAs()) - return FnType->getReturnType().getNonReferenceType(); - - // Unresolved call: try to guess the return type. - if (const auto *OE = llvm::dyn_cast(CE->getCallee())) { - // If all candidates have the same approximate return type, use it. - // Discard references and const to allow more to be "the same". - // (In particular, if there's one candidate + ADL, resolve it). - const Type *Common = nullptr; - for (const auto *D : OE->decls()) { - QualType ReturnType; - if (const auto *FD = llvm::dyn_cast(D)) - ReturnType = FD->getReturnType(); - else if (const auto *FTD = llvm::dyn_cast(D)) - ReturnType = FTD->getTemplatedDecl()->getReturnType(); - if (ReturnType.isNull()) - continue; - const Type *Candidate = - ReturnType.getNonReferenceType().getCanonicalType().getTypePtr(); - if (Common && Common != Candidate) - return Unresolved; // Multiple candidates. - Common = Candidate; - } - if (Common != nullptr) - return QualType(Common, 0); - } - } - // A dependent member: resolve using HeuristicResolver. - if (const auto *CDSME = llvm::dyn_cast(E)) { - for (const auto *Member : Resolver.resolveMemberExpr(CDSME)) { - if (const auto *VD = dyn_cast(Member)) { - return VD->getType().getNonReferenceType(); - } - } - } - // A reference to an `auto` variable: approximate-resolve its initializer. - if (const auto *DRE = llvm::dyn_cast(E)) { - if (const auto *VD = llvm::dyn_cast(DRE->getDecl())) { - if (VD->hasInit()) - return getApproximateType(VD->getInit(), Resolver); - } - } - if (const auto *UO = llvm::dyn_cast(E)) { - if (UO->getOpcode() == UnaryOperatorKind::UO_Deref) { - // We recurse into the subexpression because it could be of dependent - // type. - if (auto Pointee = - getApproximateType(UO->getSubExpr(), Resolver)->getPointeeType(); - !Pointee.isNull()) - return Pointee; - // Our caller expects a non-null result, even though the SubType is - // supposed to have a pointee. Fall through to Unresolved anyway. - } - } - return Unresolved; + QualType Result = Resolver.resolveExprToType(E); + if (Result.isNull()) + return Result; + Result = Resolver.simplifyType(Result.getNonReferenceType(), E, false); + if (Result.isNull()) + return Result; + return Result.getNonReferenceType(); } // If \p Base is ParenListExpr, assume a chain of comma operators and pick the From 3bb9353ce772d75a7da0af225d33cc05616171eb Mon Sep 17 00:00:00 2001 From: Nathan Ridge Date: Wed, 27 Aug 2025 00:50:30 -0400 Subject: [PATCH 3/3] [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 { 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 { return true; if (auto *AT = D->getType()->getContainedAutoType()) { + if (AT->isUndeducedAutoType()) { + if (const auto *VD = dyn_cast(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 { }; } // namespace -std::optional getDeducedType(ASTContext &ASTCtx, SourceLocation Loc) { +std::optional 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 getDeducedType(ASTContext &, SourceLocation Loc); +std::optional 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 a7cf45c632827..c88c78481c250 100644 --- a/clang-tools-extra/clangd/Hover.cpp +++ b/clang-tools-extra/clangd/Hover.cpp @@ -1308,7 +1308,9 @@ std::optional 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 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 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()) { if (llvm::isa(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(TL->getTypePtr())) @@ -2161,7 +2163,8 @@ std::vector findType(ParsedAST &AST, Position Pos, // information about the type you may have not known before // (since unique_ptr> != unique_ptr). 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 ExpandDeducedType::apply(const Selection &Inputs) { auto &SrcMgr = Inputs.AST->getSourceManager(); std::optional 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 6407349d9af9b..658ce43e38e07 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 + struct [[A]] {}; + template + void foo(A 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 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;");