kadircet created this revision. kadircet added reviewers: ioeric, ilya-biryukov. Herald added subscribers: cfe-commits, arphaman, jkorous, MaskRay, mgorny. Herald added a project: clang.
Prepares ground for printing template arguments as written in the source code, part of re-landing rC356541 <https://reviews.llvm.org/rC356541> with D59599 <https://reviews.llvm.org/D59599> applied. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D59639 Files: clang-tools-extra/clangd/AST.cpp clang-tools-extra/clangd/AST.h clang-tools-extra/unittests/clangd/ASTUtilsTests.cpp clang-tools-extra/unittests/clangd/CMakeLists.txt clang/lib/AST/TypePrinter.cpp
Index: clang/lib/AST/TypePrinter.cpp =================================================================== --- clang/lib/AST/TypePrinter.cpp +++ clang/lib/AST/TypePrinter.cpp @@ -1632,6 +1632,21 @@ 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 auto &Kind = A.getArgument().getKind(); + assert(Kind != TemplateArgument::Null && + "TemplateArgumentKind can not be null!"); + if (Kind == TemplateArgument::ArgKind::Type) + return A.getTypeSourceInfo()->getType().print(OS, PP); + return A.getArgument().print(PP, OS); +} + template<typename TA> static void printTo(raw_ostream &OS, ArrayRef<TA> Args, const PrintingPolicy &Policy, bool SkipBrackets) { @@ -1653,7 +1668,7 @@ } else { if (!FirstArg) OS << Comma; - Argument.print(Policy, ArgOS); + printArgument(Arg, Policy, ArgOS); } StringRef ArgString = ArgOS.str(); Index: clang-tools-extra/unittests/clangd/CMakeLists.txt =================================================================== --- clang-tools-extra/unittests/clangd/CMakeLists.txt +++ clang-tools-extra/unittests/clangd/CMakeLists.txt @@ -10,6 +10,7 @@ add_extra_unittest(ClangdTests Annotations.cpp + ASTUtilsTests.cpp BackgroundIndexTests.cpp CancellationTests.cpp ClangdTests.cpp Index: clang-tools-extra/unittests/clangd/ASTUtilsTests.cpp =================================================================== --- /dev/null +++ clang-tools-extra/unittests/clangd/ASTUtilsTests.cpp @@ -0,0 +1,63 @@ +#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.h" + +namespace clang { +namespace clangd { +namespace { + +using testing::ElementsAre; + +TEST(ASTUtils, PrintTemplateArgs) { + Annotations Test(R"cpp( + template <class X> class Bar {}; + template <> class ^Bar<double> {}; + + template <class T, class U, template<typename> class Z, int Q> struct Foo {}; + template struct ^Foo<int, bool, Bar, 8>; + template <typename T> struct ^Foo<T *, T, Bar, 3> {}; + + template <typename ...> class Baz {}; + template <> class ^Baz<int, bool> {}; + template <typename T> class ^Baz<T, T *> {}; + + template <int ...> void Foz() {}; + template <> void ^Foz<3, 5, 8>() {}; + + template <template <class> class ...> class Aux {}; + template <> class ^Aux<Bar, Bar> {}; + template <template <class> T> class ^Aux<T, T> {}; + + template <typename T> T var = 1234; + template <> int ^var<int> = 1; + )cpp"); + auto AST = TestTU::withCode(Test.code()).build(); + struct Visitor : RecursiveASTVisitor<Visitor> { + Visitor(std::vector<Position> Points) : Points(std::move(Points)) {} + bool VisitNamedDecl(const NamedDecl *ND) { + auto Pos = sourceLocToPosition(ND->getASTContext().getSourceManager(), + ND->getLocation()); + if (Pos != Points[TemplateArgs.size()]) + return true; + TemplateArgs.push_back(printTemplateArgsAsWritten(*ND)); + return true; + } + std::vector<std::string> TemplateArgs; + const std::vector<Position> Points; + }; + Visitor V(Test.points()); + V.TraverseDecl(AST.getASTContext().getTranslationUnitDecl()); + EXPECT_THAT(V.TemplateArgs, + ElementsAre("<double>", "<int, bool, Bar, 8>", "<T *, T, Bar, 3>", + "<int, bool>", "<T, T *>", "<3, 5, 8>", "<Bar, Bar>", + "<T, T>", "<int>")); +} + +} // namespace +} // namespace clangd +} // namespace clang Index: clang-tools-extra/clangd/AST.h =================================================================== --- clang-tools-extra/clangd/AST.h +++ clang-tools-extra/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 including enclosing '<' and '>', e.g for +/// a partial specialization like: template <typename U> struct Foo<int, U> will +/// return '<int, U>'. +/// Returns an empty string if type is not a template specialization. +std::string printTemplateArgsAsWritten(const NamedDecl &ND); + /// Gets the symbol ID for a declaration, if possible. llvm::Optional<SymbolID> getSymbolID(const Decl *D); Index: clang-tools-extra/clangd/AST.cpp =================================================================== --- clang-tools-extra/clangd/AST.cpp +++ clang-tools-extra/clangd/AST.cpp @@ -16,10 +16,29 @@ #include "clang/Index/USRGeneration.h" #include "llvm/Support/Casting.h" #include "llvm/Support/ScopedPrinter.h" +#include "llvm/Support/raw_ostream.h" namespace clang { namespace clangd { +namespace { +llvm::Optional<llvm::ArrayRef<TemplateArgumentLoc>> +getTemplateSpecializationArgLocs(const NamedDecl &ND) { + if (auto *Func = llvm::dyn_cast<FunctionDecl>(&ND)) { + if (auto *Args = Func->getTemplateSpecializationArgsAsWritten()) + return Args->arguments(); + } else if (auto *Cls = + llvm::dyn_cast<ClassTemplatePartialSpecializationDecl>(&ND)) { + if (auto *Args = Cls->getTemplateArgsAsWritten()) + return Args->arguments(); + } else if (auto *Var = llvm::dyn_cast<VarTemplateSpecializationDecl>(&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 @@ -105,6 +124,29 @@ return "(anonymous)"; } +std::string printTemplateArgsAsWritten(const NamedDecl &ND) { + std::string TemplateArgs; + llvm::raw_string_ostream OS(TemplateArgs); + PrintingPolicy Policy(ND.getASTContext().getLangOpts()); + if (auto Args = getTemplateSpecializationArgLocs(ND)) + printTemplateArgumentList(OS, *Args, Policy); + else if (auto *Cls = llvm::dyn_cast<ClassTemplateSpecializationDecl>(&ND)) { + if (const TypeSourceInfo *TSI = Cls->getTypeAsWritten()) { + auto STL = TSI->getTypeLoc().getAs<TemplateSpecializationTypeLoc>(); + llvm::SmallVector<TemplateArgumentLoc, 8> 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, e.g. friend decls. + 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<NamespaceDecl>(Ctx))
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits