[clang-tools-extra] r358272 - [clangd] Print template arguments helper

Kadir Cetinkaya via cfe-commits cfe-commits at lists.llvm.org
Fri Apr 12 03:09:15 PDT 2019


Author: kadircet
Date: Fri Apr 12 03:09:14 2019
New Revision: 358272

URL: http://llvm.org/viewvc/llvm-project?rev=358272&view=rev
Log:
[clangd] Print template arguments helper

Summary:
Prepares ground for printing template arguments as written in the
source code, part of re-landing rC356541 with D59599 applied.

Reviewers: ioeric, ilya-biryukov

Subscribers: mgorny, MaskRay, jkorous, arphaman, cfe-commits

Tags: #clang

Differential Revision: https://reviews.llvm.org/D59639

Added:
    clang-tools-extra/trunk/unittests/clangd/PrintASTTests.cpp
Modified:
    clang-tools-extra/trunk/clangd/AST.cpp
    clang-tools-extra/trunk/clangd/AST.h
    clang-tools-extra/trunk/unittests/clangd/CMakeLists.txt
    clang-tools-extra/trunk/unittests/clangd/FindSymbolsTests.cpp

Modified: clang-tools-extra/trunk/clangd/AST.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/AST.cpp?rev=358272&r1=358271&r2=358272&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/AST.cpp (original)
+++ clang-tools-extra/trunk/clangd/AST.cpp Fri Apr 12 03:09:14 2019
@@ -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<llvm::ArrayRef<TemplateArgumentLoc>>
+getTemplateSpecializationArgLocs(const NamedDecl &ND) {
+  if (auto *Func = llvm::dyn_cast<FunctionDecl>(&ND)) {
+    if (const ASTTemplateArgumentListInfo *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
@@ -65,17 +87,6 @@ std::string printQualifiedName(const Nam
   return QName;
 }
 
-static const TemplateArgumentList *
-getTemplateSpecializationArgs(const NamedDecl &ND) {
-  if (auto *Func = llvm::dyn_cast<FunctionDecl>(&ND))
-    return Func->getTemplateSpecializationArgs();
-  if (auto *Cls = llvm::dyn_cast<ClassTemplateSpecializationDecl>(&ND))
-    return &Cls->getTemplateInstantiationArgs();
-  if (auto *Var = llvm::dyn_cast<VarTemplateSpecializationDecl>(&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 @@ std::string printName(const ASTContext &
   }
   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 @@ std::string printName(const ASTContext &
   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<llvm::ArrayRef<TemplateArgumentLoc>> Args =
+          getTemplateSpecializationArgLocs(ND)) {
+    printTemplateArgumentList(OS, *Args, Policy);
+  } else if (auto *Cls = llvm::dyn_cast<ClassTemplateSpecializationDecl>(&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<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 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<NamespaceDecl>(Ctx))

Modified: clang-tools-extra/trunk/clangd/AST.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/AST.h?rev=358272&r1=358271&r2=358272&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/AST.h (original)
+++ clang-tools-extra/trunk/clangd/AST.h Fri Apr 12 03:09:14 2019
@@ -47,6 +47,12 @@ std::string printNamespaceScope(const De
 /// "(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
+/// <typename U> struct Foo<int, U> will return '<int, U>'. 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<SymbolID> getSymbolID(const Decl *D);
 

Modified: clang-tools-extra/trunk/unittests/clangd/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clangd/CMakeLists.txt?rev=358272&r1=358271&r2=358272&view=diff
==============================================================================
--- clang-tools-extra/trunk/unittests/clangd/CMakeLists.txt (original)
+++ clang-tools-extra/trunk/unittests/clangd/CMakeLists.txt Fri Apr 12 03:09:14 2019
@@ -13,6 +13,7 @@ include_directories(
 
 add_extra_unittest(ClangdTests
   Annotations.cpp
+  PrintASTTests.cpp
   BackgroundIndexTests.cpp
   CancellationTests.cpp
   ClangdTests.cpp

Modified: clang-tools-extra/trunk/unittests/clangd/FindSymbolsTests.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clangd/FindSymbolsTests.cpp?rev=358272&r1=358271&r2=358272&view=diff
==============================================================================
--- clang-tools-extra/trunk/unittests/clangd/FindSymbolsTests.cpp (original)
+++ clang-tools-extra/trunk/unittests/clangd/FindSymbolsTests.cpp Fri Apr 12 03:09:14 2019
@@ -525,11 +525,9 @@ TEST_F(DocumentSymbolsTest, Template) {
           AllOf(WithName("Tmpl<double>"), WithKind(SymbolKind::Struct),
                 Children()),
           AllOf(WithName("funcTmpl"), Children()),
-          // FIXME(ibiryukov): template args should be <int> to match the code.
-          AllOf(WithName("funcTmpl<int, double, float>"), Children()),
+          AllOf(WithName("funcTmpl<int>"), Children()),
           AllOf(WithName("varTmpl"), Children()),
-          // FIXME(ibiryukov): template args should be <int> to match the code.
-          AllOf(WithName("varTmpl<int, double>"), Children())));
+          AllOf(WithName("varTmpl<int>"), Children())));
 }
 
 TEST_F(DocumentSymbolsTest, Namespaces) {

Added: clang-tools-extra/trunk/unittests/clangd/PrintASTTests.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clangd/PrintASTTests.cpp?rev=358272&view=auto
==============================================================================
--- clang-tools-extra/trunk/unittests/clangd/PrintASTTests.cpp (added)
+++ clang-tools-extra/trunk/unittests/clangd/PrintASTTests.cpp Fri Apr 12 03:09:14 2019
@@ -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<const char *> Expected;
+};
+class ASTUtils : public testing::Test,
+                 public ::testing::WithParamInterface<Case> {};
+
+TEST_P(ASTUtils, PrintTemplateArgs) {
+  auto Pair = GetParam();
+  Annotations Test(Pair.AnnotatedCode);
+  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[TemplateArgsAtPoints.size()])
+        return true;
+      TemplateArgsAtPoints.push_back(printTemplateSpecializationArgs(*ND));
+      return true;
+    }
+    std::vector<std::string> TemplateArgsAtPoints;
+    const std::vector<Position> 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<Case>({
+                            {
+                                R"cpp(
+                                  template <class X> class Bar {};
+                                  template <> class ^Bar<double> {};)cpp",
+                                {"<double>"}},
+                            {
+                                R"cpp(
+                                  template <class X> class Bar {};
+                                  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> {};)cpp",
+                                {"<int, bool, Bar, 8>", "<T *, T, Bar, 3>"}},
+                            {
+                                R"cpp(
+                                  template <int ...> void Foz() {};
+                                  template <> void ^Foz<3, 5, 8>() {};)cpp",
+                                {"<3, 5, 8>"}},
+                            {
+                                R"cpp(
+                                  template <class X> class Bar {};
+                                  template <template <class> class ...>
+                                  class Aux {};
+                                  template <> class ^Aux<Bar, Bar> {};
+                                  template <template <class> T>
+                                  class ^Aux<T, T> {};)cpp",
+                                {"<Bar, Bar>", "<T, T>"}},
+                            {
+                                R"cpp(
+                                  template <typename T> T var = 1234;
+                                  template <> int ^var<int> = 1;)cpp",
+                                {"<int>"}},
+                            {
+                                R"cpp(
+                                  template <typename T> struct Foo;
+                                  struct Bar { friend class Foo<int>; };
+                                  template <> struct ^Foo<int> {};)cpp",
+                                {"<int>"}},
+                        })));
+} // namespace
+} // namespace clangd
+} // namespace clang




More information about the cfe-commits mailing list