|  | //===-- 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 <memory> | 
|  |  | 
|  | 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 <string>")); | 
|  | EXPECT_THAT(CXX, HasSubstr("#include <cstdio>")); | 
|  | EXPECT_THAT(CXX, Not(HasSubstr("#include <ios646.h>"))); | 
|  |  | 
|  | LO.CPlusPlus = false; | 
|  | auto C = getStdlibUmbrellaHeader(LO).str(); | 
|  | EXPECT_THAT(C, Not(HasSubstr("#include <string>"))); | 
|  | EXPECT_THAT(C, Not(HasSubstr("#include <cstdio>"))); | 
|  | EXPECT_THAT(C, HasSubstr("#include <stdio.h>")); | 
|  | } | 
|  |  | 
|  | 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 <platform_stuff.h> | 
|  | #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 <foo.h>", 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<llvm::StringRef> 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 <vector> 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 <stdio.h>"; | 
|  | 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> class vector; }"; | 
|  | FS.Files["stdlib/list"] = | 
|  | " namespace std { template <typename T> 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 |