14#include "clang/Basic/SourceLocation.h"
15#include "clang/Basic/SourceManager.h"
16#include "clang/Tooling/Tooling.h"
17#include "gmock/gmock.h"
18#include "gtest/gtest.h"
25using ::testing::AllOf;
26using ::testing::ElementsAre;
27using ::testing::EndsWith;
30using ::testing::UnorderedElementsAre;
31using ::testing::UnorderedPointwise;
37MATCHER_P(hasDigest, Digest,
"") {
return arg.Digest == Digest; }
39MATCHER_P(hasName, Name,
"") {
return arg.Name == Name; }
42 llvm::StringRef
URI = ::testing::get<0>(arg);
43 const std::string &
Path = ::testing::get<1>(arg);
48 return (arg.IncludeHeaders.size() == 1) &&
49 (arg.IncludeHeaders.begin()->IncludeHeader == P);
52::testing::Matcher<const IncludeGraphNode &>
53includesAre(
const std::vector<std::string> &Includes) {
55 UnorderedPointwise(hasSameURI(), Includes));
58void checkNodesAreInitialized(
const IndexFileIn &IndexFile,
59 const std::vector<std::string> &Paths) {
60 ASSERT_TRUE(IndexFile.Sources);
61 EXPECT_THAT(Paths.size(), IndexFile.Sources->size());
62 for (llvm::StringRef
Path : Paths) {
64 const auto &Node = IndexFile.Sources->lookup(
URI);
66 EXPECT_EQ(Node.URI.data(), IndexFile.Sources->find(
URI)->getKeyData());
70std::map<std::string, const IncludeGraphNode &> toMap(
const IncludeGraph &IG) {
71 std::map<std::string, const IncludeGraphNode &> Nodes;
73 Nodes.emplace(std::string(I.getKey()), I.getValue());
77class IndexActionTest :
public ::testing::Test {
79 IndexActionTest() : InMemoryFileSystem(new llvm::vfs::InMemoryFileSystem) {}
82 runIndexingAction(llvm::StringRef MainFilePath,
83 const std::vector<std::string> &ExtraArgs = {}) {
84 IndexFileIn IndexFile;
85 llvm::IntrusiveRefCntPtr<FileManager> Files(
86 new FileManager(FileSystemOptions(), InMemoryFileSystem));
89 Opts, [&](IndexFileIn Result) { IndexFile = std::move(Result); });
91 std::vector<std::string> Args = {
"index_action",
"-fsyntax-only",
92 "-xc++",
"-std=c++11",
94 Args.insert(Args.end(), ExtraArgs.begin(), ExtraArgs.end());
95 Args.push_back(std::string(MainFilePath));
97 tooling::ToolInvocation Invocation(
98 Args, std::move(Action), Files.get(),
99 std::make_shared<PCHContainerOperations>());
103 checkNodesAreInitialized(IndexFile, FilePaths);
107 void addFile(llvm::StringRef
Path, llvm::StringRef Content) {
108 InMemoryFileSystem->addFile(
Path, 0,
109 llvm::MemoryBuffer::getMemBufferCopy(Content));
110 FilePaths.push_back(std::string(
Path));
114 SymbolCollector::Options Opts;
115 std::vector<std::string> FilePaths;
116 llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem;
119TEST_F(IndexActionTest, CollectIncludeGraph) {
120 std::string MainFilePath =
testPath(
"main.cpp");
121 std::string MainCode =
"#include \"level1.h\"";
122 std::string Level1HeaderPath =
testPath(
"level1.h");
123 std::string Level1HeaderCode =
"#include \"level2.h\"";
124 std::string Level2HeaderPath =
testPath(
"level2.h");
125 std::string Level2HeaderCode =
"";
127 addFile(MainFilePath, MainCode);
128 addFile(Level1HeaderPath, Level1HeaderCode);
129 addFile(Level2HeaderPath, Level2HeaderCode);
131 IndexFileIn IndexFile = runIndexingAction(MainFilePath);
132 auto Nodes = toMap(*IndexFile.Sources);
135 UnorderedElementsAre(
136 Pair(toUri(MainFilePath),
137 AllOf(isTU(), includesAre({Level1HeaderPath}),
138 hasDigest(
digest(MainCode)))),
139 Pair(toUri(Level1HeaderPath),
140 AllOf(Not(isTU()), includesAre({Level2HeaderPath}),
141 hasDigest(
digest(Level1HeaderCode)))),
142 Pair(toUri(Level2HeaderPath),
143 AllOf(Not(isTU()), includesAre({}),
144 hasDigest(
digest(Level2HeaderCode))))));
147TEST_F(IndexActionTest, IncludeGraphSelfInclude) {
148 std::string MainFilePath =
testPath(
"main.cpp");
149 std::string MainCode =
"#include \"header.h\"";
150 std::string HeaderPath =
testPath(
"header.h");
151 std::string HeaderCode = R
"cpp(
157 addFile(MainFilePath, MainCode);
158 addFile(HeaderPath, HeaderCode);
160 IndexFileIn IndexFile = runIndexingAction(MainFilePath);
161 auto Nodes = toMap(*IndexFile.Sources);
165 UnorderedElementsAre(
166 Pair(toUri(MainFilePath), AllOf(isTU(), includesAre({HeaderPath}),
167 hasDigest(
digest(MainCode)))),
168 Pair(toUri(HeaderPath), AllOf(Not(isTU()), includesAre({HeaderPath}),
169 hasDigest(
digest(HeaderCode))))));
172TEST_F(IndexActionTest, IncludeGraphSkippedFile) {
173 std::string MainFilePath =
testPath(
"main.cpp");
174 std::string MainCode = R
"cpp(
179 std::string CommonHeaderPath = testPath("common.h");
180 std::string CommonHeaderCode = R
"cpp(
186 std::string HeaderPath = testPath("header.h");
187 std::string HeaderCode = R
"cpp(
191 addFile(MainFilePath, MainCode);
192 addFile(HeaderPath, HeaderCode);
193 addFile(CommonHeaderPath, CommonHeaderCode);
195 IndexFileIn IndexFile = runIndexingAction(MainFilePath);
196 auto Nodes = toMap(*IndexFile.Sources);
199 Nodes, UnorderedElementsAre(
200 Pair(toUri(MainFilePath),
201 AllOf(isTU(), includesAre({HeaderPath, CommonHeaderPath}),
202 hasDigest(
digest(MainCode)))),
203 Pair(toUri(HeaderPath),
204 AllOf(Not(isTU()), includesAre({CommonHeaderPath}),
205 hasDigest(
digest(HeaderCode)))),
206 Pair(toUri(CommonHeaderPath),
207 AllOf(Not(isTU()), includesAre({}),
208 hasDigest(
digest(CommonHeaderCode))))));
211TEST_F(IndexActionTest, IncludeGraphDynamicInclude) {
212 std::string MainFilePath =
testPath(
"main.cpp");
213 std::string MainCode = R
"cpp(
215 #define FOO "main.cpp"
217 #define FOO "header.h"
221 std::string HeaderPath = testPath("header.h");
222 std::string HeaderCode =
"";
224 addFile(MainFilePath, MainCode);
225 addFile(HeaderPath, HeaderCode);
227 IndexFileIn IndexFile = runIndexingAction(MainFilePath);
228 auto Nodes = toMap(*IndexFile.Sources);
232 UnorderedElementsAre(
233 Pair(toUri(MainFilePath),
234 AllOf(isTU(), includesAre({MainFilePath, HeaderPath}),
235 hasDigest(
digest(MainCode)))),
236 Pair(toUri(HeaderPath), AllOf(Not(isTU()), includesAre({}),
237 hasDigest(
digest(HeaderCode))))));
240TEST_F(IndexActionTest, NoWarnings) {
241 std::string MainFilePath =
testPath(
"main.cpp");
242 std::string MainCode = R
"cpp(
244 if (x = 1) // -Wparentheses
246 if (x = 1) // -Wparentheses
251 addFile(MainFilePath, MainCode);
255 MainFilePath, {
"-ferror-limit=1",
"-Wparentheses",
"-Werror"});
256 ASSERT_TRUE(IndexFile.Sources);
257 ASSERT_NE(0u, IndexFile.Sources->size());
258 EXPECT_THAT(*IndexFile.Symbols, ElementsAre(hasName(
"foo"), hasName(
"bar")));
261TEST_F(IndexActionTest, SkipFiles) {
262 std::string MainFilePath =
testPath(
"main.cpp");
263 addFile(MainFilePath, R
"cpp(
272 auto unskippable1() { return S(); }
277 auto unskippable2() { return S(); }
279 Opts.FileFilter = [](const SourceManager &SM, FileID F) {
280 return !SM.getFileEntryRefForID(F)->getName().ends_with(
"bad.h");
282 IndexFileIn IndexFile = runIndexingAction(MainFilePath, {
"-std=c++14"});
283 EXPECT_THAT(*IndexFile.Symbols,
284 UnorderedElementsAre(hasName(
"S"), hasName(
"s"), hasName(
"f1"),
285 hasName(
"unskippable1")));
286 for (
const auto &Pair : *IndexFile.Refs)
287 for (
const auto &
Ref : Pair.second)
291TEST_F(IndexActionTest, SkipNestedSymbols) {
292 std::string MainFilePath =
testPath(
"main.cpp");
293 addFile(MainFilePath, R
"cpp(
316 IndexFileIn IndexFile = runIndexingAction(MainFilePath, {"-std=c++14"});
317 EXPECT_THAT(*IndexFile.Symbols, testing::Contains(hasName(
"foo")));
318 EXPECT_THAT(*IndexFile.Symbols, testing::Contains(hasName(
"Bar")));
319 EXPECT_THAT(*IndexFile.Symbols, Not(testing::Contains(hasName(
"Baz"))));
322TEST_F(IndexActionTest, SymbolFromCC) {
323 std::string MainFilePath =
testPath(
"main.cpp");
324 addFile(MainFilePath, R
"cpp(
332 Opts.FileFilter = [](const SourceManager &SM, FileID F) {
333 return !SM.getFileEntryRefForID(F)->getName().ends_with(
"main.h");
335 IndexFileIn IndexFile = runIndexingAction(MainFilePath, {
"-std=c++14"});
336 EXPECT_THAT(*IndexFile.Symbols,
337 UnorderedElementsAre(AllOf(
342TEST_F(IndexActionTest, IncludeHeaderForwardDecls) {
343 std::string MainFilePath =
testPath(
"main.cpp");
344 addFile(MainFilePath, R
"cpp(
359// This decl is important, as otherwise we detect control macro for the file,
360// before handling definition of Foo.
364 IndexFileIn IndexFile = runIndexingAction(MainFilePath);
365 EXPECT_THAT(*IndexFile.Symbols,
366 testing::Contains(AllOf(
369 << *IndexFile.Symbols->begin();
A URI describes the location of a source file.
static llvm::Expected< URI > create(llvm::StringRef AbsolutePath, llvm::StringRef Scheme)
Creates a URI for a file in the given scheme.
FIXME: Skip testing on windows temporarily due to the different escaping code mode.
TEST_F(BackgroundIndexTest, NoCrashOnErrorFile)
FileDigest digest(llvm::StringRef Content)
static const char * toString(OffsetEncoding OE)
std::string testPath(PathRef File, llvm::sys::path::Style Style)
llvm::StringMap< IncludeGraphNode > IncludeGraph
std::unique_ptr< FrontendAction > createStaticIndexingAction(SymbolCollector::Options Opts, std::function< void(IndexFileIn)> IndexContentsCallback)
std::string Path
A typedef to represent a file path.
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
std::vector< llvm::StringRef > DirectIncludes
Represents a symbol occurrence in the source file.
SymbolLocation Location
The source location where the symbol is named.