blob: f70de71edfc3dbc9bffb6554d1ac04f96765227b [file] [log] [blame]
//===-- 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 <stdio.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).startswith("<");
}
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