From 61ef0ab70196bfdc4c301e12384aa91938b15cc4 Mon Sep 17 00:00:00 2001 From: David Goldman Date: Mon, 18 Jul 2022 16:32:59 -0400 Subject: [PATCH] [clangd] Add decl/def support to SymbolDetails Add an optional declarationRange and definitionRange to SymbolDetails. This will allow SourceKit-LSP to implement toggling between goto definition/declaration based on whether the symbol at the cursor is a definition or declaration. Differential Revision: https://reviews.llvm.org/D130041 --- clang-tools-extra/clangd/AST.cpp | 1 + clang-tools-extra/clangd/Protocol.cpp | 10 +- clang-tools-extra/clangd/Protocol.h | 4 + clang-tools-extra/clangd/XRefs.cpp | 10 +- clang-tools-extra/clangd/test/symbol-info.test | 30 ++- .../clangd/unittests/SymbolInfoTests.cpp | 224 +++++++++++++-------- 6 files changed, 195 insertions(+), 84 deletions(-) diff --git a/clang-tools-extra/clangd/AST.cpp b/clang-tools-extra/clangd/AST.cpp index 85c3257..31db4cf 100644 --- a/clang-tools-extra/clangd/AST.cpp +++ b/clang-tools-extra/clangd/AST.cpp @@ -14,6 +14,7 @@ #include "clang/AST/Decl.h" #include "clang/AST/DeclBase.h" #include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclObjC.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/DeclarationName.h" #include "clang/AST/ExprCXX.h" diff --git a/clang-tools-extra/clangd/Protocol.cpp b/clang-tools-extra/clangd/Protocol.cpp index ab75faa..622f527 100644 --- a/clang-tools-extra/clangd/Protocol.cpp +++ b/clang-tools-extra/clangd/Protocol.cpp @@ -734,7 +734,9 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &O, bool operator==(const SymbolDetails &LHS, const SymbolDetails &RHS) { return LHS.name == RHS.name && LHS.containerName == RHS.containerName && - LHS.USR == RHS.USR && LHS.ID == RHS.ID; + LHS.USR == RHS.USR && LHS.ID == RHS.ID && + LHS.declarationRange == RHS.declarationRange && + LHS.definitionRange == RHS.definitionRange; } llvm::json::Value toJSON(const SymbolDetails &P) { @@ -755,6 +757,12 @@ llvm::json::Value toJSON(const SymbolDetails &P) { if (P.ID) Result["id"] = P.ID.str(); + if (P.declarationRange) + Result["declarationRange"] = *P.declarationRange; + + if (P.definitionRange) + Result["definitionRange"] = *P.definitionRange; + // FIXME: workaround for older gcc/clang return std::move(Result); } diff --git a/clang-tools-extra/clangd/Protocol.h b/clang-tools-extra/clangd/Protocol.h index 0087017..add24da 100644 --- a/clang-tools-extra/clangd/Protocol.h +++ b/clang-tools-extra/clangd/Protocol.h @@ -1093,6 +1093,10 @@ struct SymbolDetails { std::string USR; SymbolID ID; + + llvm::Optional declarationRange; + + llvm::Optional definitionRange; }; llvm::json::Value toJSON(const SymbolDetails &); llvm::raw_ostream &operator<<(llvm::raw_ostream &, const SymbolDetails &); diff --git a/clang-tools-extra/clangd/XRefs.cpp b/clang-tools-extra/clangd/XRefs.cpp index c871ea0..c6a843e 100644 --- a/clang-tools-extra/clangd/XRefs.cpp +++ b/clang-tools-extra/clangd/XRefs.cpp @@ -1481,7 +1481,7 @@ std::vector getSymbolInfo(ParsedAST &AST, Position Pos) { llvm::consumeError(CurLoc.takeError()); return {}; } - + auto MainFilePath = AST.tuPath(); std::vector Results; // We also want the targets of using-decls, so we include @@ -1489,6 +1489,8 @@ std::vector getSymbolInfo(ParsedAST &AST, Position Pos) { DeclRelationSet Relations = DeclRelation::TemplatePattern | DeclRelation::Alias | DeclRelation::Underlying; for (const NamedDecl *D : getDeclAtPosition(AST, *CurLoc, Relations)) { + D = getPreferredDecl(D); + SymbolDetails NewSymbol; std::string QName = printQualifiedName(*D); auto SplitQName = splitQualifiedName(QName); @@ -1505,6 +1507,12 @@ std::vector getSymbolInfo(ParsedAST &AST, Position Pos) { NewSymbol.USR = std::string(USR.str()); NewSymbol.ID = SymbolID(NewSymbol.USR); } + if (const NamedDecl *Def = getDefinition(D)) + NewSymbol.definitionRange = makeLocation( + AST.getASTContext(), nameLocation(*Def, SM), MainFilePath); + NewSymbol.declarationRange = + makeLocation(AST.getASTContext(), nameLocation(*D, SM), MainFilePath); + Results.push_back(std::move(NewSymbol)); } diff --git a/clang-tools-extra/clangd/test/symbol-info.test b/clang-tools-extra/clangd/test/symbol-info.test index 1142f3b..ba788e1 100644 --- a/clang-tools-extra/clangd/test/symbol-info.test +++ b/clang-tools-extra/clangd/test/symbol-info.test @@ -1,10 +1,36 @@ # RUN: clangd -lit-test < %s | FileCheck %s {"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}} --- -{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///simple.cpp","languageId":"cpp","version":1,"text":"void foo(); int main() { foo(); }\n"}}} +{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///simple.cpp","languageId":"cpp","version":1,"text":"void foo(); void foo() {} int main() { foo(); }\n"}}} --- -{"jsonrpc":"2.0","id":1,"method":"textDocument/symbolInfo","params":{"textDocument":{"uri":"test:///simple.cpp"},"position":{"line":0,"character":27}}} +{"jsonrpc":"2.0","id":1,"method":"textDocument/symbolInfo","params":{"textDocument":{"uri":"test:///simple.cpp"},"position":{"line":0,"character":40}}} # CHECK: "containerName": null, +# CHECK-NEXT: "declarationRange": { +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 8, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 5, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: } +# CHECK-NEXT: }, +# CHECK-NEXT: "uri": "file://{{.*}}/clangd-test/simple.cpp" +# CHECK-NEXT: }, +# CHECK-NEXT: "definitionRange": { +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 20, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 17, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: } +# CHECK-NEXT: }, +# CHECK-NEXT: "uri": "file://{{.*}}/clangd-test/simple.cpp" +# CHECK-NEXT: }, # CHECK-NEXT: "id": "CA2EBE44A1D76D2A", # CHECK-NEXT: "name": "foo", # CHECK-NEXT: "usr": "c:@F@foo#" diff --git a/clang-tools-extra/clangd/unittests/SymbolInfoTests.cpp b/clang-tools-extra/clangd/unittests/SymbolInfoTests.cpp index 1076352..f68f87f 100644 --- a/clang-tools-extra/clangd/unittests/SymbolInfoTests.cpp +++ b/clang-tools-extra/clangd/unittests/SymbolInfoTests.cpp @@ -18,45 +18,79 @@ namespace { using ::testing::UnorderedElementsAreArray; -auto CreateExpectedSymbolDetails = [](const std::string &Name, - const std::string &Container, - const std::string &USR) { - return SymbolDetails{Name, Container, USR, SymbolID(USR)}; +// Partial SymbolDetails with the rest filled in at testing time. +struct ExpectedSymbolDetails { + std::string Name; + std::string Container; + std::string USR; + const char *DeclMarker = nullptr; + const char *DefMarker = nullptr; }; TEST(SymbolInfoTests, All) { - std::pair> + std::pair> TestInputExpectedOutput[] = { { R"cpp( // Simple function reference - declaration - void foo(); + void $decl[[foo]](); int bar() { fo^o(); } )cpp", - {CreateExpectedSymbolDetails("foo", "", "c:@F@foo#")}}, + {ExpectedSymbolDetails{"foo", "", "c:@F@foo#", "decl"}}}, { R"cpp( // Simple function reference - definition - void foo() {} + void $def[[foo]]() {} int bar() { fo^o(); } )cpp", - {CreateExpectedSymbolDetails("foo", "", "c:@F@foo#")}}, + {ExpectedSymbolDetails{"foo", "", "c:@F@foo#", "def", "def"}}}, + { + R"cpp( // Simple function reference - decl and def + void $decl[[foo]](); + void $def[[foo]]() {} + int bar() { + fo^o(); + } + )cpp", + {ExpectedSymbolDetails{"foo", "", "c:@F@foo#", "decl", "def"}}}, + { + R"cpp( // Simple class reference - decl and def + @interface $decl[[Foo]] + @end + @implementation $def[[Foo]] + @end + void doSomething(F^oo *obj) {} + )cpp", + {ExpectedSymbolDetails{"Foo", "", "c:objc(cs)Foo", "decl", + "def"}}}, + { + R"cpp( // Simple method reference - decl and def + @interface Foo + - (void)$decl[[foo]]; + @end + @implementation Foo + - (void)$def[[fo^o]] {} + @end + )cpp", + {ExpectedSymbolDetails{"foo", "Foo::", "c:objc(cs)Foo(im)foo", + "decl", "def"}}}, { R"cpp( // Function in namespace reference namespace bar { - void foo(); + void $decl[[foo]](); int baz() { fo^o(); } } )cpp", - {CreateExpectedSymbolDetails("foo", "bar::", "c:@N@bar@F@foo#")}}, + {ExpectedSymbolDetails{"foo", "bar::", "c:@N@bar@F@foo#", + "decl"}}}, { R"cpp( // Function in different namespace reference namespace bar { - void foo(); + void $decl[[foo]](); } namespace barbar { int baz() { @@ -64,10 +98,11 @@ TEST(SymbolInfoTests, All) { } } )cpp", - {CreateExpectedSymbolDetails("foo", "bar::", "c:@N@bar@F@foo#")}}, + {ExpectedSymbolDetails{"foo", "bar::", "c:@N@bar@F@foo#", + "decl"}}}, { R"cpp( // Function in global namespace reference - void foo(); + void $decl[[foo]](); namespace Nbar { namespace Nbaz { int baz() { @@ -76,11 +111,11 @@ TEST(SymbolInfoTests, All) { } } )cpp", - {CreateExpectedSymbolDetails("foo", "", "c:@F@foo#")}}, + {ExpectedSymbolDetails{"foo", "", "c:@F@foo#", "decl"}}}, { R"cpp( // Function in anonymous namespace reference namespace { - void foo(); + void $decl[[foo]](); } namespace barbar { int baz() { @@ -88,13 +123,13 @@ TEST(SymbolInfoTests, All) { } } )cpp", - {CreateExpectedSymbolDetails("foo", "(anonymous)", - "c:TestTU.cpp@aN@F@foo#")}}, + {ExpectedSymbolDetails{"foo", "(anonymous)", + "c:TestTU.cpp@aN@F@foo#", "decl"}}}, { R"cpp( // Function reference - ADL namespace bar { struct BarType {}; - void foo(const BarType&); + void $decl[[foo]](const BarType&); } namespace barbar { int baz() { @@ -103,67 +138,71 @@ TEST(SymbolInfoTests, All) { } } )cpp", - {CreateExpectedSymbolDetails( - "foo", "bar::", "c:@N@bar@F@foo#&1$@N@bar@S@BarType#")}}, + {ExpectedSymbolDetails{ + "foo", "bar::", "c:@N@bar@F@foo#&1$@N@bar@S@BarType#", + "decl"}}}, { R"cpp( // Global value reference - int value; + int $def[[value]]; void foo(int) { } void bar() { foo(val^ue); } )cpp", - {CreateExpectedSymbolDetails("value", "", "c:@value")}}, + {ExpectedSymbolDetails{"value", "", "c:@value", "def", "def"}}}, { R"cpp( // Local value reference - void foo() { int aaa; int bbb = aa^a; } + void foo() { int $def[[aaa]]; int bbb = aa^a; } )cpp", - {CreateExpectedSymbolDetails("aaa", "foo", - "c:TestTU.cpp@49@F@foo#@aaa")}}, + {ExpectedSymbolDetails{"aaa", "foo", "c:TestTU.cpp@49@F@foo#@aaa", + "def", "def"}}}, { R"cpp( // Function param - void bar(int aaa) { + void bar(int $def[[aaa]]) { int bbb = a^aa; } )cpp", - {CreateExpectedSymbolDetails("aaa", "bar", - "c:TestTU.cpp@38@F@bar#I#@aaa")}}, + {ExpectedSymbolDetails{ + "aaa", "bar", "c:TestTU.cpp@38@F@bar#I#@aaa", "def", "def"}}}, { R"cpp( // Lambda capture void foo() { - int ii; + int $def[[ii]]; auto lam = [ii]() { return i^i; }; } )cpp", - {CreateExpectedSymbolDetails("ii", "foo", - "c:TestTU.cpp@54@F@foo#@ii")}}, + {ExpectedSymbolDetails{"ii", "foo", "c:TestTU.cpp@54@F@foo#@ii", + "def", "def"}}}, { R"cpp( // Macro reference #define MACRO 5\nint i = MAC^RO; )cpp", - {CreateExpectedSymbolDetails("MACRO", "", - "c:TestTU.cpp@38@macro@MACRO")}}, + {ExpectedSymbolDetails{"MACRO", "", + "c:TestTU.cpp@38@macro@MACRO"}}}, { R"cpp( // Macro reference #define MACRO 5\nint i = MACRO^; )cpp", - {CreateExpectedSymbolDetails("MACRO", "", - "c:TestTU.cpp@38@macro@MACRO")}}, + {ExpectedSymbolDetails{"MACRO", "", + "c:TestTU.cpp@38@macro@MACRO"}}}, { R"cpp( // Multiple symbols returned - using overloaded function name - void foo() {} - void foo(bool) {} - void foo(int) {} + void $def[[foo]]() {} + void $def_bool[[foo]](bool) {} + void $def_int[[foo]](int) {} namespace bar { - using ::fo^o; + using ::$decl[[fo^o]]; } )cpp", - {CreateExpectedSymbolDetails("foo", "", "c:@F@foo#"), - CreateExpectedSymbolDetails("foo", "", "c:@F@foo#b#"), - CreateExpectedSymbolDetails("foo", "", "c:@F@foo#I#"), - CreateExpectedSymbolDetails("foo", "bar::", "c:@N@bar@UD@foo")}}, + {ExpectedSymbolDetails{"foo", "", "c:@F@foo#", "def", "def"}, + ExpectedSymbolDetails{"foo", "", "c:@F@foo#b#", "def_bool", + "def_bool"}, + ExpectedSymbolDetails{"foo", "", "c:@F@foo#I#", "def_int", + "def_int"}, + ExpectedSymbolDetails{"foo", "bar::", "c:@N@bar@UD@foo", + "decl"}}}, { R"cpp( // Multiple symbols returned - implicit conversion struct foo {}; @@ -172,133 +211,142 @@ TEST(SymbolInfoTests, All) { }; void func_baz1(bar) {} void func_baz2() { - foo ff; + foo $def[[ff]]; func_baz1(f^f); } )cpp", - {CreateExpectedSymbolDetails( - "ff", "func_baz2", "c:TestTU.cpp@218@F@func_baz2#@ff")}}, + {ExpectedSymbolDetails{"ff", "func_baz2", + "c:TestTU.cpp@218@F@func_baz2#@ff", "def", + "def"}}}, { R"cpp( // Type reference - declaration - struct foo; + struct $decl[[foo]]; void bar(fo^o*); )cpp", - {CreateExpectedSymbolDetails("foo", "", "c:@S@foo")}}, + {ExpectedSymbolDetails{"foo", "", "c:@S@foo", "decl"}}}, { R"cpp( // Type reference - definition - struct foo {}; + struct $def[[foo]] {}; void bar(fo^o*); )cpp", - {CreateExpectedSymbolDetails("foo", "", "c:@S@foo")}}, + {ExpectedSymbolDetails{"foo", "", "c:@S@foo", "def", "def"}}}, { R"cpp( // Type Reference - template argument - struct foo {}; + struct $def[[foo]] {}; template struct bar {}; void baz() { bar b; } )cpp", - {CreateExpectedSymbolDetails("foo", "", "c:@S@foo")}}, + {ExpectedSymbolDetails{"foo", "", "c:@S@foo", "def", "def"}}}, { R"cpp( // Template parameter reference - type param - template struct bar { + template struct bar { T^T t; }; )cpp", - {CreateExpectedSymbolDetails("TT", "bar::", "c:TestTU.cpp@65")}}, + {ExpectedSymbolDetails{"TT", "bar::", "c:TestTU.cpp@65", "def", + "def"}}}, { R"cpp( // Template parameter reference - type param - template struct bar { + template struct bar { int a = N^N; }; )cpp", - {CreateExpectedSymbolDetails("NN", "bar::", "c:TestTU.cpp@65")}}, + {ExpectedSymbolDetails{"NN", "bar::", "c:TestTU.cpp@65", "def", + "def"}}}, { R"cpp( // Class member reference - objec struct foo { - int aa; + int $def[[aa]]; }; void bar() { foo f; f.a^a; } )cpp", - {CreateExpectedSymbolDetails("aa", "foo::", "c:@S@foo@FI@aa")}}, + {ExpectedSymbolDetails{"aa", "foo::", "c:@S@foo@FI@aa", "def", + "def"}}}, { R"cpp( // Class member reference - pointer struct foo { - int aa; + int $def[[aa]]; }; void bar() { &foo::a^a; } )cpp", - {CreateExpectedSymbolDetails("aa", "foo::", "c:@S@foo@FI@aa")}}, + {ExpectedSymbolDetails{"aa", "foo::", "c:@S@foo@FI@aa", "def", + "def"}}}, { R"cpp( // Class method reference - objec struct foo { - void aa() {} + void $def[[aa]]() {} }; void bar() { foo f; f.a^a(); } )cpp", - {CreateExpectedSymbolDetails("aa", "foo::", "c:@S@foo@F@aa#")}}, + {ExpectedSymbolDetails{"aa", "foo::", "c:@S@foo@F@aa#", "def", + "def"}}}, { R"cpp( // Class method reference - pointer struct foo { - void aa() {} + void $def[[aa]]() {} }; void bar() { &foo::a^a; } )cpp", - {CreateExpectedSymbolDetails("aa", "foo::", "c:@S@foo@F@aa#")}}, + {ExpectedSymbolDetails{"aa", "foo::", "c:@S@foo@F@aa#", "def", + "def"}}}, { R"cpp( // Typedef - typedef int foo; + typedef int $decl[[foo]]; void bar() { fo^o a; } )cpp", - {CreateExpectedSymbolDetails("foo", "", "c:TestTU.cpp@T@foo")}}, + {ExpectedSymbolDetails{"foo", "", "c:TestTU.cpp@T@foo", "decl"}}}, { R"cpp( // Type alias - using foo = int; + using $decl[[foo]] = int; void bar() { fo^o a; } )cpp", - {CreateExpectedSymbolDetails("foo", "", "c:@foo")}}, + {ExpectedSymbolDetails{"foo", "", "c:@foo", "decl"}}}, { R"cpp( // Namespace reference - namespace foo {} + namespace $decl[[foo]] {} using namespace fo^o; )cpp", - {CreateExpectedSymbolDetails("foo", "", "c:@N@foo")}}, + {ExpectedSymbolDetails{"foo", "", "c:@N@foo", "decl"}}}, { R"cpp( // Enum value reference - enum foo { bar, baz }; + enum foo { $def[[bar]], baz }; void f() { foo fff = ba^r; } )cpp", - {CreateExpectedSymbolDetails("bar", "foo", "c:@E@foo@bar")}}, + {ExpectedSymbolDetails{"bar", "foo", "c:@E@foo@bar", "def", + "def"}}}, { R"cpp( // Enum class value reference - enum class foo { bar, baz }; + enum class foo { $def[[bar]], baz }; void f() { foo fff = foo::ba^r; } )cpp", - {CreateExpectedSymbolDetails("bar", "foo::", "c:@E@foo@bar")}}, + {ExpectedSymbolDetails{"bar", "foo::", "c:@E@foo@bar", "def", + "def"}}}, { R"cpp( // Parameters in declarations - void foo(int ba^r); + void foo(int $def[[ba^r]]); )cpp", - {CreateExpectedSymbolDetails("bar", "foo", - "c:TestTU.cpp@50@F@foo#I#@bar")}}, + {ExpectedSymbolDetails{ + "bar", "foo", "c:TestTU.cpp@50@F@foo#I#@bar", "def", "def"}}}, { R"cpp( // Type inference with auto keyword struct foo {}; @@ -321,10 +369,26 @@ TEST(SymbolInfoTests, All) { for (const auto &T : TestInputExpectedOutput) { Annotations TestInput(T.first); - auto AST = TestTU::withCode(TestInput.code()).build(); + TestTU TU; + TU.Code = std::string(TestInput.code()); + TU.ExtraArgs.push_back("-xobjective-c++"); + auto AST = TU.build(); + + std::vector Expected; + for (const auto &Sym : T.second) { + llvm::Optional Decl, Def; + if (Sym.DeclMarker) + Decl = Location{URIForFile::canonicalize(testPath(TU.Filename), ""), + TestInput.range(Sym.DeclMarker)}; + if (Sym.DefMarker) + Def = Location{URIForFile::canonicalize(testPath(TU.Filename), ""), + TestInput.range(Sym.DefMarker)}; + Expected.push_back( + {Sym.Name, Sym.Container, Sym.USR, SymbolID(Sym.USR), Decl, Def}); + } EXPECT_THAT(getSymbolInfo(AST, TestInput.point()), - UnorderedElementsAreArray(T.second)) + UnorderedElementsAreArray(Expected)) << T.first; } } -- 2.7.4