diff --git a/clang-tools-extra/clangd/ClangdServer.cpp b/clang-tools-extra/clangd/ClangdServer.cpp --- a/clang-tools-extra/clangd/ClangdServer.cpp +++ b/clang-tools-extra/clangd/ClangdServer.cpp @@ -79,6 +79,7 @@ void onMainAST(PathRef Path, ParsedAST &AST, PublishFn Publish) override { WithContextValue ASTCtx(ParsedAST::CurrentASTContext, &AST.getASTContext()); + WithContextValue SemaCtx(ParsedAST::CurrentSema, &AST.getSema()); if (FIndex) FIndex->updateMain(Path, AST); diff --git a/clang-tools-extra/clangd/FindTarget.cpp b/clang-tools-extra/clangd/FindTarget.cpp --- a/clang-tools-extra/clangd/FindTarget.cpp +++ b/clang-tools-extra/clangd/FindTarget.cpp @@ -34,6 +34,7 @@ #include "clang/Basic/OperatorKinds.h" #include "clang/Basic/SourceLocation.h" #include "clang/Basic/Specifiers.h" +#include "clang/Sema/Template.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/Casting.h" @@ -60,10 +61,33 @@ return S; } +// FIXME: Use ASTContext::getCanonicalTemplateArgument() +TemplateArgument canonicalizeTemplateArg(const TemplateArgument &Arg) { + switch (Arg.getKind()) { + case TemplateArgument::Type: { + return TemplateArgument(Arg.getAsType().getCanonicalType()); + } + default: + return Arg; + } +} + +// A structure that resolveTypeToRecordDecl() can optionally populate +// in the case where it discards template arguments. +// The discarded template arguments are saved, along with other info +// the caller may need to substitute the arguments back into declarations +// it derives from the result of the operation (such as, into members of +// the RecordDecl). +struct SubstInfo { + // The template arguments that were discarded. + SmallVector TemplateArgs; +}; + // Helper function for getMembersReferencedViaDependentName() // which takes a possibly-dependent type `T` and heuristically // resolves it to a CXXRecordDecl in which we can try name lookup. -CXXRecordDecl *resolveTypeToRecordDecl(const Type *T) { +CXXRecordDecl *resolveTypeToRecordDecl(const Type *T, + SubstInfo *OutSubstInfo = nullptr) { assert(T); if (const auto *RT = T->getAs()) @@ -78,14 +102,55 @@ if (!TST) return nullptr; - const ClassTemplateDecl *TD = dyn_cast_or_null( + ClassTemplateDecl *TD = dyn_cast_or_null( TST->getTemplateName().getAsTemplateDecl()); if (!TD) return nullptr; + if (OutSubstInfo) { + for (const TemplateArgument &Arg : TST->template_arguments()) { + OutSubstInfo->TemplateArgs.push_back(canonicalizeTemplateArg(Arg)); + } + } return TD->getTemplatedDecl(); } +const NamedDecl *getTemplatePattern(const NamedDecl *D) { + if (const CXXRecordDecl *CRD = dyn_cast(D)) { + if (const auto *Result = CRD->getTemplateInstantiationPattern()) + return Result; + // getTemplateInstantiationPattern returns null if the Specialization is + // incomplete (e.g. the type didn't need to be complete), fall back to the + // primary template. + if (CRD->getTemplateSpecializationKind() == TSK_Undeclared) + if (const auto *Spec = dyn_cast(CRD)) + return Spec->getSpecializedTemplate()->getTemplatedDecl(); + } else if (const FunctionDecl *FD = dyn_cast(D)) { + return FD->getTemplateInstantiationPattern(); + } else if (auto *VD = dyn_cast(D)) { + // Hmm: getTIP returns its arg if it's not an instantiation?! + VarDecl *T = VD->getTemplateInstantiationPattern(); + return (T == D) ? nullptr : T; + } else if (const auto *ED = dyn_cast(D)) { + return ED->getInstantiatedFromMemberEnum(); + } else if (isa(D) || isa(D)) { + if (const auto *Parent = llvm::dyn_cast(D->getDeclContext())) + if (const DeclContext *ParentPat = + dyn_cast_or_null(getTemplatePattern(Parent))) + for (const NamedDecl *BaseND : ParentPat->lookup(D->getDeclName())) + if (!BaseND->isImplicit() && BaseND->getKind() == D->getKind()) + return BaseND; + } else if (const auto *ECD = dyn_cast(D)) { + if (const auto *ED = dyn_cast(ECD->getDeclContext())) { + if (const EnumDecl *Pattern = ED->getInstantiatedFromMemberEnum()) { + for (const NamedDecl *BaseECD : Pattern->lookup(ECD->getDeclName())) + return BaseECD; + } + } + } + return nullptr; +} + // Given a tag-decl type and a member name, heuristically resolve the // name to one or more declarations. // The current heuristic is simply to look up the name in the primary @@ -108,10 +173,44 @@ auto Result = ET->getDecl()->lookup(Name); return {Result.begin(), Result.end()}; } - if (auto *RD = resolveTypeToRecordDecl(T)) { - if (!RD->hasDefinition()) - return {}; + SubstInfo SubstInfo; + if (auto *RD = resolveTypeToRecordDecl(T, &SubstInfo)) { + if (!RD->hasDefinition()) { + // FIXME: Explain why we try the template pattern here. + RD = const_cast( + cast(getTemplatePattern(RD))); + if (!RD->hasDefinition()) { + return {}; + } + } RD = RD->getDefinition(); + + // If resolveTypeToRecordDecl() discarded template arguments, + // substitute them back into the decl here. This makes the + // results of lookupDependentName() more useful; for example, + // if the incoming type was `vector>`, and we are looking + // up the name `front()`, the return type of the lookup result + // will be `vector` rather than just a template-parameter, making + // it possible to do another lookup in the return type. + if (!SubstInfo.TemplateArgs.empty()) { + auto *Sema = Context::current().getExisting(ParsedAST::CurrentSema); + Sema::InstantiatingTemplate Inst(*Sema, RD->getLocation(), RD); + void *InsertPos = nullptr; + ClassTemplateDecl *CT = RD->getDescribedClassTemplate(); + assert(CT); + ClassTemplateSpecializationDecl *SubstRD = + CT->findSpecialization(SubstInfo.TemplateArgs, InsertPos); + if (!SubstRD) { + SubstRD = ClassTemplateSpecializationDecl::Create( + Sema->getASTContext(), CT->getTemplatedDecl()->getTagKind(), + CT->getDeclContext(), CT->getTemplatedDecl()->getBeginLoc(), + CT->getLocation(), CT, SubstInfo.TemplateArgs, nullptr); + Sema->InstantiateClassTemplateSpecialization(RD->getLocation(), SubstRD, + TSK_ImplicitInstantiation); + } + RD = SubstRD; + } + return RD->lookupDependentName(Name, Filter); } return {}; @@ -258,42 +357,6 @@ return nullptr; } -const NamedDecl *getTemplatePattern(const NamedDecl *D) { - if (const CXXRecordDecl *CRD = dyn_cast(D)) { - if (const auto *Result = CRD->getTemplateInstantiationPattern()) - return Result; - // getTemplateInstantiationPattern returns null if the Specialization is - // incomplete (e.g. the type didn't need to be complete), fall back to the - // primary template. - if (CRD->getTemplateSpecializationKind() == TSK_Undeclared) - if (const auto *Spec = dyn_cast(CRD)) - return Spec->getSpecializedTemplate()->getTemplatedDecl(); - } else if (const FunctionDecl *FD = dyn_cast(D)) { - return FD->getTemplateInstantiationPattern(); - } else if (auto *VD = dyn_cast(D)) { - // Hmm: getTIP returns its arg if it's not an instantiation?! - VarDecl *T = VD->getTemplateInstantiationPattern(); - return (T == D) ? nullptr : T; - } else if (const auto *ED = dyn_cast(D)) { - return ED->getInstantiatedFromMemberEnum(); - } else if (isa(D) || isa(D)) { - if (const auto *Parent = llvm::dyn_cast(D->getDeclContext())) - if (const DeclContext *ParentPat = - dyn_cast_or_null(getTemplatePattern(Parent))) - for (const NamedDecl *BaseND : ParentPat->lookup(D->getDeclName())) - if (!BaseND->isImplicit() && BaseND->getKind() == D->getKind()) - return BaseND; - } else if (const auto *ECD = dyn_cast(D)) { - if (const auto *ED = dyn_cast(ECD->getDeclContext())) { - if (const EnumDecl *Pattern = ED->getInstantiatedFromMemberEnum()) { - for (const NamedDecl *BaseECD : Pattern->lookup(ECD->getDeclName())) - return BaseECD; - } - } - } - return nullptr; -} - // TargetFinder locates the entities that an AST node refers to. // // Typically this is (possibly) one declaration and (possibly) one type, but diff --git a/clang-tools-extra/clangd/ParsedAST.h b/clang-tools-extra/clangd/ParsedAST.h --- a/clang-tools-extra/clangd/ParsedAST.h +++ b/clang-tools-extra/clangd/ParsedAST.h @@ -68,6 +68,8 @@ ASTContext &getASTContext(); const ASTContext &getASTContext() const; + Sema &getSema(); + Preprocessor &getPreprocessor(); std::shared_ptr getPreprocessorPtr(); const Preprocessor &getPreprocessor() const; @@ -110,9 +112,10 @@ /// AST. Might be None if no Preamble is used. llvm::Optional preambleVersion() const; - /// This context variable makes available the ASTContext of the AST being - /// operated on currently. + /// This context variable makes available the ASTContext and Sema of the AST + /// being operated on currently. static Key CurrentASTContext; + static Key CurrentSema; private: ParsedAST(llvm::StringRef Version, diff --git a/clang-tools-extra/clangd/ParsedAST.cpp b/clang-tools-extra/clangd/ParsedAST.cpp --- a/clang-tools-extra/clangd/ParsedAST.cpp +++ b/clang-tools-extra/clangd/ParsedAST.cpp @@ -238,6 +238,7 @@ } // namespace Key ParsedAST::CurrentASTContext; +Key ParsedAST::CurrentSema; llvm::Optional ParsedAST::build(llvm::StringRef Filename, const ParseInputs &Inputs, @@ -473,6 +474,8 @@ return Clang->getASTContext(); } +Sema &ParsedAST::getSema() { return Clang->getSema(); } + Preprocessor &ParsedAST::getPreprocessor() { return Clang->getPreprocessor(); } std::shared_ptr ParsedAST::getPreprocessorPtr() { diff --git a/clang-tools-extra/clangd/TUScheduler.cpp b/clang-tools-extra/clangd/TUScheduler.cpp --- a/clang-tools-extra/clangd/TUScheduler.cpp +++ b/clang-tools-extra/clangd/TUScheduler.cpp @@ -721,8 +721,9 @@ return Action(error(llvm::errc::invalid_argument, "invalid AST")); vlog("ASTWorker running {0} on version {2} of {1}", Name, FileName, FileInputs.Version); - WithContextValue Ctx(ParsedAST::CurrentASTContext, - &(*AST)->getASTContext()); + WithContextValue ASTCtx(ParsedAST::CurrentASTContext, + &(*AST)->getASTContext()); + WithContextValue SemaCtx(ParsedAST::CurrentSema, &(*AST)->getSema()); Action(InputsAndAST{FileInputs, **AST}); }; startTask(Name, std::move(Task), /*UpdateType=*/None, Invalidation); diff --git a/clang-tools-extra/clangd/unittests/FindTargetTests.cpp b/clang-tools-extra/clangd/unittests/FindTargetTests.cpp --- a/clang-tools-extra/clangd/unittests/FindTargetTests.cpp +++ b/clang-tools-extra/clangd/unittests/FindTargetTests.cpp @@ -227,7 +227,8 @@ }; )cpp"; EXPECT_DECLS("UnresolvedUsingValueDecl", {"using Base::waldo", Rel::Alias}, - {"void waldo()"}); + {"void waldo()", Rel::TemplatePattern}, + {"void waldo()", Rel::TemplateInstantiation}); } TEST_F(TargetDeclTest, ConstructorInitList) { @@ -737,6 +738,20 @@ )cpp"; EXPECT_DECLS("CXXDependentScopeMemberExpr", "template T convert() const"); + + Code = R"cpp( + template + struct vector { + T front(); + }; + template + void foo(vector> c) { + c.front().[[front]](); + } + )cpp"; + EXPECT_DECLS("CXXDependentScopeMemberExpr", + {"T front()", Rel::TemplatePattern}, + {"type-parameter-0-0 front()", Rel::TemplateInstantiation}); } TEST_F(TargetDeclTest, DependentTypes) { @@ -750,7 +765,8 @@ template void foo(typename A::[[B]]); )cpp"; - EXPECT_DECLS("DependentNameTypeLoc", "struct B"); + EXPECT_DECLS("DependentNameTypeLoc", {"struct B", Rel::TemplatePattern}, + {"struct B", Rel::TemplateInstantiation}); // Heuristic resolution of dependent type name which doesn't get a TypeLoc Code = R"cpp( @@ -760,7 +776,8 @@ template void foo(typename A::[[B]]::C); )cpp"; - EXPECT_DECLS("NestedNameSpecifierLoc", "struct B"); + EXPECT_DECLS("NestedNameSpecifierLoc", {"struct B", Rel::TemplatePattern}, + {"struct B", Rel::TemplateInstantiation}); // Heuristic resolution of dependent type name whose qualifier is also // dependent @@ -986,37 +1003,36 @@ TEST_F(FindExplicitReferencesTest, All) { std::pair Cases[] = - { - // Simple expressions. - {R"cpp( + {// Simple expressions. + {R"cpp( int global; int func(); void foo(int param) { $0^global = $1^param + $2^func(); } )cpp", - "0: targets = {global}\n" - "1: targets = {param}\n" - "2: targets = {func}\n"}, - {R"cpp( + "0: targets = {global}\n" + "1: targets = {param}\n" + "2: targets = {func}\n"}, + {R"cpp( struct X { int a; }; void foo(X x) { $0^x.$1^a = 10; } )cpp", - "0: targets = {x}\n" - "1: targets = {X::a}\n"}, - {R"cpp( + "0: targets = {x}\n" + "1: targets = {X::a}\n"}, + {R"cpp( // error-ok: testing with broken code int bar(); int foo() { return $0^bar() + $1^bar(42); } )cpp", - "0: targets = {bar}\n" - "1: targets = {bar}\n"}, - // Namespaces and aliases. - {R"cpp( + "0: targets = {bar}\n" + "1: targets = {bar}\n"}, + // Namespaces and aliases. + {R"cpp( namespace ns {} namespace alias = ns; void foo() { @@ -1024,19 +1040,19 @@ using namespace $1^alias; } )cpp", - "0: targets = {ns}\n" - "1: targets = {alias}\n"}, - // Using declarations. - {R"cpp( + "0: targets = {ns}\n" + "1: targets = {alias}\n"}, + // Using declarations. + {R"cpp( namespace ns { int global; } void foo() { using $0^ns::$1^global; } )cpp", - "0: targets = {ns}\n" - "1: targets = {ns::global}, qualifier = 'ns::'\n"}, - // Simple types. - {R"cpp( + "0: targets = {ns}\n" + "1: targets = {ns::global}, qualifier = 'ns::'\n"}, + // Simple types. + {R"cpp( struct Struct { int a; }; using Typedef = int; void foo() { @@ -1045,13 +1061,13 @@ static_cast<$4^Struct*>(0); } )cpp", - "0: targets = {Struct}\n" - "1: targets = {x}, decl\n" - "2: targets = {Typedef}\n" - "3: targets = {y}, decl\n" - "4: targets = {Struct}\n"}, - // Name qualifiers. - {R"cpp( + "0: targets = {Struct}\n" + "1: targets = {x}, decl\n" + "2: targets = {Typedef}\n" + "3: targets = {y}, decl\n" + "4: targets = {Struct}\n"}, + // Name qualifiers. + {R"cpp( namespace a { namespace b { struct S { typedef int type; }; } } void foo() { $0^a::$1^b::$2^S $3^x; @@ -1059,16 +1075,16 @@ $6^S::$7^type $8^y; } )cpp", - "0: targets = {a}\n" - "1: targets = {a::b}, qualifier = 'a::'\n" - "2: targets = {a::b::S}, qualifier = 'a::b::'\n" - "3: targets = {x}, decl\n" - "4: targets = {a}\n" - "5: targets = {a::b}, qualifier = 'a::'\n" - "6: targets = {a::b::S}\n" - "7: targets = {a::b::S::type}, qualifier = 'struct S::'\n" - "8: targets = {y}, decl\n"}, - {R"cpp( + "0: targets = {a}\n" + "1: targets = {a::b}, qualifier = 'a::'\n" + "2: targets = {a::b::S}, qualifier = 'a::b::'\n" + "3: targets = {x}, decl\n" + "4: targets = {a}\n" + "5: targets = {a::b}, qualifier = 'a::'\n" + "6: targets = {a::b::S}\n" + "7: targets = {a::b::S::type}, qualifier = 'struct S::'\n" + "8: targets = {y}, decl\n"}, + {R"cpp( void foo() { $0^ten: // PRINT "HELLO WORLD!" goto $1^ten; @@ -1085,12 +1101,12 @@ $2^vector $3^vb; } )cpp", - "0: targets = {vector}\n" - "1: targets = {vi}, decl\n" - "2: targets = {vector}\n" - "3: targets = {vb}, decl\n"}, - // Template type aliases. - {R"cpp( + "0: targets = {vector}\n" + "1: targets = {vi}, decl\n" + "2: targets = {vector}\n" + "3: targets = {vb}, decl\n"}, + // Template type aliases. + {R"cpp( template struct vector { using value_type = T; }; template <> struct vector { using value_type = bool; }; template using valias = vector; @@ -1099,12 +1115,12 @@ $2^valias $3^vb; } )cpp", - "0: targets = {valias}\n" - "1: targets = {vi}, decl\n" - "2: targets = {valias}\n" - "3: targets = {vb}, decl\n"}, - // Injected class name. - {R"cpp( + "0: targets = {valias}\n" + "1: targets = {vi}, decl\n" + "2: targets = {valias}\n" + "3: targets = {vb}, decl\n"}, + // Injected class name. + {R"cpp( namespace foo { template class $1^Bar { @@ -1113,13 +1129,13 @@ }; } )cpp", - "0: targets = {foo::Bar::T}, decl\n" - "1: targets = {foo::Bar}, decl\n" - "2: targets = {foo::Bar}\n" - "3: targets = {foo::Bar::f}, decl\n" - "4: targets = {foo::Bar}\n"}, - // MemberExpr should know their using declaration. - {R"cpp( + "0: targets = {foo::Bar::T}, decl\n" + "1: targets = {foo::Bar}, decl\n" + "2: targets = {foo::Bar}\n" + "3: targets = {foo::Bar::f}, decl\n" + "4: targets = {foo::Bar}\n"}, + // MemberExpr should know their using declaration. + {R"cpp( struct X { void func(int); }; struct Y : X { using X::func; @@ -1128,10 +1144,10 @@ $0^y.$1^func(1); } )cpp", - "0: targets = {y}\n" - "1: targets = {Y::func}\n"}, - // DeclRefExpr should know their using declaration. - {R"cpp( + "0: targets = {y}\n" + "1: targets = {Y::func}\n"}, + // DeclRefExpr should know their using declaration. + {R"cpp( namespace ns { void bar(int); } using ns::bar; @@ -1139,9 +1155,9 @@ $0^bar(10); } )cpp", - "0: targets = {bar}\n"}, - // References from a macro. - {R"cpp( + "0: targets = {bar}\n"}, + // References from a macro. + {R"cpp( #define FOO a #define BAR b @@ -1149,10 +1165,10 @@ $0^FOO+$1^BAR; } )cpp", - "0: targets = {a}\n" - "1: targets = {b}\n"}, - // No references from implicit nodes. - {R"cpp( + "0: targets = {a}\n" + "1: targets = {b}\n"}, + // No references from implicit nodes. + {R"cpp( struct vector { int *begin(); int *end(); @@ -1164,15 +1180,15 @@ } } )cpp", - "0: targets = {x}, decl\n" - "1: targets = {vector}\n" - "2: targets = {x}\n"}, + "0: targets = {x}, decl\n" + "1: targets = {vector}\n" + "2: targets = {x}\n"}, // Handle UnresolvedLookupExpr. // FIXME // This case fails when expensive checks are enabled. // Seems like the order of ns1::func and ns2::func isn't defined. #ifndef EXPENSIVE_CHECKS - {R"cpp( + {R"cpp( namespace ns1 { void func(char*); } namespace ns2 { void func(int*); } using namespace ns1; @@ -1183,11 +1199,11 @@ $0^func($1^t); } )cpp", - "0: targets = {ns1::func, ns2::func}\n" - "1: targets = {t}\n"}, + "0: targets = {ns1::func, ns2::func}\n" + "1: targets = {t}\n"}, #endif - // Handle UnresolvedMemberExpr. - {R"cpp( + // Handle UnresolvedMemberExpr. + {R"cpp( struct X { void func(char*); void func(int*); @@ -1198,11 +1214,11 @@ $0^x.$1^func($2^t); } )cpp", - "0: targets = {x}\n" - "1: targets = {X::func, X::func}\n" - "2: targets = {t}\n"}, - // Handle DependentScopeDeclRefExpr. - {R"cpp( + "0: targets = {x}\n" + "1: targets = {X::func, X::func}\n" + "2: targets = {t}\n"}, + // Handle DependentScopeDeclRefExpr. + {R"cpp( template struct S { static int value; @@ -1213,11 +1229,11 @@ $0^S<$1^T>::$2^value; } )cpp", - "0: targets = {S}\n" - "1: targets = {T}\n" - "2: targets = {S::value}, qualifier = 'S::'\n"}, - // Handle CXXDependentScopeMemberExpr. - {R"cpp( + "0: targets = {S}\n" + "1: targets = {T}\n" + "2: targets = {S::value}, qualifier = 'S::'\n"}, + // Handle CXXDependentScopeMemberExpr. + {R"cpp( template struct S { int value; @@ -1228,10 +1244,10 @@ $0^t.$1^value; } )cpp", - "0: targets = {t}\n" - "1: targets = {S::value}\n"}, - // Type template parameters. - {R"cpp( + "0: targets = {t}\n" + "1: targets = {S::value}\n"}, + // Type template parameters. + {R"cpp( template void foo() { static_cast<$0^T>(0); @@ -1239,21 +1255,21 @@ $2^T $3^t; } )cpp", - "0: targets = {T}\n" - "1: targets = {T}\n" - "2: targets = {T}\n" - "3: targets = {t}, decl\n"}, - // Non-type template parameters. - {R"cpp( + "0: targets = {T}\n" + "1: targets = {T}\n" + "2: targets = {T}\n" + "3: targets = {t}, decl\n"}, + // Non-type template parameters. + {R"cpp( template void foo() { int $0^x = $1^I; } )cpp", - "0: targets = {x}, decl\n" - "1: targets = {I}\n"}, - // Template template parameters. - {R"cpp( + "0: targets = {x}, decl\n" + "1: targets = {I}\n"}, + // Template template parameters. + {R"cpp( template struct vector {}; template class TT, template class ...TP> @@ -1264,16 +1280,16 @@ $6^foo<$7^TP...>(); } )cpp", - "0: targets = {TT}\n" - "1: targets = {x}, decl\n" - "2: targets = {foo}\n" - "3: targets = {TT}\n" - "4: targets = {foo}\n" - "5: targets = {vector}\n" - "6: targets = {foo}\n" - "7: targets = {TP}\n"}, - // Non-type template parameters with declarations. - {R"cpp( + "0: targets = {TT}\n" + "1: targets = {x}, decl\n" + "2: targets = {foo}\n" + "3: targets = {TT}\n" + "4: targets = {foo}\n" + "5: targets = {vector}\n" + "6: targets = {foo}\n" + "7: targets = {TP}\n"}, + // Non-type template parameters with declarations. + {R"cpp( int func(); template struct wrapper {}; @@ -1283,12 +1299,12 @@ $3^FuncParam(); } )cpp", - "0: targets = {wrapper<&func>}\n" - "1: targets = {func}\n" - "2: targets = {w}, decl\n" - "3: targets = {FuncParam}\n"}, - // declaration references. - {R"cpp( + "0: targets = {wrapper<&func>}\n" + "1: targets = {func}\n" + "2: targets = {w}, decl\n" + "3: targets = {FuncParam}\n"}, + // declaration references. + {R"cpp( namespace ns {} class S {}; void foo() { @@ -1300,19 +1316,19 @@ namespace $9^NS = $10^ns; } )cpp", - "0: targets = {Foo}, decl\n" - "1: targets = {foo()::Foo::Foo}, decl\n" - "2: targets = {Foo}\n" - "3: targets = {foo()::Foo::field}, decl\n" - "4: targets = {Var}, decl\n" - "5: targets = {E}, decl\n" - "6: targets = {foo()::ABC}, decl\n" - "7: targets = {INT}, decl\n" - "8: targets = {INT2}, decl\n" - "9: targets = {NS}, decl\n" - "10: targets = {ns}\n"}, - // User-defined conversion operator. - {R"cpp( + "0: targets = {Foo}, decl\n" + "1: targets = {foo()::Foo::Foo}, decl\n" + "2: targets = {Foo}\n" + "3: targets = {foo()::Foo::field}, decl\n" + "4: targets = {Var}, decl\n" + "5: targets = {E}, decl\n" + "6: targets = {foo()::ABC}, decl\n" + "7: targets = {INT}, decl\n" + "8: targets = {INT2}, decl\n" + "9: targets = {NS}, decl\n" + "10: targets = {ns}\n"}, + // User-defined conversion operator. + {R"cpp( void foo() { class $0^Bar {}; class $1^Foo { @@ -1325,18 +1341,18 @@ $7^f.$8^operator $9^Bar(); } )cpp", - "0: targets = {Bar}, decl\n" - "1: targets = {Foo}, decl\n" - "2: targets = {foo()::Foo::operator Bar}, decl\n" - "3: targets = {Bar}\n" - "4: targets = {Bar}\n" - "5: targets = {Foo}\n" - "6: targets = {f}, decl\n" - "7: targets = {f}\n" - "8: targets = {foo()::Foo::operator Bar}\n" - "9: targets = {Bar}\n"}, - // Destructor. - {R"cpp( + "0: targets = {Bar}, decl\n" + "1: targets = {Foo}, decl\n" + "2: targets = {foo()::Foo::operator Bar}, decl\n" + "3: targets = {Bar}\n" + "4: targets = {Bar}\n" + "5: targets = {Foo}\n" + "6: targets = {f}, decl\n" + "7: targets = {f}\n" + "8: targets = {foo()::Foo::operator Bar}\n" + "9: targets = {Bar}\n"}, + // Destructor. + {R"cpp( void foo() { class $0^Foo { public: @@ -1351,18 +1367,18 @@ $6^f.~ /*...*/ $7^Foo(); } )cpp", - "0: targets = {Foo}, decl\n" - // FIXME: It's better to target destructor's FunctionDecl instead of - // the type itself (similar to constructor). - "1: targets = {Foo}\n" - "2: targets = {foo()::Foo::destructMe}, decl\n" - "3: targets = {Foo}\n" - "4: targets = {Foo}\n" - "5: targets = {f}, decl\n" - "6: targets = {f}\n" - "7: targets = {Foo}\n"}, - // cxx constructor initializer. - {R"cpp( + "0: targets = {Foo}, decl\n" + // FIXME: It's better to target destructor's FunctionDecl instead of + // the type itself (similar to constructor). + "1: targets = {Foo}\n" + "2: targets = {foo()::Foo::destructMe}, decl\n" + "3: targets = {Foo}\n" + "4: targets = {Foo}\n" + "5: targets = {f}, decl\n" + "6: targets = {f}\n" + "7: targets = {Foo}\n"}, + // cxx constructor initializer. + {R"cpp( class Base {}; void foo() { // member initializer @@ -1382,34 +1398,34 @@ }; } )cpp", - "0: targets = {X}, decl\n" - "1: targets = {foo()::X::abc}, decl\n" - "2: targets = {foo()::X::X}, decl\n" - "3: targets = {foo()::X::abc}\n" - "4: targets = {Derived}, decl\n" - "5: targets = {Base}\n" - "6: targets = {Base}\n" - "7: targets = {foo()::Derived::B}, decl\n" - "8: targets = {foo()::Derived::Derived}, decl\n" - "9: targets = {Base}\n" - "10: targets = {Foo}, decl\n" - "11: targets = {foo()::Foo::Foo}, decl\n" - "12: targets = {foo()::Foo::Foo}, decl\n" - "13: targets = {Foo}\n"}, - // Anonymous entities should not be reported. - { - R"cpp( + "0: targets = {X}, decl\n" + "1: targets = {foo()::X::abc}, decl\n" + "2: targets = {foo()::X::X}, decl\n" + "3: targets = {foo()::X::abc}\n" + "4: targets = {Derived}, decl\n" + "5: targets = {Base}\n" + "6: targets = {Base}\n" + "7: targets = {foo()::Derived::B}, decl\n" + "8: targets = {foo()::Derived::Derived}, decl\n" + "9: targets = {Base}\n" + "10: targets = {Foo}, decl\n" + "11: targets = {foo()::Foo::Foo}, decl\n" + "12: targets = {foo()::Foo::Foo}, decl\n" + "13: targets = {Foo}\n"}, + // Anonymous entities should not be reported. + { + R"cpp( void foo() { class {} $0^x; int (*$1^fptr)(int $2^a, int) = nullptr; } )cpp", - "0: targets = {x}, decl\n" - "1: targets = {fptr}, decl\n" - "2: targets = {a}, decl\n"}, - // Namespace aliases should be handled properly. - { - R"cpp( + "0: targets = {x}, decl\n" + "1: targets = {fptr}, decl\n" + "2: targets = {a}, decl\n"}, + // Namespace aliases should be handled properly. + { + R"cpp( namespace ns { struct Type {}; } namespace alias = ns; namespace rec_alias = alias; @@ -1420,28 +1436,28 @@ $6^rec_alias::$7^Type $8^c; } )cpp", - "0: targets = {ns}\n" - "1: targets = {ns::Type}, qualifier = 'ns::'\n" - "2: targets = {a}, decl\n" - "3: targets = {alias}\n" - "4: targets = {ns::Type}, qualifier = 'alias::'\n" - "5: targets = {b}, decl\n" - "6: targets = {rec_alias}\n" - "7: targets = {ns::Type}, qualifier = 'rec_alias::'\n" - "8: targets = {c}, decl\n"}, - // Handle SizeOfPackExpr. - { - R"cpp( + "0: targets = {ns}\n" + "1: targets = {ns::Type}, qualifier = 'ns::'\n" + "2: targets = {a}, decl\n" + "3: targets = {alias}\n" + "4: targets = {ns::Type}, qualifier = 'alias::'\n" + "5: targets = {b}, decl\n" + "6: targets = {rec_alias}\n" + "7: targets = {ns::Type}, qualifier = 'rec_alias::'\n" + "8: targets = {c}, decl\n"}, + // Handle SizeOfPackExpr. + { + R"cpp( template void foo() { constexpr int $0^size = sizeof...($1^E); }; )cpp", - "0: targets = {size}, decl\n" - "1: targets = {E}\n"}, - // Class template argument deduction - { - R"cpp( + "0: targets = {size}, decl\n" + "1: targets = {E}\n"}, + // Class template argument deduction + { + R"cpp( template struct Test { Test(T); @@ -1450,51 +1466,51 @@ $0^Test $1^a(5); } )cpp", - "0: targets = {Test}\n" - "1: targets = {a}, decl\n"}, - // Templates - {R"cpp( + "0: targets = {Test}\n" + "1: targets = {a}, decl\n"}, + // Templates + {R"cpp( namespace foo { template class $1^Bar {}; } )cpp", - "0: targets = {foo::Bar::T}, decl\n" - "1: targets = {foo::Bar}, decl\n"}, - // Templates - {R"cpp( + "0: targets = {foo::Bar::T}, decl\n" + "1: targets = {foo::Bar}, decl\n"}, + // Templates + {R"cpp( namespace foo { template void $1^func(); } )cpp", - "0: targets = {T}, decl\n" - "1: targets = {foo::func}, decl\n"}, - // Templates - {R"cpp( + "0: targets = {T}, decl\n" + "1: targets = {foo::func}, decl\n"}, + // Templates + {R"cpp( namespace foo { template $1^T $2^x; } )cpp", - "0: targets = {foo::T}, decl\n" - "1: targets = {foo::T}\n" - "2: targets = {foo::x}, decl\n"}, - // Templates - {R"cpp( + "0: targets = {foo::T}, decl\n" + "1: targets = {foo::T}\n" + "2: targets = {foo::x}, decl\n"}, + // Templates + {R"cpp( template class vector {}; namespace foo { template using $1^V = $2^vector<$3^T>; } )cpp", - "0: targets = {foo::T}, decl\n" - "1: targets = {foo::V}, decl\n" - "2: targets = {vector}\n" - "3: targets = {foo::T}\n"}, - // Concept - { - R"cpp( + "0: targets = {foo::T}, decl\n" + "1: targets = {foo::V}, decl\n" + "2: targets = {vector}\n" + "3: targets = {foo::T}\n"}, + // Concept + { + R"cpp( template concept Drawable = requires (T t) { t.draw(); }; @@ -1505,17 +1521,17 @@ } } )cpp", - "0: targets = {T}, decl\n" - "1: targets = {Drawable}\n" - "2: targets = {T}\n" - "3: targets = {foo::bar}, decl\n" - "4: targets = {T}\n" - "5: targets = {t}, decl\n" - "6: targets = {t}\n" - "7: targets = {}\n"}, - // Objective-C: properties - { - R"cpp( + "0: targets = {T}, decl\n" + "1: targets = {Drawable}\n" + "2: targets = {T}\n" + "3: targets = {foo::bar}, decl\n" + "4: targets = {T}\n" + "5: targets = {t}, decl\n" + "6: targets = {t}\n" + "7: targets = {}\n"}, + // Objective-C: properties + { + R"cpp( @interface I {} @property(retain) I* x; @property(retain) I* y; @@ -1525,12 +1541,12 @@ $0^f.$1^x.$2^y = 0; } )cpp", - "0: targets = {f}\n" - "1: targets = {I::x}\n" - "2: targets = {I::y}\n"}, - // Objective-C: implicit properties - { - R"cpp( + "0: targets = {f}\n" + "1: targets = {I::x}\n" + "2: targets = {I::y}\n"}, + // Objective-C: implicit properties + { + R"cpp( @interface I {} -(I*)x; -(void)setY:(I*)y; @@ -1540,11 +1556,11 @@ $0^f.$1^x.$2^y = 0; } )cpp", - "0: targets = {f}\n" - "1: targets = {I::x}\n" - "2: targets = {I::setY:}\n"}, - // Designated initializers. - {R"cpp( + "0: targets = {f}\n" + "1: targets = {I::x}\n" + "2: targets = {I::setY:}\n"}, + // Designated initializers. + {R"cpp( void foo() { struct $0^Foo { int $1^Bar; @@ -1552,12 +1568,12 @@ $2^Foo $3^f { .$4^Bar = 42 }; } )cpp", - "0: targets = {Foo}, decl\n" - "1: targets = {foo()::Foo::Bar}, decl\n" - "2: targets = {Foo}\n" - "3: targets = {f}, decl\n" - "4: targets = {foo()::Foo::Bar}\n"}, - {R"cpp( + "0: targets = {Foo}, decl\n" + "1: targets = {foo()::Foo::Bar}, decl\n" + "2: targets = {Foo}\n" + "3: targets = {f}, decl\n" + "4: targets = {foo()::Foo::Bar}\n"}, + {R"cpp( void foo() { struct $0^Baz { int $1^Field; diff --git a/clang-tools-extra/clangd/unittests/TestTU.h b/clang-tools-extra/clangd/unittests/TestTU.h --- a/clang-tools-extra/clangd/unittests/TestTU.h +++ b/clang-tools-extra/clangd/unittests/TestTU.h @@ -76,9 +76,10 @@ // to eliminate this option some day. bool OverlayRealFileSystemForModules = false; - // This is used to add the ASTContext of the AST produced by build() to the - // clangd Context. + // This is used to add the ASTContext and Sema of the AST produced by build() + // to the clangd Context. mutable std::unique_ptr ASTCtx; + mutable std::unique_ptr SemaCtx; // By default, build() will report Error diagnostics as GTest errors. // Suppress this behavior by adding an 'error-ok' comment to the code. diff --git a/clang-tools-extra/clangd/unittests/TestTU.cpp b/clang-tools-extra/clangd/unittests/TestTU.cpp --- a/clang-tools-extra/clangd/unittests/TestTU.cpp +++ b/clang-tools-extra/clangd/unittests/TestTU.cpp @@ -141,6 +141,8 @@ } ASTCtx = std::make_unique(ParsedAST::CurrentASTContext, &AST->getASTContext()); + SemaCtx = std::make_unique(ParsedAST::CurrentSema, + &AST->getSema()); return std::move(*AST); } diff --git a/clang-tools-extra/clangd/unittests/XRefsTests.cpp b/clang-tools-extra/clangd/unittests/XRefsTests.cpp --- a/clang-tools-extra/clangd/unittests/XRefsTests.cpp +++ b/clang-tools-extra/clangd/unittests/XRefsTests.cpp @@ -1165,7 +1165,8 @@ for (const auto *Case : Tests) { SCOPED_TRACE(Case); auto T = Annotations(Case); - auto AST = TestTU::withCode(T.code()).build(); + TestTU TU = TestTU::withCode(T.code()); + auto AST = TU.build(); EXPECT_THAT(locateSymbolAt(AST, T.point()), UnorderedPointwise(DeclRange(), T.ranges())); }