clang-tools 24.0.0git
SymbolCollectorTests.cpp
Go to the documentation of this file.
1//===-- SymbolCollectorTests.cpp -------------------------------*- 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 "Annotations.h"
10#include "FindSymbols.h"
11#include "TestFS.h"
12#include "TestTU.h"
13#include "URI.h"
14#include "clang-include-cleaner/Record.h"
16#include "clang/Basic/FileManager.h"
17#include "clang/Basic/FileSystemOptions.h"
18#include "clang/Basic/SourceLocation.h"
19#include "clang/Frontend/CompilerInstance.h"
20#include "clang/Index/IndexingAction.h"
21#include "clang/Index/IndexingOptions.h"
22#include "clang/Tooling/Tooling.h"
23#include "llvm/ADT/IntrusiveRefCntPtr.h"
24#include "llvm/ADT/StringRef.h"
25#include "llvm/Support/MemoryBuffer.h"
26#include "llvm/Support/VirtualFileSystem.h"
27#include "gmock/gmock-matchers.h"
28#include "gmock/gmock.h"
29#include "gtest/gtest.h"
30
31#include <memory>
32#include <optional>
33#include <string>
34#include <utility>
35
36namespace clang {
37namespace clangd {
38namespace {
39
40using ::testing::_;
41using ::testing::AllOf;
42using ::testing::Contains;
43using ::testing::Each;
44using ::testing::ElementsAre;
45using ::testing::Field;
46using ::testing::IsEmpty;
47using ::testing::Not;
48using ::testing::Pair;
49using ::testing::UnorderedElementsAre;
50using ::testing::UnorderedElementsAreArray;
51
52// GMock helpers for matching Symbol.
53MATCHER_P(labeled, Label, "") {
54 return (arg.Name + arg.Signature).str() == Label;
55}
56MATCHER_P(returnType, D, "") { return arg.ReturnType == D; }
57MATCHER_P(doc, D, "") { return arg.Documentation == D; }
58MATCHER_P(snippet, S, "") {
59 return (arg.Name + arg.CompletionSnippetSuffix).str() == S;
60}
61MATCHER_P(qName, Name, "") { return (arg.Scope + arg.Name).str() == Name; }
62MATCHER_P(templateArgs, TemplArgs, "") {
63 return arg.TemplateSpecializationArgs == TemplArgs;
64}
65MATCHER_P(hasKind, Kind, "") { return arg.SymInfo.Kind == Kind; }
66MATCHER_P(declURI, P, "") {
67 return StringRef(arg.CanonicalDeclaration.FileURI) == P;
68}
69MATCHER_P(defURI, P, "") { return StringRef(arg.Definition.FileURI) == P; }
70MATCHER(includeHeader, "") { return !arg.IncludeHeaders.empty(); }
71MATCHER_P(includeHeader, P, "") {
72 return (arg.IncludeHeaders.size() == 1) &&
73 (arg.IncludeHeaders.begin()->IncludeHeader == P);
74}
75MATCHER_P2(IncludeHeaderWithRef, includeHeader, References, "") {
76 return (arg.IncludeHeader == includeHeader) && (arg.References == References);
77}
78bool rangesMatch(const SymbolLocation &Loc, const Range &R) {
79 return std::make_tuple(Loc.Start.line(), Loc.Start.column(), Loc.End.line(),
80 Loc.End.column()) ==
81 std::make_tuple(R.start.line, R.start.character, R.end.line,
82 R.end.character);
83}
84MATCHER_P(declRange, Pos, "") {
85 return rangesMatch(arg.CanonicalDeclaration, Pos);
86}
87MATCHER_P(defRange, Pos, "") { return rangesMatch(arg.Definition, Pos); }
88MATCHER_P(refCount, R, "") { return int(arg.References) == R; }
89MATCHER_P(forCodeCompletion, IsIndexedForCodeCompletion, "") {
90 return static_cast<bool>(arg.Flags & Symbol::IndexedForCodeCompletion) ==
91 IsIndexedForCodeCompletion;
92}
93MATCHER(deprecated, "") { return arg.Flags & Symbol::Deprecated; }
94MATCHER(implementationDetail, "") {
95 return arg.Flags & Symbol::ImplementationDetail;
96}
97MATCHER(visibleOutsideFile, "") {
98 return static_cast<bool>(arg.Flags & Symbol::VisibleOutsideFile);
99}
100MATCHER(refRange, "") {
101 const Ref &Pos = ::testing::get<0>(arg);
102 const Range &Range = ::testing::get<1>(arg);
103 return rangesMatch(Pos.Location, Range);
104}
105MATCHER_P2(OverriddenBy, Subject, Object, "") {
106 return arg == Relation{Subject.ID, RelationKind::OverriddenBy, Object.ID};
107}
108MATCHER(isSpelled, "") {
109 return static_cast<bool>(arg.Kind & RefKind::Spelled);
110}
111::testing::Matcher<const std::vector<Ref> &>
112haveRanges(const std::vector<Range> Ranges) {
113 return ::testing::UnorderedPointwise(refRange(), Ranges);
114}
115
116class ShouldCollectSymbolTest : public ::testing::Test {
117public:
118 void build(llvm::StringRef HeaderCode, llvm::StringRef Code = "") {
119 File.HeaderFilename = HeaderName;
120 File.Filename = FileName;
121 File.HeaderCode = std::string(HeaderCode);
122 File.Code = std::string(Code);
123 AST = File.build();
124 }
125
126 // build() must have been called.
127 bool shouldCollect(llvm::StringRef Name, bool Qualified = true) {
128 assert(AST);
129 const NamedDecl &ND =
130 Qualified ? findDecl(*AST, Name) : findUnqualifiedDecl(*AST, Name);
131 const SourceManager &SM = AST->getSourceManager();
132 bool MainFile = isInsideMainFile(ND.getBeginLoc(), SM);
134 ND, AST->getASTContext(), SymbolCollector::Options(), MainFile);
135 }
136
137protected:
138 std::string HeaderName = "f.h";
139 std::string FileName = "f.cpp";
140 TestTU File;
141 std::optional<ParsedAST> AST; // Initialized after build.
142};
143
144TEST_F(ShouldCollectSymbolTest, ShouldCollectSymbol) {
145 build(R"(
146 namespace nx {
147 class X{};
148 auto f() { int Local; } // auto ensures function body is parsed.
149 struct { int x; } var;
150 }
151 )",
152 R"(
153 class InMain {};
154 namespace { class InAnonymous {}; }
155 static void g();
156 )");
157 auto AST = File.build();
158 EXPECT_TRUE(shouldCollect("nx"));
159 EXPECT_TRUE(shouldCollect("nx::X"));
160 EXPECT_TRUE(shouldCollect("nx::f"));
161 EXPECT_TRUE(shouldCollect("InMain"));
162 EXPECT_TRUE(shouldCollect("InAnonymous", /*Qualified=*/false));
163 EXPECT_TRUE(shouldCollect("g"));
164
165 EXPECT_FALSE(shouldCollect("Local", /*Qualified=*/false));
166}
167
168TEST_F(ShouldCollectSymbolTest, CollectLocalClassesAndVirtualMethods) {
169 build(R"(
170 namespace nx {
171 auto f() {
172 int Local;
173 auto LocalLambda = [&](){
174 Local++;
175 class ClassInLambda{};
176 return Local;
177 };
178 } // auto ensures function body is parsed.
179 auto foo() {
180 class LocalBase {
181 virtual void LocalVirtual();
182 void LocalConcrete();
183 int BaseMember;
184 };
185 }
186 } // namespace nx
187 )",
188 "");
189 auto AST = File.build();
190 EXPECT_FALSE(shouldCollect("Local", /*Qualified=*/false));
191 EXPECT_TRUE(shouldCollect("ClassInLambda", /*Qualified=*/false));
192 EXPECT_TRUE(shouldCollect("LocalBase", /*Qualified=*/false));
193 EXPECT_TRUE(shouldCollect("LocalVirtual", /*Qualified=*/false));
194 EXPECT_TRUE(shouldCollect("LocalConcrete", /*Qualified=*/false));
195 EXPECT_FALSE(shouldCollect("BaseMember", /*Qualified=*/false));
196 EXPECT_FALSE(shouldCollect("Local", /*Qualified=*/false));
197}
198
199TEST_F(ShouldCollectSymbolTest, NoPrivateProtoSymbol) {
200 HeaderName = "f.proto.h";
201 build(
202 R"(// Generated by the protocol buffer compiler. DO NOT EDIT!
203 namespace nx {
204 enum Outer_Enum : int {
205 Outer_Enum_KIND1,
206 Outer_Enum_Kind_2,
207 };
208 bool Outer_Enum_IsValid(int);
209
210 class Outer_Inner {};
211 class Outer {
212 using Inner = Outer_Inner;
213 using Enum = Outer_Enum;
214 static constexpr Enum KIND1 = Outer_Enum_KIND1;
215 static constexpr Enum Kind_2 = Outer_Enum_Kind_2;
216 static bool Enum_IsValid(int);
217 int &x();
218 void set_x();
219 void _internal_set_x();
220
221 int &Outer_y();
222 };
223 enum Foo {
224 FOO_VAL1,
225 Foo_VAL2,
226 };
227 bool Foo_IsValid(int);
228 })");
229
230 // Make sure all the mangled names for Outer::Enum is discarded.
231 EXPECT_FALSE(shouldCollect("nx::Outer_Enum"));
232 EXPECT_FALSE(shouldCollect("nx::Outer_Enum_KIND1"));
233 EXPECT_FALSE(shouldCollect("nx::Outer_Enum_Kind_2"));
234 EXPECT_FALSE(shouldCollect("nx::Outer_Enum_IsValid"));
235 // But nested aliases are preserved.
236 EXPECT_TRUE(shouldCollect("nx::Outer::Enum"));
237 EXPECT_TRUE(shouldCollect("nx::Outer::KIND1"));
238 EXPECT_TRUE(shouldCollect("nx::Outer::Kind_2"));
239 EXPECT_TRUE(shouldCollect("nx::Outer::Enum_IsValid"));
240
241 // Check for Outer::Inner.
242 EXPECT_FALSE(shouldCollect("nx::Outer_Inner"));
243 EXPECT_TRUE(shouldCollect("nx::Outer"));
244 EXPECT_TRUE(shouldCollect("nx::Outer::Inner"));
245
246 // Make sure field related information is preserved, unless it's explicitly
247 // marked with `_internal_`.
248 EXPECT_TRUE(shouldCollect("nx::Outer::x"));
249 EXPECT_TRUE(shouldCollect("nx::Outer::set_x"));
250 EXPECT_FALSE(shouldCollect("nx::Outer::_internal_set_x"));
251 EXPECT_TRUE(shouldCollect("nx::Outer::Outer_y"));
252
253 // Handling of a top-level enum
254 EXPECT_TRUE(shouldCollect("nx::Foo::FOO_VAL1"));
255 EXPECT_TRUE(shouldCollect("nx::FOO_VAL1"));
256 EXPECT_TRUE(shouldCollect("nx::Foo_IsValid"));
257 // Our heuristic goes wrong here, if the user has a nested name that starts
258 // with parent's name.
259 EXPECT_FALSE(shouldCollect("nx::Foo::Foo_VAL2"));
260 EXPECT_FALSE(shouldCollect("nx::Foo_VAL2"));
261}
262
263TEST_F(ShouldCollectSymbolTest, DoubleCheckProtoHeaderComment) {
264 HeaderName = "f.proto.h";
265 build(R"(
266 namespace nx {
267 class Top_Level {};
268 enum Kind {
269 Kind_Fine
270 };
271 }
272 )");
273 EXPECT_TRUE(shouldCollect("nx::Top_Level"));
274 EXPECT_TRUE(shouldCollect("nx::Kind_Fine"));
275}
276
277class SymbolIndexActionFactory : public tooling::FrontendActionFactory {
278public:
279 SymbolIndexActionFactory(SymbolCollector::Options COpts)
280 : COpts(std::move(COpts)) {}
281
282 std::unique_ptr<FrontendAction> create() override {
283 class IndexAction : public ASTFrontendAction {
284 public:
285 IndexAction(std::shared_ptr<index::IndexDataConsumer> DataConsumer,
286 const index::IndexingOptions &Opts,
287 std::shared_ptr<include_cleaner::PragmaIncludes> PI)
288 : DataConsumer(std::move(DataConsumer)), Opts(Opts),
289 PI(std::move(PI)) {}
290
291 std::unique_ptr<ASTConsumer>
292 CreateASTConsumer(CompilerInstance &CI, llvm::StringRef InFile) override {
293 PI->record(CI);
294 return createIndexingASTConsumer(DataConsumer, Opts,
295 CI.getPreprocessorPtr());
296 }
297
298 bool BeginInvocation(CompilerInstance &CI) override {
299 // Make the compiler parse all comments.
300 CI.getLangOpts().CommentOpts.ParseAllComments = true;
301 return true;
302 }
303
304 private:
305 std::shared_ptr<index::IndexDataConsumer> DataConsumer;
306 index::IndexingOptions Opts;
307 std::shared_ptr<include_cleaner::PragmaIncludes> PI;
308 };
309 index::IndexingOptions IndexOpts;
310 IndexOpts.SystemSymbolFilter =
311 index::IndexingOptions::SystemSymbolFilterKind::All;
312 IndexOpts.IndexFunctionLocals = true;
313 std::shared_ptr<include_cleaner::PragmaIncludes> PI =
314 std::make_shared<include_cleaner::PragmaIncludes>();
315 COpts.PragmaIncludes = PI.get();
316 Collector = std::make_shared<SymbolCollector>(COpts);
317 return std::make_unique<IndexAction>(Collector, std::move(IndexOpts),
318 std::move(PI));
319 }
320
321 std::shared_ptr<SymbolCollector> Collector;
322 SymbolCollector::Options COpts;
323};
324
325class SymbolCollectorTest : public ::testing::Test {
326public:
327 SymbolCollectorTest()
328 : InMemoryFileSystem(new llvm::vfs::InMemoryFileSystem),
329 TestHeaderName(testPath("symbol.h")),
330 TestFileName(testPath("symbol.cc")) {
331 TestHeaderURI = URI::create(TestHeaderName).toString();
332 TestFileURI = URI::create(TestFileName).toString();
333 }
334
335 // Note that unlike TestTU, no automatic header guard is added.
336 // HeaderCode should start with #pragma once to be treated as modular.
337 bool runSymbolCollector(llvm::StringRef HeaderCode, llvm::StringRef MainCode,
338 const std::vector<std::string> &ExtraArgs = {}) {
339 llvm::IntrusiveRefCntPtr<FileManager> Files(
340 new FileManager(FileSystemOptions(), InMemoryFileSystem));
341
342 auto Factory = std::make_unique<SymbolIndexActionFactory>(CollectorOpts);
343
344 std::vector<std::string> Args = {"symbol_collector", "-fsyntax-only",
345 "-xc++", "-include", TestHeaderName};
346 Args.insert(Args.end(), ExtraArgs.begin(), ExtraArgs.end());
347 // This allows to override the "-xc++" with something else, i.e.
348 // -xobjective-c++.
349 Args.push_back(TestFileName);
350
351 tooling::ToolInvocation Invocation(
352 Args, Factory->create(), Files.get(),
353 std::make_shared<PCHContainerOperations>());
354
355 // Multiple calls to runSymbolCollector with different contents will fail
356 // to update the filesystem! Why are we sharing one across tests, anyway?
357 EXPECT_TRUE(InMemoryFileSystem->addFile(
358 TestHeaderName, 0, llvm::MemoryBuffer::getMemBuffer(HeaderCode)));
359 EXPECT_TRUE(InMemoryFileSystem->addFile(
360 TestFileName, 0, llvm::MemoryBuffer::getMemBuffer(MainCode)));
361 Invocation.run();
362 Symbols = Factory->Collector->takeSymbols();
363 Refs = Factory->Collector->takeRefs();
364 Relations = Factory->Collector->takeRelations();
365 return true;
366 }
367
368protected:
369 llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem;
370 std::string TestHeaderName;
371 std::string TestHeaderURI;
372 std::string TestFileName;
373 std::string TestFileURI;
374 SymbolSlab Symbols;
375 RefSlab Refs;
376 RelationSlab Relations;
377 SymbolCollector::Options CollectorOpts;
378};
379
380TEST_F(SymbolCollectorTest, CollectSymbols) {
381 const std::string Header = R"(
382 class Foo {
383 Foo() {}
384 Foo(int a) {}
385 void f();
386 friend void f1();
387 friend class Friend;
388 Foo& operator=(const Foo&);
389 ~Foo();
390 class Nested {
391 void f();
392 };
393 };
394 class Friend {
395 };
396
397 void f1();
398 inline void f2() {}
399 static const int KInt = 2;
400 const char* kStr = "123";
401
402 namespace {
403 void ff() {} // ignore
404 }
405
406 void f1() {
407 auto LocalLambda = [&](){
408 class ClassInLambda{};
409 };
410 }
411
412 namespace foo {
413 // Type alias
414 typedef int int32;
415 using int32_t = int32;
416
417 // Variable
418 int v1;
419
420 // Namespace
421 namespace bar {
422 int v2;
423 }
424 // Namespace alias
425 namespace baz = bar;
426
427 using bar::v2;
428 } // namespace foo
429 )";
430 runSymbolCollector(Header, /*Main=*/"");
431 EXPECT_THAT(Symbols,
432 UnorderedElementsAreArray(
433 {AllOf(qName("Foo"), forCodeCompletion(true)),
434 AllOf(qName("Foo::Foo"), forCodeCompletion(false)),
435 AllOf(qName("Foo::Foo"), forCodeCompletion(false)),
436 AllOf(qName("Foo::f"), forCodeCompletion(false)),
437 AllOf(qName("Foo::~Foo"), forCodeCompletion(false)),
438 AllOf(qName("Foo::operator="), forCodeCompletion(false)),
439 AllOf(qName("Foo::Nested"), forCodeCompletion(false)),
440 AllOf(qName("Foo::Nested::f"), forCodeCompletion(false)),
441 AllOf(qName("ClassInLambda"), forCodeCompletion(false)),
442 AllOf(qName("Friend"), forCodeCompletion(true)),
443 AllOf(qName("f1"), forCodeCompletion(true)),
444 AllOf(qName("f2"), forCodeCompletion(true)),
445 AllOf(qName("KInt"), forCodeCompletion(true)),
446 AllOf(qName("kStr"), forCodeCompletion(true)),
447 AllOf(qName("foo"), forCodeCompletion(true)),
448 AllOf(qName("foo::bar"), forCodeCompletion(true)),
449 AllOf(qName("foo::int32"), forCodeCompletion(true)),
450 AllOf(qName("foo::int32_t"), forCodeCompletion(true)),
451 AllOf(qName("foo::v1"), forCodeCompletion(true)),
452 AllOf(qName("foo::bar::v2"), forCodeCompletion(true)),
453 AllOf(qName("foo::v2"), forCodeCompletion(true)),
454 AllOf(qName("foo::baz"), forCodeCompletion(true))}));
455}
456
457TEST_F(SymbolCollectorTest, FileLocal) {
458 const std::string Header = R"(
459 class Foo {};
460 namespace {
461 class Ignored {};
462 }
463 void bar();
464 )";
465 const std::string Main = R"(
466 class ForwardDecl;
467 void bar() {}
468 static void a();
469 class B {};
470 namespace {
471 void c();
472 }
473 )";
474 runSymbolCollector(Header, Main);
475 EXPECT_THAT(Symbols,
476 UnorderedElementsAre(
477 AllOf(qName("Foo"), visibleOutsideFile()),
478 AllOf(qName("bar"), visibleOutsideFile()),
479 AllOf(qName("a"), Not(visibleOutsideFile())),
480 AllOf(qName("B"), Not(visibleOutsideFile())),
481 AllOf(qName("c"), Not(visibleOutsideFile())),
482 // FIXME: ForwardDecl likely *is* visible outside.
483 AllOf(qName("ForwardDecl"), Not(visibleOutsideFile()))));
484}
485
486TEST_F(SymbolCollectorTest, Template) {
487 Annotations Header(R"(
488 // Primary template and explicit specialization are indexed, instantiation
489 // is not.
490 template <class T, class U> struct [[Tmpl]] {T $xdecl[[x]] = 0;};
491 template <> struct $specdecl[[Tmpl]]<int, bool> {};
492 template <class U> struct $partspecdecl[[Tmpl]]<bool, U> {};
493 extern template struct Tmpl<float, bool>;
494 template struct Tmpl<double, bool>;
495 )");
496 runSymbolCollector(Header.code(), /*Main=*/"");
497 EXPECT_THAT(Symbols,
498 UnorderedElementsAre(
499 AllOf(qName("Tmpl"), declRange(Header.range()),
500 forCodeCompletion(true)),
501 AllOf(qName("Tmpl"), declRange(Header.range("specdecl")),
502 forCodeCompletion(false)),
503 AllOf(qName("Tmpl"), declRange(Header.range("partspecdecl")),
504 forCodeCompletion(false)),
505 AllOf(qName("Tmpl::x"), declRange(Header.range("xdecl")),
506 forCodeCompletion(false))));
507}
508
509TEST_F(SymbolCollectorTest, templateArgs) {
510 Annotations Header(R"(
511 template <class X> class $barclasstemp[[Bar]] {};
512 template <class T, class U, template<typename> class Z, int Q>
513 struct [[Tmpl]] { T $xdecl[[x]] = 0; };
514
515 // template-template, non-type and type full spec
516 template <> struct $specdecl[[Tmpl]]<int, bool, Bar, 3> {};
517
518 // template-template, non-type and type partial spec
519 template <class U, int T> struct $partspecdecl[[Tmpl]]<bool, U, Bar, T> {};
520 // instantiation
521 extern template struct Tmpl<float, bool, Bar, 8>;
522 // instantiation
523 template struct Tmpl<double, bool, Bar, 2>;
524
525 template <typename ...> class $fooclasstemp[[Foo]] {};
526 // parameter-packs full spec
527 template<> class $parampack[[Foo]]<Bar<int>, int, double> {};
528 // parameter-packs partial spec
529 template<class T> class $parampackpartial[[Foo]]<T, T> {};
530
531 template <int ...> class $bazclasstemp[[Baz]] {};
532 // non-type parameter-packs full spec
533 template<> class $parampacknontype[[Baz]]<3, 5, 8> {};
534 // non-type parameter-packs partial spec
535 template<int T> class $parampacknontypepartial[[Baz]]<T, T> {};
536
537 template <template <class> class ...> class $fozclasstemp[[Foz]] {};
538 // template-template parameter-packs full spec
539 template<> class $parampacktempltempl[[Foz]]<Bar, Bar> {};
540 // template-template parameter-packs partial spec
541 template<template <class> class T>
542 class $parampacktempltemplpartial[[Foz]]<T, T> {};
543 )");
544 runSymbolCollector(Header.code(), /*Main=*/"");
545 EXPECT_THAT(
546 Symbols,
547 AllOf(
548 Contains(AllOf(qName("Tmpl"), templateArgs("<int, bool, Bar, 3>"),
549 declRange(Header.range("specdecl")),
550 forCodeCompletion(false))),
551 Contains(AllOf(qName("Tmpl"), templateArgs("<bool, U, Bar, T>"),
552 declRange(Header.range("partspecdecl")),
553 forCodeCompletion(false))),
554 Contains(AllOf(qName("Foo"), templateArgs("<Bar<int>, int, double>"),
555 declRange(Header.range("parampack")),
556 forCodeCompletion(false))),
557 Contains(AllOf(qName("Foo"), templateArgs("<T, T>"),
558 declRange(Header.range("parampackpartial")),
559 forCodeCompletion(false))),
560 Contains(AllOf(qName("Baz"), templateArgs("<3, 5, 8>"),
561 declRange(Header.range("parampacknontype")),
562 forCodeCompletion(false))),
563 Contains(AllOf(qName("Baz"), templateArgs("<T, T>"),
564 declRange(Header.range("parampacknontypepartial")),
565 forCodeCompletion(false))),
566 Contains(AllOf(qName("Foz"), templateArgs("<Bar, Bar>"),
567 declRange(Header.range("parampacktempltempl")),
568 forCodeCompletion(false))),
569 Contains(AllOf(qName("Foz"), templateArgs("<T, T>"),
570 declRange(Header.range("parampacktempltemplpartial")),
571 forCodeCompletion(false)))));
572}
573
574TEST_F(SymbolCollectorTest, ObjCRefs) {
575 Annotations Header(R"(
576 @interface Person
577 - (void)$talk[[talk]];
578 - (void)$say[[say]]:(id)something;
579 @end
580 @interface Person (Category)
581 - (void)categoryMethod;
582 - (void)multiArg:(id)a method:(id)b;
583 @end
584 )");
585 Annotations Main(R"(
586 @implementation Person
587 - (void)$talk[[talk]] {}
588 - (void)$say[[say]]:(id)something {}
589 @end
590
591 void fff(Person *p) {
592 [p $talk[[talk]]];
593 [p $say[[say]]:0];
594 [p categoryMethod];
595 [p multiArg:0 method:0];
596 }
597 )");
598 CollectorOpts.RefFilter = RefKind::All;
599 CollectorOpts.CollectMainFileRefs = true;
600 TestFileName = testPath("test.m");
601 runSymbolCollector(Header.code(), Main.code(),
602 {"-fblocks", "-xobjective-c++", "-Wno-objc-root-class"});
603 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Person::talk").ID,
604 haveRanges(Main.ranges("talk")))));
605 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Person::say:").ID,
606 haveRanges(Main.ranges("say")))));
607 EXPECT_THAT(Refs,
608 Contains(Pair(findSymbol(Symbols, "Person::categoryMethod").ID,
609 ElementsAre(isSpelled()))));
610 EXPECT_THAT(Refs,
611 Contains(Pair(findSymbol(Symbols, "Person::multiArg:method:").ID,
612 ElementsAre(isSpelled()))));
613}
614
615TEST_F(SymbolCollectorTest, ObjCSymbols) {
616 const std::string Header = R"(
617 @interface Person
618 - (void)someMethodName:(void*)name1 lastName:(void*)lName;
619 @end
620
621 @implementation Person
622 - (void)someMethodName:(void*)name1 lastName:(void*)lName{
623 int foo;
624 ^(int param){ int bar; };
625 }
626 @end
627
628 @interface Person (MyCategory)
629 - (void)someMethodName2:(void*)name2;
630 @end
631
632 @implementation Person (MyCategory)
633 - (void)someMethodName2:(void*)name2 {
634 int foo2;
635 }
636 @end
637
638 @protocol MyProtocol
639 - (void)someMethodName3:(void*)name3;
640 @end
641 )";
642 TestFileName = testPath("test.m");
643 runSymbolCollector(Header, /*Main=*/"", {"-fblocks", "-xobjective-c++"});
644 EXPECT_THAT(Symbols,
645 UnorderedElementsAre(
646 qName("Person"), qName("Person::someMethodName:lastName:"),
647 AllOf(qName("MyCategory"), forCodeCompletion(false)),
648 qName("Person::someMethodName2:"), qName("MyProtocol"),
649 qName("MyProtocol::someMethodName3:")));
650}
651
652TEST_F(SymbolCollectorTest, ObjCPropertyImpl) {
653 const std::string Header = R"(
654 @interface Container
655 @property(nonatomic) int magic;
656 @end
657
658 @implementation Container
659 @end
660 )";
661 TestFileName = testPath("test.m");
662 runSymbolCollector(Header, /*Main=*/"", {"-xobjective-c++"});
663 EXPECT_THAT(Symbols, Contains(qName("Container")));
664 EXPECT_THAT(Symbols, Contains(qName("Container::magic")));
665 // FIXME: Results also contain Container::_magic on some platforms.
666 // Figure out why it's platform-dependent.
667}
668
669TEST_F(SymbolCollectorTest, ObjCLocations) {
670 Annotations Header(R"(
671 // Declared in header, defined in main.
672 @interface $dogdecl[[Dog]]
673 @end
674 @interface $fluffydecl[[Dog]] (Fluffy)
675 @end
676 )");
677 Annotations Main(R"(
678 @interface Dog ()
679 @end
680 @implementation $dogdef[[Dog]]
681 @end
682 @implementation $fluffydef[[Dog]] (Fluffy)
683 @end
684 // Category with no declaration (only implementation).
685 @implementation $ruff[[Dog]] (Ruff)
686 @end
687 // Implicitly defined interface.
688 @implementation $catdog[[CatDog]]
689 @end
690 )");
691 runSymbolCollector(Header.code(), Main.code(),
692 {"-xobjective-c++", "-Wno-objc-root-class"});
693 EXPECT_THAT(Symbols,
694 UnorderedElementsAre(
695 AllOf(qName("Dog"), declRange(Header.range("dogdecl")),
696 defRange(Main.range("dogdef"))),
697 AllOf(qName("Fluffy"), declRange(Header.range("fluffydecl")),
698 defRange(Main.range("fluffydef"))),
699 AllOf(qName("CatDog"), declRange(Main.range("catdog")),
700 defRange(Main.range("catdog"))),
701 AllOf(qName("Ruff"), declRange(Main.range("ruff")),
702 defRange(Main.range("ruff")))));
703}
704
705TEST_F(SymbolCollectorTest, ObjCForwardDecls) {
706 Annotations Header(R"(
707 // Forward declared in header, declared and defined in main.
708 @protocol Barker;
709 @class Dog;
710 // Never fully declared so Clang latches onto this decl.
711 @class $catdogdecl[[CatDog]];
712 )");
713 Annotations Main(R"(
714 @protocol $barkerdecl[[Barker]]
715 - (void)woof;
716 @end
717 @interface $dogdecl[[Dog]]<Barker>
718 - (void)woof;
719 @end
720 @implementation $dogdef[[Dog]]
721 - (void)woof {}
722 @end
723 @implementation $catdogdef[[CatDog]]
724 @end
725 )");
726 runSymbolCollector(Header.code(), Main.code(),
727 {"-xobjective-c++", "-Wno-objc-root-class"});
728 EXPECT_THAT(Symbols,
729 UnorderedElementsAre(
730 AllOf(qName("CatDog"), declRange(Header.range("catdogdecl")),
731 defRange(Main.range("catdogdef"))),
732 AllOf(qName("Dog"), declRange(Main.range("dogdecl")),
733 defRange(Main.range("dogdef"))),
734 AllOf(qName("Barker"), declRange(Main.range("barkerdecl"))),
735 qName("Barker::woof"), qName("Dog::woof")));
736}
737
738TEST_F(SymbolCollectorTest, ObjCClassExtensions) {
739 Annotations Header(R"(
740 @interface $catdecl[[Cat]]
741 @end
742 )");
743 Annotations Main(R"(
744 @interface Cat ()
745 - (void)meow;
746 @end
747 @interface Cat ()
748 - (void)pur;
749 @end
750 )");
751 runSymbolCollector(Header.code(), Main.code(),
752 {"-xobjective-c++", "-Wno-objc-root-class"});
753 EXPECT_THAT(Symbols,
754 UnorderedElementsAre(
755 AllOf(qName("Cat"), declRange(Header.range("catdecl"))),
756 qName("Cat::meow"), qName("Cat::pur")));
757}
758
759TEST_F(SymbolCollectorTest, ObjCFrameworkIncludeHeader) {
760 CollectorOpts.CollectIncludePath = true;
761 auto FrameworksPath = testPath("Frameworks/");
762 std::string FrameworkHeader = R"(
763 __attribute((objc_root_class))
764 @interface NSObject
765 @end
766 )";
767 InMemoryFileSystem->addFile(
768 testPath("Frameworks/Foundation.framework/Headers/NSObject.h"), 0,
769 llvm::MemoryBuffer::getMemBuffer(FrameworkHeader));
770 std::string PrivateFrameworkHeader = R"(
771 #import <Foundation/NSObject.h>
772
773 @interface PrivateClass : NSObject
774 @end
775 )";
776 InMemoryFileSystem->addFile(
777 testPath(
778 "Frameworks/Foundation.framework/PrivateHeaders/NSObject+Private.h"),
779 0, llvm::MemoryBuffer::getMemBuffer(PrivateFrameworkHeader));
780
781 std::string Header = R"(
782 #import <Foundation/NSObject+Private.h>
783 #import <Foundation/NSObject.h>
784
785 @interface Container : NSObject
786 @end
787 )";
788 std::string Main = "";
789 TestFileName = testPath("test.m");
790 runSymbolCollector(Header, Main, {"-F", FrameworksPath, "-xobjective-c++"});
791 EXPECT_THAT(
792 Symbols,
793 UnorderedElementsAre(
794 AllOf(qName("NSObject"), includeHeader("<Foundation/NSObject.h>")),
795 AllOf(qName("PrivateClass"),
796 includeHeader("<Foundation/NSObject+Private.h>")),
797 AllOf(qName("Container"))));
798
799 // After adding the umbrella headers, we should use that spelling instead.
800 std::string UmbrellaHeader = R"(
801 #import <Foundation/NSObject.h>
802 )";
803 InMemoryFileSystem->addFile(
804 testPath("Frameworks/Foundation.framework/Headers/Foundation.h"), 0,
805 llvm::MemoryBuffer::getMemBuffer(UmbrellaHeader));
806 std::string PrivateUmbrellaHeader = R"(
807 #import <Foundation/NSObject+Private.h>
808 )";
809 InMemoryFileSystem->addFile(
810 testPath("Frameworks/Foundation.framework/PrivateHeaders/"
811 "Foundation_Private.h"),
812 0, llvm::MemoryBuffer::getMemBuffer(PrivateUmbrellaHeader));
813 runSymbolCollector(Header, Main, {"-F", FrameworksPath, "-xobjective-c++"});
814 EXPECT_THAT(
815 Symbols,
816 UnorderedElementsAre(
817 AllOf(qName("NSObject"), includeHeader("<Foundation/Foundation.h>")),
818 AllOf(qName("PrivateClass"),
819 includeHeader("<Foundation/Foundation_Private.h>")),
820 AllOf(qName("Container"))));
821
822 runSymbolCollector(Header, Main,
823 {"-iframework", FrameworksPath, "-xobjective-c++"});
824 EXPECT_THAT(
825 Symbols,
826 UnorderedElementsAre(
827 AllOf(qName("NSObject"), includeHeader("<Foundation/Foundation.h>")),
828 AllOf(qName("PrivateClass"),
829 includeHeader("<Foundation/Foundation_Private.h>")),
830 AllOf(qName("Container"))));
831}
832
833TEST_F(SymbolCollectorTest, Locations) {
834 Annotations Header(R"cpp(
835 // Declared in header, defined in main.
836 extern int $xdecl[[X]];
837 class $clsdecl[[Cls]];
838 void $printdecl[[print]]();
839
840 // Declared in header, defined nowhere.
841 extern int $zdecl[[Z]];
842
843 void $foodecl[[fo\
844o]]();
845 )cpp");
846 Annotations Main(R"cpp(
847 int $xdef[[X]] = 42;
848 class $clsdef[[Cls]] {};
849 void $printdef[[print]]() {}
850
851 // Declared/defined in main only.
852 int $ydecl[[Y]];
853 )cpp");
854 runSymbolCollector(Header.code(), Main.code());
855 EXPECT_THAT(Symbols,
856 UnorderedElementsAre(
857 AllOf(qName("X"), declRange(Header.range("xdecl")),
858 defRange(Main.range("xdef"))),
859 AllOf(qName("Cls"), declRange(Header.range("clsdecl")),
860 defRange(Main.range("clsdef"))),
861 AllOf(qName("print"), declRange(Header.range("printdecl")),
862 defRange(Main.range("printdef"))),
863 AllOf(qName("Z"), declRange(Header.range("zdecl"))),
864 AllOf(qName("foo"), declRange(Header.range("foodecl"))),
865 AllOf(qName("Y"), declRange(Main.range("ydecl")))));
866}
867
868TEST_F(SymbolCollectorTest, Refs) {
869 Annotations Header(R"(
870 #define MACRO(X) (X + 1)
871 class Foo {
872 public:
873 Foo() {}
874 Foo(int);
875 };
876 class Bar;
877 void func();
878
879 namespace NS {} // namespace ref is ignored
880 )");
881 Annotations Main(R"(
882 class $bar[[Bar]] {};
883
884 void $func[[func]]();
885
886 void fff() {
887 $foo[[Foo]] foo;
888 $bar[[Bar]] bar;
889 $func[[func]]();
890 int abc = 0;
891 $foo[[Foo]] foo2 = abc;
892 abc = $macro[[MACRO]](1);
893 }
894 )");
895 Annotations SymbolsOnlyInMainCode(R"(
896 #define FUNC(X) (X+1)
897 int a;
898 void b() {}
899 static const int c = FUNC(1);
900 class d {};
901 )");
902 CollectorOpts.RefFilter = RefKind::All;
903 CollectorOpts.CollectMacro = true;
904 runSymbolCollector(Header.code(),
905 (Main.code() + SymbolsOnlyInMainCode.code()).str());
906 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Foo").ID,
907 haveRanges(Main.ranges("foo")))));
908 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Bar").ID,
909 haveRanges(Main.ranges("bar")))));
910 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "func").ID,
911 haveRanges(Main.ranges("func")))));
912 EXPECT_THAT(Refs, Not(Contains(Pair(findSymbol(Symbols, "NS").ID, _))));
913 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "MACRO").ID,
914 haveRanges(Main.ranges("macro")))));
915 // - (a, b) externally visible and should have refs.
916 // - (c, FUNC) externally invisible and had no refs collected.
917 auto MainSymbols =
918 TestTU::withHeaderCode(SymbolsOnlyInMainCode.code()).headerSymbols();
919 EXPECT_THAT(Refs, Contains(Pair(findSymbol(MainSymbols, "a").ID, _)));
920 EXPECT_THAT(Refs, Contains(Pair(findSymbol(MainSymbols, "b").ID, _)));
921 EXPECT_THAT(Refs, Not(Contains(Pair(findSymbol(MainSymbols, "c").ID, _))));
922 EXPECT_THAT(Refs, Not(Contains(Pair(findSymbol(MainSymbols, "FUNC").ID, _))));
923
924 // Run the collector again with CollectMainFileRefs = true.
925 // We need to recreate InMemoryFileSystem because runSymbolCollector()
926 // calls MemoryBuffer::getMemBuffer(), which makes the buffers unusable
927 // after runSymbolCollector() exits.
928 InMemoryFileSystem = new llvm::vfs::InMemoryFileSystem();
929 CollectorOpts.CollectMainFileRefs = true;
930 runSymbolCollector(Header.code(),
931 (Main.code() + SymbolsOnlyInMainCode.code()).str());
932 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "a").ID, _)));
933 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "b").ID, _)));
934 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "c").ID, _)));
935 // However, references to main-file macros are not collected.
936 EXPECT_THAT(Refs, Not(Contains(Pair(findSymbol(Symbols, "FUNC").ID, _))));
937}
938
939TEST_F(SymbolCollectorTest, RefContainers) {
940 Annotations Code(R"cpp(
941 int $toplevel1[[f1]](int);
942 void f2() {
943 (void) $ref1a[[f1]](1);
944 auto fptr = &$ref1b[[f1]];
945 }
946 int $toplevel2[[v1]] = $ref2[[f1]](2);
947 void f3(int arg = $ref3[[f1]](3));
948 struct S1 {
949 int $classscope1[[member1]] = $ref4[[f1]](4);
950 int $classscope2[[member2]] = 42;
951 };
952 constexpr int f4(int x) { return x + 1; }
953 template <int I = $ref5[[f4]](0)> struct S2 {};
954 S2<$ref6[[f4]](0)> v2;
955 S2<$ref7a[[f4]](0)> f5(S2<$ref7b[[f4]](0)>);
956 namespace N {
957 void $namespacescope1[[f6]]();
958 int $namespacescope2[[v3]];
959 }
960 )cpp");
961 CollectorOpts.RefFilter = RefKind::All;
962 CollectorOpts.CollectMainFileRefs = true;
963 runSymbolCollector("", Code.code());
964 auto FindRefWithRange = [&](Range R) -> std::optional<Ref> {
965 for (auto &Entry : Refs) {
966 for (auto &Ref : Entry.second) {
967 if (rangesMatch(Ref.Location, R))
968 return Ref;
969 }
970 }
971 return std::nullopt;
972 };
973 auto Container = [&](llvm::StringRef RangeName) {
974 auto Ref = FindRefWithRange(Code.range(RangeName));
975 EXPECT_TRUE(bool(Ref));
976 return Ref->Container;
977 };
978 EXPECT_EQ(Container("ref1a"),
979 findSymbol(Symbols, "f2").ID); // function body (call)
980 EXPECT_EQ(Container("ref1b"),
981 findSymbol(Symbols, "f2").ID); // function body (address-of)
982 EXPECT_EQ(Container("ref2"),
983 findSymbol(Symbols, "v1").ID); // variable initializer
984 EXPECT_EQ(Container("ref3"),
985 findSymbol(Symbols, "f3").ID); // function parameter default value
986 EXPECT_EQ(Container("ref4"),
987 findSymbol(Symbols, "S1::member1").ID); // member initializer
988 EXPECT_EQ(Container("ref5"),
989 findSymbol(Symbols, "S2").ID); // template parameter default value
990 EXPECT_EQ(Container("ref6"),
991 findSymbol(Symbols, "v2").ID); // type of variable
992 EXPECT_EQ(Container("ref7a"),
993 findSymbol(Symbols, "f5").ID); // return type of function
994 EXPECT_EQ(Container("ref7b"),
995 findSymbol(Symbols, "f5").ID); // parameter type of function
996
997 EXPECT_FALSE(Container("classscope1").isNull());
998 EXPECT_FALSE(Container("namespacescope1").isNull());
999
1000 EXPECT_EQ(Container("toplevel1"), Container("toplevel2"));
1001 EXPECT_EQ(Container("classscope1"), Container("classscope2"));
1002 EXPECT_EQ(Container("namespacescope1"), Container("namespacescope2"));
1003
1004 EXPECT_NE(Container("toplevel1"), Container("namespacescope1"));
1005 EXPECT_NE(Container("toplevel1"), Container("classscope1"));
1006 EXPECT_NE(Container("classscope1"), Container("namespacescope1"));
1007}
1008
1009TEST_F(SymbolCollectorTest, MacroRefInHeader) {
1010 Annotations Header(R"(
1011 #define $foo[[FOO]](X) (X + 1)
1012 #define $bar[[BAR]](X) (X + 2)
1013
1014 // Macro defined multiple times.
1015 #define $ud1[[UD]] 1
1016 int ud_1 = $ud1[[UD]];
1017 #undef UD
1018
1019 #define $ud2[[UD]] 2
1020 int ud_2 = $ud2[[UD]];
1021 #undef UD
1022
1023 // Macros from token concatenations not included.
1024 #define $concat[[CONCAT]](X) X##A()
1025 #define $prepend[[PREPEND]](X) MACRO##X()
1026 #define $macroa[[MACROA]]() 123
1027 int B = $concat[[CONCAT]](MACRO);
1028 int D = $prepend[[PREPEND]](A);
1029
1030 void fff() {
1031 int abc = $foo[[FOO]](1) + $bar[[BAR]]($foo[[FOO]](1));
1032 }
1033 )");
1034 CollectorOpts.RefFilter = RefKind::All;
1035 CollectorOpts.RefsInHeaders = true;
1036 // Need this to get the SymbolID for macros for tests.
1037 CollectorOpts.CollectMacro = true;
1038
1039 runSymbolCollector(Header.code(), "");
1040
1041 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "FOO").ID,
1042 haveRanges(Header.ranges("foo")))));
1043 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "BAR").ID,
1044 haveRanges(Header.ranges("bar")))));
1045 // No unique ID for multiple symbols named UD. Check for ranges only.
1046 EXPECT_THAT(Refs, Contains(Pair(_, haveRanges(Header.ranges("ud1")))));
1047 EXPECT_THAT(Refs, Contains(Pair(_, haveRanges(Header.ranges("ud2")))));
1048 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "CONCAT").ID,
1049 haveRanges(Header.ranges("concat")))));
1050 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "PREPEND").ID,
1051 haveRanges(Header.ranges("prepend")))));
1052 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "MACROA").ID,
1053 haveRanges(Header.ranges("macroa")))));
1054}
1055
1056TEST_F(SymbolCollectorTest, MacroRefWithoutCollectingSymbol) {
1057 Annotations Header(R"(
1058 #define $foo[[FOO]](X) (X + 1)
1059 int abc = $foo[[FOO]](1);
1060 )");
1061 CollectorOpts.RefFilter = RefKind::All;
1062 CollectorOpts.RefsInHeaders = true;
1063 CollectorOpts.CollectMacro = false;
1064 runSymbolCollector(Header.code(), "");
1065 EXPECT_THAT(Refs, Contains(Pair(_, haveRanges(Header.ranges("foo")))));
1066}
1067
1068TEST_F(SymbolCollectorTest, MacrosWithRefFilter) {
1069 Annotations Header("#define $macro[[MACRO]](X) (X + 1)");
1070 Annotations Main("void foo() { int x = $macro[[MACRO]](1); }");
1071 CollectorOpts.RefFilter = RefKind::Unknown;
1072 runSymbolCollector(Header.code(), Main.code());
1073 EXPECT_THAT(Refs, IsEmpty());
1074}
1075
1076TEST_F(SymbolCollectorTest, SpelledReferences) {
1077 struct {
1078 llvm::StringRef Header;
1079 llvm::StringRef Main;
1080 llvm::StringRef TargetSymbolName;
1081 } TestCases[] = {
1082 {
1083 R"cpp(
1084 struct Foo;
1085 #define MACRO Foo
1086 )cpp",
1087 R"cpp(
1088 struct $spelled[[Foo]] {
1089 $spelled[[Foo]]();
1090 ~$spelled[[Foo]]();
1091 };
1092 $spelled[[Foo]] Variable1;
1093 $implicit[[MACRO]] Variable2;
1094 )cpp",
1095 "Foo",
1096 },
1097 {
1098 R"cpp(
1099 class Foo {
1100 public:
1101 Foo() = default;
1102 };
1103 )cpp",
1104 R"cpp(
1105 void f() { Foo $implicit[[f]]; f = $spelled[[Foo]]();}
1106 )cpp",
1107 "Foo::Foo" /// constructor.
1108 },
1109 { // Unclean identifiers
1110 R"cpp(
1111 struct Foo {};
1112 )cpp",
1113 R"cpp(
1114 $spelled[[Fo\
1115o]] f{};
1116 )cpp",
1117 "Foo",
1118 },
1119 };
1120 CollectorOpts.RefFilter = RefKind::All;
1121 CollectorOpts.RefsInHeaders = false;
1122 for (const auto& T : TestCases) {
1123 SCOPED_TRACE(T.Header + "\n---\n" + T.Main);
1124 Annotations Header(T.Header);
1125 Annotations Main(T.Main);
1126 // Reset the file system.
1127 InMemoryFileSystem = new llvm::vfs::InMemoryFileSystem;
1128 runSymbolCollector(Header.code(), Main.code());
1129
1130 const auto SpelledRanges = Main.ranges("spelled");
1131 const auto ImplicitRanges = Main.ranges("implicit");
1132 RefSlab::Builder SpelledSlabBuilder, ImplicitSlabBuilder;
1133 const auto TargetID = findSymbol(Symbols, T.TargetSymbolName).ID;
1134 for (const auto &SymbolAndRefs : Refs) {
1135 const auto ID = SymbolAndRefs.first;
1136 if (ID != TargetID)
1137 continue;
1138 for (const auto &Ref : SymbolAndRefs.second)
1140 SpelledSlabBuilder.insert(ID, Ref);
1141 else
1142 ImplicitSlabBuilder.insert(ID, Ref);
1143 }
1144 const auto SpelledRefs = std::move(SpelledSlabBuilder).build(),
1145 ImplicitRefs = std::move(ImplicitSlabBuilder).build();
1146 EXPECT_EQ(SpelledRanges.empty(), SpelledRefs.empty());
1147 EXPECT_EQ(ImplicitRanges.empty(), ImplicitRefs.empty());
1148 if (!SpelledRanges.empty())
1149 EXPECT_THAT(SpelledRefs,
1150 Contains(Pair(TargetID, haveRanges(SpelledRanges))));
1151 if (!ImplicitRanges.empty())
1152 EXPECT_THAT(ImplicitRefs,
1153 Contains(Pair(TargetID, haveRanges(ImplicitRanges))));
1154 }
1155}
1156
1157TEST_F(SymbolCollectorTest, NameReferences) {
1158 CollectorOpts.RefFilter = RefKind::All;
1159 CollectorOpts.RefsInHeaders = true;
1160 Annotations Header(R"(
1161 class [[Foo]] {
1162 public:
1163 [[Foo]]() {}
1164 ~[[Foo]]() {}
1165 };
1166 )");
1167 CollectorOpts.RefFilter = RefKind::All;
1168 runSymbolCollector(Header.code(), "");
1169 // When we find references for class Foo, we expect to see all
1170 // constructor/destructor references.
1171 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Foo").ID,
1172 haveRanges(Header.ranges()))));
1173}
1174
1175TEST_F(SymbolCollectorTest, RefsOnMacros) {
1176 // Refs collected from SymbolCollector behave in the same way as
1177 // AST-based xrefs.
1178 CollectorOpts.RefFilter = RefKind::All;
1179 CollectorOpts.RefsInHeaders = true;
1180 Annotations Header(R"(
1181 #define TYPE(X) X
1182 #define FOO Foo
1183 #define CAT(X, Y) X##Y
1184 class [[Foo]] {};
1185 void test() {
1186 TYPE([[Foo]]) foo;
1187 [[FOO]] foo2;
1188 TYPE(TYPE([[Foo]])) foo3;
1189 [[CAT]](Fo, o) foo4;
1190 }
1191 )");
1192 CollectorOpts.RefFilter = RefKind::All;
1193 runSymbolCollector(Header.code(), "");
1194 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Foo").ID,
1195 haveRanges(Header.ranges()))));
1196}
1197
1198TEST_F(SymbolCollectorTest, HeaderAsMainFile) {
1199 CollectorOpts.RefFilter = RefKind::All;
1200 Annotations Header(R"(
1201 class $Foo[[Foo]] {};
1202
1203 void $Func[[Func]]() {
1204 $Foo[[Foo]] fo;
1205 }
1206 )");
1207 // We should collect refs to main-file symbols in all cases:
1208
1209 // 1. The main file is normal .cpp file.
1210 TestFileName = testPath("foo.cpp");
1211 runSymbolCollector("", Header.code());
1212 EXPECT_THAT(Refs,
1213 UnorderedElementsAre(Pair(findSymbol(Symbols, "Foo").ID,
1214 haveRanges(Header.ranges("Foo"))),
1215 Pair(findSymbol(Symbols, "Func").ID,
1216 haveRanges(Header.ranges("Func")))));
1217
1218 // 2. Run the .h file as main file.
1219 TestFileName = testPath("foo.h");
1220 runSymbolCollector("", Header.code(),
1221 /*ExtraArgs=*/{"-xobjective-c++-header"});
1222 EXPECT_THAT(Symbols, UnorderedElementsAre(qName("Foo"), qName("Func")));
1223 EXPECT_THAT(Refs,
1224 UnorderedElementsAre(Pair(findSymbol(Symbols, "Foo").ID,
1225 haveRanges(Header.ranges("Foo"))),
1226 Pair(findSymbol(Symbols, "Func").ID,
1227 haveRanges(Header.ranges("Func")))));
1228
1229 // 3. Run the .hh file as main file (without "-x c++-header").
1230 TestFileName = testPath("foo.hh");
1231 runSymbolCollector("", Header.code());
1232 EXPECT_THAT(Symbols, UnorderedElementsAre(qName("Foo"), qName("Func")));
1233 EXPECT_THAT(Refs,
1234 UnorderedElementsAre(Pair(findSymbol(Symbols, "Foo").ID,
1235 haveRanges(Header.ranges("Foo"))),
1236 Pair(findSymbol(Symbols, "Func").ID,
1237 haveRanges(Header.ranges("Func")))));
1238}
1239
1240TEST_F(SymbolCollectorTest, RefsInHeaders) {
1241 CollectorOpts.RefFilter = RefKind::All;
1242 CollectorOpts.RefsInHeaders = true;
1243 CollectorOpts.CollectMacro = true;
1244 Annotations Header(R"(
1245 #define $macro[[MACRO]](x) (x+1)
1246 class $foo[[Foo]] {};
1247 )");
1248 runSymbolCollector(Header.code(), "");
1249 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Foo").ID,
1250 haveRanges(Header.ranges("foo")))));
1251 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "MACRO").ID,
1252 haveRanges(Header.ranges("macro")))));
1253}
1254
1255TEST_F(SymbolCollectorTest, BaseOfRelations) {
1256 std::string Header = R"(
1257 class Base {};
1258 class Derived : public Base {};
1259 )";
1260 runSymbolCollector(Header, /*Main=*/"");
1261 const Symbol &Base = findSymbol(Symbols, "Base");
1262 const Symbol &Derived = findSymbol(Symbols, "Derived");
1263 EXPECT_THAT(Relations,
1264 Contains(Relation{Base.ID, RelationKind::BaseOf, Derived.ID}));
1265}
1266
1267TEST_F(SymbolCollectorTest, OverrideRelationsSimpleInheritance) {
1268 std::string Header = R"cpp(
1269 class A {
1270 virtual void foo();
1271 };
1272 class B : public A {
1273 void foo() override; // A::foo
1274 virtual void bar();
1275 };
1276 class C : public B {
1277 void bar() override; // B::bar
1278 };
1279 class D: public C {
1280 void foo() override; // B::foo
1281 void bar() override; // C::bar
1282 };
1283 )cpp";
1284 runSymbolCollector(Header, /*Main=*/"");
1285 const Symbol &AFoo = findSymbol(Symbols, "A::foo");
1286 const Symbol &BFoo = findSymbol(Symbols, "B::foo");
1287 const Symbol &DFoo = findSymbol(Symbols, "D::foo");
1288
1289 const Symbol &BBar = findSymbol(Symbols, "B::bar");
1290 const Symbol &CBar = findSymbol(Symbols, "C::bar");
1291 const Symbol &DBar = findSymbol(Symbols, "D::bar");
1292
1293 std::vector<Relation> Result;
1294 for (const Relation &R : Relations)
1295 if (R.Predicate == RelationKind::OverriddenBy)
1296 Result.push_back(R);
1297 EXPECT_THAT(Result, UnorderedElementsAre(
1298 OverriddenBy(AFoo, BFoo), OverriddenBy(BBar, CBar),
1299 OverriddenBy(BFoo, DFoo), OverriddenBy(CBar, DBar)));
1300}
1301
1302TEST_F(SymbolCollectorTest, OverrideRelationsMultipleInheritance) {
1303 std::string Header = R"cpp(
1304 class A {
1305 virtual void foo();
1306 };
1307 class B {
1308 virtual void bar();
1309 };
1310 class C : public B {
1311 void bar() override; // B::bar
1312 virtual void baz();
1313 }
1314 class D : public A, C {
1315 void foo() override; // A::foo
1316 void bar() override; // C::bar
1317 void baz() override; // C::baz
1318 };
1319 )cpp";
1320 runSymbolCollector(Header, /*Main=*/"");
1321 const Symbol &AFoo = findSymbol(Symbols, "A::foo");
1322 const Symbol &BBar = findSymbol(Symbols, "B::bar");
1323 const Symbol &CBar = findSymbol(Symbols, "C::bar");
1324 const Symbol &CBaz = findSymbol(Symbols, "C::baz");
1325 const Symbol &DFoo = findSymbol(Symbols, "D::foo");
1326 const Symbol &DBar = findSymbol(Symbols, "D::bar");
1327 const Symbol &DBaz = findSymbol(Symbols, "D::baz");
1328
1329 std::vector<Relation> Result;
1330 for (const Relation &R : Relations)
1331 if (R.Predicate == RelationKind::OverriddenBy)
1332 Result.push_back(R);
1333 EXPECT_THAT(Result, UnorderedElementsAre(
1334 OverriddenBy(BBar, CBar), OverriddenBy(AFoo, DFoo),
1335 OverriddenBy(CBar, DBar), OverriddenBy(CBaz, DBaz)));
1336}
1337
1338TEST_F(SymbolCollectorTest, SymbolTagsWithIndexing) {
1339 // Test that verifies symbol tags are correctly set when the AST is indexed
1340 // through FileIndex, which triggers the full indexing path through
1341 // SymbolCollector::addDeclaration where S.Tags = computeSymbolTags(ND)
1342 std::string Header = R"cpp(
1343 class A {
1344 public:
1345 virtual ~A() = default;
1346 virtual void f1() = 0;
1347 void f2() const;
1348 protected:
1349 void f3(){}
1350 private:
1351 static void f4(){}
1352 };
1353
1354 void A::f2() const {}
1355
1356 class B final: public A {
1357 public:
1358 void f1() final {}
1359 };
1360 )cpp";
1361
1362 runSymbolCollector(Header, /*Main=*/"");
1363 const Symbol &A = findSymbol(Symbols, "A");
1364 EXPECT_THAT(getSymbolTags(A),
1365 UnorderedElementsAre(SymbolTag::Abstract, SymbolTag::Declaration,
1367
1368 const Symbol &B = findSymbol(Symbols, "B");
1369 EXPECT_THAT(getSymbolTags(B),
1370 UnorderedElementsAre(SymbolTag::Final, SymbolTag::Declaration,
1372 const Symbol &Bf1 = findSymbol(Symbols, "B::f1");
1373 EXPECT_THAT(getSymbolTags(Bf1),
1374 UnorderedElementsAre(
1377}
1378
1379TEST_F(SymbolCollectorTest, ObjCOverrideRelationsSimpleInheritance) {
1380 std::string Header = R"cpp(
1381 @interface A
1382 - (void)foo;
1383 @end
1384 @interface B : A
1385 - (void)foo; // A::foo
1386 - (void)bar;
1387 @end
1388 @interface C : B
1389 - (void)bar; // B::bar
1390 @end
1391 @interface D : C
1392 - (void)foo; // B::foo
1393 - (void)bar; // C::bar
1394 @end
1395 )cpp";
1396 runSymbolCollector(Header, /*Main=*/"",
1397 {"-xobjective-c++", "-Wno-objc-root-class"});
1398 const Symbol &AFoo = findSymbol(Symbols, "A::foo");
1399 const Symbol &BFoo = findSymbol(Symbols, "B::foo");
1400 const Symbol &DFoo = findSymbol(Symbols, "D::foo");
1401
1402 const Symbol &BBar = findSymbol(Symbols, "B::bar");
1403 const Symbol &CBar = findSymbol(Symbols, "C::bar");
1404 const Symbol &DBar = findSymbol(Symbols, "D::bar");
1405
1406 std::vector<Relation> Result;
1407 for (const Relation &R : Relations)
1408 if (R.Predicate == RelationKind::OverriddenBy)
1409 Result.push_back(R);
1410 EXPECT_THAT(Result, UnorderedElementsAre(
1411 OverriddenBy(AFoo, BFoo), OverriddenBy(BBar, CBar),
1412 OverriddenBy(BFoo, DFoo), OverriddenBy(CBar, DBar)));
1413}
1414
1415TEST_F(SymbolCollectorTest, CountReferences) {
1416 const std::string Header = R"(
1417 class W;
1418 class X {};
1419 class Y;
1420 class Z {}; // not used anywhere
1421 Y* y = nullptr; // used in header doesn't count
1422 #define GLOBAL_Z(name) Z name;
1423 )";
1424 const std::string Main = R"(
1425 W* w = nullptr;
1426 W* w2 = nullptr; // only one usage counts
1427 X x();
1428 class V;
1429 class Y{}; // definition doesn't count as a reference
1430 V* v = nullptr;
1431 GLOBAL_Z(z); // Not a reference to Z, we don't spell the type.
1432 )";
1433 CollectorOpts.CountReferences = true;
1434 runSymbolCollector(Header, Main);
1435 EXPECT_THAT(
1436 Symbols,
1437 UnorderedElementsAreArray(
1438 {AllOf(qName("W"), refCount(1)), AllOf(qName("X"), refCount(1)),
1439 AllOf(qName("Y"), refCount(0)), AllOf(qName("Z"), refCount(0)),
1440 AllOf(qName("y"), refCount(0)), AllOf(qName("z"), refCount(0)),
1441 AllOf(qName("x"), refCount(0)), AllOf(qName("w"), refCount(0)),
1442 AllOf(qName("w2"), refCount(0)), AllOf(qName("V"), refCount(1)),
1443 AllOf(qName("v"), refCount(0))}));
1444}
1445
1446TEST_F(SymbolCollectorTest, SymbolRelativeNoFallback) {
1447 runSymbolCollector("class Foo {};", /*Main=*/"");
1448 EXPECT_THAT(Symbols, UnorderedElementsAre(
1449 AllOf(qName("Foo"), declURI(TestHeaderURI))));
1450}
1451
1452TEST_F(SymbolCollectorTest, SymbolRelativeWithFallback) {
1453 TestHeaderName = "x.h";
1454 TestFileName = "x.cpp";
1455 TestHeaderURI = URI::create(testPath(TestHeaderName)).toString();
1456 CollectorOpts.FallbackDir = testRoot();
1457 runSymbolCollector("class Foo {};", /*Main=*/"");
1458 EXPECT_THAT(Symbols, UnorderedElementsAre(
1459 AllOf(qName("Foo"), declURI(TestHeaderURI))));
1460}
1461
1462TEST_F(SymbolCollectorTest, UnittestURIScheme) {
1463 // Use test URI scheme from URITests.cpp
1464 TestHeaderName = testPath("x.h");
1465 TestFileName = testPath("x.cpp");
1466 runSymbolCollector("class Foo {};", /*Main=*/"");
1467 EXPECT_THAT(Symbols, UnorderedElementsAre(
1468 AllOf(qName("Foo"), declURI("unittest:///x.h"))));
1469}
1470
1471TEST_F(SymbolCollectorTest, IncludeEnums) {
1472 const std::string Header = R"(
1473 enum {
1474 Red
1475 };
1476 enum Color {
1477 Green
1478 };
1479 enum class Color2 {
1480 Yellow
1481 };
1482 namespace ns {
1483 enum {
1484 Black
1485 };
1486 }
1487 class Color3 {
1488 enum {
1489 Blue
1490 };
1491 };
1492 )";
1493 runSymbolCollector(Header, /*Main=*/"");
1494 EXPECT_THAT(Symbols,
1495 UnorderedElementsAre(
1496 AllOf(qName("Red"), forCodeCompletion(true)),
1497 AllOf(qName("Color"), forCodeCompletion(true)),
1498 AllOf(qName("Green"), forCodeCompletion(true)),
1499 AllOf(qName("Color2"), forCodeCompletion(true)),
1500 AllOf(qName("Color2::Yellow"), forCodeCompletion(true)),
1501 AllOf(qName("ns"), forCodeCompletion(true)),
1502 AllOf(qName("ns::Black"), forCodeCompletion(true)),
1503 AllOf(qName("Color3"), forCodeCompletion(true)),
1504 AllOf(qName("Color3::Blue"), forCodeCompletion(true))));
1505}
1506
1507TEST_F(SymbolCollectorTest, NamelessSymbols) {
1508 const std::string Header = R"(
1509 struct {
1510 int a;
1511 } Foo;
1512 )";
1513 runSymbolCollector(Header, /*Main=*/"");
1514 EXPECT_THAT(Symbols,
1515 UnorderedElementsAre(qName("Foo"), qName("(unnamed struct)::a")));
1516}
1517
1518TEST_F(SymbolCollectorTest, SymbolFormedFromRegisteredSchemeFromMacro) {
1519
1520 Annotations Header(R"(
1521 #define FF(name) \
1522 class name##_Test {};
1523
1524 $expansion[[FF]](abc);
1525
1526 #define FF2() \
1527 class $spelling[[Test]] {};
1528
1529 FF2();
1530 )");
1531
1532 runSymbolCollector(Header.code(), /*Main=*/"");
1533 EXPECT_THAT(Symbols,
1534 UnorderedElementsAre(
1535 AllOf(qName("abc_Test"), declRange(Header.range("expansion")),
1536 declURI(TestHeaderURI)),
1537 AllOf(qName("Test"), declRange(Header.range("spelling")),
1538 declURI(TestHeaderURI))));
1539}
1540
1541TEST_F(SymbolCollectorTest, SymbolFormedByCLI) {
1542 Annotations Header(R"(
1543 #ifdef NAME
1544 class $expansion[[NAME]] {};
1545 #endif
1546 )");
1547 runSymbolCollector(Header.code(), /*Main=*/"", /*ExtraArgs=*/{"-DNAME=name"});
1548 EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(
1549 qName("name"), declRange(Header.range("expansion")),
1550 declURI(TestHeaderURI))));
1551}
1552
1553TEST_F(SymbolCollectorTest, SymbolsInMainFile) {
1554 const std::string Main = R"(
1555 class Foo {};
1556 void f1();
1557 inline void f2() {}
1558
1559 namespace {
1560 void ff() {}
1561 }
1562 namespace foo {
1563 namespace {
1564 class Bar {};
1565 }
1566 }
1567 void main_f() {}
1568 void f1() {}
1569 )";
1570 runSymbolCollector(/*Header=*/"", Main);
1571 EXPECT_THAT(Symbols, UnorderedElementsAre(
1572 qName("Foo"), qName("f1"), qName("f2"), qName("ff"),
1573 qName("foo"), qName("foo::Bar"), qName("main_f")));
1574}
1575
1576TEST_F(SymbolCollectorTest, Documentation) {
1577 const std::string Header = R"(
1578 // doc Foo
1579 class Foo {
1580 // doc f
1581 int f();
1582 };
1583 )";
1584 CollectorOpts.StoreAllDocumentation = false;
1585 runSymbolCollector(Header, /* Main */ "");
1586 EXPECT_THAT(Symbols,
1587 UnorderedElementsAre(
1588 AllOf(qName("Foo"), doc("doc Foo"), forCodeCompletion(true)),
1589 AllOf(qName("Foo::f"), doc(""), returnType(""),
1590 forCodeCompletion(false))));
1591
1592 CollectorOpts.StoreAllDocumentation = true;
1593 runSymbolCollector(Header, /* Main */ "");
1594 EXPECT_THAT(Symbols,
1595 UnorderedElementsAre(
1596 AllOf(qName("Foo"), doc("doc Foo"), forCodeCompletion(true)),
1597 AllOf(qName("Foo::f"), doc("doc f"), returnType(""),
1598 forCodeCompletion(false))));
1599}
1600
1601TEST_F(SymbolCollectorTest, DocumentationInMain) {
1602 const std::string Header = R"(
1603 // doc Foo
1604 class Foo {
1605 void f();
1606 };
1607 )";
1608 const std::string Main = R"(
1609 // doc f
1610 void Foo::f() {}
1611 )";
1612 CollectorOpts.StoreAllDocumentation = true;
1613 runSymbolCollector(Header, Main);
1614 EXPECT_THAT(Symbols,
1615 UnorderedElementsAre(
1616 AllOf(qName("Foo"), doc("doc Foo"), forCodeCompletion(true)),
1617 AllOf(qName("Foo::f"), doc("doc f"), returnType(""),
1618 forCodeCompletion(false))));
1619}
1620
1621TEST_F(SymbolCollectorTest, DocumentationAtDeclThenDef) {
1622 const std::string Header = R"(
1623 class Foo {
1624 // doc f decl
1625 void f();
1626 };
1627 )";
1628 const std::string Main = R"(
1629 // doc f def
1630 void Foo::f() {}
1631 )";
1632 CollectorOpts.StoreAllDocumentation = true;
1633 runSymbolCollector(Header, Main);
1634 EXPECT_THAT(Symbols,
1635 UnorderedElementsAre(AllOf(qName("Foo")),
1636 AllOf(qName("Foo::f"), doc("doc f decl"))));
1637}
1638
1639TEST_F(SymbolCollectorTest, DocumentationAtDefThenDecl) {
1640 const std::string Header = R"(
1641 // doc f def
1642 void f() {}
1643
1644 // doc f decl
1645 void f();
1646 )";
1647 CollectorOpts.StoreAllDocumentation = true;
1648 runSymbolCollector(Header, "" /*Main*/);
1649 EXPECT_THAT(Symbols,
1650 UnorderedElementsAre(AllOf(qName("f"), doc("doc f def"))));
1651}
1652
1653TEST_F(SymbolCollectorTest, ClassMembers) {
1654 const std::string Header = R"(
1655 class Foo {
1656 void f() {}
1657 void g();
1658 static void sf() {}
1659 static void ssf();
1660 static int x;
1661 };
1662 )";
1663 const std::string Main = R"(
1664 void Foo::g() {}
1665 void Foo::ssf() {}
1666 )";
1667 runSymbolCollector(Header, Main);
1668 EXPECT_THAT(
1669 Symbols,
1670 UnorderedElementsAre(
1671 qName("Foo"),
1672 AllOf(qName("Foo::f"), returnType(""), forCodeCompletion(false)),
1673 AllOf(qName("Foo::g"), returnType(""), forCodeCompletion(false)),
1674 AllOf(qName("Foo::sf"), returnType(""), forCodeCompletion(false)),
1675 AllOf(qName("Foo::ssf"), returnType(""), forCodeCompletion(false)),
1676 AllOf(qName("Foo::x"), returnType(""), forCodeCompletion(false))));
1677}
1678
1679TEST_F(SymbolCollectorTest, Scopes) {
1680 const std::string Header = R"(
1681 namespace na {
1682 class Foo {};
1683 namespace nb {
1684 class Bar {};
1685 }
1686 }
1687 )";
1688 runSymbolCollector(Header, /*Main=*/"");
1689 EXPECT_THAT(Symbols,
1690 UnorderedElementsAre(qName("na"), qName("na::nb"),
1691 qName("na::Foo"), qName("na::nb::Bar")));
1692}
1693
1694TEST_F(SymbolCollectorTest, ExternC) {
1695 const std::string Header = R"(
1696 extern "C" { class Foo {}; }
1697 namespace na {
1698 extern "C" { class Bar {}; }
1699 }
1700 )";
1701 runSymbolCollector(Header, /*Main=*/"");
1702 EXPECT_THAT(Symbols, UnorderedElementsAre(qName("na"), qName("Foo"),
1703 qName("na::Bar")));
1704}
1705
1706TEST_F(SymbolCollectorTest, SkipInlineNamespace) {
1707 const std::string Header = R"(
1708 namespace na {
1709 inline namespace nb {
1710 class Foo {};
1711 }
1712 }
1713 namespace na {
1714 // This is still inlined.
1715 namespace nb {
1716 class Bar {};
1717 }
1718 }
1719 )";
1720 runSymbolCollector(Header, /*Main=*/"");
1721 EXPECT_THAT(Symbols,
1722 UnorderedElementsAre(qName("na"), qName("na::nb"),
1723 qName("na::Foo"), qName("na::Bar")));
1724}
1725
1726TEST_F(SymbolCollectorTest, SymbolWithDocumentation) {
1727 const std::string Header = R"(
1728 namespace nx {
1729 /// Foo comment.
1730 int ff(int x, double y) { return 0; }
1731 }
1732 )";
1733 runSymbolCollector(Header, /*Main=*/"");
1734 EXPECT_THAT(
1735 Symbols,
1736 UnorderedElementsAre(
1737 qName("nx"), AllOf(qName("nx::ff"), labeled("ff(int x, double y)"),
1738 returnType("int"), doc("Foo comment."))));
1739}
1740
1741TEST_F(SymbolCollectorTest, snippet) {
1742 const std::string Header = R"(
1743 namespace nx {
1744 void f() {}
1745 int ff(int x, double y) { return 0; }
1746 }
1747 )";
1748 runSymbolCollector(Header, /*Main=*/"");
1749 EXPECT_THAT(Symbols,
1750 UnorderedElementsAre(
1751 qName("nx"),
1752 AllOf(qName("nx::f"), labeled("f()"), snippet("f()")),
1753 AllOf(qName("nx::ff"), labeled("ff(int x, double y)"),
1754 snippet("ff(${1:int x}, ${2:double y})"))));
1755}
1756
1757TEST_F(SymbolCollectorTest, IncludeHeaderSameAsFileURI) {
1758 CollectorOpts.CollectIncludePath = true;
1759 runSymbolCollector("#pragma once\nclass Foo {};", /*Main=*/"");
1760 EXPECT_THAT(Symbols, UnorderedElementsAre(
1761 AllOf(qName("Foo"), declURI(TestHeaderURI))));
1762 EXPECT_THAT(Symbols.begin()->IncludeHeaders,
1763 UnorderedElementsAre(IncludeHeaderWithRef(TestHeaderURI, 1u)));
1764}
1765
1766TEST_F(SymbolCollectorTest, CanonicalSTLHeader) {
1767 CollectorOpts.CollectIncludePath = true;
1768 runSymbolCollector(
1769 R"cpp(
1770 namespace std {
1771 class string {};
1772 // Move overloads have special handling.
1773 template <typename _T> T&& move(_T&& __value);
1774 template <typename _I, typename _O> _O move(_I, _I, _O);
1775 template <typename _T, typename _O, typename _I> _O move(
1776 _T&&, _O, _O, _I);
1777 }
1778 )cpp",
1779 /*Main=*/"");
1780 EXPECT_THAT(
1781 Symbols,
1782 UnorderedElementsAre(
1783 qName("std"),
1784 AllOf(qName("std::string"), declURI(TestHeaderURI),
1785 includeHeader("<string>")),
1786 // Parameter names are demangled.
1787 AllOf(labeled("move(T &&value)"), includeHeader("<utility>")),
1788 AllOf(labeled("move(I, I, O)"), includeHeader("<algorithm>")),
1789 AllOf(labeled("move(T &&, O, O, I)"), includeHeader("<algorithm>"))));
1790}
1791
1792TEST_F(SymbolCollectorTest, IWYUPragma) {
1793 CollectorOpts.CollectIncludePath = true;
1794 const std::string Header = R"(
1795 // IWYU pragma: private, include the/good/header.h
1796 class Foo {};
1797 )";
1798 runSymbolCollector(Header, /*Main=*/"");
1799 EXPECT_THAT(Symbols, UnorderedElementsAre(
1800 AllOf(qName("Foo"), declURI(TestHeaderURI),
1801 includeHeader("\"the/good/header.h\""))));
1802}
1803
1804TEST_F(SymbolCollectorTest, IWYUPragmaWithDoubleQuotes) {
1805 CollectorOpts.CollectIncludePath = true;
1806 const std::string Header = R"(
1807 // IWYU pragma: private, include "the/good/header.h"
1808 class Foo {};
1809 )";
1810 runSymbolCollector(Header, /*Main=*/"");
1811 EXPECT_THAT(Symbols, UnorderedElementsAre(
1812 AllOf(qName("Foo"), declURI(TestHeaderURI),
1813 includeHeader("\"the/good/header.h\""))));
1814}
1815
1816TEST_F(SymbolCollectorTest, IWYUPragmaExport) {
1817 CollectorOpts.CollectIncludePath = true;
1818 const std::string Header = R"cpp(#pragma once
1819 #include "exporter.h"
1820 )cpp";
1821 auto ExporterFile = testPath("exporter.h");
1822 InMemoryFileSystem->addFile(
1823 ExporterFile, 0, llvm::MemoryBuffer::getMemBuffer(R"cpp(#pragma once
1824 #include "private.h" // IWYU pragma: export
1825 )cpp"));
1826 auto PrivateFile = testPath("private.h");
1827 InMemoryFileSystem->addFile(
1828 PrivateFile, 0, llvm::MemoryBuffer::getMemBuffer("class Foo {};"));
1829 runSymbolCollector(Header, /*Main=*/"",
1830 /*ExtraArgs=*/{"-I", testRoot()});
1831 EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(
1832 qName("Foo"),
1833 includeHeader(URI::create(ExporterFile).toString()),
1834 declURI(URI::create(PrivateFile).toString()))));
1835}
1836
1837TEST_F(SymbolCollectorTest, MainFileIsHeaderWhenSkipIncFile) {
1838 CollectorOpts.CollectIncludePath = true;
1839 // To make this case as hard as possible, we won't tell clang main is a
1840 // header. No extension, no -x c++-header.
1841 TestFileName = testPath("no_ext_main");
1842 TestFileURI = URI::create(TestFileName).toString();
1843 auto IncFile = testPath("test.inc");
1844 auto IncURI = URI::create(IncFile).toString();
1845 InMemoryFileSystem->addFile(IncFile, 0,
1846 llvm::MemoryBuffer::getMemBuffer("class X {};"));
1847 runSymbolCollector("", R"cpp(
1848 // Can't use #pragma once in a main file clang doesn't think is a header.
1849 #ifndef MAIN_H_
1850 #define MAIN_H_
1851 #include "test.inc"
1852 #endif
1853 )cpp",
1854 /*ExtraArgs=*/{"-I", testRoot()});
1855 EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(qName("X"), declURI(IncURI),
1856 includeHeader(TestFileURI))));
1857}
1858
1859TEST_F(SymbolCollectorTest, IncFileInNonHeader) {
1860 CollectorOpts.CollectIncludePath = true;
1861 TestFileName = testPath("main.cc");
1862 TestFileURI = URI::create(TestFileName).toString();
1863 auto IncFile = testPath("test.inc");
1864 auto IncURI = URI::create(IncFile).toString();
1865 InMemoryFileSystem->addFile(IncFile, 0,
1866 llvm::MemoryBuffer::getMemBuffer("class X {};"));
1867 runSymbolCollector("", R"cpp(
1868 #include "test.inc"
1869 )cpp",
1870 /*ExtraArgs=*/{"-I", testRoot()});
1871 EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(qName("X"), declURI(IncURI),
1872 Not(includeHeader()))));
1873}
1874
1875// Features that depend on header-guards are fragile. Header guards are only
1876// recognized when the file ends, so we have to defer checking for them.
1877TEST_F(SymbolCollectorTest, HeaderGuardDetected) {
1878 CollectorOpts.CollectIncludePath = true;
1879 CollectorOpts.CollectMacro = true;
1880 runSymbolCollector(R"cpp(
1881 #ifndef HEADER_GUARD_
1882 #define HEADER_GUARD_
1883
1884 // Symbols are seen before the header guard is complete.
1885 #define MACRO
1886 int decl();
1887
1888 #endif // Header guard is recognized here.
1889 )cpp",
1890 "");
1891 EXPECT_THAT(Symbols, Not(Contains(qName("HEADER_GUARD_"))));
1892 EXPECT_THAT(Symbols, Each(includeHeader()));
1893}
1894
1895TEST_F(SymbolCollectorTest, NonModularHeader) {
1896 auto TU = TestTU::withHeaderCode("int x();");
1897 EXPECT_THAT(TU.headerSymbols(), ElementsAre(includeHeader()));
1898
1899 // Files missing include guards aren't eligible for insertion.
1900 TU.ImplicitHeaderGuard = false;
1901 EXPECT_THAT(TU.headerSymbols(), ElementsAre(Not(includeHeader())));
1902
1903 // We recognize some patterns of trying to prevent insertion.
1904 TU = TestTU::withHeaderCode(R"cpp(
1905#ifndef SECRET
1906#error "This file isn't safe to include directly"
1907#endif
1908 int x();
1909 )cpp");
1910 TU.ExtraArgs.push_back("-DSECRET"); // *we're* able to include it.
1911 EXPECT_THAT(TU.headerSymbols(), ElementsAre(Not(includeHeader())));
1912}
1913
1914TEST_F(SymbolCollectorTest, AvoidUsingFwdDeclsAsCanonicalDecls) {
1915 CollectorOpts.CollectIncludePath = true;
1916 Annotations Header(R"(
1917 #pragma once
1918 // Forward declarations of TagDecls.
1919 class C;
1920 struct S;
1921 union U;
1922
1923 // Canonical declarations.
1924 class $cdecl[[C]] {};
1925 struct $sdecl[[S]] {};
1926 union $udecl[[U]] {int $xdecl[[x]]; bool $ydecl[[y]];};
1927 )");
1928 runSymbolCollector(Header.code(), /*Main=*/"");
1929 EXPECT_THAT(
1930 Symbols,
1931 UnorderedElementsAre(
1932 AllOf(qName("C"), declURI(TestHeaderURI),
1933 declRange(Header.range("cdecl")), includeHeader(TestHeaderURI),
1934 defURI(TestHeaderURI), defRange(Header.range("cdecl"))),
1935 AllOf(qName("S"), declURI(TestHeaderURI),
1936 declRange(Header.range("sdecl")), includeHeader(TestHeaderURI),
1937 defURI(TestHeaderURI), defRange(Header.range("sdecl"))),
1938 AllOf(qName("U"), declURI(TestHeaderURI),
1939 declRange(Header.range("udecl")), includeHeader(TestHeaderURI),
1940 defURI(TestHeaderURI), defRange(Header.range("udecl"))),
1941 AllOf(qName("U::x"), declURI(TestHeaderURI),
1942 declRange(Header.range("xdecl")), defURI(TestHeaderURI),
1943 defRange(Header.range("xdecl"))),
1944 AllOf(qName("U::y"), declURI(TestHeaderURI),
1945 declRange(Header.range("ydecl")), defURI(TestHeaderURI),
1946 defRange(Header.range("ydecl")))));
1947}
1948
1949TEST_F(SymbolCollectorTest, ClassForwardDeclarationIsCanonical) {
1950 CollectorOpts.CollectIncludePath = true;
1951 runSymbolCollector(/*Header=*/"#pragma once\nclass X;",
1952 /*Main=*/"class X {};");
1953 EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(
1954 qName("X"), declURI(TestHeaderURI),
1955 includeHeader(TestHeaderURI), defURI(TestFileURI))));
1956}
1957
1958TEST_F(SymbolCollectorTest, UTF16Character) {
1959 // ö is 2-bytes.
1960 Annotations Header(/*Header=*/"class [[pörk]] {};");
1961 runSymbolCollector(Header.code(), /*Main=*/"");
1962 EXPECT_THAT(Symbols, UnorderedElementsAre(
1963 AllOf(qName("pörk"), declRange(Header.range()))));
1964}
1965
1966TEST_F(SymbolCollectorTest, DoNotIndexSymbolsInFriendDecl) {
1967 Annotations Header(R"(
1968 namespace nx {
1969 class $z[[Z]] {};
1970 class X {
1971 friend class Y;
1972 friend class Z;
1973 friend void foo();
1974 friend void $bar[[bar]]() {}
1975 };
1976 class $y[[Y]] {};
1977 void $foo[[foo]]();
1978 }
1979 )");
1980 runSymbolCollector(Header.code(), /*Main=*/"");
1981
1982 EXPECT_THAT(Symbols,
1983 UnorderedElementsAre(
1984 qName("nx"), qName("nx::X"),
1985 AllOf(qName("nx::Y"), declRange(Header.range("y"))),
1986 AllOf(qName("nx::Z"), declRange(Header.range("z"))),
1987 AllOf(qName("nx::foo"), declRange(Header.range("foo"))),
1988 AllOf(qName("nx::bar"), declRange(Header.range("bar")))));
1989}
1990
1991TEST_F(SymbolCollectorTest, ReferencesInFriendDecl) {
1992 const std::string Header = R"(
1993 class X;
1994 class Y;
1995 )";
1996 const std::string Main = R"(
1997 class C {
1998 friend ::X;
1999 friend class Y;
2000 };
2001 )";
2002 CollectorOpts.CountReferences = true;
2003 runSymbolCollector(Header, Main);
2004 EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(qName("X"), refCount(1)),
2005 AllOf(qName("Y"), refCount(1)),
2006 AllOf(qName("C"), refCount(0))));
2007}
2008
2009TEST_F(SymbolCollectorTest, Origin) {
2010 CollectorOpts.Origin = SymbolOrigin::Static;
2011 runSymbolCollector("class Foo {};", /*Main=*/"");
2012 EXPECT_THAT(Symbols, UnorderedElementsAre(
2014 InMemoryFileSystem = new llvm::vfs::InMemoryFileSystem;
2015 CollectorOpts.CollectMacro = true;
2016 runSymbolCollector("#define FOO", /*Main=*/"");
2017 EXPECT_THAT(Symbols, UnorderedElementsAre(
2019}
2020
2021TEST_F(SymbolCollectorTest, CollectMacros) {
2022 CollectorOpts.CollectIncludePath = true;
2023 Annotations Header(R"(
2024 #pragma once
2025 #define X 1
2026 #define $mac[[MAC]](x) int x
2027 #define $used[[USED]](y) float y;
2028
2029 MAC(p);
2030 )");
2031
2032 Annotations Main(R"(
2033 #define $main[[MAIN]] 1
2034 USED(t);
2035 )");
2036 CollectorOpts.CountReferences = true;
2037 CollectorOpts.CollectMacro = true;
2038 runSymbolCollector(Header.code(), Main.code());
2039 EXPECT_THAT(
2040 Symbols,
2041 UnorderedElementsAre(
2042 qName("p"), qName("t"),
2043 AllOf(qName("X"), declURI(TestHeaderURI),
2044 includeHeader(TestHeaderURI)),
2045 AllOf(labeled("MAC(x)"), refCount(0),
2046
2047 declRange(Header.range("mac")), visibleOutsideFile()),
2048 AllOf(labeled("USED(y)"), refCount(1),
2049 declRange(Header.range("used")), visibleOutsideFile()),
2050 AllOf(labeled("MAIN"), refCount(0), declRange(Main.range("main")),
2051 Not(visibleOutsideFile()))));
2052}
2053
2054TEST_F(SymbolCollectorTest, DeprecatedSymbols) {
2055 const std::string Header = R"(
2056 void TestClangc() __attribute__((deprecated("", "")));
2057 void TestClangd();
2058 )";
2059 runSymbolCollector(Header, /**/ "");
2060 EXPECT_THAT(Symbols, UnorderedElementsAre(
2061 AllOf(qName("TestClangc"), deprecated()),
2062 AllOf(qName("TestClangd"), Not(deprecated()))));
2063}
2064
2065TEST_F(SymbolCollectorTest, implementationDetail) {
2066 const std::string Header = R"(
2067 #define DECL_NAME(x, y) x##_##y##_Decl
2068 #define DECL(x, y) class DECL_NAME(x, y) {};
2069 DECL(X, Y); // X_Y_Decl
2070
2071 class Public {};
2072 )";
2073 runSymbolCollector(Header, /**/ "");
2074 EXPECT_THAT(Symbols,
2075 UnorderedElementsAre(
2076 AllOf(qName("X_Y_Decl"), implementationDetail()),
2077 AllOf(qName("Public"), Not(implementationDetail()))));
2078}
2079
2080TEST_F(SymbolCollectorTest, UsingDecl) {
2081 const char *Header = R"(
2082 void foo();
2083 namespace std {
2084 using ::foo;
2085 })";
2086 runSymbolCollector(Header, /**/ "");
2087 EXPECT_THAT(Symbols, Contains(qName("std::foo")));
2088}
2089
2090TEST_F(SymbolCollectorTest, CBuiltins) {
2091 // In C, printf in stdio.h is a redecl of an implicit builtin.
2092 const char *Header = R"(
2093 extern int printf(const char*, ...);
2094 )";
2095 runSymbolCollector(Header, /**/ "", {"-xc"});
2096 EXPECT_THAT(Symbols, Contains(qName("printf")));
2097}
2098
2099TEST_F(SymbolCollectorTest, InvalidSourceLoc) {
2100 const char *Header = R"(
2101 void operator delete(void*)
2102 __attribute__((__externally_visible__));)";
2103 runSymbolCollector(Header, /**/ "");
2104 EXPECT_THAT(Symbols, Contains(qName("operator delete")));
2105}
2106
2107TEST_F(SymbolCollectorTest, BadUTF8) {
2108 // Extracted from boost/spirit/home/support/char_encoding/iso8859_1.hpp
2109 // This looks like UTF-8 and fools clang, but has high-ISO-8859-1 comments.
2110 const char *Header = "int PUNCT = 0;\n"
2111 "/* \xa1 */ int types[] = { /* \xa1 */PUNCT };";
2112 CollectorOpts.RefFilter = RefKind::All;
2113 CollectorOpts.RefsInHeaders = true;
2114 runSymbolCollector(Header, "");
2115 EXPECT_THAT(Symbols, Contains(AllOf(qName("types"), doc("\xef\xbf\xbd "))));
2116 EXPECT_THAT(Symbols, Contains(qName("PUNCT")));
2117 // Reference is stored, although offset within line is not reliable.
2118 EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "PUNCT").ID, _)));
2119}
2120
2121TEST_F(SymbolCollectorTest, MacrosInHeaders) {
2122 CollectorOpts.CollectMacro = true;
2123 TestFileName = testPath("test.h");
2124 runSymbolCollector("", "#define X");
2125 EXPECT_THAT(Symbols,
2126 UnorderedElementsAre(AllOf(qName("X"), forCodeCompletion(true))));
2127}
2128
2129// Regression test for a crash-bug we used to have.
2130TEST_F(SymbolCollectorTest, UndefOfModuleMacro) {
2131 auto TU = TestTU::withCode(R"cpp(#include "bar.h")cpp");
2132 TU.AdditionalFiles["bar.h"] = R"cpp(
2133 #include "foo.h"
2134 #undef X
2135 )cpp";
2136 TU.AdditionalFiles["foo.h"] = "#define X 1";
2137 TU.AdditionalFiles["module.modulemap"] = R"cpp(
2138 module foo {
2139 header "foo.h"
2140 export *
2141 }
2142 )cpp";
2143 TU.ExtraArgs.push_back("-fmodules");
2144 TU.ExtraArgs.push_back("-fmodule-map-file=" + testPath("module.modulemap"));
2145 TU.OverlayRealFileSystemForModules = true;
2146
2147 TU.build();
2148 // We mostly care about not crashing, but verify that we didn't insert garbage
2149 // about X too.
2150 EXPECT_THAT(TU.headerSymbols(), Not(Contains(qName("X"))));
2151}
2152
2153TEST_F(SymbolCollectorTest, NoCrashOnObjCMethodCStyleParam) {
2154 auto TU = TestTU::withCode(R"objc(
2155 @interface Foo
2156 - (void)fun:(bool)foo, bool bar;
2157 @end
2158 )objc");
2159 TU.ExtraArgs.push_back("-xobjective-c++");
2160
2161 TU.build();
2162 // We mostly care about not crashing.
2163 EXPECT_THAT(TU.headerSymbols(),
2164 UnorderedElementsAre(qName("Foo"), qName("Foo::fun:")));
2165}
2166
2167TEST_F(SymbolCollectorTest, Reserved) {
2168 const char *Header = R"cpp(
2169 #pragma once
2170 void __foo();
2171 namespace _X { int secret; }
2172 )cpp";
2173
2174 CollectorOpts.CollectReserved = true;
2175 runSymbolCollector(Header, "");
2176 EXPECT_THAT(Symbols, UnorderedElementsAre(qName("__foo"), qName("_X"),
2177 qName("_X::secret")));
2178
2179 CollectorOpts.CollectReserved = false;
2180 runSymbolCollector(Header, "");
2181 EXPECT_THAT(Symbols, UnorderedElementsAre(qName("__foo"), qName("_X"),
2182 qName("_X::secret")));
2183
2184 // Ugly: for some reason we reuse the test filesystem across tests.
2185 // You can't overwrite the same filename with new content!
2186 InMemoryFileSystem = new llvm::vfs::InMemoryFileSystem;
2187 runSymbolCollector("#pragma GCC system_header\n" + std::string(Header), "");
2188 EXPECT_THAT(Symbols, IsEmpty());
2189}
2190
2191TEST_F(SymbolCollectorTest, ReservedSymbolInIntrinsicHeader) {
2192 const char *Header = R"cpp(
2193 #pragma once
2194 void __foo();
2195 )cpp";
2196
2197 TestHeaderName = "xintrin.h";
2198 TestHeaderURI = URI::create(testPath(TestHeaderName)).toString();
2199 InMemoryFileSystem = new llvm::vfs::InMemoryFileSystem;
2200 CollectorOpts.FallbackDir = testRoot();
2201 runSymbolCollector("#pragma GCC system_header\n" + std::string(Header), "");
2202 EXPECT_THAT(Symbols, UnorderedElementsAre(qName("__foo")));
2203}
2204
2205TEST_F(SymbolCollectorTest, Concepts) {
2206 const char *Header = R"cpp(
2207 template <class T>
2208 concept A = sizeof(T) <= 8;
2209 )cpp";
2210 runSymbolCollector("", Header, {"-std=c++20"});
2211 EXPECT_THAT(Symbols,
2212 UnorderedElementsAre(AllOf(
2213 qName("A"), hasKind(clang::index::SymbolKind::Concept))));
2214}
2215
2216TEST_F(SymbolCollectorTest, IncludeHeaderForwardDecls) {
2217 CollectorOpts.CollectIncludePath = true;
2218 const std::string Header = R"cpp(#pragma once
2219struct Foo;
2220#include "full.h"
2221)cpp";
2222 auto FullFile = testPath("full.h");
2223 InMemoryFileSystem->addFile(FullFile, 0,
2224 llvm::MemoryBuffer::getMemBuffer(R"cpp(
2225#pragma once
2226struct Foo {};)cpp"));
2227 runSymbolCollector(Header, /*Main=*/"",
2228 /*ExtraArgs=*/{"-I", testRoot()});
2229 EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(
2230 qName("Foo"),
2231 includeHeader(URI::create(FullFile).toString()))))
2232 << *Symbols.begin();
2233}
2234} // namespace
2235} // namespace clangd
2236} // namespace clang
Same as llvm::Annotations, but adjusts functions to LSP-specific types for positions and ranges.
Definition Annotations.h:23
RefSlab::Builder is a mutable container that can 'freeze' to RefSlab.
Definition Ref.h:135
static bool shouldCollectSymbol(const NamedDecl &ND, const ASTContext &ASTCtx, const Options &Opts, bool IsMainFileSymbol)
Returns true is ND should be collected.
static llvm::Expected< URI > create(llvm::StringRef AbsolutePath, llvm::StringRef Scheme)
Creates a URI for a file in the given scheme.
Definition URI.cpp:208
FIXME: Skip testing on windows temporarily due to the different escaping code mode.
Definition AST.cpp:44
TEST_F(BackgroundIndexTest, NoCrashOnErrorFile)
const NamedDecl & findDecl(ParsedAST &AST, llvm::StringRef QName)
Definition TestTU.cpp:220
std::vector< SymbolTag > getSymbolTags(const Symbol &S)
Returns the SymbolTag values for the given indexed S.
bool isInsideMainFile(SourceLocation Loc, const SourceManager &SM)
Returns true iff Loc is inside the main file.
MATCHER_P2(hasFlag, Flag, Path, "")
static const char * toString(OffsetEncoding OE)
MATCHER_P(named, N, "")
std::string testPath(PathRef File, llvm::sys::path::Style Style)
Definition TestFS.cpp:94
const NamedDecl & findUnqualifiedDecl(ParsedAST &AST, llvm::StringRef Name)
Definition TestTU.cpp:261
const Symbol & findSymbol(const SymbolSlab &Slab, llvm::StringRef QName)
Definition TestTU.cpp:186
const char * testRoot()
Definition TestFS.cpp:85
cppcoreguidelines::ProBoundsAvoidUncheckedContainerAccessCheck P
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Represents a symbol occurrence in the source file.
Definition Ref.h:88
RefKind Kind
Definition Ref.h:91
SymbolID Container
The ID of the symbol whose definition contains this reference.
Definition Ref.h:95
SymbolLocation Location
The source location where the symbol is named.
Definition Ref.h:90
Represents a relation between two symbols.
Definition Relation.h:32
Ensure we have enough bits to represent all SymbolTag values.
Definition Symbol.h:49
@ IndexedForCodeCompletion
Whether or not this symbol is meant to be used for the code completion.
Definition Symbol.h:155
@ Deprecated
Indicates if the symbol is deprecated.
Definition Symbol.h:157
@ ImplementationDetail
Symbol is an implementation detail.
Definition Symbol.h:159
@ VisibleOutsideFile
Symbol is visible to other files (not e.g. a static helper function).
Definition Symbol.h:161
SymbolID ID
The ID of the symbol.
Definition Symbol.h:51
SymbolOrigin Origin
Where this symbol came from. Usually an index provides a constant value.
Definition Symbol.h:55
static TestTU withHeaderCode(llvm::StringRef HeaderCode)
Definition TestTU.h:42
SymbolSlab headerSymbols() const
Definition TestTU.cpp:164
static TestTU withCode(llvm::StringRef Code)
Definition TestTU.h:36