212 lines · cpp
1//===-- StdLibTests.cpp -----------------------------------------*- C++ -*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "Annotations.h"10#include "ClangdServer.h"11#include "CodeComplete.h"12#include "Compiler.h"13#include "Config.h"14#include "SyncAPI.h"15#include "TestFS.h"16#include "index/Index.h"17#include "index/StdLib.h"18#include "clang/Basic/LangOptions.h"19#include "clang/Basic/SourceManager.h"20#include "llvm/Testing/Support/Error.h"21#include "gmock/gmock.h"22#include "gtest/gtest.h"23#include <memory>24 25using namespace testing;26 27namespace clang {28namespace clangd {29namespace {30 31// Check the generated header sources contains usual standard library headers.32TEST(StdLibTests, getStdlibUmbrellaHeader) {33 LangOptions LO;34 LO.CPlusPlus = true;35 36 auto CXX = getStdlibUmbrellaHeader(LO).str();37 EXPECT_THAT(CXX, HasSubstr("#include <string>"));38 EXPECT_THAT(CXX, HasSubstr("#include <cstdio>"));39 EXPECT_THAT(CXX, Not(HasSubstr("#include <ios646.h>")));40 41 LO.CPlusPlus = false;42 auto C = getStdlibUmbrellaHeader(LO).str();43 EXPECT_THAT(C, Not(HasSubstr("#include <string>")));44 EXPECT_THAT(C, Not(HasSubstr("#include <cstdio>")));45 EXPECT_THAT(C, HasSubstr("#include <stdio.h>"));46}47 48MATCHER_P(Named, Name, "") { return arg.Name == Name; }49 50// Build an index, and check if it contains the right symbols.51TEST(StdLibTests, indexStandardLibrary) {52 MockFS FS;53 FS.Files["std/foo.h"] = R"cpp(54 #include <platform_stuff.h>55 #if __cplusplus >= 201703L56 int foo17();57 #elif __cplusplus >= 201402L58 int foo14();59 #else60 bool foo98();61 #endif62 )cpp";63 FS.Files["nonstd/platform_stuff.h"] = "int magic = 42;";64 65 ParseInputs OriginalInputs;66 OriginalInputs.TFS = &FS;67 OriginalInputs.CompileCommand.Filename = testPath("main.cc");68 OriginalInputs.CompileCommand.CommandLine = {"clang++", testPath("main.cc"),69 "-isystemstd/",70 "-isystemnonstd/", "-std=c++14"};71 OriginalInputs.CompileCommand.Directory = testRoot();72 IgnoreDiagnostics Diags;73 auto CI = buildCompilerInvocation(OriginalInputs, Diags);74 ASSERT_TRUE(CI);75 76 StdLibLocation Loc;77 Loc.Paths.push_back(testPath("std/"));78 79 auto Symbols =80 indexStandardLibrary("#include <foo.h>", std::move(CI), Loc, FS);81 EXPECT_THAT(Symbols, ElementsAre(Named("foo14")));82}83 84TEST(StdLibTests, StdLibSet) {85 StdLibSet Set;86 MockFS FS;87 FS.Files["std/_"] = "";88 FS.Files["libc/_"] = "";89 90 HeaderSearchOptions HSOpts;91 auto Add = [&](const LangOptions &LO,92 std::vector<llvm::StringRef> SearchPath) {93 SourceManagerForFile SM("scratch", "");94 SM.get().getFileManager().setVirtualFileSystem(FS.view(std::nullopt));95 HeaderSearch HS(HSOpts, SM.get(), SM.get().getDiagnostics(), LO,96 /*Target=*/nullptr);97 for (auto P : SearchPath)98 HS.AddSearchPath(99 DirectoryLookup(100 cantFail(SM.get().getFileManager().getDirectoryRef(testPath(P))),101 SrcMgr::C_System, /*isFramework=*/false),102 true);103 return Set.add(LO, HS);104 };105 106 Config Cfg;107 Cfg.Index.StandardLibrary = false;108 WithContextValue Disabled(Config::Key, std::move(Cfg));109 110 LangOptions LO;111 LO.CPlusPlus = true;112 EXPECT_FALSE(Add(LO, {"std"})) << "Disabled in config";113 114 Cfg = Config();115 Cfg.Index.StandardLibrary = true;116 WithContextValue Enabled(Config::Key, std::move(Cfg));117 118 EXPECT_FALSE(Add(LO, {"std"})) << "No <vector> found";119 FS.Files["std/vector"] = "class vector;";120 EXPECT_TRUE(Add(LO, {"std"})) << "Indexing as C++98";121 EXPECT_FALSE(Add(LO, {"std"})) << "Don't reindex";122 LO.CPlusPlus11 = true;123 EXPECT_TRUE(Add(LO, {"std"})) << "Indexing as C++11";124 LO.CPlusPlus = false;125 EXPECT_FALSE(Add(LO, {"libc"})) << "No <stdio.h>";126 FS.Files["libc/stdio.h"] = true;127 EXPECT_TRUE(Add(LO, {"libc"})) << "Indexing as C";128}129 130MATCHER_P(StdlibSymbol, Name, "") {131 return arg.Name == Name && arg.Includes.size() == 1 &&132 llvm::StringRef(arg.Includes.front().Header).starts_with("<");133}134 135TEST(StdLibTests, EndToEnd) {136 Config Cfg;137 Cfg.Index.StandardLibrary = true;138 WithContextValue Enabled(Config::Key, std::move(Cfg));139 140 MockFS FS;141 FS.Files["stdlib/vector"] =142 "namespace std { template <class> class vector; }";143 FS.Files["stdlib/list"] =144 " namespace std { template <typename T> class list; }";145 MockCompilationDatabase CDB;146 CDB.ExtraClangFlags.push_back("-isystem" + testPath("stdlib"));147 ClangdServer::Options Opts = ClangdServer::optsForTest();148 Opts.BuildDynamicSymbolIndex = true; // also used for stdlib index149 ClangdServer Server(CDB, FS, Opts);150 151 Annotations A("std::^");152 153 Server.addDocument(testPath("foo.cc"), A.code());154 ASSERT_TRUE(Server.blockUntilIdleForTest());155 clangd::CodeCompleteOptions CCOpts;156 auto Completions =157 cantFail(runCodeComplete(Server, testPath("foo.cc"), A.point(), CCOpts));158 EXPECT_THAT(159 Completions.Completions,160 UnorderedElementsAre(StdlibSymbol("list"), StdlibSymbol("vector")));161}162 163TEST(StdLibTests, StdLibDocComments) {164 Config Cfg;165 Cfg.Index.StandardLibrary = true;166 WithContextValue Enabled(Config::Key, std::move(Cfg));167 168 MockFS FS;169 FS.Files["stdlib/vector"] = R"cpp(170 namespace std {171 struct vector {172 /**doc comment*/173 struct inner {};174 };175 }176 )cpp";177 MockCompilationDatabase CDB;178 CDB.ExtraClangFlags.push_back("-isystem" + testPath("stdlib"));179 ClangdServer::Options Opts = ClangdServer::optsForTest();180 Opts.BuildDynamicSymbolIndex = true; // also used for stdlib index181 ClangdServer Server(CDB, FS, Opts);182 183 // Open an empty file. <vector> will not be part of the preamble index,184 // but it will be part of the stdlib index.185 Server.addDocument(testPath("foo.cc"), "");186 187 // Wait for the stdlib index to be built.188 ASSERT_TRUE(Server.blockUntilIdleForTest());189 190 // Check that the index contains the doc comment.191 SymbolSlab Result;192 EXPECT_THAT_ERROR(runCustomAction(Server, testPath("foo.cc"),193 [&](InputsAndAST Inputs) {194 FuzzyFindRequest Req;195 Req.Query = "inner";196 Req.Scopes = {"std::vector::"};197 SymbolSlab::Builder Builder;198 Inputs.Inputs.Index->fuzzyFind(199 Req, [&](const Symbol &Sym) {200 Builder.insert(Sym);201 });202 Result = std::move(Builder).build();203 }),204 llvm::Succeeded());205 ASSERT_EQ(Result.size(), 1u);206 EXPECT_EQ(Result.begin()->Documentation, "doc comment");207}208 209} // namespace210} // namespace clangd211} // namespace clang212