//===-- StdLibTests.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 "ClangdServer.h" #include "CodeComplete.h" #include "Compiler.h" #include "Config.h" #include "SyncAPI.h" #include "TestFS.h" #include "index/StdLib.h" #include "clang/Basic/LangOptions.h" #include "clang/Basic/SourceManager.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include using namespace testing; namespace clang { namespace clangd { namespace { // Check the generated header sources contains usual standard library headers. TEST(StdLibTests, getStdlibUmbrellaHeader) { LangOptions LO; LO.CPlusPlus = true; auto CXX = getStdlibUmbrellaHeader(LO).str(); EXPECT_THAT(CXX, HasSubstr("#include ")); EXPECT_THAT(CXX, HasSubstr("#include ")); EXPECT_THAT(CXX, Not(HasSubstr("#include "))); LO.CPlusPlus = false; auto C = getStdlibUmbrellaHeader(LO).str(); EXPECT_THAT(C, Not(HasSubstr("#include "))); EXPECT_THAT(C, Not(HasSubstr("#include "))); EXPECT_THAT(C, HasSubstr("#include ")); } MATCHER_P(Named, Name, "") { return arg.Name == Name; } // Build an index, and check if it contains the right symbols. TEST(StdLibTests, indexStandardLibrary) { MockFS FS; FS.Files["std/foo.h"] = R"cpp( #include #if __cplusplus >= 201703L int foo17(); #elif __cplusplus >= 201402L int foo14(); #else bool foo98(); #endif )cpp"; FS.Files["nonstd/platform_stuff.h"] = "int magic = 42;"; ParseInputs OriginalInputs; OriginalInputs.TFS = &FS; OriginalInputs.CompileCommand.Filename = testPath("main.cc"); OriginalInputs.CompileCommand.CommandLine = {"clang++", testPath("main.cc"), "-isystemstd/", "-isystemnonstd/", "-std=c++14"}; OriginalInputs.CompileCommand.Directory = testRoot(); IgnoreDiagnostics Diags; auto CI = buildCompilerInvocation(OriginalInputs, Diags); ASSERT_TRUE(CI); StdLibLocation Loc; Loc.Paths.push_back(testPath("std/")); auto Symbols = indexStandardLibrary("#include ", std::move(CI), Loc, FS); EXPECT_THAT(Symbols, ElementsAre(Named("foo14"))); } TEST(StdLibTests, StdLibSet) { StdLibSet Set; MockFS FS; FS.Files["std/_"] = ""; FS.Files["libc/_"] = ""; auto Add = [&](const LangOptions &LO, std::vector SearchPath) { SourceManagerForFile SM("scratch", ""); SM.get().getFileManager().setVirtualFileSystem(FS.view(std::nullopt)); HeaderSearch HS(/*HSOpts=*/nullptr, SM.get(), SM.get().getDiagnostics(), LO, /*Target=*/nullptr); for (auto P : SearchPath) HS.AddSearchPath( DirectoryLookup( cantFail(SM.get().getFileManager().getDirectoryRef(testPath(P))), SrcMgr::C_System, /*isFramework=*/false), true); return Set.add(LO, HS); }; Config Cfg; Cfg.Index.StandardLibrary = false; WithContextValue Disabled(Config::Key, std::move(Cfg)); LangOptions LO; LO.CPlusPlus = true; EXPECT_FALSE(Add(LO, {"std"})) << "Disabled in config"; Cfg = Config(); Cfg.Index.StandardLibrary = true; WithContextValue Enabled(Config::Key, std::move(Cfg)); EXPECT_FALSE(Add(LO, {"std"})) << "No found"; FS.Files["std/vector"] = "class vector;"; EXPECT_TRUE(Add(LO, {"std"})) << "Indexing as C++98"; EXPECT_FALSE(Add(LO, {"std"})) << "Don't reindex"; LO.CPlusPlus11 = true; EXPECT_TRUE(Add(LO, {"std"})) << "Indexing as C++11"; LO.CPlusPlus = false; EXPECT_FALSE(Add(LO, {"libc"})) << "No "; FS.Files["libc/stdio.h"] = true; EXPECT_TRUE(Add(LO, {"libc"})) << "Indexing as C"; } MATCHER_P(StdlibSymbol, Name, "") { return arg.Name == Name && arg.Includes.size() == 1 && llvm::StringRef(arg.Includes.front().Header).starts_with("<"); } TEST(StdLibTests, EndToEnd) { Config Cfg; Cfg.Index.StandardLibrary = true; WithContextValue Enabled(Config::Key, std::move(Cfg)); MockFS FS; FS.Files["stdlib/vector"] = "namespace std { template class vector; }"; FS.Files["stdlib/list"] = " namespace std { template class list; }"; MockCompilationDatabase CDB; CDB.ExtraClangFlags.push_back("-isystem" + testPath("stdlib")); ClangdServer::Options Opts = ClangdServer::optsForTest(); Opts.BuildDynamicSymbolIndex = true; // also used for stdlib index ClangdServer Server(CDB, FS, Opts); Annotations A("std::^"); Server.addDocument(testPath("foo.cc"), A.code()); ASSERT_TRUE(Server.blockUntilIdleForTest()); clangd::CodeCompleteOptions CCOpts; auto Completions = cantFail(runCodeComplete(Server, testPath("foo.cc"), A.point(), CCOpts)); EXPECT_THAT( Completions.Completions, UnorderedElementsAre(StdlibSymbol("list"), StdlibSymbol("vector"))); } } // namespace } // namespace clangd } // namespace clang