xref: /llvm-project/clang-tools-extra/clangd/unittests/StdLibTests.cpp (revision d5953e3e3092f7142a07aa012fc9665ede09e53b)
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-exception
6 //
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/StdLib.h"
17 #include "clang/Basic/LangOptions.h"
18 #include "clang/Basic/SourceManager.h"
19 #include "gmock/gmock.h"
20 #include "gtest/gtest.h"
21 #include <memory>
22 
23 using namespace testing;
24 
25 namespace clang {
26 namespace clangd {
27 namespace {
28 
29 // Check the generated header sources contains usual standard library headers.
TEST(StdLibTests,getStdlibUmbrellaHeader)30 TEST(StdLibTests, getStdlibUmbrellaHeader) {
31   LangOptions LO;
32   LO.CPlusPlus = true;
33 
34   auto CXX = getStdlibUmbrellaHeader(LO).str();
35   EXPECT_THAT(CXX, HasSubstr("#include <string>"));
36   EXPECT_THAT(CXX, HasSubstr("#include <cstdio>"));
37   EXPECT_THAT(CXX, Not(HasSubstr("#include <ios646.h>")));
38 
39   LO.CPlusPlus = false;
40   auto C = getStdlibUmbrellaHeader(LO).str();
41   EXPECT_THAT(C, Not(HasSubstr("#include <string>")));
42   EXPECT_THAT(C, Not(HasSubstr("#include <cstdio>")));
43   EXPECT_THAT(C, HasSubstr("#include <stdio.h>"));
44 }
45 
46 MATCHER_P(Named, Name, "") { return arg.Name == Name; }
47 
48 // Build an index, and check if it contains the right symbols.
TEST(StdLibTests,indexStandardLibrary)49 TEST(StdLibTests, indexStandardLibrary) {
50   MockFS FS;
51   FS.Files["std/foo.h"] = R"cpp(
52   #include <platform_stuff.h>
53   #if __cplusplus >= 201703L
54     int foo17();
55   #elif __cplusplus >= 201402L
56     int foo14();
57   #else
58     bool foo98();
59   #endif
60   )cpp";
61   FS.Files["nonstd/platform_stuff.h"] = "int magic = 42;";
62 
63   ParseInputs OriginalInputs;
64   OriginalInputs.TFS = &FS;
65   OriginalInputs.CompileCommand.Filename = testPath("main.cc");
66   OriginalInputs.CompileCommand.CommandLine = {"clang++", testPath("main.cc"),
67                                                "-isystemstd/",
68                                                "-isystemnonstd/", "-std=c++14"};
69   OriginalInputs.CompileCommand.Directory = testRoot();
70   IgnoreDiagnostics Diags;
71   auto CI = buildCompilerInvocation(OriginalInputs, Diags);
72   ASSERT_TRUE(CI);
73 
74   StdLibLocation Loc;
75   Loc.Paths.push_back(testPath("std/"));
76 
77   auto Symbols =
78       indexStandardLibrary("#include <foo.h>", std::move(CI), Loc, FS);
79   EXPECT_THAT(Symbols, ElementsAre(Named("foo14")));
80 }
81 
TEST(StdLibTests,StdLibSet)82 TEST(StdLibTests, StdLibSet) {
83   StdLibSet Set;
84   MockFS FS;
85   FS.Files["std/_"] = "";
86   FS.Files["libc/_"] = "";
87 
88   auto Add = [&](const LangOptions &LO,
89                  std::vector<llvm::StringRef> SearchPath) {
90     SourceManagerForFile SM("scratch", "");
91     SM.get().getFileManager().setVirtualFileSystem(FS.view(std::nullopt));
92     HeaderSearch HS(/*HSOpts=*/nullptr, SM.get(), SM.get().getDiagnostics(), LO,
93                     /*Target=*/nullptr);
94     for (auto P : SearchPath)
95       HS.AddSearchPath(
96           DirectoryLookup(
97               cantFail(SM.get().getFileManager().getDirectoryRef(testPath(P))),
98               SrcMgr::C_System, /*isFramework=*/false),
99           true);
100     return Set.add(LO, HS);
101   };
102 
103   Config Cfg;
104   Cfg.Index.StandardLibrary = false;
105   WithContextValue Disabled(Config::Key, std::move(Cfg));
106 
107   LangOptions LO;
108   LO.CPlusPlus = true;
109   EXPECT_FALSE(Add(LO, {"std"})) << "Disabled in config";
110 
111   Cfg = Config();
112   Cfg.Index.StandardLibrary = true;
113   WithContextValue Enabled(Config::Key, std::move(Cfg));
114 
115   EXPECT_FALSE(Add(LO, {"std"})) << "No <vector> found";
116   FS.Files["std/vector"] = "class vector;";
117   EXPECT_TRUE(Add(LO, {"std"})) << "Indexing as C++98";
118   EXPECT_FALSE(Add(LO, {"std"})) << "Don't reindex";
119   LO.CPlusPlus11 = true;
120   EXPECT_TRUE(Add(LO, {"std"})) << "Indexing as C++11";
121   LO.CPlusPlus = false;
122   EXPECT_FALSE(Add(LO, {"libc"})) << "No <stdio.h>";
123   FS.Files["libc/stdio.h"] = true;
124   EXPECT_TRUE(Add(LO, {"libc"})) << "Indexing as C";
125 }
126 
127 MATCHER_P(StdlibSymbol, Name, "") {
128   return arg.Name == Name && arg.Includes.size() == 1 &&
129          llvm::StringRef(arg.Includes.front().Header).starts_with("<");
130 }
131 
TEST(StdLibTests,EndToEnd)132 TEST(StdLibTests, EndToEnd) {
133   Config Cfg;
134   Cfg.Index.StandardLibrary = true;
135   WithContextValue Enabled(Config::Key, std::move(Cfg));
136 
137   MockFS FS;
138   FS.Files["stdlib/vector"] =
139       "namespace std { template <class> class vector; }";
140   FS.Files["stdlib/list"] =
141       " namespace std { template <typename T> class list; }";
142   MockCompilationDatabase CDB;
143   CDB.ExtraClangFlags.push_back("-isystem" + testPath("stdlib"));
144   ClangdServer::Options Opts = ClangdServer::optsForTest();
145   Opts.BuildDynamicSymbolIndex = true; // also used for stdlib index
146   ClangdServer Server(CDB, FS, Opts);
147 
148   Annotations A("std::^");
149 
150   Server.addDocument(testPath("foo.cc"), A.code());
151   ASSERT_TRUE(Server.blockUntilIdleForTest());
152   clangd::CodeCompleteOptions CCOpts;
153   auto Completions =
154       cantFail(runCodeComplete(Server, testPath("foo.cc"), A.point(), CCOpts));
155   EXPECT_THAT(
156       Completions.Completions,
157       UnorderedElementsAre(StdlibSymbol("list"), StdlibSymbol("vector")));
158 }
159 
160 } // namespace
161 } // namespace clangd
162 } // namespace clang
163