| //===-- SymbolInfoTests.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 "Annotations.h" |
| #include "ParsedAST.h" |
| #include "TestTU.h" |
| #include "XRefs.h" |
| #include "gmock/gmock.h" |
| #include "gtest/gtest.h" |
| #include <optional> |
| |
| namespace clang { |
| namespace clangd { |
| namespace { |
| |
| using ::testing::UnorderedElementsAreArray; |
| |
| // 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<const char *, std::vector<ExpectedSymbolDetails>> |
| TestInputExpectedOutput[] = { |
| { |
| R"cpp( // Simple function reference - declaration |
| void $decl[[foo]](); |
| int bar() { |
| fo^o(); |
| } |
| )cpp", |
| {ExpectedSymbolDetails{"foo", "", "c:@F@foo#", "decl"}}}, |
| { |
| R"cpp( // Simple function reference - definition |
| void $def[[foo]]() {} |
| int bar() { |
| fo^o(); |
| } |
| )cpp", |
| {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 $decl[[foo]](); |
| int baz() { |
| fo^o(); |
| } |
| } |
| )cpp", |
| {ExpectedSymbolDetails{"foo", "bar::", "c:@N@bar@F@foo#", |
| "decl"}}}, |
| { |
| R"cpp( // Function in different namespace reference |
| namespace bar { |
| void $decl[[foo]](); |
| } |
| namespace barbar { |
| int baz() { |
| bar::fo^o(); |
| } |
| } |
| )cpp", |
| {ExpectedSymbolDetails{"foo", "bar::", "c:@N@bar@F@foo#", |
| "decl"}}}, |
| { |
| R"cpp( // Function in global namespace reference |
| void $decl[[foo]](); |
| namespace Nbar { |
| namespace Nbaz { |
| int baz() { |
| ::fo^o(); |
| } |
| } |
| } |
| )cpp", |
| {ExpectedSymbolDetails{"foo", "", "c:@F@foo#", "decl"}}}, |
| { |
| R"cpp( // Function in anonymous namespace reference |
| namespace { |
| void $decl[[foo]](); |
| } |
| namespace barbar { |
| int baz() { |
| fo^o(); |
| } |
| } |
| )cpp", |
| {ExpectedSymbolDetails{"foo", "(anonymous)", |
| "c:TestTU.cpp@aN@F@foo#", "decl"}}}, |
| { |
| R"cpp( // Function reference - ADL |
| namespace bar { |
| struct BarType {}; |
| void $decl[[foo]](const BarType&); |
| } |
| namespace barbar { |
| int baz() { |
| bar::BarType b; |
| fo^o(b); |
| } |
| } |
| )cpp", |
| {ExpectedSymbolDetails{ |
| "foo", "bar::", "c:@N@bar@F@foo#&1$@N@bar@S@BarType#", |
| "decl"}}}, |
| { |
| R"cpp( // Global value reference |
| int $def[[value]]; |
| void foo(int) { } |
| void bar() { |
| foo(val^ue); |
| } |
| )cpp", |
| {ExpectedSymbolDetails{"value", "", "c:@value", "def", "def"}}}, |
| { |
| R"cpp( // Local value reference |
| void foo() { int $def[[aaa]]; int bbb = aa^a; } |
| )cpp", |
| {ExpectedSymbolDetails{"aaa", "foo", "c:TestTU.cpp@49@F@foo#@aaa", |
| "def", "def"}}}, |
| { |
| R"cpp( // Function param |
| void bar(int $def[[aaa]]) { |
| int bbb = a^aa; |
| } |
| )cpp", |
| {ExpectedSymbolDetails{ |
| "aaa", "bar", "c:TestTU.cpp@38@F@bar#I#@aaa", "def", "def"}}}, |
| { |
| R"cpp( // Lambda capture |
| void foo() { |
| int $def[[ii]]; |
| auto lam = [ii]() { |
| return i^i; |
| }; |
| } |
| )cpp", |
| {ExpectedSymbolDetails{"ii", "foo", "c:TestTU.cpp@54@F@foo#@ii", |
| "def", "def"}}}, |
| { |
| R"cpp( // Macro reference |
| #define MACRO 5\nint i = MAC^RO; |
| )cpp", |
| {ExpectedSymbolDetails{"MACRO", "", |
| "c:TestTU.cpp@38@macro@MACRO"}}}, |
| { |
| R"cpp( // Macro reference |
| #define MACRO 5\nint i = MACRO^; |
| )cpp", |
| {ExpectedSymbolDetails{"MACRO", "", |
| "c:TestTU.cpp@38@macro@MACRO"}}}, |
| { |
| R"cpp( // Multiple symbols returned - using overloaded function name |
| void $def[[foo]]() {} |
| void $def_bool[[foo]](bool) {} |
| void $def_int[[foo]](int) {} |
| namespace bar { |
| using ::$decl[[fo^o]]; |
| } |
| )cpp", |
| {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 {}; |
| struct bar { |
| bar(const foo&) {} |
| }; |
| void func_baz1(bar) {} |
| void func_baz2() { |
| foo $def[[ff]]; |
| func_baz1(f^f); |
| } |
| )cpp", |
| {ExpectedSymbolDetails{"ff", "func_baz2", |
| "c:TestTU.cpp@218@F@func_baz2#@ff", "def", |
| "def"}}}, |
| { |
| R"cpp( // Type reference - declaration |
| struct $decl[[foo]]; |
| void bar(fo^o*); |
| )cpp", |
| {ExpectedSymbolDetails{"foo", "", "c:@S@foo", "decl"}}}, |
| { |
| R"cpp( // Type reference - definition |
| struct $def[[foo]] {}; |
| void bar(fo^o*); |
| )cpp", |
| {ExpectedSymbolDetails{"foo", "", "c:@S@foo", "def", "def"}}}, |
| { |
| R"cpp( // Type Reference - template argument |
| struct $def[[foo]] {}; |
| template<class T> struct bar {}; |
| void baz() { |
| bar<fo^o> b; |
| } |
| )cpp", |
| {ExpectedSymbolDetails{"foo", "", "c:@S@foo", "def", "def"}}}, |
| { |
| R"cpp( // Template parameter reference - type param |
| template<class $def[[TT]]> struct bar { |
| T^T t; |
| }; |
| )cpp", |
| {ExpectedSymbolDetails{"TT", "bar::", "c:TestTU.cpp@65", "def", |
| "def"}}}, |
| { |
| R"cpp( // Template parameter reference - type param |
| template<int $def[[NN]]> struct bar { |
| int a = N^N; |
| }; |
| )cpp", |
| {ExpectedSymbolDetails{"NN", "bar::", "c:TestTU.cpp@65", "def", |
| "def"}}}, |
| { |
| R"cpp( // Class member reference - objec |
| struct foo { |
| int $def[[aa]]; |
| }; |
| void bar() { |
| foo f; |
| f.a^a; |
| } |
| )cpp", |
| {ExpectedSymbolDetails{"aa", "foo::", "c:@S@foo@FI@aa", "def", |
| "def"}}}, |
| { |
| R"cpp( // Class member reference - pointer |
| struct foo { |
| int $def[[aa]]; |
| }; |
| void bar() { |
| &foo::a^a; |
| } |
| )cpp", |
| {ExpectedSymbolDetails{"aa", "foo::", "c:@S@foo@FI@aa", "def", |
| "def"}}}, |
| { |
| R"cpp( // Class method reference - objec |
| struct foo { |
| void $def[[aa]]() {} |
| }; |
| void bar() { |
| foo f; |
| f.a^a(); |
| } |
| )cpp", |
| {ExpectedSymbolDetails{"aa", "foo::", "c:@S@foo@F@aa#", "def", |
| "def"}}}, |
| { |
| R"cpp( // Class method reference - pointer |
| struct foo { |
| void $def[[aa]]() {} |
| }; |
| void bar() { |
| &foo::a^a; |
| } |
| )cpp", |
| {ExpectedSymbolDetails{"aa", "foo::", "c:@S@foo@F@aa#", "def", |
| "def"}}}, |
| { |
| R"cpp( // Typedef |
| typedef int $decl[[foo]]; |
| void bar() { |
| fo^o a; |
| } |
| )cpp", |
| {ExpectedSymbolDetails{"foo", "", "c:TestTU.cpp@T@foo", "decl"}}}, |
| { |
| R"cpp( // Type alias |
| using $decl[[foo]] = int; |
| void bar() { |
| fo^o a; |
| } |
| )cpp", |
| {ExpectedSymbolDetails{"foo", "", "c:@foo", "decl"}}}, |
| { |
| R"cpp( // Namespace reference |
| namespace $decl[[foo]] {} |
| using namespace fo^o; |
| )cpp", |
| {ExpectedSymbolDetails{"foo", "", "c:@N@foo", "decl"}}}, |
| { |
| R"cpp( // Enum value reference |
| enum foo { $def[[bar]], baz }; |
| void f() { |
| foo fff = ba^r; |
| } |
| )cpp", |
| {ExpectedSymbolDetails{"bar", "foo", "c:@E@foo@bar", "def", |
| "def"}}}, |
| { |
| R"cpp( // Enum class value reference |
| enum class foo { $def[[bar]], baz }; |
| void f() { |
| foo fff = foo::ba^r; |
| } |
| )cpp", |
| {ExpectedSymbolDetails{"bar", "foo::", "c:@E@foo@bar", "def", |
| "def"}}}, |
| { |
| R"cpp( // Parameters in declarations |
| void foo(int $def[[ba^r]]); |
| )cpp", |
| {ExpectedSymbolDetails{ |
| "bar", "foo", "c:TestTU.cpp@50@F@foo#I#@bar", "def", "def"}}}, |
| { |
| R"cpp( // Type inference with auto keyword |
| struct foo {}; |
| foo getfoo() { return foo{}; } |
| void f() { |
| au^to a = getfoo(); |
| } |
| )cpp", |
| {/* not implemented */}}, |
| { |
| R"cpp( // decltype |
| struct foo {}; |
| void f() { |
| foo f; |
| declt^ype(f); |
| } |
| )cpp", |
| {/* not implemented */}}, |
| }; |
| |
| for (const auto &T : TestInputExpectedOutput) { |
| Annotations TestInput(T.first); |
| TestTU TU; |
| TU.Code = std::string(TestInput.code()); |
| TU.ExtraArgs.push_back("-xobjective-c++"); |
| auto AST = TU.build(); |
| |
| std::vector<SymbolDetails> Expected; |
| for (const auto &Sym : T.second) { |
| std::optional<Location> 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(Expected)) |
| << T.first; |
| } |
| } |
| |
| } // namespace |
| } // namespace clangd |
| } // namespace clang |