1 | |
2 | |
3 | |
4 | |
5 | |
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 | |
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(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 | |
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 | |
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 | |
63 | }; |
64 | |
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: |
72 | void initialize(ASTContext &Ctx) override { |
73 | AST = &Ctx; |
74 | IndexDataConsumer::initialize(Ctx); |
75 | } |
76 | |
77 | bool handleDeclOccurence(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 | |
94 | bool handleMacroOccurence(const IdentifierInfo *Name, const MacroInfo *MI, |
95 | SymbolRoleSet Roles, SourceLocation Loc) override { |
96 | TestSymbol S; |
97 | S.SymInfo = getSymbolInfoForMacro(*MI); |
98 | S.QName = 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: |
113 | IndexAction(std::shared_ptr<Indexer> Index, |
114 | IndexingOptions Opts = IndexingOptions()) |
115 | : Index(std::move(Index)), Opts(Opts) {} |
116 | |
117 | protected: |
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 llvm::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 | |
156 | TEST(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 | |
162 | TEST(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 | |
176 | TEST(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 | |
191 | TEST(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(8, 7)), |
207 | DeclAt(Position(5, 12)))), |
208 | Contains(AllOf(QName("Foo"), WrittenAt(Position(7, 7)), |
209 | DeclAt(Position(3, 12)))))); |
210 | } |
211 | |
212 | TEST(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(8, 7)), |
228 | DeclAt(Position(5, 12))))); |
229 | } |
230 | |
231 | TEST(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 | |
255 | TEST(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 | |
269 | TEST(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(2, 12))), |
284 | AllOf(QName("Foo::Foo"), Kind(SymbolKind::Constructor), |
285 | WrittenAt(Position(3, 7))), |
286 | AllOf(QName("Foo"), Kind(SymbolKind::Struct), |
287 | HasRole(SymbolRole::NameReference), WrittenAt(Position(3, 7))), |
288 | AllOf(QName("Foo::~Foo"), Kind(SymbolKind::Destructor), |
289 | WrittenAt(Position(4, 7))), |
290 | AllOf(QName("Foo"), Kind(SymbolKind::Struct), |
291 | HasRole(SymbolRole::NameReference), |
292 | WrittenAt(Position(4, 8))))); |
293 | } |
294 | |
295 | } |
296 | } |
297 | } |
298 | |