diff options
Diffstat (limited to 'unittests/clangd/SymbolCollectorTests.cpp')
-rw-r--r-- | unittests/clangd/SymbolCollectorTests.cpp | 402 |
1 files changed, 304 insertions, 98 deletions
diff --git a/unittests/clangd/SymbolCollectorTests.cpp b/unittests/clangd/SymbolCollectorTests.cpp index 06a087cb..666d0bb0 100644 --- a/unittests/clangd/SymbolCollectorTests.cpp +++ b/unittests/clangd/SymbolCollectorTests.cpp @@ -9,6 +9,7 @@ #include "Annotations.h" #include "TestFS.h" +#include "TestTU.h" #include "index/SymbolCollector.h" #include "index/SymbolYAML.h" #include "clang/Basic/FileManager.h" @@ -35,15 +36,18 @@ using testing::UnorderedElementsAre; using testing::UnorderedElementsAreArray; // GMock helpers for matching Symbol. -MATCHER_P(Labeled, Label, "") { return arg.CompletionLabel == Label; } -MATCHER(HasDetail, "") { return arg.Detail; } -MATCHER_P(Detail, D, "") { - return arg.Detail && arg.Detail->CompletionDetail == D; +MATCHER_P(Labeled, Label, "") { + return (arg.Name + arg.Signature).str() == Label; +} +MATCHER(HasReturnType, "") { + return arg.Detail && !arg.Detail->ReturnType.empty(); +} +MATCHER_P(ReturnType, D, "") { + return arg.Detail && arg.Detail->ReturnType == D; } MATCHER_P(Doc, D, "") { return arg.Detail && arg.Detail->Documentation == D; } -MATCHER_P(Plain, Text, "") { return arg.CompletionPlainInsertText == Text; } MATCHER_P(Snippet, S, "") { - return arg.CompletionSnippetInsertText == S; + return (arg.Name + arg.CompletionSnippetSuffix).str() == S; } MATCHER_P(QName, Name, "") { return (arg.Scope + arg.Name).str() == Name; } MATCHER_P(DeclURI, P, "") { return arg.CanonicalDeclaration.FileURI == P; } @@ -67,11 +71,94 @@ MATCHER_P(DefRange, Pos, "") { Pos.end.character); } MATCHER_P(Refs, R, "") { return int(arg.References) == R; } +MATCHER_P(ForCodeCompletion, IsIndexedForCodeCompletion, "") { + return arg.IsIndexedForCodeCompletion == IsIndexedForCodeCompletion; +} namespace clang { namespace clangd { namespace { + +class ShouldCollectSymbolTest : public ::testing::Test { +public: + void build(StringRef HeaderCode, StringRef Code = "") { + File.HeaderFilename = HeaderName; + File.Filename = FileName; + File.HeaderCode = HeaderCode; + File.Code = Code; + AST = File.build(); + } + + // build() must have been called. + bool shouldCollect(StringRef Name, bool Qualified = true) { + assert(AST.hasValue()); + return SymbolCollector::shouldCollectSymbol( + Qualified ? findDecl(*AST, Name) : findAnyDecl(*AST, Name), + AST->getASTContext(), SymbolCollector::Options()); + } + +protected: + std::string HeaderName = "f.h"; + std::string FileName = "f.cpp"; + TestTU File; + Optional<ParsedAST> AST; // Initialized after build. +}; + +TEST_F(ShouldCollectSymbolTest, ShouldCollectSymbol) { + build(R"( + namespace nx { + class X{} + void f() { int Local; } + struct { int x } var; + namespace { class InAnonymous {}; } + } + )", + "class InMain {};"); + auto AST = File.build(); + EXPECT_TRUE(shouldCollect("nx")); + EXPECT_TRUE(shouldCollect("nx::X")); + EXPECT_TRUE(shouldCollect("nx::f")); + + EXPECT_FALSE(shouldCollect("InMain")); + EXPECT_FALSE(shouldCollect("Local", /*Qualified=*/false)); + EXPECT_FALSE(shouldCollect("InAnonymous", /*Qualified=*/false)); +} + +TEST_F(ShouldCollectSymbolTest, NoPrivateProtoSymbol) { + HeaderName = "f.proto.h"; + build( + R"(// Generated by the protocol buffer compiler. DO NOT EDIT! + namespace nx { + class Top_Level {}; + class TopLevel {}; + enum Kind { + KIND_OK, + Kind_Not_Ok, + }; + })"); + EXPECT_TRUE(shouldCollect("nx::TopLevel")); + EXPECT_TRUE(shouldCollect("nx::Kind::KIND_OK")); + EXPECT_TRUE(shouldCollect("nx::Kind")); + + EXPECT_FALSE(shouldCollect("nx::Top_Level")); + EXPECT_FALSE(shouldCollect("nx::Kind::Kind_Not_Ok")); +} + +TEST_F(ShouldCollectSymbolTest, DoubleCheckProtoHeaderComment) { + HeaderName = "f.proto.h"; + build(R"( + namespace nx { + class Top_Level {}; + enum Kind { + Kind_Fine + }; + } + )"); + EXPECT_TRUE(shouldCollect("nx::Top_Level")); + EXPECT_TRUE(shouldCollect("nx::Kind_Fine")); +} + class SymbolIndexActionFactory : public tooling::FrontendActionFactory { public: SymbolIndexActionFactory(SymbolCollector::Options COpts, @@ -132,9 +219,13 @@ public: CollectorOpts, PragmaHandler.get()); std::vector<std::string> Args = { - "symbol_collector", "-fsyntax-only", "-xc++", "-std=c++11", - "-include", TestHeaderName, TestFileName}; + "symbol_collector", "-fsyntax-only", "-xc++", + "-std=c++11", "-include", TestHeaderName}; Args.insert(Args.end(), ExtraArgs.begin(), ExtraArgs.end()); + // This allows to override the "-xc++" with something else, i.e. + // -xobjective-c++. + Args.push_back(TestFileName); + tooling::ToolInvocation Invocation( Args, Factory->create(), Files.get(), @@ -163,8 +254,20 @@ protected: TEST_F(SymbolCollectorTest, CollectSymbols) { const std::string Header = R"( class Foo { + Foo() {} + Foo(int a) {} + void f(); + friend void f1(); + friend class Friend; + Foo& operator=(const Foo&); + ~Foo(); + class Nested { void f(); + }; }; + class Friend { + }; + void f1(); inline void f2() {} static const int KInt = 2; @@ -200,23 +303,78 @@ TEST_F(SymbolCollectorTest, CollectSymbols) { runSymbolCollector(Header, /*Main=*/""); EXPECT_THAT(Symbols, UnorderedElementsAreArray( - {QName("Foo"), QName("f1"), QName("f2"), QName("KInt"), - QName("kStr"), QName("foo"), QName("foo::bar"), - QName("foo::int32"), QName("foo::int32_t"), QName("foo::v1"), - QName("foo::bar::v2"), QName("foo::baz")})); + {AllOf(QName("Foo"), ForCodeCompletion(true)), + AllOf(QName("Foo::Foo"), ForCodeCompletion(false)), + AllOf(QName("Foo::Foo"), ForCodeCompletion(false)), + AllOf(QName("Foo::f"), ForCodeCompletion(false)), + AllOf(QName("Foo::~Foo"), ForCodeCompletion(false)), + AllOf(QName("Foo::operator="), ForCodeCompletion(false)), + AllOf(QName("Foo::Nested"), ForCodeCompletion(false)), + AllOf(QName("Foo::Nested::f"), ForCodeCompletion(false)), + + AllOf(QName("Friend"), ForCodeCompletion(true)), + AllOf(QName("f1"), ForCodeCompletion(true)), + AllOf(QName("f2"), ForCodeCompletion(true)), + AllOf(QName("KInt"), ForCodeCompletion(true)), + AllOf(QName("kStr"), ForCodeCompletion(true)), + AllOf(QName("foo"), ForCodeCompletion(true)), + AllOf(QName("foo::bar"), ForCodeCompletion(true)), + AllOf(QName("foo::int32"), ForCodeCompletion(true)), + AllOf(QName("foo::int32_t"), ForCodeCompletion(true)), + AllOf(QName("foo::v1"), ForCodeCompletion(true)), + AllOf(QName("foo::bar::v2"), ForCodeCompletion(true)), + AllOf(QName("foo::baz"), ForCodeCompletion(true))})); } TEST_F(SymbolCollectorTest, Template) { Annotations Header(R"( // Template is indexed, specialization and instantiation is not. - template <class T> struct [[Tmpl]] {T x = 0;}; + template <class T> struct [[Tmpl]] {T $xdecl[[x]] = 0;}; template <> struct Tmpl<int> {}; extern template struct Tmpl<float>; template struct Tmpl<double>; )"); runSymbolCollector(Header.code(), /*Main=*/""); - EXPECT_THAT(Symbols, UnorderedElementsAreArray({AllOf( - QName("Tmpl"), DeclRange(Header.range()))})); + EXPECT_THAT(Symbols, + UnorderedElementsAreArray( + {AllOf(QName("Tmpl"), DeclRange(Header.range())), + AllOf(QName("Tmpl::x"), DeclRange(Header.range("xdecl")))})); +} + +TEST_F(SymbolCollectorTest, ObjCSymbols) { + const std::string Header = R"( + @interface Person + - (void)someMethodName:(void*)name1 lastName:(void*)lName; + @end + + @implementation Person + - (void)someMethodName:(void*)name1 lastName:(void*)lName{ + int foo; + ^(int param){ int bar; }; + } + @end + + @interface Person (MyCategory) + - (void)someMethodName2:(void*)name2; + @end + + @implementation Person (MyCategory) + - (void)someMethodName2:(void*)name2 { + int foo2; + } + @end + + @protocol MyProtocol + - (void)someMethodName3:(void*)name3; + @end + )"; + TestFileName = "test.m"; + runSymbolCollector(Header, /*Main=*/"", {"-fblocks", "-xobjective-c++"}); + EXPECT_THAT(Symbols, + UnorderedElementsAre( + QName("Person"), QName("Person::someMethodName:lastName:"), + QName("MyCategory"), QName("Person::someMethodName2:"), + QName("MyProtocol"), QName("MyProtocol::someMethodName3:"))); } TEST_F(SymbolCollectorTest, Locations) { @@ -298,17 +456,15 @@ TEST_F(SymbolCollectorTest, SymbolRelativeWithFallback) { UnorderedElementsAre(AllOf(QName("Foo"), DeclURI(TestHeaderURI)))); } -#ifndef _WIN32 TEST_F(SymbolCollectorTest, CustomURIScheme) { // Use test URI scheme from URITests.cpp CollectorOpts.URISchemes.insert(CollectorOpts.URISchemes.begin(), "unittest"); - TestHeaderName = testPath("test-root/x.h"); - TestFileName = testPath("test-root/x.cpp"); + TestHeaderName = testPath("x.h"); + TestFileName = testPath("x.cpp"); runSymbolCollector("class Foo {};", /*Main=*/""); - EXPECT_THAT(Symbols, - UnorderedElementsAre(AllOf(QName("Foo"), DeclURI("unittest:x.h")))); + EXPECT_THAT(Symbols, UnorderedElementsAre( + AllOf(QName("Foo"), DeclURI("unittest:///x.h")))); } -#endif TEST_F(SymbolCollectorTest, InvalidURIScheme) { // Use test URI scheme from URITests.cpp @@ -334,7 +490,7 @@ TEST_F(SymbolCollectorTest, IncludeEnums) { Green }; enum class Color2 { - Yellow // ignore + Yellow }; namespace ns { enum { @@ -343,20 +499,26 @@ TEST_F(SymbolCollectorTest, IncludeEnums) { } )"; runSymbolCollector(Header, /*Main=*/""); - EXPECT_THAT(Symbols, UnorderedElementsAre(QName("Red"), QName("Color"), - QName("Green"), QName("Color2"), - QName("ns"), QName("ns::Black"))); + EXPECT_THAT(Symbols, + UnorderedElementsAre( + AllOf(QName("Red"), ForCodeCompletion(true)), + AllOf(QName("Color"), ForCodeCompletion(true)), + AllOf(QName("Green"), ForCodeCompletion(true)), + AllOf(QName("Color2"), ForCodeCompletion(true)), + AllOf(QName("Color2::Yellow"), ForCodeCompletion(false)), + AllOf(QName("ns"), ForCodeCompletion(true)), + AllOf(QName("ns::Black"), ForCodeCompletion(true)))); } -TEST_F(SymbolCollectorTest, IgnoreNamelessSymbols) { +TEST_F(SymbolCollectorTest, NamelessSymbols) { const std::string Header = R"( struct { int a; } Foo; )"; runSymbolCollector(Header, /*Main=*/""); - EXPECT_THAT(Symbols, - UnorderedElementsAre(QName("Foo"))); + EXPECT_THAT(Symbols, UnorderedElementsAre(QName("Foo"), + QName("(anonymous struct)::a"))); } TEST_F(SymbolCollectorTest, SymbolFormedFromMacro) { @@ -417,7 +579,7 @@ TEST_F(SymbolCollectorTest, IgnoreSymbolsInMainFile) { UnorderedElementsAre(QName("Foo"), QName("f1"), QName("f2"))); } -TEST_F(SymbolCollectorTest, IgnoreClassMembers) { +TEST_F(SymbolCollectorTest, ClassMembers) { const std::string Header = R"( class Foo { void f() {} @@ -432,7 +594,10 @@ TEST_F(SymbolCollectorTest, IgnoreClassMembers) { void Foo::ssf() {} )"; runSymbolCollector(Header, Main); - EXPECT_THAT(Symbols, UnorderedElementsAre(QName("Foo"))); + EXPECT_THAT(Symbols, + UnorderedElementsAre(QName("Foo"), QName("Foo::f"), + QName("Foo::g"), QName("Foo::sf"), + QName("Foo::ssf"), QName("Foo::x"))); } TEST_F(SymbolCollectorTest, Scopes) { @@ -494,10 +659,10 @@ TEST_F(SymbolCollectorTest, SymbolWithDocumentation) { Symbols, UnorderedElementsAre( QName("nx"), AllOf(QName("nx::ff"), Labeled("ff(int x, double y)"), - Detail("int"), Doc("Foo comment.")))); + ReturnType("int"), Doc("Foo comment.")))); } -TEST_F(SymbolCollectorTest, PlainAndSnippet) { +TEST_F(SymbolCollectorTest, Snippet) { const std::string Header = R"( namespace nx { void f() {} @@ -505,13 +670,12 @@ TEST_F(SymbolCollectorTest, PlainAndSnippet) { } )"; runSymbolCollector(Header, /*Main=*/""); - EXPECT_THAT( - Symbols, - UnorderedElementsAre( - QName("nx"), - AllOf(QName("nx::f"), Labeled("f()"), Plain("f"), Snippet("f()")), - AllOf(QName("nx::ff"), Labeled("ff(int x, double y)"), Plain("ff"), - Snippet("ff(${1:int x}, ${2:double y})")))); + EXPECT_THAT(Symbols, + UnorderedElementsAre( + QName("nx"), + AllOf(QName("nx::f"), Labeled("f()"), Snippet("f()")), + AllOf(QName("nx::ff"), Labeled("ff(int x, double y)"), + Snippet("ff(${1:int x}, ${2:double y})")))); } TEST_F(SymbolCollectorTest, YAMLConversions) { @@ -531,12 +695,10 @@ CanonicalDeclaration: End: Line: 1 Column: 1 -CompletionLabel: 'Foo1-label' -CompletionFilterText: 'filter' -CompletionPlainInsertText: 'plain' +IsIndexedForCodeCompletion: true Detail: Documentation: 'Foo doc' - CompletionDetail: 'int' + ReturnType: 'int' ... )"; const std::string YAML2 = R"( @@ -555,23 +717,24 @@ CanonicalDeclaration: End: Line: 1 Column: 1 -CompletionLabel: 'Foo2-label' -CompletionFilterText: 'filter' -CompletionPlainInsertText: 'plain' -CompletionSnippetInsertText: 'snippet' +IsIndexedForCodeCompletion: false +Signature: '-sig' +CompletionSnippetSuffix: '-snippet' ... )"; - auto Symbols1 = SymbolsFromYAML(YAML1); + auto Symbols1 = symbolsFromYAML(YAML1); EXPECT_THAT(Symbols1, - UnorderedElementsAre(AllOf( - QName("clang::Foo1"), Labeled("Foo1-label"), Doc("Foo doc"), - Detail("int"), DeclURI("file:///path/foo.h")))); - auto Symbols2 = SymbolsFromYAML(YAML2); + UnorderedElementsAre(AllOf(QName("clang::Foo1"), Labeled("Foo1"), + Doc("Foo doc"), ReturnType("int"), + DeclURI("file:///path/foo.h"), + ForCodeCompletion(true)))); + auto Symbols2 = symbolsFromYAML(YAML2); EXPECT_THAT(Symbols2, UnorderedElementsAre(AllOf( - QName("clang::Foo2"), Labeled("Foo2-label"), - Not(HasDetail()), DeclURI("file:///path/bar.h")))); + QName("clang::Foo2"), Labeled("Foo2-sig"), + Not(HasReturnType()), DeclURI("file:///path/bar.h"), + ForCodeCompletion(false)))); std::string ConcatenatedYAML; { @@ -579,7 +742,7 @@ CompletionSnippetInsertText: 'snippet' SymbolsToYAML(Symbols1, OS); SymbolsToYAML(Symbols2, OS); } - auto ConcatenatedSymbols = SymbolsFromYAML(ConcatenatedYAML); + auto ConcatenatedSymbols = symbolsFromYAML(ConcatenatedYAML); EXPECT_THAT(ConcatenatedSymbols, UnorderedElementsAre(QName("clang::Foo1"), QName("clang::Foo2"))); @@ -741,23 +904,27 @@ TEST_F(SymbolCollectorTest, AvoidUsingFwdDeclsAsCanonicalDecls) { // Canonical declarations. class $cdecl[[C]] {}; struct $sdecl[[S]] {}; - union $udecl[[U]] {int x; bool y;}; + union $udecl[[U]] {int $xdecl[[x]]; bool $ydecl[[y]];}; )"); runSymbolCollector(Header.code(), /*Main=*/""); - EXPECT_THAT(Symbols, - UnorderedElementsAre( - AllOf(QName("C"), DeclURI(TestHeaderURI), - DeclRange(Header.range("cdecl")), - IncludeHeader(TestHeaderURI), DefURI(TestHeaderURI), - DefRange(Header.range("cdecl"))), - AllOf(QName("S"), DeclURI(TestHeaderURI), - DeclRange(Header.range("sdecl")), - IncludeHeader(TestHeaderURI), DefURI(TestHeaderURI), - DefRange(Header.range("sdecl"))), - AllOf(QName("U"), DeclURI(TestHeaderURI), - DeclRange(Header.range("udecl")), - IncludeHeader(TestHeaderURI), DefURI(TestHeaderURI), - DefRange(Header.range("udecl"))))); + EXPECT_THAT( + Symbols, + UnorderedElementsAre( + AllOf(QName("C"), DeclURI(TestHeaderURI), + DeclRange(Header.range("cdecl")), IncludeHeader(TestHeaderURI), + DefURI(TestHeaderURI), DefRange(Header.range("cdecl"))), + AllOf(QName("S"), DeclURI(TestHeaderURI), + DeclRange(Header.range("sdecl")), IncludeHeader(TestHeaderURI), + DefURI(TestHeaderURI), DefRange(Header.range("sdecl"))), + AllOf(QName("U"), DeclURI(TestHeaderURI), + DeclRange(Header.range("udecl")), IncludeHeader(TestHeaderURI), + DefURI(TestHeaderURI), DefRange(Header.range("udecl"))), + AllOf(QName("U::x"), DeclURI(TestHeaderURI), + DeclRange(Header.range("xdecl")), DefURI(TestHeaderURI), + DefRange(Header.range("xdecl"))), + AllOf(QName("U::y"), DeclURI(TestHeaderURI), + DeclRange(Header.range("ydecl")), DefURI(TestHeaderURI), + DefRange(Header.range("ydecl"))))); } TEST_F(SymbolCollectorTest, ClassForwardDeclarationIsCanonical) { @@ -776,40 +943,79 @@ TEST_F(SymbolCollectorTest, UTF16Character) { AllOf(QName("pörk"), DeclRange(Header.range())))); } -TEST_F(SymbolCollectorTest, FilterPrivateProtoSymbols) { - TestHeaderName = testPath("x.proto.h"); - const std::string Header = - R"(// Generated by the protocol buffer compiler. DO NOT EDIT! - namespace nx { - class Top_Level {}; - class TopLevel {}; - enum Kind { - KIND_OK, - Kind_Not_Ok, - }; - bool operator<(const TopLevel &, const TopLevel &); - })"; - runSymbolCollector(Header, /*Main=*/""); +TEST_F(SymbolCollectorTest, DoNotIndexSymbolsInFriendDecl) { + Annotations Header(R"( + namespace nx { + class $z[[Z]] {}; + class X { + friend class Y; + friend class Z; + friend void foo(); + friend void $bar[[bar]]() {} + }; + class $y[[Y]] {}; + void $foo[[foo]](); + } + )"); + runSymbolCollector(Header.code(), /*Main=*/""); + EXPECT_THAT(Symbols, - UnorderedElementsAre(QName("nx"), QName("nx::TopLevel"), - QName("nx::Kind"), QName("nx::KIND_OK"), - QName("nx::operator<"))); + UnorderedElementsAre( + QName("nx"), QName("nx::X"), + AllOf(QName("nx::Y"), DeclRange(Header.range("y"))), + AllOf(QName("nx::Z"), DeclRange(Header.range("z"))), + AllOf(QName("nx::foo"), DeclRange(Header.range("foo"))), + AllOf(QName("nx::bar"), DeclRange(Header.range("bar"))))); } -TEST_F(SymbolCollectorTest, DoubleCheckProtoHeaderComment) { - TestHeaderName = testPath("x.proto.h"); +TEST_F(SymbolCollectorTest, ReferencesInFriendDecl) { const std::string Header = R"( - namespace nx { - class Top_Level {}; - enum Kind { - Kind_Fine + class X; + class Y; + )"; + const std::string Main = R"( + class C { + friend ::X; + friend class Y; }; - } )"; - runSymbolCollector(Header, /*Main=*/""); - EXPECT_THAT(Symbols, - UnorderedElementsAre(QName("nx"), QName("nx::Top_Level"), - QName("nx::Kind"), QName("nx::Kind_Fine"))); + CollectorOpts.CountReferences = true; + runSymbolCollector(Header, Main); + EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(QName("X"), Refs(1)), + AllOf(QName("Y"), Refs(1)))); +} + +TEST_F(SymbolCollectorTest, Origin) { + CollectorOpts.Origin = SymbolOrigin::Static; + runSymbolCollector("class Foo {};", /*Main=*/""); + EXPECT_THAT(Symbols, UnorderedElementsAre( + Field(&Symbol::Origin, SymbolOrigin::Static))); +} + +TEST_F(SymbolCollectorTest, CollectMacros) { + CollectorOpts.CollectIncludePath = true; + Annotations Header(R"( + #define X 1 + #define $mac[[MAC]](x) int x + #define $used[[USED]](y) float y; + + MAC(p); + )"); + const std::string Main = R"( + #define MAIN 1 // not indexed + USED(t); + )"; + CollectorOpts.CountReferences = true; + CollectorOpts.CollectMacro = true; + runSymbolCollector(Header.code(), Main); + EXPECT_THAT( + Symbols, + UnorderedElementsAre( + QName("p"), + AllOf(QName("X"), DeclURI(TestHeaderURI), + IncludeHeader(TestHeaderURI)), + AllOf(Labeled("MAC(x)"), Refs(0), DeclRange(Header.range("mac"))), + AllOf(Labeled("USED(y)"), Refs(1), DeclRange(Header.range("used"))))); } } // namespace |