Skip to content
Closed
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
21 changes: 17 additions & 4 deletions clang-tools-extra/clangd/AST.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
4 changes: 3 additions & 1 deletion clang-tools-extra/clangd/AST.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ namespace clang {
class SourceManager;
class Decl;
class DynTypedNode;
class HeuristicResolver;

namespace clangd {

Expand Down Expand Up @@ -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
Expand Down
4 changes: 3 additions & 1 deletion clang-tools-extra/clangd/Hover.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1308,7 +1308,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);
Expand Down
11 changes: 7 additions & 4 deletions clang-tools-extra/clangd/XRefs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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).
Expand All @@ -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()))
Expand Down Expand Up @@ -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));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down
3 changes: 2 additions & 1 deletion clang-tools-extra/clangd/unittests/ASTTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
4 changes: 2 additions & 2 deletions clang-tools-extra/clangd/unittests/HoverTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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
Expand Down
10 changes: 9 additions & 1 deletion clang-tools-extra/clangd/unittests/XRefsTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {} };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"));
Expand Down Expand Up @@ -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;");
Expand Down
6 changes: 4 additions & 2 deletions clang/include/clang/Sema/HeuristicResolver.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,6 @@ class HeuristicResolver {
std::vector<const NamedDecl *>
resolveDeclRefExpr(const DependentScopeDeclRefExpr *RE) const;
std::vector<const NamedDecl *>
resolveTypeOfCallExpr(const CallExpr *CE) const;
std::vector<const NamedDecl *>
resolveCalleeOfCallExpr(const CallExpr *CE) const;
std::vector<const NamedDecl *>
resolveUsingValueDecl(const UnresolvedUsingValueDecl *UUVD) const;
Expand Down Expand Up @@ -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
Expand Down
93 changes: 66 additions & 27 deletions clang/lib/Sema/HeuristicResolver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ class HeuristicResolverImpl {
resolveMemberExpr(const CXXDependentScopeMemberExpr *ME);
std::vector<const NamedDecl *>
resolveDeclRefExpr(const DependentScopeDeclRefExpr *RE);
std::vector<const NamedDecl *> resolveTypeOfCallExpr(const CallExpr *CE);
std::vector<const NamedDecl *> resolveCalleeOfCallExpr(const CallExpr *CE);
std::vector<const NamedDecl *>
resolveUsingValueDecl(const UnresolvedUsingValueDecl *UUVD);
Expand All @@ -51,6 +50,7 @@ class HeuristicResolverImpl {
llvm::function_ref<bool(const NamedDecl *ND)> 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:
Expand All @@ -72,10 +72,8 @@ class HeuristicResolverImpl {
resolveDependentMember(QualType T, DeclarationName Name,
llvm::function_ref<bool(const NamedDecl *ND)> Filter);

// Try to heuristically resolve the type of a possibly-dependent expression
// `E`.
QualType resolveExprToType(const Expr *E);
std::vector<const NamedDecl *> resolveExprToDecls(const Expr *E);
QualType resolveTypeOfCallExpr(const CallExpr *CE);

bool findOrdinaryMemberInDependentClasses(const CXXBaseSpecifier *Specifier,
CXXBasePath &Path,
Expand All @@ -97,18 +95,25 @@ const auto TemplateFilter = [](const NamedDecl *D) {
return isa<TemplateDecl>(D);
};

QualType resolveDeclsToType(const std::vector<const NamedDecl *> &Decls,
ASTContext &Ctx) {
if (Decls.size() != 1) // Names an overload set -- just bail.
return QualType();
if (const auto *TD = dyn_cast<TypeDecl>(Decls[0]))
QualType resolveDeclToType(const NamedDecl *D, ASTContext &Ctx) {
if (const auto *TempD = dyn_cast<TemplateDecl>(D)) {
D = TempD->getTemplatedDecl();
}
if (const auto *TD = dyn_cast<TypeDecl>(D))
return Ctx.getCanonicalTypeDeclType(TD);
if (const auto *VD = dyn_cast<ValueDecl>(Decls[0])) {
if (const auto *VD = dyn_cast<ValueDecl>(D)) {
return VD->getType();
}
return QualType();
}

QualType resolveDeclsToType(const std::vector<const NamedDecl *> &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<TemplateSpecializationType>()) {
return TST->getTemplateName();
Expand Down Expand Up @@ -315,19 +320,29 @@ HeuristicResolverImpl::resolveDeclRefExpr(const DependentScopeDeclRefExpr *RE) {
return resolveDependentMember(Qualifier, RE->getDeclName(), StaticFilter);
}

std::vector<const NamedDecl *>
HeuristicResolverImpl::resolveTypeOfCallExpr(const CallExpr *CE) {
QualType CalleeType = resolveExprToType(CE->getCallee());
if (CalleeType.isNull())
return {};
if (const auto *FnTypePtr = CalleeType->getAs<PointerType>())
CalleeType = FnTypePtr->getPointeeType();
if (const FunctionType *FnType = CalleeType->getAs<FunctionType>()) {
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<const NamedDecl *> 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<PointerType>())
CalleeType = FnTypePtr->getPointeeType();
if (const FunctionType *FnType = CalleeType->getAs<FunctionType>()) {
QualType ReturnType =
simplifyType(FnType->getReturnType(), nullptr, false);
if (!CommonReturnType.isNull() && CommonReturnType != ReturnType) {
return {}; // conflicting return types
}
CommonReturnType = ReturnType;
}
}
return {};
return CommonReturnType;
}

std::vector<const NamedDecl *>
Expand Down Expand Up @@ -378,15 +393,41 @@ HeuristicResolverImpl::resolveExprToDecls(const Expr *E) {
return {OE->decls_begin(), OE->decls_end()};
}
if (const auto *CE = dyn_cast<CallExpr>(E)) {
return resolveTypeOfCallExpr(CE);
QualType T = resolveTypeOfCallExpr(CE);
if (const auto *D = resolveTypeToTagDecl(T)) {
return {D};
}
return {};
}
if (const auto *ME = dyn_cast<MemberExpr>(E))
return {ME->getMemberDecl()};
if (const auto *DRE = dyn_cast<DeclRefExpr>(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<CallExpr>(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<UnaryOperator>(E->IgnoreParenCasts())) {
if (UO->getOpcode() == UnaryOperatorKind::UO_Deref) {
if (auto Pointee = getPointeeType(resolveExprToType(UO->getSubExpr()));
!Pointee.isNull()) {
return Pointee;
}
}
}

std::vector<const NamedDecl *> Decls = resolveExprToDecls(E);
if (!Decls.empty())
return resolveDeclsToType(Decls, Ctx);
Expand Down Expand Up @@ -565,10 +606,6 @@ std::vector<const NamedDecl *> HeuristicResolver::resolveDeclRefExpr(
return HeuristicResolverImpl(Ctx).resolveDeclRefExpr(RE);
}
std::vector<const NamedDecl *>
HeuristicResolver::resolveTypeOfCallExpr(const CallExpr *CE) const {
return HeuristicResolverImpl(Ctx).resolveTypeOfCallExpr(CE);
}
std::vector<const NamedDecl *>
HeuristicResolver::resolveCalleeOfCallExpr(const CallExpr *CE) const {
return HeuristicResolverImpl(Ctx).resolveCalleeOfCallExpr(CE);
}
Expand Down Expand Up @@ -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);
Expand Down
Loading
Loading