1 //===--- IndexTests.cpp - Test indexing actions -----------------*- 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 //===----------------------------------------------------------------------===//
9 #include "clang/AST/ASTConsumer.h"
10 #include "clang/AST/ASTContext.h"
11 #include "clang/AST/Decl.h"
12 #include "clang/Basic/SourceLocation.h"
13 #include "clang/Basic/SourceManager.h"
14 #include "clang/Frontend/CompilerInstance.h"
15 #include "clang/Frontend/FrontendAction.h"
16 #include "clang/Index/IndexDataConsumer.h"
17 #include "clang/Index/IndexSymbol.h"
18 #include "clang/Index/IndexingAction.h"
19 #include "clang/Lex/Preprocessor.h"
20 #include "clang/Tooling/Tooling.h"
21 #include "llvm/ADT/StringRef.h"
22 #include "llvm/Support/VirtualFileSystem.h"
23 #include "gmock/gmock.h"
24 #include "gtest/gtest.h"
34 Position(size_t Line
= 0, size_t Column
= 0) : Line(Line
), Column(Column
) {}
36 static Position
fromSourceLocation(SourceLocation Loc
,
37 const SourceManager
&SM
) {
40 std::tie(FID
, Offset
) = SM
.getDecomposedSpellingLoc(Loc
);
42 P
.Line
= SM
.getLineNumber(FID
, Offset
);
43 P
.Column
= SM
.getColumnNumber(FID
, Offset
);
48 bool operator==(const Position
&LHS
, const Position
&RHS
) {
49 return std::tie(LHS
.Line
, LHS
.Column
) == std::tie(RHS
.Line
, RHS
.Column
);
52 llvm::raw_ostream
&operator<<(llvm::raw_ostream
&OS
, const Position
&Pos
) {
53 return OS
<< Pos
.Line
<< ':' << Pos
.Column
;
62 // FIXME: add more information.
65 llvm::raw_ostream
&operator<<(llvm::raw_ostream
&OS
, const TestSymbol
&S
) {
66 return OS
<< S
.QName
<< '[' << S
.WrittenPos
<< ']' << '@' << S
.DeclPos
<< '('
67 << static_cast<unsigned>(S
.SymInfo
.Kind
) << ')';
70 class Indexer
: public IndexDataConsumer
{
72 void initialize(ASTContext
&Ctx
) override
{
74 IndexDataConsumer::initialize(Ctx
);
77 bool handleDeclOccurrence(const Decl
*D
, SymbolRoleSet Roles
,
78 ArrayRef
<SymbolRelation
>, SourceLocation Loc
,
79 ASTNodeInfo
) override
{
80 const auto *ND
= llvm::dyn_cast
<NamedDecl
>(D
);
84 S
.SymInfo
= getSymbolInfo(D
);
85 S
.QName
= ND
->getQualifiedNameAsString();
86 S
.WrittenPos
= Position::fromSourceLocation(Loc
, AST
->getSourceManager());
88 Position::fromSourceLocation(D
->getLocation(), AST
->getSourceManager());
90 Symbols
.push_back(std::move(S
));
94 bool handleMacroOccurrence(const IdentifierInfo
*Name
, const MacroInfo
*MI
,
95 SymbolRoleSet Roles
, SourceLocation Loc
) override
{
97 S
.SymInfo
= getSymbolInfoForMacro(*MI
);
98 S
.QName
= std::string(Name
->getName());
99 S
.WrittenPos
= Position::fromSourceLocation(Loc
, AST
->getSourceManager());
100 S
.DeclPos
= Position::fromSourceLocation(MI
->getDefinitionLoc(),
101 AST
->getSourceManager());
103 Symbols
.push_back(std::move(S
));
107 std::vector
<TestSymbol
> Symbols
;
108 const ASTContext
*AST
= nullptr;
111 class IndexAction
: public ASTFrontendAction
{
113 IndexAction(std::shared_ptr
<Indexer
> Index
,
114 IndexingOptions Opts
= IndexingOptions())
115 : Index(std::move(Index
)), Opts(Opts
) {}
118 std::unique_ptr
<ASTConsumer
> CreateASTConsumer(CompilerInstance
&CI
,
119 StringRef InFile
) override
{
120 class Consumer
: public ASTConsumer
{
121 std::shared_ptr
<Indexer
> Index
;
122 std::shared_ptr
<Preprocessor
> PP
;
123 IndexingOptions Opts
;
126 Consumer(std::shared_ptr
<Indexer
> Index
, std::shared_ptr
<Preprocessor
> PP
,
127 IndexingOptions Opts
)
128 : Index(std::move(Index
)), PP(std::move(PP
)), Opts(Opts
) {}
130 void HandleTranslationUnit(ASTContext
&Ctx
) override
{
131 std::vector
<Decl
*> DeclsToIndex(
132 Ctx
.getTranslationUnitDecl()->decls().begin(),
133 Ctx
.getTranslationUnitDecl()->decls().end());
134 indexTopLevelDecls(Ctx
, *PP
, DeclsToIndex
, *Index
, Opts
);
137 return std::make_unique
<Consumer
>(Index
, CI
.getPreprocessorPtr(), Opts
);
141 std::shared_ptr
<Indexer
> Index
;
142 IndexingOptions Opts
;
145 using testing::AllOf
;
146 using testing::Contains
;
148 using testing::UnorderedElementsAre
;
150 MATCHER_P(QName
, Name
, "") { return arg
.QName
== Name
; }
151 MATCHER_P(WrittenAt
, Pos
, "") { return arg
.WrittenPos
== Pos
; }
152 MATCHER_P(DeclAt
, Pos
, "") { return arg
.DeclPos
== Pos
; }
153 MATCHER_P(Kind
, SymKind
, "") { return arg
.SymInfo
.Kind
== SymKind
; }
154 MATCHER_P(HasRole
, Role
, "") { return arg
.Roles
& static_cast<unsigned>(Role
); }
156 TEST(IndexTest
, Simple
) {
157 auto Index
= std::make_shared
<Indexer
>();
158 tooling::runToolOnCode(std::make_unique
<IndexAction
>(Index
),
159 "class X {}; void f() {}");
160 EXPECT_THAT(Index
->Symbols
, UnorderedElementsAre(QName("X"), QName("f")));
163 TEST(IndexTest
, IndexPreprocessorMacros
) {
164 std::string Code
= R
"cpp(
166 #define INDEX_MAC_UNDEF 1
167 #undef INDEX_MAC_UNDEF
168 #define INDEX_MAC_REDEF 1
169 #undef INDEX_MAC_REDEF
170 #define INDEX_MAC_REDEF 2
172 auto Index
= std::make_shared
<Indexer
>();
173 IndexingOptions Opts
;
174 Opts
.IndexMacrosInPreprocessor
= true;
175 tooling::runToolOnCode(std::make_unique
<IndexAction
>(Index
, Opts
), Code
);
176 EXPECT_THAT(Index
->Symbols
,
177 Contains(AllOf(QName("INDEX_MAC"), WrittenAt(Position(2, 13)),
178 DeclAt(Position(2, 13)),
179 HasRole(SymbolRole::Definition
))));
182 AllOf(Contains(AllOf(QName("INDEX_MAC_UNDEF"), WrittenAt(Position(3, 13)),
183 DeclAt(Position(3, 13)),
184 HasRole(SymbolRole::Definition
))),
185 Contains(AllOf(QName("INDEX_MAC_UNDEF"), WrittenAt(Position(4, 12)),
186 DeclAt(Position(3, 13)),
187 HasRole(SymbolRole::Undefinition
)))));
190 AllOf(Contains(AllOf(QName("INDEX_MAC_REDEF"), WrittenAt(Position(5, 13)),
191 DeclAt(Position(5, 13)),
192 HasRole(SymbolRole::Definition
))),
193 Contains(AllOf(QName("INDEX_MAC_REDEF"), WrittenAt(Position(6, 12)),
194 DeclAt(Position(5, 13)),
195 HasRole(SymbolRole::Undefinition
))),
196 Contains(AllOf(QName("INDEX_MAC_REDEF"), WrittenAt(Position(7, 13)),
197 DeclAt(Position(7, 13)),
198 HasRole(SymbolRole::Definition
)))));
200 Opts
.IndexMacrosInPreprocessor
= false;
201 Index
->Symbols
.clear();
202 tooling::runToolOnCode(std::make_unique
<IndexAction
>(Index
, Opts
), Code
);
203 EXPECT_THAT(Index
->Symbols
, UnorderedElementsAre());
206 TEST(IndexTest
, IndexParametersInDecls
) {
207 std::string Code
= "void foo(int bar);";
208 auto Index
= std::make_shared
<Indexer
>();
209 IndexingOptions Opts
;
210 Opts
.IndexFunctionLocals
= true;
211 Opts
.IndexParametersInDeclarations
= true;
212 tooling::runToolOnCode(std::make_unique
<IndexAction
>(Index
, Opts
), Code
);
213 EXPECT_THAT(Index
->Symbols
, Contains(QName("bar")));
215 Opts
.IndexParametersInDeclarations
= false;
216 Index
->Symbols
.clear();
217 tooling::runToolOnCode(std::make_unique
<IndexAction
>(Index
, Opts
), Code
);
218 EXPECT_THAT(Index
->Symbols
, Not(Contains(QName("bar"))));
221 TEST(IndexTest
, IndexLabels
) {
222 std::string Code
= R
"cpp(
229 auto Index
= std::make_shared
<Indexer
>();
230 IndexingOptions Opts
;
231 Opts
.IndexFunctionLocals
= true;
232 tooling::runToolOnCode(std::make_unique
<IndexAction
>(Index
, Opts
), Code
);
233 EXPECT_THAT(Index
->Symbols
,
234 Contains(AllOf(QName("theLabel"), WrittenAt(Position(3, 16)),
235 DeclAt(Position(4, 11)))));
237 Opts
.IndexFunctionLocals
= false;
238 Index
->Symbols
.clear();
239 tooling::runToolOnCode(std::make_unique
<IndexAction
>(Index
, Opts
), Code
);
240 EXPECT_THAT(Index
->Symbols
, Not(Contains(QName("theLabel"))));
243 TEST(IndexTest
, IndexExplicitTemplateInstantiation
) {
244 std::string Code
= R
"cpp(
245 template <typename T>
246 struct Foo { void bar() {} };
248 struct Foo<int> { void bar() {} };
254 auto Index
= std::make_shared
<Indexer
>();
255 IndexingOptions Opts
;
256 tooling::runToolOnCode(std::make_unique
<IndexAction
>(Index
, Opts
), Code
);
257 EXPECT_THAT(Index
->Symbols
,
258 AllOf(Contains(AllOf(QName("Foo"), WrittenAt(Position(8, 7)),
259 DeclAt(Position(5, 12)))),
260 Contains(AllOf(QName("Foo"), WrittenAt(Position(7, 7)),
261 DeclAt(Position(3, 12))))));
264 TEST(IndexTest
, IndexTemplateInstantiationPartial
) {
265 std::string Code
= R
"cpp(
266 template <typename T1, typename T2>
267 struct Foo { void bar() {} };
268 template <typename T>
269 struct Foo<T, int> { void bar() {} };
275 auto Index
= std::make_shared
<Indexer
>();
276 IndexingOptions Opts
;
277 tooling::runToolOnCode(std::make_unique
<IndexAction
>(Index
, Opts
), Code
);
278 EXPECT_THAT(Index
->Symbols
,
279 Contains(AllOf(QName("Foo"), WrittenAt(Position(8, 7)),
280 DeclAt(Position(5, 12)))));
283 TEST(IndexTest
, IndexTypeParmDecls
) {
284 std::string Code
= R
"cpp(
285 template <typename T, int I, template<typename> class C, typename NoRef>
291 auto Index
= std::make_shared
<Indexer
>();
292 IndexingOptions Opts
;
293 tooling::runToolOnCode(std::make_unique
<IndexAction
>(Index
, Opts
), Code
);
294 EXPECT_THAT(Index
->Symbols
, AllOf(Not(Contains(QName("Foo::T"))),
295 Not(Contains(QName("Foo::I"))),
296 Not(Contains(QName("Foo::C"))),
297 Not(Contains(QName("Foo::NoRef")))));
299 Opts
.IndexTemplateParameters
= true;
300 Index
->Symbols
.clear();
301 tooling::runToolOnCode(std::make_unique
<IndexAction
>(Index
, Opts
), Code
);
302 EXPECT_THAT(Index
->Symbols
,
303 AllOf(Contains(AllOf(QName("Foo::T"),
304 Kind(SymbolKind::TemplateTypeParm
))),
305 Contains(AllOf(QName("Foo::I"),
306 Kind(SymbolKind::NonTypeTemplateParm
))),
307 Contains(AllOf(QName("Foo::C"),
308 Kind(SymbolKind::TemplateTemplateParm
))),
309 Contains(QName("Foo::NoRef"))));
312 TEST(IndexTest
, UsingDecls
) {
313 std::string Code
= R
"cpp(
319 auto Index
= std::make_shared
<Indexer
>();
320 IndexingOptions Opts
;
321 tooling::runToolOnCode(std::make_unique
<IndexAction
>(Index
, Opts
), Code
);
322 EXPECT_THAT(Index
->Symbols
,
323 Contains(AllOf(QName("std::foo"), Kind(SymbolKind::Using
))));
326 TEST(IndexTest
, Constructors
) {
327 std::string Code
= R
"cpp(
333 auto Index
= std::make_shared
<Indexer
>();
334 IndexingOptions Opts
;
335 tooling::runToolOnCode(std::make_unique
<IndexAction
>(Index
, Opts
), Code
);
338 UnorderedElementsAre(
339 AllOf(QName("Foo"), Kind(SymbolKind::Struct
),
340 WrittenAt(Position(2, 12))),
341 AllOf(QName("Foo::Foo"), Kind(SymbolKind::Constructor
),
342 WrittenAt(Position(3, 7))),
343 AllOf(QName("Foo"), Kind(SymbolKind::Struct
),
344 HasRole(SymbolRole::NameReference
), WrittenAt(Position(3, 7))),
345 AllOf(QName("Foo::~Foo"), Kind(SymbolKind::Destructor
),
346 WrittenAt(Position(4, 7))),
347 AllOf(QName("Foo"), Kind(SymbolKind::Struct
),
348 HasRole(SymbolRole::NameReference
),
349 WrittenAt(Position(4, 8)))));
352 TEST(IndexTest
, InjecatedNameClass
) {
353 std::string Code
= R
"cpp(
354 template <typename T>
359 auto Index
= std::make_shared
<Indexer
>();
360 IndexingOptions Opts
;
361 tooling::runToolOnCode(std::make_unique
<IndexAction
>(Index
, Opts
), Code
);
362 EXPECT_THAT(Index
->Symbols
,
363 UnorderedElementsAre(AllOf(QName("Foo"), Kind(SymbolKind::Class
),
364 WrittenAt(Position(3, 11))),
365 AllOf(QName("Foo::f"),
366 Kind(SymbolKind::InstanceMethod
),
367 WrittenAt(Position(4, 12))),
368 AllOf(QName("Foo"), Kind(SymbolKind::Class
),
369 HasRole(SymbolRole::Reference
),
370 WrittenAt(Position(4, 14)))));
373 TEST(IndexTest
, VisitDefaultArgs
) {
374 std::string Code
= R
"cpp(
376 void f(int s = var) {}
378 auto Index
= std::make_shared
<Indexer
>();
379 IndexingOptions Opts
;
380 Opts
.IndexFunctionLocals
= true;
381 Opts
.IndexParametersInDeclarations
= true;
382 tooling::runToolOnCode(std::make_unique
<IndexAction
>(Index
, Opts
), Code
);
383 EXPECT_THAT(Index
->Symbols
,
384 Contains(AllOf(QName("var"), HasRole(SymbolRole::Reference
),
385 WrittenAt(Position(3, 20)))));
388 TEST(IndexTest
, RelationBaseOf
) {
389 std::string Code
= R
"cpp(
391 template <typename> class B {};
394 auto Index
= std::make_shared
<Indexer
>();
395 tooling::runToolOnCode(std::make_unique
<IndexAction
>(Index
), Code
);
396 // A should not be the base of anything.
397 EXPECT_THAT(Index
->Symbols
,
398 Contains(AllOf(QName("A"), HasRole(SymbolRole::Reference
),
399 Not(HasRole(SymbolRole::RelationBaseOf
)))));
402 TEST(IndexTest
, EnumBase
) {
403 std::string Code
= R
"cpp(
404 typedef int MyTypedef;
405 enum Foo : MyTypedef;
406 enum Foo : MyTypedef {};
408 auto Index
= std::make_shared
<Indexer
>();
409 tooling::runToolOnCode(std::make_unique
<IndexAction
>(Index
), Code
);
412 AllOf(Contains(AllOf(QName("MyTypedef"), HasRole(SymbolRole::Reference
),
413 WrittenAt(Position(3, 16)))),
414 Contains(AllOf(QName("MyTypedef"), HasRole(SymbolRole::Reference
),
415 WrittenAt(Position(4, 16))))));
418 TEST(IndexTest
, NonTypeTemplateParameter
) {
419 std::string Code
= R
"cpp(
420 enum class Foobar { foo };
422 constexpr void func() {}
424 auto Index
= std::make_shared
<Indexer
>();
425 tooling::runToolOnCode(std::make_unique
<IndexAction
>(Index
), Code
);
426 EXPECT_THAT(Index
->Symbols
,
427 Contains(AllOf(QName("Foobar"), HasRole(SymbolRole::Reference
),
428 WrittenAt(Position(3, 15)))));
431 TEST(IndexTest
, ReadWriteRoles
) {
432 std::string Code
= R
"cpp(
440 auto Index
= std::make_shared
<Indexer
>();
441 IndexingOptions Opts
;
442 Opts
.IndexFunctionLocals
= true;
443 tooling::runToolOnCode(std::make_unique
<IndexAction
>(Index
, Opts
), Code
);
446 AllOf(Contains(AllOf(QName("foo"), HasRole(SymbolRole::Write
),
447 WrittenAt(Position(4, 7)))),
448 Contains(AllOf(QName("foo"),
449 HasRole(static_cast<unsigned>(SymbolRole::Read
) |
450 static_cast<unsigned>(SymbolRole::Write
)),
451 WrittenAt(Position(5, 7)))),
452 Contains(AllOf(QName("foo"), HasRole(SymbolRole::Read
),
453 WrittenAt(Position(6, 17))))));