[clang-tools-extra] bda7d0a - [clangd] Improve goToDefinition on auto and dectype

Sam McCall via cfe-commits cfe-commits at lists.llvm.org
Tue Dec 15 07:37:39 PST 2020


Author: Quentin Chateau
Date: 2020-12-15T16:32:22+01:00
New Revision: bda7d0af970718c243d93b22a8449c20156e574f

URL: https://github.com/llvm/llvm-project/commit/bda7d0af970718c243d93b22a8449c20156e574f
DIFF: https://github.com/llvm/llvm-project/commit/bda7d0af970718c243d93b22a8449c20156e574f.diff

LOG: [clangd] Improve goToDefinition on auto and dectype

locateSymbolAt (used in goToDeclaration) follows the
deduced type instead of failing to locate the declaration.

Reviewed By: sammccall

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

Added: 
    

Modified: 
    clang-tools-extra/clangd/XRefs.cpp
    clang-tools-extra/clangd/unittests/ASTTests.cpp
    clang-tools-extra/clangd/unittests/XRefsTests.cpp

Removed: 
    


################################################################################
diff  --git a/clang-tools-extra/clangd/XRefs.cpp b/clang-tools-extra/clangd/XRefs.cpp
index ac4543026a9f..c7ec401c6479 100644
--- a/clang-tools-extra/clangd/XRefs.cpp
+++ b/clang-tools-extra/clangd/XRefs.cpp
@@ -463,6 +463,42 @@ locateASTReferent(SourceLocation CurLoc, const syntax::Token *TouchedIdentifier,
   return Result;
 }
 
+std::vector<LocatedSymbol> locateSymbolForType(const ParsedAST &AST,
+                                               const QualType &Type) {
+  const auto &SM = AST.getSourceManager();
+  auto MainFilePath =
+      getCanonicalPath(SM.getFileEntryForID(SM.getMainFileID()), SM);
+  if (!MainFilePath) {
+    elog("Failed to get a path for the main file, so no symbol.");
+    return {};
+  }
+
+  auto Decls = targetDecl(DynTypedNode::create(Type.getNonReferenceType()),
+                          DeclRelation::TemplatePattern | DeclRelation::Alias);
+  if (Decls.empty())
+    return {};
+
+  std::vector<LocatedSymbol> Results;
+  const auto &ASTContext = AST.getASTContext();
+
+  for (const NamedDecl *D : Decls) {
+    D = getPreferredDecl(D);
+
+    auto Loc = makeLocation(ASTContext, nameLocation(*D, SM), *MainFilePath);
+    if (!Loc)
+      continue;
+
+    Results.emplace_back();
+    Results.back().Name = printName(ASTContext, *D);
+    Results.back().PreferredDeclaration = *Loc;
+    if (const NamedDecl *Def = getDefinition(D))
+      Results.back().Definition =
+          makeLocation(ASTContext, nameLocation(*Def, SM), *MainFilePath);
+  }
+
+  return Results;
+}
+
 bool tokenSpelledAt(SourceLocation SpellingLoc, const syntax::TokenBuffer &TB) {
   auto ExpandedTokens = TB.expandedTokens(
       TB.sourceManager().getMacroArgExpandedLocation(SpellingLoc));
@@ -707,15 +743,31 @@ std::vector<LocatedSymbol> locateSymbolAt(ParsedAST &AST, Position Pos,
     return {};
   }
 
-  const syntax::Token *TouchedIdentifier =
-      syntax::spelledIdentifierTouching(*CurLoc, AST.getTokens());
-  if (TouchedIdentifier)
-    if (auto Macro =
-            locateMacroReferent(*TouchedIdentifier, AST, *MainFilePath))
-      // Don't look at the AST or index if we have a macro result.
-      // (We'd just return declarations referenced from the macro's
-      // expansion.)
-      return {*std::move(Macro)};
+  const syntax::Token *TouchedIdentifier = nullptr;
+  auto TokensTouchingCursor =
+      syntax::spelledTokensTouching(*CurLoc, AST.getTokens());
+  for (const syntax::Token &Tok : TokensTouchingCursor) {
+    if (Tok.kind() == tok::identifier) {
+      if (auto Macro = locateMacroReferent(Tok, AST, *MainFilePath))
+        // Don't look at the AST or index if we have a macro result.
+        // (We'd just return declarations referenced from the macro's
+        // expansion.)
+        return {*std::move(Macro)};
+
+      TouchedIdentifier = &Tok;
+      break;
+    }
+
+    if (Tok.kind() == tok::kw_auto || Tok.kind() == tok::kw_decltype) {
+      // go-to-definition on auto should find the definition of the deduced
+      // type, if possible
+      if (auto Deduced = getDeducedType(AST.getASTContext(), Tok.location())) {
+        auto LocSym = locateSymbolForType(AST, *Deduced);
+        if (!LocSym.empty())
+          return LocSym;
+      }
+    }
+  }
 
   ASTNodeKind NodeKind;
   auto ASTResults = locateASTReferent(*CurLoc, TouchedIdentifier, AST,

diff  --git a/clang-tools-extra/clangd/unittests/ASTTests.cpp b/clang-tools-extra/clangd/unittests/ASTTests.cpp
index 21f70dcc3168..4c52c72d703c 100644
--- a/clang-tools-extra/clangd/unittests/ASTTests.cpp
+++ b/clang-tools-extra/clangd/unittests/ASTTests.cpp
@@ -26,23 +26,168 @@ namespace clang {
 namespace clangd {
 namespace {
 
-TEST(GetDeducedType, KwAutoExpansion) {
+TEST(GetDeducedType, KwAutoKwDecltypeExpansion) {
   struct Test {
     StringRef AnnotatedCode;
     const char *DeducedType;
   } Tests[] = {
       {"^auto i = 0;", "int"},
       {"^auto f(){ return 1;};", "int"},
+      {
+          R"cpp( // auto on struct in a namespace
+              namespace ns1 { struct S {}; }
+              ^auto v = ns1::S{};
+          )cpp",
+          "struct ns1::S",
+      },
+      {
+          R"cpp( // decltype on struct
+              namespace ns1 { struct S {}; }
+              ns1::S i;
+              ^decltype(i) j;
+          )cpp",
+          "ns1::S",
+      },
+      {
+          R"cpp(// decltype(auto) on struct&
+            namespace ns1 {
+            struct S {};
+            } // namespace ns1
+
+            ns1::S i;
+            ns1::S& j = i;
+            ^decltype(auto) k = j;
+          )cpp",
+          "struct ns1::S &",
+      },
+      {
+          R"cpp( // auto on template class
+              class X;
+              template<typename T> class Foo {};
+              ^auto v = Foo<X>();
+          )cpp",
+          "class Foo<class X>",
+      },
+      {
+          R"cpp( // auto on initializer list.
+              namespace std
+              {
+                template<class _E>
+                class [[initializer_list]] {};
+              }
+
+              ^auto i = {1,2};
+          )cpp",
+          "class std::initializer_list<int>",
+      },
+      {
+          R"cpp( // auto in function return type with trailing return type
+            struct Foo {};
+            ^auto test() -> decltype(Foo()) {
+              return Foo();
+            }
+          )cpp",
+          "struct Foo",
+      },
+      {
+          R"cpp( // decltype in trailing return type
+            struct Foo {};
+            auto test() -> ^decltype(Foo()) {
+              return Foo();
+            }
+          )cpp",
+          "struct Foo",
+      },
+      {
+          R"cpp( // auto in function return type
+            struct Foo {};
+            ^auto test() {
+              return Foo();
+            }
+          )cpp",
+          "struct Foo",
+      },
+      {
+          R"cpp( // auto& in function return type
+            struct Foo {};
+            ^auto& test() {
+              static Foo x;
+              return x;
+            }
+          )cpp",
+          "struct Foo",
+      },
+      {
+          R"cpp( // auto* in function return type
+            struct Foo {};
+            ^auto* test() {
+              Foo *x;
+              return x;
+            }
+          )cpp",
+          "struct Foo",
+      },
+      {
+          R"cpp( // const auto& in function return type
+            struct Foo {};
+            const ^auto& test() {
+              static Foo x;
+              return x;
+            }
+          )cpp",
+          "struct Foo",
+      },
+      {
+          R"cpp( // decltype(auto) in function return (value)
+            struct Foo {};
+            ^decltype(auto) test() {
+              return Foo();
+            }
+          )cpp",
+          "struct Foo",
+      },
+      {
+          R"cpp( // decltype(auto) in function return (ref)
+            struct Foo {};
+            ^decltype(auto) test() {
+              static Foo x;
+              return (x);
+            }
+          )cpp",
+          "struct Foo &",
+      },
+      {
+          R"cpp( // decltype(auto) in function return (const ref)
+            struct Foo {};
+            ^decltype(auto) test() {
+              static const Foo x;
+              return (x);
+            }
+          )cpp",
+          "const struct Foo &",
+      },
+      {
+          R"cpp( // auto on alias
+            struct Foo {};
+            using Bar = Foo;
+            ^auto x = Bar();
+          )cpp",
+          // FIXME: it'd be nice if this resolved to the alias instead
+          "struct Foo",
+      },
   };
   for (Test T : Tests) {
     Annotations File(T.AnnotatedCode);
     auto AST = TestTU::withCode(File.code()).build();
     SourceManagerForFile SM("foo.cpp", File.code());
 
+    SCOPED_TRACE(File.code());
+    EXPECT_FALSE(File.points().empty());
     for (Position Pos : File.points()) {
       auto Location = sourceLocationInMainFile(SM.get(), Pos);
       ASSERT_TRUE(!!Location) << llvm::toString(Location.takeError());
       auto DeducedType = getDeducedType(AST.getASTContext(), *Location);
+      ASSERT_TRUE(DeducedType);
       EXPECT_EQ(DeducedType->getAsString(), T.DeducedType);
     }
   }

diff  --git a/clang-tools-extra/clangd/unittests/XRefsTests.cpp b/clang-tools-extra/clangd/unittests/XRefsTests.cpp
index dfe1f6ce6ccc..26e06dad9250 100644
--- a/clang-tools-extra/clangd/unittests/XRefsTests.cpp
+++ b/clang-tools-extra/clangd/unittests/XRefsTests.cpp
@@ -640,6 +640,134 @@ TEST(LocateSymbol, All) {
         struct Fo^o<T*> {};
       )cpp",
 
+      R"cpp(// auto builtin type (not supported)
+        ^auto x = 42;
+      )cpp",
+
+      R"cpp(// auto on lambda
+        auto x = [[[]]]{};
+        ^auto y = x;
+      )cpp",
+
+      R"cpp(// auto on struct
+        namespace ns1 {
+        struct [[S1]] {};
+        } // namespace ns1
+
+        ^auto x = ns1::S1{};
+      )cpp",
+
+      R"cpp(// decltype on struct
+        namespace ns1 {
+        struct [[S1]] {};
+        } // namespace ns1
+
+        ns1::S1 i;
+        ^decltype(i) j;
+      )cpp",
+
+      R"cpp(// decltype(auto) on struct
+        namespace ns1 {
+        struct [[S1]] {};
+        } // namespace ns1
+
+        ns1::S1 i;
+        ns1::S1& j = i;
+        ^decltype(auto) k = j;
+      )cpp",
+
+      R"cpp(// auto on template class
+        template<typename T> class [[Foo]] {};
+
+        ^auto x = Foo<int>();
+      )cpp",
+
+      R"cpp(// auto on template class with forward declared class
+        template<typename T> class [[Foo]] {};
+        class X;
+
+        ^auto x = Foo<X>();
+      )cpp",
+
+      R"cpp(// auto on specialized template class
+        template<typename T> class Foo {};
+        template<> class [[Foo]]<int> {};
+
+        ^auto x = Foo<int>();
+      )cpp",
+
+      R"cpp(// auto on initializer list.
+        namespace std
+        {
+          template<class _E>
+          class [[initializer_list]] {};
+        }
+
+        ^auto i = {1,2};
+      )cpp",
+
+      R"cpp(// auto function return with trailing type
+        struct [[Bar]] {};
+        ^auto test() -> decltype(Bar()) {
+          return Bar();
+        }
+      )cpp",
+
+      R"cpp(// decltype in trailing return type
+        struct [[Bar]] {};
+        auto test() -> ^decltype(Bar()) {
+          return Bar();
+        }
+      )cpp",
+
+      R"cpp(// auto in function return
+        struct [[Bar]] {};
+        ^auto test() {
+          return Bar();
+        }
+      )cpp",
+
+      R"cpp(// auto& in function return
+        struct [[Bar]] {};
+        ^auto& test() {
+          static Bar x;
+          return x;
+        }
+      )cpp",
+
+      R"cpp(// auto* in function return
+        struct [[Bar]] {};
+        ^auto* test() {
+          Bar* x;
+          return x;
+        }
+      )cpp",
+
+      R"cpp(// const auto& in function return
+        struct [[Bar]] {};
+        const ^auto& test() {
+          static Bar x;
+          return x;
+        }
+      )cpp",
+
+      R"cpp(// decltype(auto) in function return
+        struct [[Bar]] {};
+        ^decltype(auto) test() {
+          return Bar();
+        }
+      )cpp",
+
+      R"cpp(// decltype of function with trailing return type.
+        struct [[Bar]] {};
+        auto test() -> decltype(Bar()) {
+          return Bar();
+        }
+        void foo() {
+          ^decltype(test()) i = test();
+        }
+      )cpp",
+
       R"cpp(// Override specifier jumps to overridden method
         class Y { virtual void $decl[[a]]() = 0; };
         class X : Y { void a() ^override {} };


        


More information about the cfe-commits mailing list