2018-12-01 00:59:00 +08:00
|
|
|
//===------ IndexActionTests.cpp -------------------------------*- C++ -*-===//
|
|
|
|
//
|
2019-01-19 16:50:56 +08:00
|
|
|
// 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
|
2018-12-01 00:59:00 +08:00
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
#include "TestFS.h"
|
|
|
|
#include "index/IndexAction.h"
|
|
|
|
#include "clang/Tooling/Tooling.h"
|
|
|
|
#include "gmock/gmock.h"
|
|
|
|
#include "gtest/gtest.h"
|
|
|
|
|
|
|
|
namespace clang {
|
|
|
|
namespace clangd {
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
using ::testing::AllOf;
|
|
|
|
using ::testing::ElementsAre;
|
|
|
|
using ::testing::Not;
|
|
|
|
using ::testing::Pair;
|
|
|
|
using ::testing::UnorderedElementsAre;
|
|
|
|
using ::testing::UnorderedPointwise;
|
|
|
|
|
|
|
|
std::string toUri(llvm::StringRef Path) { return URI::create(Path).toString(); }
|
|
|
|
|
|
|
|
MATCHER(IsTU, "") { return arg.IsTU; }
|
|
|
|
|
|
|
|
MATCHER_P(HasDigest, Digest, "") { return arg.Digest == Digest; }
|
|
|
|
|
2019-03-29 01:07:28 +08:00
|
|
|
MATCHER_P(HasName, Name, "") { return arg.Name == Name; }
|
|
|
|
|
2018-12-01 00:59:00 +08:00
|
|
|
MATCHER(HasSameURI, "") {
|
2019-05-06 18:08:47 +08:00
|
|
|
llvm::StringRef URI = ::testing::get<0>(arg);
|
|
|
|
const std::string &Path = ::testing::get<1>(arg);
|
2018-12-01 00:59:00 +08:00
|
|
|
return toUri(Path) == URI;
|
|
|
|
}
|
|
|
|
|
2019-05-06 18:08:47 +08:00
|
|
|
::testing::Matcher<const IncludeGraphNode &>
|
2018-12-01 00:59:00 +08:00
|
|
|
IncludesAre(const std::vector<std::string> &Includes) {
|
|
|
|
return ::testing::Field(&IncludeGraphNode::DirectIncludes,
|
|
|
|
UnorderedPointwise(HasSameURI(), Includes));
|
|
|
|
}
|
|
|
|
|
|
|
|
void checkNodesAreInitialized(const IndexFileIn &IndexFile,
|
|
|
|
const std::vector<std::string> &Paths) {
|
2019-03-29 01:07:28 +08:00
|
|
|
ASSERT_TRUE(IndexFile.Sources);
|
2018-12-01 00:59:00 +08:00
|
|
|
EXPECT_THAT(Paths.size(), IndexFile.Sources->size());
|
|
|
|
for (llvm::StringRef Path : Paths) {
|
|
|
|
auto URI = toUri(Path);
|
|
|
|
const auto &Node = IndexFile.Sources->lookup(URI);
|
|
|
|
// Uninitialized nodes will have an empty URI.
|
|
|
|
EXPECT_EQ(Node.URI.data(), IndexFile.Sources->find(URI)->getKeyData());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::map<std::string, const IncludeGraphNode &> toMap(const IncludeGraph &IG) {
|
|
|
|
std::map<std::string, const IncludeGraphNode &> Nodes;
|
|
|
|
for (auto &I : IG)
|
|
|
|
Nodes.emplace(I.getKey(), I.getValue());
|
|
|
|
return Nodes;
|
|
|
|
}
|
|
|
|
|
|
|
|
class IndexActionTest : public ::testing::Test {
|
|
|
|
public:
|
|
|
|
IndexActionTest() : InMemoryFileSystem(new llvm::vfs::InMemoryFileSystem) {}
|
|
|
|
|
|
|
|
IndexFileIn
|
|
|
|
runIndexingAction(llvm::StringRef MainFilePath,
|
|
|
|
const std::vector<std::string> &ExtraArgs = {}) {
|
|
|
|
IndexFileIn IndexFile;
|
2019-01-07 23:45:19 +08:00
|
|
|
llvm::IntrusiveRefCntPtr<FileManager> Files(
|
2018-12-01 00:59:00 +08:00
|
|
|
new FileManager(FileSystemOptions(), InMemoryFileSystem));
|
|
|
|
|
|
|
|
auto Action = createStaticIndexingAction(
|
|
|
|
SymbolCollector::Options(),
|
|
|
|
[&](SymbolSlab S) { IndexFile.Symbols = std::move(S); },
|
|
|
|
[&](RefSlab R) { IndexFile.Refs = std::move(R); },
|
|
|
|
[&](IncludeGraph IG) { IndexFile.Sources = std::move(IG); });
|
|
|
|
|
|
|
|
std::vector<std::string> Args = {"index_action", "-fsyntax-only",
|
|
|
|
"-xc++", "-std=c++11",
|
|
|
|
"-iquote", testRoot()};
|
|
|
|
Args.insert(Args.end(), ExtraArgs.begin(), ExtraArgs.end());
|
|
|
|
Args.push_back(MainFilePath);
|
|
|
|
|
|
|
|
tooling::ToolInvocation Invocation(
|
|
|
|
Args, Action.release(), Files.get(),
|
|
|
|
std::make_shared<PCHContainerOperations>());
|
|
|
|
|
|
|
|
Invocation.run();
|
|
|
|
|
|
|
|
checkNodesAreInitialized(IndexFile, FilePaths);
|
|
|
|
return IndexFile;
|
|
|
|
}
|
|
|
|
|
|
|
|
void addFile(llvm::StringRef Path, llvm::StringRef Content) {
|
|
|
|
InMemoryFileSystem->addFile(Path, 0,
|
|
|
|
llvm::MemoryBuffer::getMemBuffer(Content));
|
|
|
|
FilePaths.push_back(Path);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected:
|
|
|
|
std::vector<std::string> FilePaths;
|
2019-01-07 23:45:19 +08:00
|
|
|
llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem;
|
2018-12-01 00:59:00 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
TEST_F(IndexActionTest, CollectIncludeGraph) {
|
|
|
|
std::string MainFilePath = testPath("main.cpp");
|
|
|
|
std::string MainCode = "#include \"level1.h\"";
|
|
|
|
std::string Level1HeaderPath = testPath("level1.h");
|
|
|
|
std::string Level1HeaderCode = "#include \"level2.h\"";
|
|
|
|
std::string Level2HeaderPath = testPath("level2.h");
|
|
|
|
std::string Level2HeaderCode = "";
|
|
|
|
|
|
|
|
addFile(MainFilePath, MainCode);
|
|
|
|
addFile(Level1HeaderPath, Level1HeaderCode);
|
|
|
|
addFile(Level2HeaderPath, Level2HeaderCode);
|
|
|
|
|
|
|
|
IndexFileIn IndexFile = runIndexingAction(MainFilePath);
|
|
|
|
auto Nodes = toMap(*IndexFile.Sources);
|
|
|
|
|
|
|
|
EXPECT_THAT(Nodes,
|
|
|
|
UnorderedElementsAre(
|
|
|
|
Pair(toUri(MainFilePath),
|
|
|
|
AllOf(IsTU(), IncludesAre({Level1HeaderPath}),
|
|
|
|
HasDigest(digest(MainCode)))),
|
|
|
|
Pair(toUri(Level1HeaderPath),
|
|
|
|
AllOf(Not(IsTU()), IncludesAre({Level2HeaderPath}),
|
|
|
|
HasDigest(digest(Level1HeaderCode)))),
|
|
|
|
Pair(toUri(Level2HeaderPath),
|
|
|
|
AllOf(Not(IsTU()), IncludesAre({}),
|
|
|
|
HasDigest(digest(Level2HeaderCode))))));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(IndexActionTest, IncludeGraphSelfInclude) {
|
|
|
|
std::string MainFilePath = testPath("main.cpp");
|
|
|
|
std::string MainCode = "#include \"header.h\"";
|
|
|
|
std::string HeaderPath = testPath("header.h");
|
|
|
|
std::string HeaderCode = R"cpp(
|
|
|
|
#ifndef _GUARD_
|
|
|
|
#define _GUARD_
|
|
|
|
#include "header.h"
|
|
|
|
#endif)cpp";
|
|
|
|
|
|
|
|
addFile(MainFilePath, MainCode);
|
|
|
|
addFile(HeaderPath, HeaderCode);
|
|
|
|
|
|
|
|
IndexFileIn IndexFile = runIndexingAction(MainFilePath);
|
|
|
|
auto Nodes = toMap(*IndexFile.Sources);
|
|
|
|
|
|
|
|
EXPECT_THAT(
|
|
|
|
Nodes,
|
|
|
|
UnorderedElementsAre(
|
|
|
|
Pair(toUri(MainFilePath), AllOf(IsTU(), IncludesAre({HeaderPath}),
|
|
|
|
HasDigest(digest(MainCode)))),
|
|
|
|
Pair(toUri(HeaderPath), AllOf(Not(IsTU()), IncludesAre({HeaderPath}),
|
|
|
|
HasDigest(digest(HeaderCode))))));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(IndexActionTest, IncludeGraphSkippedFile) {
|
|
|
|
std::string MainFilePath = testPath("main.cpp");
|
|
|
|
std::string MainCode = R"cpp(
|
|
|
|
#include "common.h"
|
|
|
|
#include "header.h"
|
|
|
|
)cpp";
|
|
|
|
|
|
|
|
std::string CommonHeaderPath = testPath("common.h");
|
|
|
|
std::string CommonHeaderCode = R"cpp(
|
|
|
|
#ifndef _GUARD_
|
|
|
|
#define _GUARD_
|
|
|
|
void f();
|
|
|
|
#endif)cpp";
|
|
|
|
|
|
|
|
std::string HeaderPath = testPath("header.h");
|
|
|
|
std::string HeaderCode = R"cpp(
|
|
|
|
#include "common.h"
|
|
|
|
void g();)cpp";
|
|
|
|
|
|
|
|
addFile(MainFilePath, MainCode);
|
|
|
|
addFile(HeaderPath, HeaderCode);
|
|
|
|
addFile(CommonHeaderPath, CommonHeaderCode);
|
|
|
|
|
|
|
|
IndexFileIn IndexFile = runIndexingAction(MainFilePath);
|
|
|
|
auto Nodes = toMap(*IndexFile.Sources);
|
|
|
|
|
|
|
|
EXPECT_THAT(
|
|
|
|
Nodes, UnorderedElementsAre(
|
|
|
|
Pair(toUri(MainFilePath),
|
|
|
|
AllOf(IsTU(), IncludesAre({HeaderPath, CommonHeaderPath}),
|
|
|
|
HasDigest(digest(MainCode)))),
|
|
|
|
Pair(toUri(HeaderPath),
|
|
|
|
AllOf(Not(IsTU()), IncludesAre({CommonHeaderPath}),
|
|
|
|
HasDigest(digest(HeaderCode)))),
|
|
|
|
Pair(toUri(CommonHeaderPath),
|
|
|
|
AllOf(Not(IsTU()), IncludesAre({}),
|
|
|
|
HasDigest(digest(CommonHeaderCode))))));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(IndexActionTest, IncludeGraphDynamicInclude) {
|
|
|
|
std::string MainFilePath = testPath("main.cpp");
|
|
|
|
std::string MainCode = R"cpp(
|
|
|
|
#ifndef FOO
|
|
|
|
#define FOO "main.cpp"
|
|
|
|
#else
|
|
|
|
#define FOO "header.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include FOO)cpp";
|
|
|
|
std::string HeaderPath = testPath("header.h");
|
|
|
|
std::string HeaderCode = "";
|
|
|
|
|
|
|
|
addFile(MainFilePath, MainCode);
|
|
|
|
addFile(HeaderPath, HeaderCode);
|
|
|
|
|
|
|
|
IndexFileIn IndexFile = runIndexingAction(MainFilePath);
|
|
|
|
auto Nodes = toMap(*IndexFile.Sources);
|
|
|
|
|
|
|
|
EXPECT_THAT(
|
|
|
|
Nodes,
|
|
|
|
UnorderedElementsAre(
|
|
|
|
Pair(toUri(MainFilePath),
|
|
|
|
AllOf(IsTU(), IncludesAre({MainFilePath, HeaderPath}),
|
|
|
|
HasDigest(digest(MainCode)))),
|
|
|
|
Pair(toUri(HeaderPath), AllOf(Not(IsTU()), IncludesAre({}),
|
|
|
|
HasDigest(digest(HeaderCode))))));
|
|
|
|
}
|
|
|
|
|
2019-03-29 01:07:28 +08:00
|
|
|
TEST_F(IndexActionTest, NoWarnings) {
|
|
|
|
std::string MainFilePath = testPath("main.cpp");
|
|
|
|
std::string MainCode = R"cpp(
|
|
|
|
void foo(int x) {
|
|
|
|
if (x = 1) // -Wparentheses
|
|
|
|
return;
|
|
|
|
if (x = 1) // -Wparentheses
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
void bar() {}
|
|
|
|
)cpp";
|
|
|
|
addFile(MainFilePath, MainCode);
|
|
|
|
// We set -ferror-limit so the warning-promoted-to-error would be fatal.
|
|
|
|
// This would cause indexing to stop (if warnings weren't disabled).
|
|
|
|
IndexFileIn IndexFile = runIndexingAction(
|
|
|
|
MainFilePath, {"-ferror-limit=1", "-Wparentheses", "-Werror"});
|
|
|
|
ASSERT_TRUE(IndexFile.Sources);
|
|
|
|
ASSERT_NE(0u, IndexFile.Sources->size());
|
|
|
|
EXPECT_THAT(*IndexFile.Symbols, ElementsAre(HasName("foo"), HasName("bar")));
|
|
|
|
}
|
|
|
|
|
2018-12-01 00:59:00 +08:00
|
|
|
} // namespace
|
|
|
|
} // namespace clangd
|
|
|
|
} // namespace clang
|