diff options
Diffstat (limited to 'clang-tools-extra/clangd/unittests/HeaderSourceSwitchTests.cpp')
-rw-r--r-- | clang-tools-extra/clangd/unittests/HeaderSourceSwitchTests.cpp | 169 |
1 files changed, 169 insertions, 0 deletions
diff --git a/clang-tools-extra/clangd/unittests/HeaderSourceSwitchTests.cpp b/clang-tools-extra/clangd/unittests/HeaderSourceSwitchTests.cpp index 6835fdfd0f4..1ca543d5764 100644 --- a/clang-tools-extra/clangd/unittests/HeaderSourceSwitchTests.cpp +++ b/clang-tools-extra/clangd/unittests/HeaderSourceSwitchTests.cpp @@ -10,6 +10,7 @@ #include "TestFS.h" #include "TestTU.h" +#include "index/MemIndex.h" #include "gmock/gmock.h" #include "gtest/gtest.h" @@ -71,6 +72,174 @@ TEST(HeaderSourceSwitchTest, FileHeuristic) { EXPECT_FALSE(PathResult.hasValue()); } +MATCHER_P(DeclNamed, Name, "") { + if (const NamedDecl *ND = dyn_cast<NamedDecl>(arg)) + if (ND->getQualifiedNameAsString() == Name) + return true; + return false; +} + +TEST(HeaderSourceSwitchTest, GetLocalDecls) { + TestTU TU; + TU.HeaderCode = R"cpp( + void HeaderOnly(); + )cpp"; + TU.Code = R"cpp( + void MainF1(); + class Foo {}; + namespace ns { + class Foo { + void method(); + int field; + }; + } // namespace ns + + // Non-indexable symbols + namespace { + void Ignore1() {} + } + + )cpp"; + + auto AST = TU.build(); + EXPECT_THAT(getIndexableLocalDecls(AST), + testing::UnorderedElementsAre( + DeclNamed("MainF1"), DeclNamed("Foo"), DeclNamed("ns::Foo"), + DeclNamed("ns::Foo::method"), DeclNamed("ns::Foo::field"))); +} + +TEST(HeaderSourceSwitchTest, FromHeaderToSource) { + // build a proper index, which contains symbols: + // A_Sym1, declared in TestTU.h, defined in a.cpp + // B_Sym[1-2], declared in TestTU.h, defined in b.cpp + SymbolSlab::Builder AllSymbols; + TestTU Testing; + Testing.HeaderFilename = "TestTU.h"; + Testing.HeaderCode = "void A_Sym1();"; + Testing.Filename = "a.cpp"; + Testing.Code = "void A_Sym1() {};"; + for (auto &Sym : Testing.headerSymbols()) + AllSymbols.insert(Sym); + + Testing.HeaderCode = R"cpp( + void B_Sym1(); + void B_Sym2(); + )cpp"; + Testing.Filename = "b.cpp"; + Testing.Code = R"cpp( + void B_Sym1() {} + void B_Sym2() {} + )cpp"; + for (auto &Sym : Testing.headerSymbols()) + AllSymbols.insert(Sym); + auto Index = MemIndex::build(std::move(AllSymbols).build(), {}, {}); + + // Test for swtich from .h header to .cc source + struct { + llvm::StringRef HeaderCode; + llvm::Optional<std::string> ExpectedSource; + } TestCases[] = { + {"// empty, no header found", llvm::None}, + {R"cpp( + // no definition found in the index. + void NonDefinition(); + )cpp", + llvm::None}, + {R"cpp( + void A_Sym1(); + )cpp", + testPath("a.cpp")}, + {R"cpp( + // b.cpp wins. + void A_Sym1(); + void B_Sym1(); + void B_Sym2(); + )cpp", + testPath("b.cpp")}, + {R"cpp( + // a.cpp and b.cpp have same scope, but a.cpp because "a.cpp" < "b.cpp". + void A_Sym1(); + void B_Sym1(); + )cpp", + testPath("a.cpp")}, + }; + for (const auto &Case : TestCases) { + TestTU TU = TestTU::withCode(Case.HeaderCode); + TU.Filename = "TestTU.h"; + TU.ExtraArgs.push_back("-xc++-header"); // inform clang this is a header. + auto HeaderAST = TU.build(); + EXPECT_EQ(Case.ExpectedSource, + getCorrespondingHeaderOrSource(testPath(TU.Filename), HeaderAST, + Index.get())); + } +} + +TEST(HeaderSourceSwitchTest, FromSourceToHeader) { + // build a proper index, which contains symbols: + // A_Sym1, declared in a.h, defined in TestTU.cpp + // B_Sym[1-2], declared in b.h, defined in TestTU.cpp + TestTU TUForIndex = TestTU::withCode(R"cpp( + #include "a.h" + #include "b.h" + + void A_Sym1() {} + + void B_Sym1() {} + void B_Sym2() {} + )cpp"); + TUForIndex.AdditionalFiles["a.h"] = R"cpp( + void A_Sym1(); + )cpp"; + TUForIndex.AdditionalFiles["b.h"] = R"cpp( + void B_Sym1(); + void B_Sym2(); + )cpp"; + TUForIndex.Filename = "TestTU.cpp"; + auto Index = TUForIndex.index(); + + // Test for switching from .cc source file to .h header. + struct { + llvm::StringRef SourceCode; + llvm::Optional<std::string> ExpectedResult; + } TestCases[] = { + {"// empty, no header found", llvm::None}, + {R"cpp( + // symbol not in index, no header found + void Local() {} + )cpp", + llvm::None}, + + {R"cpp( + // a.h wins. + void A_Sym1() {} + )cpp", + testPath("a.h")}, + + {R"cpp( + // b.h wins. + void A_Sym1() {} + void B_Sym1() {} + void B_Sym2() {} + )cpp", + testPath("b.h")}, + + {R"cpp( + // a.h and b.h have same scope, but a.h wins because "a.h" < "b.h". + void A_Sym1() {} + void B_Sym1() {} + )cpp", + testPath("a.h")}, + }; + for (const auto &Case : TestCases) { + TestTU TU = TestTU::withCode(Case.SourceCode); + TU.Filename = "Test.cpp"; + auto AST = TU.build(); + EXPECT_EQ(Case.ExpectedResult, + getCorrespondingHeaderOrSource(testPath(TU.Filename), AST, + Index.get())); + } +} + } // namespace } // namespace clangd } // namespace clang |