1 //===------ IndexActionTests.cpp -------------------------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
12 #include "index/IndexAction.h"
13 #include "index/Serialization.h"
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"
25 using ::testing::AllOf
;
26 using ::testing::ElementsAre
;
27 using ::testing::EndsWith
;
29 using ::testing::Pair
;
30 using ::testing::UnorderedElementsAre
;
31 using ::testing::UnorderedPointwise
;
33 std::string
toUri(llvm::StringRef Path
) { return URI::create(Path
).toString(); }
35 MATCHER(isTU
, "") { return arg
.Flags
& IncludeGraphNode::SourceFlag::IsTU
; }
37 MATCHER_P(hasDigest
, Digest
, "") { return arg
.Digest
== Digest
; }
39 MATCHER_P(hasName
, Name
, "") { return arg
.Name
== Name
; }
41 MATCHER(hasSameURI
, "") {
42 llvm::StringRef URI
= ::testing::get
<0>(arg
);
43 const std::string
&Path
= ::testing::get
<1>(arg
);
44 return toUri(Path
) == URI
;
47 MATCHER_P(includeHeader
, P
, "") {
48 return (arg
.IncludeHeaders
.size() == 1) &&
49 (arg
.IncludeHeaders
.begin()->IncludeHeader
== P
);
52 ::testing::Matcher
<const IncludeGraphNode
&>
53 includesAre(const std::vector
<std::string
> &Includes
) {
54 return ::testing::Field(&IncludeGraphNode::DirectIncludes
,
55 UnorderedPointwise(hasSameURI(), Includes
));
58 void 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
) {
63 auto URI
= toUri(Path
);
64 const auto &Node
= IndexFile
.Sources
->lookup(URI
);
65 // Uninitialized nodes will have an empty URI.
66 EXPECT_EQ(Node
.URI
.data(), IndexFile
.Sources
->find(URI
)->getKeyData());
70 std::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());
77 class 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
));
88 auto Action
= createStaticIndexingAction(
89 Opts
, [&](SymbolSlab S
) { IndexFile
.Symbols
= std::move(S
); },
90 [&](RefSlab R
) { IndexFile
.Refs
= std::move(R
); },
91 [&](RelationSlab R
) { IndexFile
.Relations
= std::move(R
); },
92 [&](IncludeGraph IG
) { IndexFile
.Sources
= std::move(IG
); });
94 std::vector
<std::string
> Args
= {"index_action", "-fsyntax-only",
95 "-xc++", "-std=c++11",
96 "-iquote", testRoot()};
97 Args
.insert(Args
.end(), ExtraArgs
.begin(), ExtraArgs
.end());
98 Args
.push_back(std::string(MainFilePath
));
100 tooling::ToolInvocation
Invocation(
101 Args
, std::move(Action
), Files
.get(),
102 std::make_shared
<PCHContainerOperations
>());
106 checkNodesAreInitialized(IndexFile
, FilePaths
);
110 void addFile(llvm::StringRef Path
, llvm::StringRef Content
) {
111 InMemoryFileSystem
->addFile(Path
, 0,
112 llvm::MemoryBuffer::getMemBufferCopy(Content
));
113 FilePaths
.push_back(std::string(Path
));
117 SymbolCollector::Options Opts
;
118 std::vector
<std::string
> FilePaths
;
119 llvm::IntrusiveRefCntPtr
<llvm::vfs::InMemoryFileSystem
> InMemoryFileSystem
;
122 TEST_F(IndexActionTest
, CollectIncludeGraph
) {
123 std::string MainFilePath
= testPath("main.cpp");
124 std::string MainCode
= "#include \"level1.h\"";
125 std::string Level1HeaderPath
= testPath("level1.h");
126 std::string Level1HeaderCode
= "#include \"level2.h\"";
127 std::string Level2HeaderPath
= testPath("level2.h");
128 std::string Level2HeaderCode
= "";
130 addFile(MainFilePath
, MainCode
);
131 addFile(Level1HeaderPath
, Level1HeaderCode
);
132 addFile(Level2HeaderPath
, Level2HeaderCode
);
134 IndexFileIn IndexFile
= runIndexingAction(MainFilePath
);
135 auto Nodes
= toMap(*IndexFile
.Sources
);
138 UnorderedElementsAre(
139 Pair(toUri(MainFilePath
),
140 AllOf(isTU(), includesAre({Level1HeaderPath
}),
141 hasDigest(digest(MainCode
)))),
142 Pair(toUri(Level1HeaderPath
),
143 AllOf(Not(isTU()), includesAre({Level2HeaderPath
}),
144 hasDigest(digest(Level1HeaderCode
)))),
145 Pair(toUri(Level2HeaderPath
),
146 AllOf(Not(isTU()), includesAre({}),
147 hasDigest(digest(Level2HeaderCode
))))));
150 TEST_F(IndexActionTest
, IncludeGraphSelfInclude
) {
151 std::string MainFilePath
= testPath("main.cpp");
152 std::string MainCode
= "#include \"header.h\"";
153 std::string HeaderPath
= testPath("header.h");
154 std::string HeaderCode
= R
"cpp(
160 addFile(MainFilePath
, MainCode
);
161 addFile(HeaderPath
, HeaderCode
);
163 IndexFileIn IndexFile
= runIndexingAction(MainFilePath
);
164 auto Nodes
= toMap(*IndexFile
.Sources
);
168 UnorderedElementsAre(
169 Pair(toUri(MainFilePath
), AllOf(isTU(), includesAre({HeaderPath
}),
170 hasDigest(digest(MainCode
)))),
171 Pair(toUri(HeaderPath
), AllOf(Not(isTU()), includesAre({HeaderPath
}),
172 hasDigest(digest(HeaderCode
))))));
175 TEST_F(IndexActionTest
, IncludeGraphSkippedFile
) {
176 std::string MainFilePath
= testPath("main.cpp");
177 std::string MainCode
= R
"cpp(
182 std::string CommonHeaderPath
= testPath("common.h");
183 std::string CommonHeaderCode
= R
"cpp(
189 std::string HeaderPath
= testPath("header.h");
190 std::string HeaderCode
= R
"cpp(
194 addFile(MainFilePath
, MainCode
);
195 addFile(HeaderPath
, HeaderCode
);
196 addFile(CommonHeaderPath
, CommonHeaderCode
);
198 IndexFileIn IndexFile
= runIndexingAction(MainFilePath
);
199 auto Nodes
= toMap(*IndexFile
.Sources
);
202 Nodes
, UnorderedElementsAre(
203 Pair(toUri(MainFilePath
),
204 AllOf(isTU(), includesAre({HeaderPath
, CommonHeaderPath
}),
205 hasDigest(digest(MainCode
)))),
206 Pair(toUri(HeaderPath
),
207 AllOf(Not(isTU()), includesAre({CommonHeaderPath
}),
208 hasDigest(digest(HeaderCode
)))),
209 Pair(toUri(CommonHeaderPath
),
210 AllOf(Not(isTU()), includesAre({}),
211 hasDigest(digest(CommonHeaderCode
))))));
214 TEST_F(IndexActionTest
, IncludeGraphDynamicInclude
) {
215 std::string MainFilePath
= testPath("main.cpp");
216 std::string MainCode
= R
"cpp(
218 #define FOO "main
.cpp
"
220 #define FOO "header
.h
"
224 std::string HeaderPath
= testPath("header.h");
225 std::string HeaderCode
= "";
227 addFile(MainFilePath
, MainCode
);
228 addFile(HeaderPath
, HeaderCode
);
230 IndexFileIn IndexFile
= runIndexingAction(MainFilePath
);
231 auto Nodes
= toMap(*IndexFile
.Sources
);
235 UnorderedElementsAre(
236 Pair(toUri(MainFilePath
),
237 AllOf(isTU(), includesAre({MainFilePath
, HeaderPath
}),
238 hasDigest(digest(MainCode
)))),
239 Pair(toUri(HeaderPath
), AllOf(Not(isTU()), includesAre({}),
240 hasDigest(digest(HeaderCode
))))));
243 TEST_F(IndexActionTest
, NoWarnings
) {
244 std::string MainFilePath
= testPath("main.cpp");
245 std::string MainCode
= R
"cpp(
247 if (x = 1) // -Wparentheses
249 if (x = 1) // -Wparentheses
254 addFile(MainFilePath
, MainCode
);
255 // We set -ferror-limit so the warning-promoted-to-error would be fatal.
256 // This would cause indexing to stop (if warnings weren't disabled).
257 IndexFileIn IndexFile
= runIndexingAction(
258 MainFilePath
, {"-ferror-limit=1", "-Wparentheses", "-Werror"});
259 ASSERT_TRUE(IndexFile
.Sources
);
260 ASSERT_NE(0u, IndexFile
.Sources
->size());
261 EXPECT_THAT(*IndexFile
.Symbols
, ElementsAre(hasName("foo"), hasName("bar")));
264 TEST_F(IndexActionTest
, SkipFiles
) {
265 std::string MainFilePath
= testPath("main.cpp");
266 addFile(MainFilePath
, R
"cpp(
272 addFile(testPath("good.h"), R
"cpp(
275 auto unskippable1() { return S(); }
277 addFile(testPath("bad.h"), R
"cpp(
280 auto unskippable2() { return S(); }
282 Opts
.FileFilter
= [](const SourceManager
&SM
, FileID F
) {
283 return !SM
.getFileEntryRefForID(F
)->getName().endswith("bad.h");
285 IndexFileIn IndexFile
= runIndexingAction(MainFilePath
, {"-std=c++14"});
286 EXPECT_THAT(*IndexFile
.Symbols
,
287 UnorderedElementsAre(hasName("S"), hasName("s"), hasName("f1"),
288 hasName("unskippable1")));
289 for (const auto &Pair
: *IndexFile
.Refs
)
290 for (const auto &Ref
: Pair
.second
)
291 EXPECT_THAT(Ref
.Location
.FileURI
, EndsWith("good.h"));
294 TEST_F(IndexActionTest
, SkipNestedSymbols
) {
295 std::string MainFilePath
= testPath("main.cpp");
296 addFile(MainFilePath
, R
"cpp(
319 IndexFileIn IndexFile
= runIndexingAction(MainFilePath
, {"-std=c++14"});
320 EXPECT_THAT(*IndexFile
.Symbols
, testing::Contains(hasName("foo")));
321 EXPECT_THAT(*IndexFile
.Symbols
, testing::Contains(hasName("Bar")));
322 EXPECT_THAT(*IndexFile
.Symbols
, Not(testing::Contains(hasName("Baz"))));
325 TEST_F(IndexActionTest
, SymbolFromCC
) {
326 std::string MainFilePath
= testPath("main.cpp");
327 addFile(MainFilePath
, R
"cpp(
331 addFile(testPath("main.h"), R
"cpp(
335 Opts
.FileFilter
= [](const SourceManager
&SM
, FileID F
) {
336 return !SM
.getFileEntryRefForID(F
)->getName().endswith("main.h");
338 IndexFileIn IndexFile
= runIndexingAction(MainFilePath
, {"-std=c++14"});
339 EXPECT_THAT(*IndexFile
.Symbols
,
340 UnorderedElementsAre(AllOf(
342 includeHeader(URI::create(testPath("main.h")).toString()))));
345 } // namespace clangd