xref: /llvm-project/clang/unittests/Index/IndexTests.cpp (revision 51094545e28d222dc11eea513904506624d715c6)
1 //===--- IndexTests.cpp - Test indexing actions -----------------*- C++ -*-===//
2 //
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
6 //
7 //===----------------------------------------------------------------------===//
8 
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"
25 #include <memory>
26 
27 namespace clang {
28 namespace index {
29 namespace {
30 struct Position {
31   size_t Line = 0;
32   size_t Column = 0;
33 
Positionclang::index::__anon37c2cdbb0111::Position34   Position(size_t Line = 0, size_t Column = 0) : Line(Line), Column(Column) {}
35 
fromSourceLocationclang::index::__anon37c2cdbb0111::Position36   static Position fromSourceLocation(SourceLocation Loc,
37                                      const SourceManager &SM) {
38     FileID FID;
39     unsigned Offset;
40     std::tie(FID, Offset) = SM.getDecomposedSpellingLoc(Loc);
41     Position P;
42     P.Line = SM.getLineNumber(FID, Offset);
43     P.Column = SM.getColumnNumber(FID, Offset);
44     return P;
45   }
46 };
47 
operator ==(const Position & LHS,const Position & RHS)48 bool operator==(const Position &LHS, const Position &RHS) {
49   return std::tie(LHS.Line, LHS.Column) == std::tie(RHS.Line, RHS.Column);
50 }
51 
operator <<(llvm::raw_ostream & OS,const Position & Pos)52 llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Position &Pos) {
53   return OS << Pos.Line << ':' << Pos.Column;
54 }
55 
56 struct TestSymbol {
57   std::string QName;
58   Position WrittenPos;
59   Position DeclPos;
60   SymbolInfo SymInfo;
61   SymbolRoleSet Roles;
62   // FIXME: add more information.
63 };
64 
operator <<(llvm::raw_ostream & OS,const TestSymbol & S)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) << ')';
68 }
69 
70 class Indexer : public IndexDataConsumer {
71 public:
initialize(ASTContext & Ctx)72   void initialize(ASTContext &Ctx) override {
73     AST = &Ctx;
74     IndexDataConsumer::initialize(Ctx);
75   }
76 
handleDeclOccurrence(const Decl * D,SymbolRoleSet Roles,ArrayRef<SymbolRelation>,SourceLocation Loc,ASTNodeInfo)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);
81     if (!ND)
82       return true;
83     TestSymbol S;
84     S.SymInfo = getSymbolInfo(D);
85     S.QName = ND->getQualifiedNameAsString();
86     S.WrittenPos = Position::fromSourceLocation(Loc, AST->getSourceManager());
87     S.DeclPos =
88         Position::fromSourceLocation(D->getLocation(), AST->getSourceManager());
89     S.Roles = Roles;
90     Symbols.push_back(std::move(S));
91     return true;
92   }
93 
handleMacroOccurrence(const IdentifierInfo * Name,const MacroInfo * MI,SymbolRoleSet Roles,SourceLocation Loc)94   bool handleMacroOccurrence(const IdentifierInfo *Name, const MacroInfo *MI,
95                              SymbolRoleSet Roles, SourceLocation Loc) override {
96     TestSymbol S;
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());
102     S.Roles = Roles;
103     Symbols.push_back(std::move(S));
104     return true;
105   }
106 
107   std::vector<TestSymbol> Symbols;
108   const ASTContext *AST = nullptr;
109 };
110 
111 class IndexAction : public ASTFrontendAction {
112 public:
IndexAction(std::shared_ptr<Indexer> Index,IndexingOptions Opts=IndexingOptions ())113   IndexAction(std::shared_ptr<Indexer> Index,
114               IndexingOptions Opts = IndexingOptions())
115       : Index(std::move(Index)), Opts(Opts) {}
116 
117 protected:
CreateASTConsumer(CompilerInstance & CI,StringRef InFile)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;
124 
125     public:
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) {}
129 
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);
135       }
136     };
137     return std::make_unique<Consumer>(Index, CI.getPreprocessorPtr(), Opts);
138   }
139 
140 private:
141   std::shared_ptr<Indexer> Index;
142   IndexingOptions Opts;
143 };
144 
145 using testing::AllOf;
146 using testing::Contains;
147 using testing::Not;
148 using testing::UnorderedElementsAre;
149 
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); }
155 
TEST(IndexTest,Simple)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")));
161 }
162 
TEST(IndexTest,IndexPreprocessorMacros)163 TEST(IndexTest, IndexPreprocessorMacros) {
164   std::string Code = R"cpp(
165     #define INDEX_MAC 1
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
171   )cpp";
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))));
180   EXPECT_THAT(
181       Index->Symbols,
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)))));
188   EXPECT_THAT(
189       Index->Symbols,
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)))));
199 
200   Opts.IndexMacrosInPreprocessor = false;
201   Index->Symbols.clear();
202   tooling::runToolOnCode(std::make_unique<IndexAction>(Index, Opts), Code);
203   EXPECT_THAT(Index->Symbols, UnorderedElementsAre());
204 }
205 
TEST(IndexTest,IndexParametersInDecls)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")));
214 
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"))));
219 }
220 
TEST(IndexTest,IndexLabels)221 TEST(IndexTest, IndexLabels) {
222   std::string Code = R"cpp(
223         int main() {
224           goto theLabel;
225           theLabel:
226             return 1;
227         }
228       )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)))));
236 
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"))));
241 }
242 
TEST(IndexTest,IndexExplicitTemplateInstantiation)243 TEST(IndexTest, IndexExplicitTemplateInstantiation) {
244   std::string Code = R"cpp(
245     template <typename T>
246     struct Foo { void bar() {} };
247     template <>
248     struct Foo<int> { void bar() {} };
249     void foo() {
250       Foo<char> abc;
251       Foo<int> b;
252     }
253   )cpp";
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))))));
262 }
263 
TEST(IndexTest,IndexTemplateInstantiationPartial)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() {} };
270     void foo() {
271       Foo<char, char> abc;
272       Foo<int, int> b;
273     }
274   )cpp";
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)))));
281 }
282 
TEST(IndexTest,IndexTypeParmDecls)283 TEST(IndexTest, IndexTypeParmDecls) {
284   std::string Code = R"cpp(
285     template <typename T, int I, template<typename> class C, typename NoRef>
286     struct Foo {
287       T t = I;
288       C<int> x;
289     };
290   )cpp";
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")))));
298 
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"))));
310 }
311 
TEST(IndexTest,UsingDecls)312 TEST(IndexTest, UsingDecls) {
313   std::string Code = R"cpp(
314     void foo(int bar);
315     namespace std {
316       using ::foo;
317     }
318   )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))));
324 }
325 
TEST(IndexTest,Constructors)326 TEST(IndexTest, Constructors) {
327   std::string Code = R"cpp(
328     struct Foo {
329       Foo(int);
330       ~Foo();
331     };
332   )cpp";
333   auto Index = std::make_shared<Indexer>();
334   IndexingOptions Opts;
335   tooling::runToolOnCode(std::make_unique<IndexAction>(Index, Opts), Code);
336   EXPECT_THAT(
337       Index->Symbols,
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)))));
350 }
351 
TEST(IndexTest,InjecatedNameClass)352 TEST(IndexTest, InjecatedNameClass) {
353   std::string Code = R"cpp(
354     template <typename T>
355     class Foo {
356       void f(Foo x);
357     };
358   )cpp";
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)))));
371 }
372 
TEST(IndexTest,VisitDefaultArgs)373 TEST(IndexTest, VisitDefaultArgs) {
374   std::string Code = R"cpp(
375     int var = 0;
376     void f(int s = var) {}
377   )cpp";
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)))));
386 }
387 
TEST(IndexTest,RelationBaseOf)388 TEST(IndexTest, RelationBaseOf) {
389   std::string Code = R"cpp(
390     class A {};
391     template <typename> class B {};
392     class C : B<A> {};
393   )cpp";
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)))));
400 }
401 
TEST(IndexTest,EnumBase)402 TEST(IndexTest, EnumBase) {
403   std::string Code = R"cpp(
404     typedef int MyTypedef;
405     enum Foo : MyTypedef;
406     enum Foo : MyTypedef {};
407   )cpp";
408   auto Index = std::make_shared<Indexer>();
409   tooling::runToolOnCode(std::make_unique<IndexAction>(Index), Code);
410   EXPECT_THAT(
411       Index->Symbols,
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))))));
416 }
417 
TEST(IndexTest,NonTypeTemplateParameter)418 TEST(IndexTest, NonTypeTemplateParameter) {
419   std::string Code = R"cpp(
420     enum class Foobar { foo };
421     template <Foobar f>
422     constexpr void func() {}
423   )cpp";
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)))));
429 }
430 
TEST(IndexTest,ReadWriteRoles)431 TEST(IndexTest, ReadWriteRoles) {
432   std::string Code = R"cpp(
433     int main() {
434       int foo = 0;
435       foo = 2;
436       foo += 1;
437       int bar = foo;
438   }
439   )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);
444   EXPECT_THAT(
445       Index->Symbols,
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))))));
454 }
455 
456 } // namespace
457 } // namespace index
458 } // namespace clang
459