Index: cfe/trunk/lib/AST/TypePrinter.cpp =================================================================== --- cfe/trunk/lib/AST/TypePrinter.cpp +++ cfe/trunk/lib/AST/TypePrinter.cpp @@ -1632,6 +1632,19 @@ return A.getArgument(); } +static void printArgument(const TemplateArgument &A, const PrintingPolicy &PP, + llvm::raw_ostream &OS) { + A.print(PP, OS); +} + +static void printArgument(const TemplateArgumentLoc &A, + const PrintingPolicy &PP, llvm::raw_ostream &OS) { + const TemplateArgument::ArgKind &Kind = A.getArgument().getKind(); + if (Kind == TemplateArgument::ArgKind::Type) + return A.getTypeSourceInfo()->getType().print(OS, PP); + return A.getArgument().print(PP, OS); +} + template static void printTo(raw_ostream &OS, ArrayRef Args, const PrintingPolicy &Policy, bool SkipBrackets) { @@ -1653,7 +1666,8 @@ } else { if (!FirstArg) OS << Comma; - Argument.print(Policy, ArgOS); + // Tries to print the argument with location info if exists. + printArgument(Arg, Policy, ArgOS); } StringRef ArgString = ArgOS.str(); Index: clang-tools-extra/trunk/clangd/AST.h =================================================================== --- clang-tools-extra/trunk/clangd/AST.h +++ clang-tools-extra/trunk/clangd/AST.h @@ -47,6 +47,12 @@ /// "(anonymous struct)" or "(anonymous namespace)". std::string printName(const ASTContext &Ctx, const NamedDecl &ND); +/// Prints template arguments of a decl as written in the source code, including +/// enclosing '<' and '>', e.g for a partial specialization like: template +/// struct Foo will return ''. Returns an empty +/// string if decl is not a template specialization. +std::string printTemplateSpecializationArgs(const NamedDecl &ND); + /// Gets the symbol ID for a declaration, if possible. llvm::Optional getSymbolID(const Decl *D); Index: clang-tools-extra/trunk/clangd/AST.cpp =================================================================== --- clang-tools-extra/trunk/clangd/AST.cpp +++ clang-tools-extra/trunk/clangd/AST.cpp @@ -11,15 +11,37 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclTemplate.h" +#include "clang/AST/TemplateBase.h" #include "clang/Basic/SourceLocation.h" #include "clang/Basic/SourceManager.h" #include "clang/Index/USRGeneration.h" +#include "llvm/ADT/Optional.h" #include "llvm/Support/Casting.h" #include "llvm/Support/ScopedPrinter.h" +#include "llvm/Support/raw_ostream.h" namespace clang { namespace clangd { +namespace { +llvm::Optional> +getTemplateSpecializationArgLocs(const NamedDecl &ND) { + if (auto *Func = llvm::dyn_cast(&ND)) { + if (const ASTTemplateArgumentListInfo *Args = + Func->getTemplateSpecializationArgsAsWritten()) + return Args->arguments(); + } else if (auto *Cls = + llvm::dyn_cast(&ND)) { + if (auto *Args = Cls->getTemplateArgsAsWritten()) + return Args->arguments(); + } else if (auto *Var = llvm::dyn_cast(&ND)) + return Var->getTemplateArgsInfo().arguments(); + // We return None for ClassTemplateSpecializationDecls because it does not + // contain TemplateArgumentLoc information. + return llvm::None; +} +} // namespace + // Returns true if the complete name of decl \p D is spelled in the source code. // This is not the case for: // * symbols formed via macro concatenation, the spelling location will @@ -65,17 +87,6 @@ return QName; } -static const TemplateArgumentList * -getTemplateSpecializationArgs(const NamedDecl &ND) { - if (auto *Func = llvm::dyn_cast(&ND)) - return Func->getTemplateSpecializationArgs(); - if (auto *Cls = llvm::dyn_cast(&ND)) - return &Cls->getTemplateInstantiationArgs(); - if (auto *Var = llvm::dyn_cast(&ND)) - return &Var->getTemplateInstantiationArgs(); - return nullptr; -} - std::string printName(const ASTContext &Ctx, const NamedDecl &ND) { std::string Name; llvm::raw_string_ostream Out(Name); @@ -90,9 +101,7 @@ } ND.getDeclName().print(Out, PP); if (!Out.str().empty()) { - // FIXME(ibiryukov): do not show args not explicitly written by the user. - if (auto *ArgList = getTemplateSpecializationArgs(ND)) - printTemplateArgumentList(Out, ArgList->asArray(), PP); + Out << printTemplateSpecializationArgs(ND); return Out.str(); } // The name was empty, so present an anonymous entity. @@ -105,6 +114,35 @@ return "(anonymous)"; } +std::string printTemplateSpecializationArgs(const NamedDecl &ND) { + std::string TemplateArgs; + llvm::raw_string_ostream OS(TemplateArgs); + PrintingPolicy Policy(ND.getASTContext().getLangOpts()); + if (llvm::Optional> Args = + getTemplateSpecializationArgLocs(ND)) { + printTemplateArgumentList(OS, *Args, Policy); + } else if (auto *Cls = llvm::dyn_cast(&ND)) { + if (const TypeSourceInfo *TSI = Cls->getTypeAsWritten()) { + // ClassTemplateSpecializationDecls do not contain + // TemplateArgumentTypeLocs, they only have TemplateArgumentTypes. So we + // create a new argument location list from TypeSourceInfo. + auto STL = TSI->getTypeLoc().getAs(); + llvm::SmallVector ArgLocs; + ArgLocs.reserve(STL.getNumArgs()); + for (unsigned I = 0; I < STL.getNumArgs(); ++I) + ArgLocs.push_back(STL.getArgLoc(I)); + printTemplateArgumentList(OS, ArgLocs, Policy); + } else { + // FIXME: Fix cases when getTypeAsWritten returns null inside clang AST, + // e.g. friend decls. Currently we fallback to Template Arguments without + // location information. + printTemplateArgumentList(OS, Cls->getTemplateArgs().asArray(), Policy); + } + } + OS.flush(); + return TemplateArgs; +} + std::string printNamespaceScope(const DeclContext &DC) { for (const auto *Ctx = &DC; Ctx != nullptr; Ctx = Ctx->getParent()) if (const auto *NS = dyn_cast(Ctx)) Index: clang-tools-extra/trunk/unittests/clangd/CMakeLists.txt =================================================================== --- clang-tools-extra/trunk/unittests/clangd/CMakeLists.txt +++ clang-tools-extra/trunk/unittests/clangd/CMakeLists.txt @@ -13,6 +13,7 @@ add_extra_unittest(ClangdTests Annotations.cpp + PrintASTTests.cpp BackgroundIndexTests.cpp CancellationTests.cpp ClangdTests.cpp Index: clang-tools-extra/trunk/unittests/clangd/FindSymbolsTests.cpp =================================================================== --- clang-tools-extra/trunk/unittests/clangd/FindSymbolsTests.cpp +++ clang-tools-extra/trunk/unittests/clangd/FindSymbolsTests.cpp @@ -525,11 +525,9 @@ AllOf(WithName("Tmpl"), WithKind(SymbolKind::Struct), Children()), AllOf(WithName("funcTmpl"), Children()), - // FIXME(ibiryukov): template args should be to match the code. - AllOf(WithName("funcTmpl"), Children()), + AllOf(WithName("funcTmpl"), Children()), AllOf(WithName("varTmpl"), Children()), - // FIXME(ibiryukov): template args should be to match the code. - AllOf(WithName("varTmpl"), Children()))); + AllOf(WithName("varTmpl"), Children()))); } TEST_F(DocumentSymbolsTest, Namespaces) { Index: clang-tools-extra/trunk/unittests/clangd/PrintASTTests.cpp =================================================================== --- clang-tools-extra/trunk/unittests/clangd/PrintASTTests.cpp +++ clang-tools-extra/trunk/unittests/clangd/PrintASTTests.cpp @@ -0,0 +1,100 @@ +//===--- PrintASTTests.cpp ----------------------------------------- C++-*-===// +// +// 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 "AST.h" +#include "Annotations.h" +#include "Protocol.h" +#include "SourceCode.h" +#include "TestTU.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "gmock/gmock.h" +#include "gtest/gtest-param-test.h" +#include "gtest/gtest.h" +#include "gtest/internal/gtest-param-util-generated.h" + +namespace clang { +namespace clangd { +namespace { + +using testing::ElementsAreArray; + +struct Case { + const char *AnnotatedCode; + std::vector Expected; +}; +class ASTUtils : public testing::Test, + public ::testing::WithParamInterface {}; + +TEST_P(ASTUtils, PrintTemplateArgs) { + auto Pair = GetParam(); + Annotations Test(Pair.AnnotatedCode); + auto AST = TestTU::withCode(Test.code()).build(); + struct Visitor : RecursiveASTVisitor { + Visitor(std::vector Points) : Points(std::move(Points)) {} + bool VisitNamedDecl(const NamedDecl *ND) { + auto Pos = sourceLocToPosition(ND->getASTContext().getSourceManager(), + ND->getLocation()); + if (Pos != Points[TemplateArgsAtPoints.size()]) + return true; + TemplateArgsAtPoints.push_back(printTemplateSpecializationArgs(*ND)); + return true; + } + std::vector TemplateArgsAtPoints; + const std::vector Points; + }; + Visitor V(Test.points()); + V.TraverseDecl(AST.getASTContext().getTranslationUnitDecl()); + EXPECT_THAT(V.TemplateArgsAtPoints, ElementsAreArray(Pair.Expected)); +} + +INSTANTIATE_TEST_CASE_P(ASTUtilsTests, ASTUtils, + testing::ValuesIn(std::vector({ + { + R"cpp( + template class Bar {}; + template <> class ^Bar {};)cpp", + {""}}, + { + R"cpp( + template class Bar {}; + template class Z, int Q> + struct Foo {}; + template struct ^Foo; + template + struct ^Foo {};)cpp", + {"", ""}}, + { + R"cpp( + template void Foz() {}; + template <> void ^Foz<3, 5, 8>() {};)cpp", + {"<3, 5, 8>"}}, + { + R"cpp( + template class Bar {}; + template