Clang Project

clang_source_code/unittests/Index/IndexTests.cpp
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
27namespace clang {
28namespace index {
29namespace {
30struct Position {
31  size_t Line = 0;
32  size_t Column = 0;
33
34  Position(size_t Line = 0, size_t Column = 0) : Line(Line), Column(Column) {}
35
36  static Position fromSourceLocation(SourceLocation Loc,
37                                     const SourceManager &SM) {
38    FileID FID;
39    unsigned Offset;
40    std::tie(FIDOffset) = SM.getDecomposedSpellingLoc(Loc);
41    Position P;
42    P.Line = SM.getLineNumber(FIDOffset);
43    P.Column = SM.getColumnNumber(FIDOffset);
44    return P;
45  }
46};
47
48bool operator==(const Position &LHSconst Position &RHS) {
49  return std::tie(LHS.Line, LHS.Column) == std::tie(RHS.Line, RHS.Column);
50}
51
52llvm::raw_ostream &operator<<(llvm::raw_ostream &OSconst Position &Pos) {
53  return OS << Pos.Line << ':' << Pos.Column;
54}
55
56struct TestSymbol {
57  std::string QName;
58  Position WrittenPos;
59  Position DeclPos;
60  SymbolInfo SymInfo;
61  SymbolRoleSet Roles;
62  // FIXME: add more information.
63};
64
65llvm::raw_ostream &operator<<(llvm::raw_ostream &OSconst TestSymbol &S) {
66  return OS << S.QName << '[' << S.WrittenPos << ']' << '@' << S.DeclPos << '('
67            << static_cast<unsigned>(S.SymInfo.Kind) << ')';
68}
69
70class Indexer : public IndexDataConsumer {
71public:
72  void initialize(ASTContext &Ctx) override {
73    AST = &Ctx;
74    IndexDataConsumer::initialize(Ctx);
75  }
76
77  bool handleDeclOccurence(const Decl *DSymbolRoleSet 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(LocAST->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
94  bool handleMacroOccurence(const IdentifierInfo *Nameconst MacroInfo *MI,
95                            SymbolRoleSet RolesSourceLocation Loc) override {
96    TestSymbol S;
97    S.SymInfo = getSymbolInfoForMacro(*MI);
98    S.QName = Name->getName();
99    S.WrittenPos = Position::fromSourceLocation(LocAST->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<TestSymbolSymbols;
108  const ASTContext *AST = nullptr;
109};
110
111class IndexAction : public ASTFrontendAction {
112public:
113  IndexAction(std::shared_ptr<IndexerIndex,
114              IndexingOptions Opts = IndexingOptions())
115      : Index(std::move(Index)), Opts(Opts) {}
116
117protected:
118  std::unique_ptr<ASTConsumerCreateASTConsumer(CompilerInstance &CI,
119                                                 StringRef InFile) override {
120    class Consumer : public ASTConsumer {
121      std::shared_ptr<IndexerIndex;
122      std::shared_ptr<PreprocessorPP;
123      IndexingOptions Opts;
124
125    public:
126      Consumer(std::shared_ptr<IndexerIndexstd::shared_ptr<PreprocessorPP,
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, *PPDeclsToIndex, *IndexOpts);
135      }
136    };
137    return llvm::make_unique<Consumer>(Index, CI.getPreprocessorPtr(), Opts);
138  }
139
140private:
141  std::shared_ptr<IndexerIndex;
142  IndexingOptions Opts;
143};
144
145using testing::AllOf;
146using testing::Contains;
147using testing::Not;
148using testing::UnorderedElementsAre;
149
150MATCHER_P(QName, Name, "") { return arg.QName == Name; }
151MATCHER_P(WrittenAt, Pos, "") { return arg.WrittenPos == Pos; }
152MATCHER_P(DeclAt, Pos, "") { return arg.DeclPos == Pos; }
153MATCHER_P(Kind, SymKind, "") { return arg.SymInfo.Kind == SymKind; }
154MATCHER_P(HasRole, Role, "") { return arg.Roles & static_cast<unsigned>(Role); }
155
156TEST(IndexTest, Simple) {
157  auto Index = std::make_shared<Indexer>();
158  tooling::runToolOnCode(new IndexAction(Index), "class X {}; void f() {}");
159  EXPECT_THAT(Index->Symbols, UnorderedElementsAre(QName("X"), QName("f")));
160}
161
162TEST(IndexTest, IndexPreprocessorMacros) {
163  std::string Code = "#define INDEX_MAC 1";
164  auto Index = std::make_shared<Indexer>();
165  IndexingOptions Opts;
166  Opts.IndexMacrosInPreprocessor = true;
167  tooling::runToolOnCode(new IndexAction(Index, Opts), Code);
168  EXPECT_THAT(Index->Symbols, Contains(QName("INDEX_MAC")));
169
170  Opts.IndexMacrosInPreprocessor = false;
171  Index->Symbols.clear();
172  tooling::runToolOnCode(new IndexAction(Index, Opts), Code);
173  EXPECT_THAT(Index->Symbols, UnorderedElementsAre());
174}
175
176TEST(IndexTest, IndexParametersInDecls) {
177  std::string Code = "void foo(int bar);";
178  auto Index = std::make_shared<Indexer>();
179  IndexingOptions Opts;
180  Opts.IndexFunctionLocals = true;
181  Opts.IndexParametersInDeclarations = true;
182  tooling::runToolOnCode(new IndexAction(Index, Opts), Code);
183  EXPECT_THAT(Index->Symbols, Contains(QName("bar")));
184
185  Opts.IndexParametersInDeclarations = false;
186  Index->Symbols.clear();
187  tooling::runToolOnCode(new IndexAction(Index, Opts), Code);
188  EXPECT_THAT(Index->Symbols, Not(Contains(QName("bar"))));
189}
190
191TEST(IndexTest, IndexExplicitTemplateInstantiation) {
192  std::string Code = R"cpp(
193    template <typename T>
194    struct Foo { void bar() {} };
195    template <>
196    struct Foo<int> { void bar() {} };
197    void foo() {
198      Foo<char> abc;
199      Foo<int> b;
200    }
201  )cpp";
202  auto Index = std::make_shared<Indexer>();
203  IndexingOptions Opts;
204  tooling::runToolOnCode(new IndexAction(Index, Opts), Code);
205  EXPECT_THAT(Index->Symbols,
206              AllOf(Contains(AllOf(QName("Foo"), WrittenAt(Position(87)),
207                                   DeclAt(Position(512)))),
208                    Contains(AllOf(QName("Foo"), WrittenAt(Position(77)),
209                                   DeclAt(Position(312))))));
210}
211
212TEST(IndexTest, IndexTemplateInstantiationPartial) {
213  std::string Code = R"cpp(
214    template <typename T1, typename T2>
215    struct Foo { void bar() {} };
216    template <typename T>
217    struct Foo<T, int> { void bar() {} };
218    void foo() {
219      Foo<char, char> abc;
220      Foo<int, int> b;
221    }
222  )cpp";
223  auto Index = std::make_shared<Indexer>();
224  IndexingOptions Opts;
225  tooling::runToolOnCode(new IndexAction(Index, Opts), Code);
226  EXPECT_THAT(Index->Symbols,
227              Contains(AllOf(QName("Foo"), WrittenAt(Position(87)),
228                             DeclAt(Position(512)))));
229}
230
231TEST(IndexTest, IndexTypeParmDecls) {
232  std::string Code = R"cpp(
233    template <typename T, int I, template<typename> class C, typename NoRef>
234    struct Foo {
235      T t = I;
236      C<int> x;
237    };
238  )cpp";
239  auto Index = std::make_shared<Indexer>();
240  IndexingOptions Opts;
241  tooling::runToolOnCode(new IndexAction(Index, Opts), Code);
242  EXPECT_THAT(Index->Symbols, AllOf(Not(Contains(QName("Foo::T"))),
243                                    Not(Contains(QName("Foo::I"))),
244                                    Not(Contains(QName("Foo::C"))),
245                                    Not(Contains(QName("Foo::NoRef")))));
246
247  Opts.IndexTemplateParameters = true;
248  Index->Symbols.clear();
249  tooling::runToolOnCode(new IndexAction(Index, Opts), Code);
250  EXPECT_THAT(Index->Symbols,
251              AllOf(Contains(QName("Foo::T")), Contains(QName("Foo::I")),
252                    Contains(QName("Foo::C")), Contains(QName("Foo::NoRef"))));
253}
254
255TEST(IndexTest, UsingDecls) {
256  std::string Code = R"cpp(
257    void foo(int bar);
258    namespace std {
259      using ::foo;
260    }
261  )cpp";
262  auto Index = std::make_shared<Indexer>();
263  IndexingOptions Opts;
264  tooling::runToolOnCode(new IndexAction(Index, Opts), Code);
265  EXPECT_THAT(Index->Symbols,
266              Contains(AllOf(QName("std::foo"), Kind(SymbolKind::Using))));
267}
268
269TEST(IndexTest, Constructors) {
270  std::string Code = R"cpp(
271    struct Foo {
272      Foo(int);
273      ~Foo();
274    };
275  )cpp";
276  auto Index = std::make_shared<Indexer>();
277  IndexingOptions Opts;
278  tooling::runToolOnCode(new IndexAction(Index, Opts), Code);
279  EXPECT_THAT(
280      Index->Symbols,
281      UnorderedElementsAre(
282          AllOf(QName("Foo"), Kind(SymbolKind::Struct),
283                WrittenAt(Position(212))),
284          AllOf(QName("Foo::Foo"), Kind(SymbolKind::Constructor),
285                WrittenAt(Position(37))),
286          AllOf(QName("Foo"), Kind(SymbolKind::Struct),
287                HasRole(SymbolRole::NameReference), WrittenAt(Position(37))),
288          AllOf(QName("Foo::~Foo"), Kind(SymbolKind::Destructor),
289                WrittenAt(Position(47))),
290          AllOf(QName("Foo"), Kind(SymbolKind::Struct),
291                HasRole(SymbolRole::NameReference),
292                WrittenAt(Position(48)))));
293}
294
295// namespace
296// namespace index
297// namespace clang
298