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
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits