clang-tools  12.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 "TestFS.h"
11 #include "TestTU.h"
12 #include "index/SymbolCollector.h"
13 #include "clang/Basic/FileManager.h"
14 #include "clang/Basic/FileSystemOptions.h"
15 #include "clang/Frontend/CompilerInstance.h"
16 #include "clang/Index/IndexingAction.h"
17 #include "clang/Index/IndexingOptions.h"
18 #include "clang/Tooling/Tooling.h"
19 #include "llvm/ADT/IntrusiveRefCntPtr.h"
20 #include "llvm/ADT/STLExtras.h"
21 #include "llvm/ADT/StringRef.h"
22 #include "llvm/Support/MemoryBuffer.h"
23 #include "llvm/Support/VirtualFileSystem.h"
24 #include "gmock/gmock-matchers.h"
25 #include "gmock/gmock-more-matchers.h"
26 #include "gmock/gmock.h"
27 #include "gtest/gtest.h"
28 
29 #include <memory>
30 #include <string>
31 
32 namespace clang {
33 namespace clangd {
34 namespace {
35 
36 using ::testing::_;
37 using ::testing::AllOf;
38 using ::testing::Contains;
39 using ::testing::Each;
40 using ::testing::ElementsAre;
41 using ::testing::Field;
42 using ::testing::IsEmpty;
43 using ::testing::Not;
44 using ::testing::Pair;
45 using ::testing::UnorderedElementsAre;
46 using ::testing::UnorderedElementsAreArray;
47 
48 // GMock helpers for matching Symbol.
49 MATCHER_P(Labeled, Label, "") {
50  return (arg.Name + arg.Signature).str() == Label;
51 }
52 MATCHER_P(ReturnType, D, "") { return arg.ReturnType == D; }
53 MATCHER_P(Doc, D, "") { return arg.Documentation == D; }
54 MATCHER_P(Snippet, S, "") {
55  return (arg.Name + arg.CompletionSnippetSuffix).str() == S;
56 }
57 MATCHER_P(QName, Name, "") { return (arg.Scope + arg.Name).str() == Name; }
58 MATCHER_P(TemplateArgs, TemplArgs, "") {
59  return arg.TemplateSpecializationArgs == TemplArgs;
60 }
61 MATCHER_P(DeclURI, P, "") {
62  return StringRef(arg.CanonicalDeclaration.FileURI) == P;
63 }
64 MATCHER_P(DefURI, P, "") { return StringRef(arg.Definition.FileURI) == P; }
65 MATCHER(IncludeHeader, "") { return !arg.IncludeHeaders.empty(); }
66 MATCHER_P(IncludeHeader, P, "") {
67  return (arg.IncludeHeaders.size() == 1) &&
68  (arg.IncludeHeaders.begin()->IncludeHeader == P);
69 }
70 MATCHER_P2(IncludeHeaderWithRef, IncludeHeader, References, "") {
71  return (arg.IncludeHeader == IncludeHeader) && (arg.References == References);
72 }
73 MATCHER_P(DeclRange, Pos, "") {
74  return std::make_tuple(arg.CanonicalDeclaration.Start.line(),
75  arg.CanonicalDeclaration.Start.column(),
76  arg.CanonicalDeclaration.End.line(),
77  arg.CanonicalDeclaration.End.column()) ==
78  std::make_tuple(Pos.start.line, Pos.start.character, Pos.end.line,
79  Pos.end.character);
80 }
81 MATCHER_P(DefRange, Pos, "") {
82  return std::make_tuple(
83  arg.Definition.Start.line(), arg.Definition.Start.column(),
84  arg.Definition.End.line(), arg.Definition.End.column()) ==
85  std::make_tuple(Pos.start.line, Pos.start.character, Pos.end.line,
86  Pos.end.character);
87 }
88 MATCHER_P(RefCount, R, "") { return int(arg.References) == R; }
89 MATCHER_P(ForCodeCompletion, IsIndexedForCodeCompletion, "") {
90  return static_cast<bool>(arg.Flags & Symbol::IndexedForCodeCompletion) ==
91  IsIndexedForCodeCompletion;
92 }
93 MATCHER(Deprecated, "") { return arg.Flags & Symbol::Deprecated; }
94 MATCHER(ImplementationDetail, "") {
95  return arg.Flags & Symbol::ImplementationDetail;
96 }
97 MATCHER(VisibleOutsideFile, "") {
98  return static_cast<bool>(arg.Flags & Symbol::VisibleOutsideFile);
99 }
100 MATCHER(RefRange, "") {
101  const Ref &Pos = ::testing::get<0>(arg);
102  const Range &Range = ::testing::get<1>(arg);
103  return std::make_tuple(Pos.Location.Start.line(), Pos.Location.Start.column(),
104  Pos.Location.End.line(), Pos.Location.End.column()) ==
105  std::make_tuple(Range.start.line, Range.start.character,
106  Range.end.line, Range.end.character);
107 }
108 ::testing::Matcher<const std::vector<Ref> &>
109 HaveRanges(const std::vector<Range> Ranges) {
110  return ::testing::UnorderedPointwise(RefRange(), Ranges);
111 }
112 
113 class ShouldCollectSymbolTest : public ::testing::Test {
114 public:
115  void build(llvm::StringRef HeaderCode, llvm::StringRef Code = "") {
116  File.HeaderFilename = HeaderName;
117  File.Filename = FileName;
118  File.HeaderCode = std::string(HeaderCode);
119  File.Code = std::string(Code);
120  AST = File.build();
121  }
122 
123  // build() must have been called.
124  bool shouldCollect(llvm::StringRef Name, bool Qualified = true) {
125  assert(AST.hasValue());
126  const NamedDecl &ND =
127  Qualified ? findDecl(*AST, Name) : findUnqualifiedDecl(*AST, Name);
128  const SourceManager &SM = AST->getSourceManager();
129  bool MainFile = isInsideMainFile(ND.getBeginLoc(), SM);
131  ND, AST->getASTContext(), SymbolCollector::Options(), MainFile);
132  }
133 
134 protected:
135  std::string HeaderName = "f.h";
136  std::string FileName = "f.cpp";
137  TestTU File;
138  llvm::Optional<ParsedAST> AST; // Initialized after build.
139 };
140 
141 TEST_F(ShouldCollectSymbolTest, ShouldCollectSymbol) {
142  build(R"(
143  namespace nx {
144  class X{};
145  auto f() { int Local; } // auto ensures function body is parsed.
146  struct { int x; } var;
147  }
148  )",
149  R"(
150  class InMain {};
151  namespace { class InAnonymous {}; }
152  static void g();
153  )");
154  auto AST = File.build();
155  EXPECT_TRUE(shouldCollect("nx"));
156  EXPECT_TRUE(shouldCollect("nx::X"));
157  EXPECT_TRUE(shouldCollect("nx::f"));
158  EXPECT_TRUE(shouldCollect("InMain"));
159  EXPECT_TRUE(shouldCollect("InAnonymous", /*Qualified=*/false));
160  EXPECT_TRUE(shouldCollect("g"));
161 
162  EXPECT_FALSE(shouldCollect("Local", /*Qualified=*/false));
163 }
164 
165 TEST_F(ShouldCollectSymbolTest, NoPrivateProtoSymbol) {
166  HeaderName = "f.proto.h";
167  build(
168  R"(// Generated by the protocol buffer compiler. DO NOT EDIT!
169  namespace nx {
170  class Top_Level {};
171  class TopLevel {};
172  enum Kind {
173  KIND_OK,
174  Kind_Not_Ok,
175  };
176  })");
177  EXPECT_TRUE(shouldCollect("nx::TopLevel"));
178  EXPECT_TRUE(shouldCollect("nx::Kind::KIND_OK"));
179  EXPECT_TRUE(shouldCollect("nx::Kind"));
180 
181  EXPECT_FALSE(shouldCollect("nx::Top_Level"));
182  EXPECT_FALSE(shouldCollect("nx::Kind::Kind_Not_Ok"));
183 }
184 
185 TEST_F(ShouldCollectSymbolTest, DoubleCheckProtoHeaderComment) {
186  HeaderName = "f.proto.h";
187  build(R"(
188  namespace nx {
189  class Top_Level {};
190  enum Kind {
191  Kind_Fine
192  };
193  }
194  )");
195  EXPECT_TRUE(shouldCollect("nx::Top_Level"));
196  EXPECT_TRUE(shouldCollect("nx::Kind_Fine"));
197 }
198 
199 class SymbolIndexActionFactory : public tooling::FrontendActionFactory {
200 public:
201  SymbolIndexActionFactory(SymbolCollector::Options COpts,
202  CommentHandler *PragmaHandler)
203  : COpts(std::move(COpts)), PragmaHandler(PragmaHandler) {}
204 
205  std::unique_ptr<FrontendAction> create() override {
206  class IndexAction : public ASTFrontendAction {
207  public:
208  IndexAction(std::shared_ptr<index::IndexDataConsumer> DataConsumer,
209  const index::IndexingOptions &Opts,
210  CommentHandler *PragmaHandler)
211  : DataConsumer(std::move(DataConsumer)), Opts(Opts),
212  PragmaHandler(PragmaHandler) {}
213 
214  std::unique_ptr<ASTConsumer>
215  CreateASTConsumer(CompilerInstance &CI, llvm::StringRef InFile) override {
216  if (PragmaHandler)
217  CI.getPreprocessor().addCommentHandler(PragmaHandler);
218  return createIndexingASTConsumer(DataConsumer, Opts,
219  CI.getPreprocessorPtr());
220  }
221 
222  bool BeginInvocation(CompilerInstance &CI) override {
223  // Make the compiler parse all comments.
224  CI.getLangOpts().CommentOpts.ParseAllComments = true;
225  return true;
226  }
227 
228  private:
229  std::shared_ptr<index::IndexDataConsumer> DataConsumer;
230  index::IndexingOptions Opts;
231  CommentHandler *PragmaHandler;
232  };
233  index::IndexingOptions IndexOpts;
234  IndexOpts.SystemSymbolFilter =
235  index::IndexingOptions::SystemSymbolFilterKind::All;
236  IndexOpts.IndexFunctionLocals = false;
237  Collector = std::make_shared<SymbolCollector>(COpts);
238  return std::make_unique<IndexAction>(Collector, std::move(IndexOpts),
239  PragmaHandler);
240  }
241 
242  std::shared_ptr<SymbolCollector> Collector;
243  SymbolCollector::Options COpts;
244  CommentHandler *PragmaHandler;
245 };
246 
247 class SymbolCollectorTest : public ::testing::Test {
248 public:
249  SymbolCollectorTest()
251  TestHeaderName(testPath("symbol.h")),
252  TestFileName(testPath("symbol.cc")) {
254  TestFileURI = URI::create(TestFileName).toString();
255  }
256 
257  // Note that unlike TestTU, no automatic header guard is added.
258  // HeaderCode should start with #pragma once to be treated as modular.
259  bool runSymbolCollector(llvm::StringRef HeaderCode, llvm::StringRef MainCode,
260  const std::vector<std::string> &ExtraArgs = {}) {
261  llvm::IntrusiveRefCntPtr<FileManager> Files(
262  new FileManager(FileSystemOptions(), InMemoryFileSystem));
263 
264  auto Factory = std::make_unique<SymbolIndexActionFactory>(
266 
267  std::vector<std::string> Args = {"symbol_collector", "-fsyntax-only",
268  "-xc++", "-include", TestHeaderName};
269  Args.insert(Args.end(), ExtraArgs.begin(), ExtraArgs.end());
270  // This allows to override the "-xc++" with something else, i.e.
271  // -xobjective-c++.
272  Args.push_back(TestFileName);
273 
274  tooling::ToolInvocation Invocation(
275  Args, Factory->create(), Files.get(),
276  std::make_shared<PCHContainerOperations>());
277 
279  llvm::MemoryBuffer::getMemBuffer(HeaderCode));
280  InMemoryFileSystem->addFile(TestFileName, 0,
281  llvm::MemoryBuffer::getMemBuffer(MainCode));
282  Invocation.run();
283  Symbols = Factory->Collector->takeSymbols();
284  Refs = Factory->Collector->takeRefs();
285  Relations = Factory->Collector->takeRelations();
286  return true;
287  }
288 
289 protected:
290  llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem;
291  std::string TestHeaderName;
292  std::string TestHeaderURI;
293  std::string TestFileName;
294  std::string TestFileURI;
295  SymbolSlab Symbols;
296  RefSlab Refs;
297  RelationSlab Relations;
298  SymbolCollector::Options CollectorOpts;
299  std::unique_ptr<CommentHandler> PragmaHandler;
300 };
301 
302 TEST_F(SymbolCollectorTest, CollectSymbols) {
303  const std::string Header = R"(
304  class Foo {
305  Foo() {}
306  Foo(int a) {}
307  void f();
308  friend void f1();
309  friend class Friend;
310  Foo& operator=(const Foo&);
311  ~Foo();
312  class Nested {
313  void f();
314  };
315  };
316  class Friend {
317  };
318 
319  void f1();
320  inline void f2() {}
321  static const int KInt = 2;
322  const char* kStr = "123";
323 
324  namespace {
325  void ff() {} // ignore
326  }
327 
328  void f1() {}
329 
330  namespace foo {
331  // Type alias
332  typedef int int32;
333  using int32_t = int32;
334 
335  // Variable
336  int v1;
337 
338  // Namespace
339  namespace bar {
340  int v2;
341  }
342  // Namespace alias
343  namespace baz = bar;
344 
345  using bar::v2;
346  } // namespace foo
347  )";
348  runSymbolCollector(Header, /*Main=*/"");
349  EXPECT_THAT(Symbols,
350  UnorderedElementsAreArray(
351  {AllOf(QName("Foo"), ForCodeCompletion(true)),
352  AllOf(QName("Foo::Foo"), ForCodeCompletion(false)),
353  AllOf(QName("Foo::Foo"), ForCodeCompletion(false)),
354  AllOf(QName("Foo::f"), ForCodeCompletion(false)),
355  AllOf(QName("Foo::~Foo"), ForCodeCompletion(false)),
356  AllOf(QName("Foo::operator="), ForCodeCompletion(false)),
357  AllOf(QName("Foo::Nested"), ForCodeCompletion(false)),
358  AllOf(QName("Foo::Nested::f"), ForCodeCompletion(false)),
359 
360  AllOf(QName("Friend"), ForCodeCompletion(true)),
361  AllOf(QName("f1"), ForCodeCompletion(true)),
362  AllOf(QName("f2"), ForCodeCompletion(true)),
363  AllOf(QName("KInt"), ForCodeCompletion(true)),
364  AllOf(QName("kStr"), ForCodeCompletion(true)),
365  AllOf(QName("foo"), ForCodeCompletion(true)),
366  AllOf(QName("foo::bar"), ForCodeCompletion(true)),
367  AllOf(QName("foo::int32"), ForCodeCompletion(true)),
368  AllOf(QName("foo::int32_t"), ForCodeCompletion(true)),
369  AllOf(QName("foo::v1"), ForCodeCompletion(true)),
370  AllOf(QName("foo::bar::v2"), ForCodeCompletion(true)),
371  AllOf(QName("foo::v2"), ForCodeCompletion(true)),
372  AllOf(QName("foo::baz"), ForCodeCompletion(true))}));
373 }
374 
375 TEST_F(SymbolCollectorTest, FileLocal) {
376  const std::string Header = R"(
377  class Foo {};
378  namespace {
379  class Ignored {};
380  }
381  void bar();
382  )";
383  const std::string Main = R"(
384  class ForwardDecl;
385  void bar() {}
386  static void a();
387  class B {};
388  namespace {
389  void c();
390  }
391  )";
392  runSymbolCollector(Header, Main);
393  EXPECT_THAT(Symbols,
394  UnorderedElementsAre(
395  AllOf(QName("Foo"), VisibleOutsideFile()),
396  AllOf(QName("bar"), VisibleOutsideFile()),
397  AllOf(QName("a"), Not(VisibleOutsideFile())),
398  AllOf(QName("B"), Not(VisibleOutsideFile())),
399  AllOf(QName("c"), Not(VisibleOutsideFile())),
400  // FIXME: ForwardDecl likely *is* visible outside.
401  AllOf(QName("ForwardDecl"), Not(VisibleOutsideFile()))));
402 }
403 
404 TEST_F(SymbolCollectorTest, Template) {
405  Annotations Header(R"(
406  // Primary template and explicit specialization are indexed, instantiation
407  // is not.
408  template <class T, class U> struct [[Tmpl]] {T $xdecl[[x]] = 0;};
409  template <> struct $specdecl[[Tmpl]]<int, bool> {};
410  template <class U> struct $partspecdecl[[Tmpl]]<bool, U> {};
411  extern template struct Tmpl<float, bool>;
412  template struct Tmpl<double, bool>;
413  )");
414  runSymbolCollector(Header.code(), /*Main=*/"");
415  EXPECT_THAT(Symbols,
416  UnorderedElementsAre(
417  AllOf(QName("Tmpl"), DeclRange(Header.range()),
418  ForCodeCompletion(true)),
419  AllOf(QName("Tmpl"), DeclRange(Header.range("specdecl")),
420  ForCodeCompletion(false)),
421  AllOf(QName("Tmpl"), DeclRange(Header.range("partspecdecl")),
422  ForCodeCompletion(false)),
423  AllOf(QName("Tmpl::x"), DeclRange(Header.range("xdecl")),
424  ForCodeCompletion(false))));
425 }
426 
427 TEST_F(SymbolCollectorTest, TemplateArgs) {
428  Annotations Header(R"(
429  template <class X> class $barclasstemp[[Bar]] {};
430  template <class T, class U, template<typename> class Z, int Q>
431  struct [[Tmpl]] { T $xdecl[[x]] = 0; };
432 
433  // template-template, non-type and type full spec
434  template <> struct $specdecl[[Tmpl]]<int, bool, Bar, 3> {};
435 
436  // template-template, non-type and type partial spec
437  template <class U, int T> struct $partspecdecl[[Tmpl]]<bool, U, Bar, T> {};
438  // instantiation
439  extern template struct Tmpl<float, bool, Bar, 8>;
440  // instantiation
441  template struct Tmpl<double, bool, Bar, 2>;
442 
443  template <typename ...> class $fooclasstemp[[Foo]] {};
444  // parameter-packs full spec
445  template<> class $parampack[[Foo]]<Bar<int>, int, double> {};
446  // parameter-packs partial spec
447  template<class T> class $parampackpartial[[Foo]]<T, T> {};
448 
449  template <int ...> class $bazclasstemp[[Baz]] {};
450  // non-type parameter-packs full spec
451  template<> class $parampacknontype[[Baz]]<3, 5, 8> {};
452  // non-type parameter-packs partial spec
453  template<int T> class $parampacknontypepartial[[Baz]]<T, T> {};
454 
455  template <template <class> class ...> class $fozclasstemp[[Foz]] {};
456  // template-template parameter-packs full spec
457  template<> class $parampacktempltempl[[Foz]]<Bar, Bar> {};
458  // template-template parameter-packs partial spec
459  template<template <class> class T>
460  class $parampacktempltemplpartial[[Foz]]<T, T> {};
461  )");
462  runSymbolCollector(Header.code(), /*Main=*/"");
463  EXPECT_THAT(
464  Symbols,
465  AllOf(
466  Contains(AllOf(QName("Tmpl"), TemplateArgs("<int, bool, Bar, 3>"),
467  DeclRange(Header.range("specdecl")),
468  ForCodeCompletion(false))),
469  Contains(AllOf(QName("Tmpl"), TemplateArgs("<bool, U, Bar, T>"),
470  DeclRange(Header.range("partspecdecl")),
471  ForCodeCompletion(false))),
472  Contains(AllOf(QName("Foo"), TemplateArgs("<Bar<int>, int, double>"),
473  DeclRange(Header.range("parampack")),
474  ForCodeCompletion(false))),
475  Contains(AllOf(QName("Foo"), TemplateArgs("<T, T>"),
476  DeclRange(Header.range("parampackpartial")),
477  ForCodeCompletion(false))),
478  Contains(AllOf(QName("Baz"), TemplateArgs("<3, 5, 8>"),
479  DeclRange(Header.range("parampacknontype")),
480  ForCodeCompletion(false))),
481  Contains(AllOf(QName("Baz"), TemplateArgs("<T, T>"),
482  DeclRange(Header.range("parampacknontypepartial")),
483  ForCodeCompletion(false))),
484  Contains(AllOf(QName("Foz"), TemplateArgs("<Bar, Bar>"),
485  DeclRange(Header.range("parampacktempltempl")),
486  ForCodeCompletion(false))),
487  Contains(AllOf(QName("Foz"), TemplateArgs("<T, T>"),
488  DeclRange(Header.range("parampacktempltemplpartial")),
489  ForCodeCompletion(false)))));
490 }
491 
492 TEST_F(SymbolCollectorTest, ObjCSymbols) {
493  const std::string Header = R"(
494  @interface Person
495  - (void)someMethodName:(void*)name1 lastName:(void*)lName;
496  @end
497 
498  @implementation Person
499  - (void)someMethodName:(void*)name1 lastName:(void*)lName{
500  int foo;
501  ^(int param){ int bar; };
502  }
503  @end
504 
505  @interface Person (MyCategory)
506  - (void)someMethodName2:(void*)name2;
507  @end
508 
509  @implementation Person (MyCategory)
510  - (void)someMethodName2:(void*)name2 {
511  int foo2;
512  }
513  @end
514 
515  @protocol MyProtocol
516  - (void)someMethodName3:(void*)name3;
517  @end
518  )";
519  TestFileName = testPath("test.m");
520  runSymbolCollector(Header, /*Main=*/"", {"-fblocks", "-xobjective-c++"});
521  EXPECT_THAT(Symbols,
522  UnorderedElementsAre(
523  QName("Person"), QName("Person::someMethodName:lastName:"),
524  QName("MyCategory"), QName("Person::someMethodName2:"),
525  QName("MyProtocol"), QName("MyProtocol::someMethodName3:")));
526 }
527 
528 TEST_F(SymbolCollectorTest, ObjCPropertyImpl) {
529  const std::string Header = R"(
530  @interface Container
531  @property(nonatomic) int magic;
532  @end
533 
534  @implementation Container
535  @end
536  )";
537  TestFileName = testPath("test.m");
538  runSymbolCollector(Header, /*Main=*/"", {"-xobjective-c++"});
539  EXPECT_THAT(Symbols, Contains(QName("Container")));
540  EXPECT_THAT(Symbols, Contains(QName("Container::magic")));
541  // FIXME: Results also contain Container::_magic on some platforms.
542  // Figure out why it's platform-dependent.
543 }
544 
545 TEST_F(SymbolCollectorTest, Locations) {
546  Annotations Header(R"cpp(
547  // Declared in header, defined in main.
548  extern int $xdecl[[X]];
549  class $clsdecl[[Cls]];
550  void $printdecl[[print]]();
551 
552  // Declared in header, defined nowhere.
553  extern int $zdecl[[Z]];
554 
555  void $foodecl[[fo\
556 o]]();
557  )cpp");
558  Annotations Main(R"cpp(
559  int $xdef[[X]] = 42;
560  class $clsdef[[Cls]] {};
561  void $printdef[[print]]() {}
562 
563  // Declared/defined in main only.
564  int $ydecl[[Y]];
565  )cpp");
566  runSymbolCollector(Header.code(), Main.code());
567  EXPECT_THAT(Symbols,
568  UnorderedElementsAre(
569  AllOf(QName("X"), DeclRange(Header.range("xdecl")),
570  DefRange(Main.range("xdef"))),
571  AllOf(QName("Cls"), DeclRange(Header.range("clsdecl")),
572  DefRange(Main.range("clsdef"))),
573  AllOf(QName("print"), DeclRange(Header.range("printdecl")),
574  DefRange(Main.range("printdef"))),
575  AllOf(QName("Z"), DeclRange(Header.range("zdecl"))),
576  AllOf(QName("foo"), DeclRange(Header.range("foodecl"))),
577  AllOf(QName("Y"), DeclRange(Main.range("ydecl")))));
578 }
579 
580 TEST_F(SymbolCollectorTest, Refs) {
581  Annotations Header(R"(
582  #define MACRO(X) (X + 1)
583  class Foo {
584  public:
585  Foo() {}
586  Foo(int);
587  };
588  class Bar;
589  void func();
590 
591  namespace NS {} // namespace ref is ignored
592  )");
593  Annotations Main(R"(
594  class $bar[[Bar]] {};
595 
596  void $func[[func]]();
597 
598  void fff() {
599  $foo[[Foo]] foo;
600  $bar[[Bar]] bar;
601  $func[[func]]();
602  int abc = 0;
603  $foo[[Foo]] foo2 = abc;
604  abc = $macro[[MACRO]](1);
605  }
606  )");
607  Annotations SymbolsOnlyInMainCode(R"(
608  #define FUNC(X) (X+1)
609  int a;
610  void b() {}
611  static const int c = FUNC(1);
612  class d {};
613  )");
614  CollectorOpts.RefFilter = RefKind::All;
615  CollectorOpts.CollectMacro = true;
616  runSymbolCollector(Header.code(),
617  (Main.code() + SymbolsOnlyInMainCode.code()).str());
618  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Foo").ID,
619  HaveRanges(Main.ranges("foo")))));
620  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Bar").ID,
621  HaveRanges(Main.ranges("bar")))));
622  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "func").ID,
623  HaveRanges(Main.ranges("func")))));
624  EXPECT_THAT(Refs, Not(Contains(Pair(findSymbol(Symbols, "NS").ID, _))));
625  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "MACRO").ID,
626  HaveRanges(Main.ranges("macro")))));
627  // Symbols *only* in the main file (a, b, c, FUNC) had no refs collected.
628  auto MainSymbols =
629  TestTU::withHeaderCode(SymbolsOnlyInMainCode.code()).headerSymbols();
630  EXPECT_THAT(Refs, Not(Contains(Pair(findSymbol(MainSymbols, "a").ID, _))));
631  EXPECT_THAT(Refs, Not(Contains(Pair(findSymbol(MainSymbols, "b").ID, _))));
632  EXPECT_THAT(Refs, Not(Contains(Pair(findSymbol(MainSymbols, "c").ID, _))));
633  EXPECT_THAT(Refs, Not(Contains(Pair(findSymbol(MainSymbols, "FUNC").ID, _))));
634 }
635 
636 TEST_F(SymbolCollectorTest, MacroRefInHeader) {
637  Annotations Header(R"(
638  #define $foo[[FOO]](X) (X + 1)
639  #define $bar[[BAR]](X) (X + 2)
640 
641  // Macro defined multiple times.
642  #define $ud1[[UD]] 1
643  int ud_1 = $ud1[[UD]];
644  #undef UD
645 
646  #define $ud2[[UD]] 2
647  int ud_2 = $ud2[[UD]];
648  #undef UD
649 
650  // Macros from token concatenations not included.
651  #define $concat[[CONCAT]](X) X##A()
652  #define $prepend[[PREPEND]](X) MACRO##X()
653  #define $macroa[[MACROA]]() 123
654  int B = $concat[[CONCAT]](MACRO);
655  int D = $prepend[[PREPEND]](A);
656 
657  void fff() {
658  int abc = $foo[[FOO]](1) + $bar[[BAR]]($foo[[FOO]](1));
659  }
660  )");
661  CollectorOpts.RefFilter = RefKind::All;
662  CollectorOpts.RefsInHeaders = true;
663  // Need this to get the SymbolID for macros for tests.
664  CollectorOpts.CollectMacro = true;
665 
666  runSymbolCollector(Header.code(), "");
667 
668  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "FOO").ID,
669  HaveRanges(Header.ranges("foo")))));
670  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "BAR").ID,
671  HaveRanges(Header.ranges("bar")))));
672  // No unique ID for multiple symbols named UD. Check for ranges only.
673  EXPECT_THAT(Refs, Contains(Pair(_, HaveRanges(Header.ranges("ud1")))));
674  EXPECT_THAT(Refs, Contains(Pair(_, HaveRanges(Header.ranges("ud2")))));
675  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "CONCAT").ID,
676  HaveRanges(Header.ranges("concat")))));
677  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "PREPEND").ID,
678  HaveRanges(Header.ranges("prepend")))));
679  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "MACROA").ID,
680  HaveRanges(Header.ranges("macroa")))));
681 }
682 
683 TEST_F(SymbolCollectorTest, MacroRefWithoutCollectingSymbol) {
684  Annotations Header(R"(
685  #define $foo[[FOO]](X) (X + 1)
686  int abc = $foo[[FOO]](1);
687  )");
688  CollectorOpts.RefFilter = RefKind::All;
689  CollectorOpts.RefsInHeaders = true;
690  CollectorOpts.CollectMacro = false;
691  runSymbolCollector(Header.code(), "");
692  EXPECT_THAT(Refs, Contains(Pair(_, HaveRanges(Header.ranges("foo")))));
693 }
694 
695 TEST_F(SymbolCollectorTest, MacrosWithRefFilter) {
696  Annotations Header("#define $macro[[MACRO]](X) (X + 1)");
697  Annotations Main("void foo() { int x = $macro[[MACRO]](1); }");
698  CollectorOpts.RefFilter = RefKind::Unknown;
699  runSymbolCollector(Header.code(), Main.code());
700  EXPECT_THAT(Refs, IsEmpty());
701 }
702 
703 TEST_F(SymbolCollectorTest, SpelledReferences) {
704  struct {
705  llvm::StringRef Header;
706  llvm::StringRef Main;
707  llvm::StringRef TargetSymbolName;
708  } TestCases[] = {
709  {
710  R"cpp(
711  struct Foo;
712  #define MACRO Foo
713  )cpp",
714  R"cpp(
715  struct $spelled[[Foo]] {
716  $spelled[[Foo]]();
717  ~$spelled[[Foo]]();
718  };
719  $spelled[[Foo]] Variable1;
720  $implicit[[MACRO]] Variable2;
721  )cpp",
722  "Foo",
723  },
724  {
725  R"cpp(
726  class Foo {
727  public:
728  Foo() = default;
729  };
730  )cpp",
731  R"cpp(
732  void f() { Foo $implicit[[f]]; f = $spelled[[Foo]]();}
733  )cpp",
734  "Foo::Foo" /// constructor.
735  },
736  };
737  CollectorOpts.RefFilter = RefKind::All;
738  CollectorOpts.RefsInHeaders = false;
739  for (const auto& T : TestCases) {
740  Annotations Header(T.Header);
741  Annotations Main(T.Main);
742  // Reset the file system.
744  runSymbolCollector(Header.code(), Main.code());
745 
746  const auto SpelledRanges = Main.ranges("spelled");
747  const auto ImplicitRanges = Main.ranges("implicit");
748  RefSlab::Builder SpelledSlabBuilder, ImplicitSlabBuilder;
749  const auto TargetID = findSymbol(Symbols, T.TargetSymbolName).ID;
750  for (const auto &SymbolAndRefs : Refs) {
751  const auto ID = SymbolAndRefs.first;
752  if (ID != TargetID)
753  continue;
754  for (const auto &Ref : SymbolAndRefs.second)
755  if ((Ref.Kind & RefKind::Spelled) != RefKind::Unknown)
756  SpelledSlabBuilder.insert(ID, Ref);
757  else
758  ImplicitSlabBuilder.insert(ID, Ref);
759  }
760  const auto SpelledRefs = std::move(SpelledSlabBuilder).build(),
761  ImplicitRefs = std::move(ImplicitSlabBuilder).build();
762  EXPECT_THAT(SpelledRefs,
763  Contains(Pair(TargetID, HaveRanges(SpelledRanges))));
764  EXPECT_THAT(ImplicitRefs,
765  Contains(Pair(TargetID, HaveRanges(ImplicitRanges))));
766  }
767 }
768 
769 TEST_F(SymbolCollectorTest, NameReferences) {
770  CollectorOpts.RefFilter = RefKind::All;
771  CollectorOpts.RefsInHeaders = true;
772  Annotations Header(R"(
773  class [[Foo]] {
774  public:
775  [[Foo]]() {}
776  ~[[Foo]]() {}
777  };
778  )");
779  CollectorOpts.RefFilter = RefKind::All;
780  runSymbolCollector(Header.code(), "");
781  // When we find references for class Foo, we expect to see all
782  // constructor/destructor references.
783  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Foo").ID,
784  HaveRanges(Header.ranges()))));
785 }
786 
787 TEST_F(SymbolCollectorTest, RefsOnMacros) {
788  // Refs collected from SymbolCollector behave in the same way as
789  // AST-based xrefs.
790  CollectorOpts.RefFilter = RefKind::All;
791  CollectorOpts.RefsInHeaders = true;
792  Annotations Header(R"(
793  #define TYPE(X) X
794  #define FOO Foo
795  #define CAT(X, Y) X##Y
796  class [[Foo]] {};
797  void test() {
798  TYPE([[Foo]]) foo;
799  [[FOO]] foo2;
800  TYPE(TYPE([[Foo]])) foo3;
801  [[CAT]](Fo, o) foo4;
802  }
803  )");
804  CollectorOpts.RefFilter = RefKind::All;
805  runSymbolCollector(Header.code(), "");
806  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Foo").ID,
807  HaveRanges(Header.ranges()))));
808 }
809 
810 TEST_F(SymbolCollectorTest, HeaderAsMainFile) {
811  CollectorOpts.RefFilter = RefKind::All;
812  Annotations Header(R"(
813  class $Foo[[Foo]] {};
814 
815  void $Func[[Func]]() {
816  $Foo[[Foo]] fo;
817  }
818  )");
819  // The main file is normal .cpp file, we shouldn't collect any refs of symbols
820  // which are not declared in the preamble.
821  TestFileName = testPath("foo.cpp");
822  runSymbolCollector("", Header.code());
823  EXPECT_THAT(Refs, UnorderedElementsAre());
824 
825  // Run the .h file as main file, we should collect the refs.
826  TestFileName = testPath("foo.h");
827  runSymbolCollector("", Header.code(),
828  /*ExtraArgs=*/{"-xobjective-c++-header"});
829  EXPECT_THAT(Symbols, UnorderedElementsAre(QName("Foo"), QName("Func")));
830  EXPECT_THAT(Refs,
831  UnorderedElementsAre(Pair(findSymbol(Symbols, "Foo").ID,
832  HaveRanges(Header.ranges("Foo"))),
833  Pair(findSymbol(Symbols, "Func").ID,
834  HaveRanges(Header.ranges("Func")))));
835 
836  // Run the .hh file as main file (without "-x c++-header"), we should collect
837  // the refs as well.
838  TestFileName = testPath("foo.hh");
839  runSymbolCollector("", Header.code());
840  EXPECT_THAT(Symbols, UnorderedElementsAre(QName("Foo"), QName("Func")));
841  EXPECT_THAT(Refs,
842  UnorderedElementsAre(Pair(findSymbol(Symbols, "Foo").ID,
843  HaveRanges(Header.ranges("Foo"))),
844  Pair(findSymbol(Symbols, "Func").ID,
845  HaveRanges(Header.ranges("Func")))));
846 }
847 
848 TEST_F(SymbolCollectorTest, RefsInHeaders) {
849  CollectorOpts.RefFilter = RefKind::All;
850  CollectorOpts.RefsInHeaders = true;
851  CollectorOpts.CollectMacro = true;
852  Annotations Header(R"(
853  #define $macro[[MACRO]](x) (x+1)
854  class $foo[[Foo]] {};
855  )");
856  runSymbolCollector(Header.code(), "");
857  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Foo").ID,
858  HaveRanges(Header.ranges("foo")))));
859  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "MACRO").ID,
860  HaveRanges(Header.ranges("macro")))));
861 }
862 
863 TEST_F(SymbolCollectorTest, Relations) {
864  std::string Header = R"(
865  class Base {};
866  class Derived : public Base {};
867  )";
868  runSymbolCollector(Header, /*Main=*/"");
869  const Symbol &Base = findSymbol(Symbols, "Base");
870  const Symbol &Derived = findSymbol(Symbols, "Derived");
871  EXPECT_THAT(Relations,
872  Contains(Relation{Base.ID, RelationKind::BaseOf, Derived.ID}));
873 }
874 
875 TEST_F(SymbolCollectorTest, CountReferences) {
876  const std::string Header = R"(
877  class W;
878  class X {};
879  class Y;
880  class Z {}; // not used anywhere
881  Y* y = nullptr; // used in header doesn't count
882  #define GLOBAL_Z(name) Z name;
883  )";
884  const std::string Main = R"(
885  W* w = nullptr;
886  W* w2 = nullptr; // only one usage counts
887  X x();
888  class V;
889  class Y{}; // definition doesn't count as a reference
890  V* v = nullptr;
891  GLOBAL_Z(z); // Not a reference to Z, we don't spell the type.
892  )";
893  CollectorOpts.CountReferences = true;
894  runSymbolCollector(Header, Main);
895  EXPECT_THAT(
896  Symbols,
897  UnorderedElementsAreArray(
898  {AllOf(QName("W"), RefCount(1)), AllOf(QName("X"), RefCount(1)),
899  AllOf(QName("Y"), RefCount(0)), AllOf(QName("Z"), RefCount(0)),
900  AllOf(QName("y"), RefCount(0)), AllOf(QName("z"), RefCount(0)),
901  AllOf(QName("x"), RefCount(0)), AllOf(QName("w"), RefCount(0)),
902  AllOf(QName("w2"), RefCount(0)), AllOf(QName("V"), RefCount(1)),
903  AllOf(QName("v"), RefCount(0))}));
904 }
905 
906 TEST_F(SymbolCollectorTest, SymbolRelativeNoFallback) {
907  runSymbolCollector("class Foo {};", /*Main=*/"");
908  EXPECT_THAT(Symbols, UnorderedElementsAre(
909  AllOf(QName("Foo"), DeclURI(TestHeaderURI))));
910 }
911 
912 TEST_F(SymbolCollectorTest, SymbolRelativeWithFallback) {
913  TestHeaderName = "x.h";
914  TestFileName = "x.cpp";
916  CollectorOpts.FallbackDir = testRoot();
917  runSymbolCollector("class Foo {};", /*Main=*/"");
918  EXPECT_THAT(Symbols, UnorderedElementsAre(
919  AllOf(QName("Foo"), DeclURI(TestHeaderURI))));
920 }
921 
922 TEST_F(SymbolCollectorTest, UnittestURIScheme) {
923  // Use test URI scheme from URITests.cpp
924  TestHeaderName = testPath("x.h");
925  TestFileName = testPath("x.cpp");
926  runSymbolCollector("class Foo {};", /*Main=*/"");
927  EXPECT_THAT(Symbols, UnorderedElementsAre(
928  AllOf(QName("Foo"), DeclURI("unittest:///x.h"))));
929 }
930 
931 TEST_F(SymbolCollectorTest, IncludeEnums) {
932  const std::string Header = R"(
933  enum {
934  Red
935  };
936  enum Color {
937  Green
938  };
939  enum class Color2 {
940  Yellow
941  };
942  namespace ns {
943  enum {
944  Black
945  };
946  }
947  )";
948  runSymbolCollector(Header, /*Main=*/"");
949  EXPECT_THAT(Symbols,
950  UnorderedElementsAre(
951  AllOf(QName("Red"), ForCodeCompletion(true)),
952  AllOf(QName("Color"), ForCodeCompletion(true)),
953  AllOf(QName("Green"), ForCodeCompletion(true)),
954  AllOf(QName("Color2"), ForCodeCompletion(true)),
955  AllOf(QName("Color2::Yellow"), ForCodeCompletion(false)),
956  AllOf(QName("ns"), ForCodeCompletion(true)),
957  AllOf(QName("ns::Black"), ForCodeCompletion(true))));
958 }
959 
960 TEST_F(SymbolCollectorTest, NamelessSymbols) {
961  const std::string Header = R"(
962  struct {
963  int a;
964  } Foo;
965  )";
966  runSymbolCollector(Header, /*Main=*/"");
967  EXPECT_THAT(Symbols, UnorderedElementsAre(QName("Foo"),
968  QName("(anonymous struct)::a")));
969 }
970 
971 TEST_F(SymbolCollectorTest, SymbolFormedFromRegisteredSchemeFromMacro) {
972 
973  Annotations Header(R"(
974  #define FF(name) \
975  class name##_Test {};
976 
977  $expansion[[FF]](abc);
978 
979  #define FF2() \
980  class $spelling[[Test]] {};
981 
982  FF2();
983  )");
984 
985  runSymbolCollector(Header.code(), /*Main=*/"");
986  EXPECT_THAT(Symbols,
987  UnorderedElementsAre(
988  AllOf(QName("abc_Test"), DeclRange(Header.range("expansion")),
989  DeclURI(TestHeaderURI)),
990  AllOf(QName("Test"), DeclRange(Header.range("spelling")),
991  DeclURI(TestHeaderURI))));
992 }
993 
994 TEST_F(SymbolCollectorTest, SymbolFormedByCLI) {
995  Annotations Header(R"(
996  #ifdef NAME
997  class $expansion[[NAME]] {};
998  #endif
999  )");
1000  runSymbolCollector(Header.code(), /*Main=*/"", /*ExtraArgs=*/{"-DNAME=name"});
1001  EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(
1002  QName("name"), DeclRange(Header.range("expansion")),
1003  DeclURI(TestHeaderURI))));
1004 }
1005 
1006 TEST_F(SymbolCollectorTest, SymbolsInMainFile) {
1007  const std::string Main = R"(
1008  class Foo {};
1009  void f1();
1010  inline void f2() {}
1011 
1012  namespace {
1013  void ff() {}
1014  }
1015  namespace foo {
1016  namespace {
1017  class Bar {};
1018  }
1019  }
1020  void main_f() {}
1021  void f1() {}
1022  )";
1023  runSymbolCollector(/*Header=*/"", Main);
1024  EXPECT_THAT(Symbols, UnorderedElementsAre(
1025  QName("Foo"), QName("f1"), QName("f2"), QName("ff"),
1026  QName("foo"), QName("foo::Bar"), QName("main_f")));
1027 }
1028 
1029 TEST_F(SymbolCollectorTest, Documentation) {
1030  const std::string Header = R"(
1031  // Doc Foo
1032  class Foo {
1033  // Doc f
1034  int f();
1035  };
1036  )";
1037  CollectorOpts.StoreAllDocumentation = false;
1038  runSymbolCollector(Header, /* Main */ "");
1039  EXPECT_THAT(Symbols,
1040  UnorderedElementsAre(
1041  AllOf(QName("Foo"), Doc("Doc Foo"), ForCodeCompletion(true)),
1042  AllOf(QName("Foo::f"), Doc(""), ReturnType(""),
1043  ForCodeCompletion(false))));
1044 
1045  CollectorOpts.StoreAllDocumentation = true;
1046  runSymbolCollector(Header, /* Main */ "");
1047  EXPECT_THAT(Symbols,
1048  UnorderedElementsAre(
1049  AllOf(QName("Foo"), Doc("Doc Foo"), ForCodeCompletion(true)),
1050  AllOf(QName("Foo::f"), Doc("Doc f"), ReturnType(""),
1051  ForCodeCompletion(false))));
1052 }
1053 
1054 TEST_F(SymbolCollectorTest, ClassMembers) {
1055  const std::string Header = R"(
1056  class Foo {
1057  void f() {}
1058  void g();
1059  static void sf() {}
1060  static void ssf();
1061  static int x;
1062  };
1063  )";
1064  const std::string Main = R"(
1065  void Foo::g() {}
1066  void Foo::ssf() {}
1067  )";
1068  runSymbolCollector(Header, Main);
1069  EXPECT_THAT(
1070  Symbols,
1071  UnorderedElementsAre(
1072  QName("Foo"),
1073  AllOf(QName("Foo::f"), ReturnType(""), ForCodeCompletion(false)),
1074  AllOf(QName("Foo::g"), ReturnType(""), ForCodeCompletion(false)),
1075  AllOf(QName("Foo::sf"), ReturnType(""), ForCodeCompletion(false)),
1076  AllOf(QName("Foo::ssf"), ReturnType(""), ForCodeCompletion(false)),
1077  AllOf(QName("Foo::x"), ReturnType(""), ForCodeCompletion(false))));
1078 }
1079 
1080 TEST_F(SymbolCollectorTest, Scopes) {
1081  const std::string Header = R"(
1082  namespace na {
1083  class Foo {};
1084  namespace nb {
1085  class Bar {};
1086  }
1087  }
1088  )";
1089  runSymbolCollector(Header, /*Main=*/"");
1090  EXPECT_THAT(Symbols,
1091  UnorderedElementsAre(QName("na"), QName("na::nb"),
1092  QName("na::Foo"), QName("na::nb::Bar")));
1093 }
1094 
1095 TEST_F(SymbolCollectorTest, ExternC) {
1096  const std::string Header = R"(
1097  extern "C" { class Foo {}; }
1098  namespace na {
1099  extern "C" { class Bar {}; }
1100  }
1101  )";
1102  runSymbolCollector(Header, /*Main=*/"");
1103  EXPECT_THAT(Symbols, UnorderedElementsAre(QName("na"), QName("Foo"),
1104  QName("na::Bar")));
1105 }
1106 
1107 TEST_F(SymbolCollectorTest, SkipInlineNamespace) {
1108  const std::string Header = R"(
1109  namespace na {
1110  inline namespace nb {
1111  class Foo {};
1112  }
1113  }
1114  namespace na {
1115  // This is still inlined.
1116  namespace nb {
1117  class Bar {};
1118  }
1119  }
1120  )";
1121  runSymbolCollector(Header, /*Main=*/"");
1122  EXPECT_THAT(Symbols,
1123  UnorderedElementsAre(QName("na"), QName("na::nb"),
1124  QName("na::Foo"), QName("na::Bar")));
1125 }
1126 
1127 TEST_F(SymbolCollectorTest, SymbolWithDocumentation) {
1128  const std::string Header = R"(
1129  namespace nx {
1130  /// Foo comment.
1131  int ff(int x, double y) { return 0; }
1132  }
1133  )";
1134  runSymbolCollector(Header, /*Main=*/"");
1135  EXPECT_THAT(
1136  Symbols,
1137  UnorderedElementsAre(
1138  QName("nx"), AllOf(QName("nx::ff"), Labeled("ff(int x, double y)"),
1139  ReturnType("int"), Doc("Foo comment."))));
1140 }
1141 
1142 TEST_F(SymbolCollectorTest, Snippet) {
1143  const std::string Header = R"(
1144  namespace nx {
1145  void f() {}
1146  int ff(int x, double y) { return 0; }
1147  }
1148  )";
1149  runSymbolCollector(Header, /*Main=*/"");
1150  EXPECT_THAT(Symbols,
1151  UnorderedElementsAre(
1152  QName("nx"),
1153  AllOf(QName("nx::f"), Labeled("f()"), Snippet("f()")),
1154  AllOf(QName("nx::ff"), Labeled("ff(int x, double y)"),
1155  Snippet("ff(${1:int x}, ${2:double y})"))));
1156 }
1157 
1158 TEST_F(SymbolCollectorTest, IncludeHeaderSameAsFileURI) {
1159  CollectorOpts.CollectIncludePath = true;
1160  runSymbolCollector("#pragma once\nclass Foo {};", /*Main=*/"");
1161  EXPECT_THAT(Symbols, UnorderedElementsAre(
1162  AllOf(QName("Foo"), DeclURI(TestHeaderURI))));
1163  EXPECT_THAT(Symbols.begin()->IncludeHeaders,
1164  UnorderedElementsAre(IncludeHeaderWithRef(TestHeaderURI, 1u)));
1165 }
1166 
1167 TEST_F(SymbolCollectorTest, CanonicalSTLHeader) {
1168  CollectorOpts.CollectIncludePath = true;
1169  CanonicalIncludes Includes;
1170  auto Language = LangOptions();
1171  Language.CPlusPlus = true;
1172  Includes.addSystemHeadersMapping(Language);
1173  CollectorOpts.Includes = &Includes;
1174  runSymbolCollector("namespace std { class string {}; }", /*Main=*/"");
1175  EXPECT_THAT(Symbols,
1176  Contains(AllOf(QName("std::string"), DeclURI(TestHeaderURI),
1177  IncludeHeader("<string>"))));
1178 }
1179 
1180 TEST_F(SymbolCollectorTest, IWYUPragma) {
1181  CollectorOpts.CollectIncludePath = true;
1182  CanonicalIncludes Includes;
1183  PragmaHandler = collectIWYUHeaderMaps(&Includes);
1184  CollectorOpts.Includes = &Includes;
1185  const std::string Header = R"(
1186  // IWYU pragma: private, include the/good/header.h
1187  class Foo {};
1188  )";
1189  runSymbolCollector(Header, /*Main=*/"");
1190  EXPECT_THAT(Symbols, UnorderedElementsAre(
1191  AllOf(QName("Foo"), DeclURI(TestHeaderURI),
1192  IncludeHeader("\"the/good/header.h\""))));
1193 }
1194 
1195 TEST_F(SymbolCollectorTest, IWYUPragmaWithDoubleQuotes) {
1196  CollectorOpts.CollectIncludePath = true;
1197  CanonicalIncludes Includes;
1198  PragmaHandler = collectIWYUHeaderMaps(&Includes);
1199  CollectorOpts.Includes = &Includes;
1200  const std::string Header = R"(
1201  // IWYU pragma: private, include "the/good/header.h"
1202  class Foo {};
1203  )";
1204  runSymbolCollector(Header, /*Main=*/"");
1205  EXPECT_THAT(Symbols, UnorderedElementsAre(
1206  AllOf(QName("Foo"), DeclURI(TestHeaderURI),
1207  IncludeHeader("\"the/good/header.h\""))));
1208 }
1209 
1210 TEST_F(SymbolCollectorTest, SkipIncFileWhenCanonicalizeHeaders) {
1211  CollectorOpts.CollectIncludePath = true;
1212  CanonicalIncludes Includes;
1213  Includes.addMapping(TestHeaderName, "<canonical>");
1214  CollectorOpts.Includes = &Includes;
1215  auto IncFile = testPath("test.inc");
1216  auto IncURI = URI::create(IncFile).toString();
1217  InMemoryFileSystem->addFile(IncFile, 0,
1218  llvm::MemoryBuffer::getMemBuffer("class X {};"));
1219  runSymbolCollector("#include \"test.inc\"\nclass Y {};", /*Main=*/"",
1220  /*ExtraArgs=*/{"-I", testRoot()});
1221  EXPECT_THAT(Symbols,
1222  UnorderedElementsAre(AllOf(QName("X"), DeclURI(IncURI),
1223  IncludeHeader("<canonical>")),
1224  AllOf(QName("Y"), DeclURI(TestHeaderURI),
1225  IncludeHeader("<canonical>"))));
1226 }
1227 
1228 TEST_F(SymbolCollectorTest, MainFileIsHeaderWhenSkipIncFile) {
1229  CollectorOpts.CollectIncludePath = true;
1230  // To make this case as hard as possible, we won't tell clang main is a
1231  // header. No extension, no -x c++-header.
1232  TestFileName = testPath("no_ext_main");
1233  TestFileURI = URI::create(TestFileName).toString();
1234  auto IncFile = testPath("test.inc");
1235  auto IncURI = URI::create(IncFile).toString();
1236  InMemoryFileSystem->addFile(IncFile, 0,
1237  llvm::MemoryBuffer::getMemBuffer("class X {};"));
1238  runSymbolCollector("", R"cpp(
1239  // Can't use #pragma once in a main file clang doesn't think is a header.
1240  #ifndef MAIN_H_
1241  #define MAIN_H_
1242  #include "test.inc"
1243  #endif
1244  )cpp",
1245  /*ExtraArgs=*/{"-I", testRoot()});
1246  EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(QName("X"), DeclURI(IncURI),
1248 }
1249 
1250 TEST_F(SymbolCollectorTest, IncFileInNonHeader) {
1251  CollectorOpts.CollectIncludePath = true;
1252  TestFileName = testPath("main.cc");
1253  TestFileURI = URI::create(TestFileName).toString();
1254  auto IncFile = testPath("test.inc");
1255  auto IncURI = URI::create(IncFile).toString();
1256  InMemoryFileSystem->addFile(IncFile, 0,
1257  llvm::MemoryBuffer::getMemBuffer("class X {};"));
1258  runSymbolCollector("", R"cpp(
1259  #include "test.inc"
1260  )cpp",
1261  /*ExtraArgs=*/{"-I", testRoot()});
1262  EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(QName("X"), DeclURI(IncURI),
1263  Not(IncludeHeader()))));
1264 }
1265 
1266 // Features that depend on header-guards are fragile. Header guards are only
1267 // recognized when the file ends, so we have to defer checking for them.
1268 TEST_F(SymbolCollectorTest, HeaderGuardDetected) {
1269  CollectorOpts.CollectIncludePath = true;
1270  CollectorOpts.CollectMacro = true;
1271  runSymbolCollector(R"cpp(
1272  #ifndef HEADER_GUARD_
1273  #define HEADER_GUARD_
1274 
1275  // Symbols are seen before the header guard is complete.
1276  #define MACRO
1277  int decl();
1278 
1279  #endif // Header guard is recognized here.
1280  )cpp",
1281  "");
1282  EXPECT_THAT(Symbols, Not(Contains(QName("HEADER_GUARD_"))));
1283  EXPECT_THAT(Symbols, Each(IncludeHeader()));
1284 }
1285 
1286 TEST_F(SymbolCollectorTest, NonModularHeader) {
1287  auto TU = TestTU::withHeaderCode("int x();");
1288  EXPECT_THAT(TU.headerSymbols(), ElementsAre(IncludeHeader()));
1289 
1290  // Files missing include guards aren't eligible for insertion.
1291  TU.ImplicitHeaderGuard = false;
1292  EXPECT_THAT(TU.headerSymbols(), ElementsAre(Not(IncludeHeader())));
1293 
1294  // We recognize some patterns of trying to prevent insertion.
1295  TU = TestTU::withHeaderCode(R"cpp(
1296 #ifndef SECRET
1297 #error "This file isn't safe to include directly"
1298 #endif
1299  int x();
1300  )cpp");
1301  TU.ExtraArgs.push_back("-DSECRET"); // *we're* able to include it.
1302  EXPECT_THAT(TU.headerSymbols(), ElementsAre(Not(IncludeHeader())));
1303 }
1304 
1305 TEST_F(SymbolCollectorTest, AvoidUsingFwdDeclsAsCanonicalDecls) {
1306  CollectorOpts.CollectIncludePath = true;
1307  Annotations Header(R"(
1308  #pragma once
1309  // Forward declarations of TagDecls.
1310  class C;
1311  struct S;
1312  union U;
1313 
1314  // Canonical declarations.
1315  class $cdecl[[C]] {};
1316  struct $sdecl[[S]] {};
1317  union $udecl[[U]] {int $xdecl[[x]]; bool $ydecl[[y]];};
1318  )");
1319  runSymbolCollector(Header.code(), /*Main=*/"");
1320  EXPECT_THAT(
1321  Symbols,
1322  UnorderedElementsAre(
1323  AllOf(QName("C"), DeclURI(TestHeaderURI),
1324  DeclRange(Header.range("cdecl")), IncludeHeader(TestHeaderURI),
1325  DefURI(TestHeaderURI), DefRange(Header.range("cdecl"))),
1326  AllOf(QName("S"), DeclURI(TestHeaderURI),
1327  DeclRange(Header.range("sdecl")), IncludeHeader(TestHeaderURI),
1328  DefURI(TestHeaderURI), DefRange(Header.range("sdecl"))),
1329  AllOf(QName("U"), DeclURI(TestHeaderURI),
1330  DeclRange(Header.range("udecl")), IncludeHeader(TestHeaderURI),
1331  DefURI(TestHeaderURI), DefRange(Header.range("udecl"))),
1332  AllOf(QName("U::x"), DeclURI(TestHeaderURI),
1333  DeclRange(Header.range("xdecl")), DefURI(TestHeaderURI),
1334  DefRange(Header.range("xdecl"))),
1335  AllOf(QName("U::y"), DeclURI(TestHeaderURI),
1336  DeclRange(Header.range("ydecl")), DefURI(TestHeaderURI),
1337  DefRange(Header.range("ydecl")))));
1338 }
1339 
1340 TEST_F(SymbolCollectorTest, ClassForwardDeclarationIsCanonical) {
1341  CollectorOpts.CollectIncludePath = true;
1342  runSymbolCollector(/*Header=*/"#pragma once\nclass X;",
1343  /*Main=*/"class X {};");
1344  EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(
1345  QName("X"), DeclURI(TestHeaderURI),
1347 }
1348 
1349 TEST_F(SymbolCollectorTest, UTF16Character) {
1350  // ö is 2-bytes.
1351  Annotations Header(/*Header=*/"class [[pörk]] {};");
1352  runSymbolCollector(Header.code(), /*Main=*/"");
1353  EXPECT_THAT(Symbols, UnorderedElementsAre(
1354  AllOf(QName("pörk"), DeclRange(Header.range()))));
1355 }
1356 
1357 TEST_F(SymbolCollectorTest, DoNotIndexSymbolsInFriendDecl) {
1358  Annotations Header(R"(
1359  namespace nx {
1360  class $z[[Z]] {};
1361  class X {
1362  friend class Y;
1363  friend class Z;
1364  friend void foo();
1365  friend void $bar[[bar]]() {}
1366  };
1367  class $y[[Y]] {};
1368  void $foo[[foo]]();
1369  }
1370  )");
1371  runSymbolCollector(Header.code(), /*Main=*/"");
1372 
1373  EXPECT_THAT(Symbols,
1374  UnorderedElementsAre(
1375  QName("nx"), QName("nx::X"),
1376  AllOf(QName("nx::Y"), DeclRange(Header.range("y"))),
1377  AllOf(QName("nx::Z"), DeclRange(Header.range("z"))),
1378  AllOf(QName("nx::foo"), DeclRange(Header.range("foo"))),
1379  AllOf(QName("nx::bar"), DeclRange(Header.range("bar")))));
1380 }
1381 
1382 TEST_F(SymbolCollectorTest, ReferencesInFriendDecl) {
1383  const std::string Header = R"(
1384  class X;
1385  class Y;
1386  )";
1387  const std::string Main = R"(
1388  class C {
1389  friend ::X;
1390  friend class Y;
1391  };
1392  )";
1393  CollectorOpts.CountReferences = true;
1394  runSymbolCollector(Header, Main);
1395  EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(QName("X"), RefCount(1)),
1396  AllOf(QName("Y"), RefCount(1)),
1397  AllOf(QName("C"), RefCount(0))));
1398 }
1399 
1400 TEST_F(SymbolCollectorTest, Origin) {
1402  runSymbolCollector("class Foo {};", /*Main=*/"");
1403  EXPECT_THAT(Symbols, UnorderedElementsAre(
1405 }
1406 
1407 TEST_F(SymbolCollectorTest, CollectMacros) {
1408  CollectorOpts.CollectIncludePath = true;
1409  Annotations Header(R"(
1410  #pragma once
1411  #define X 1
1412  #define $mac[[MAC]](x) int x
1413  #define $used[[USED]](y) float y;
1414 
1415  MAC(p);
1416  )");
1417 
1418  Annotations Main(R"(
1419  #define $main[[MAIN]] 1
1420  USED(t);
1421  )");
1422  CollectorOpts.CountReferences = true;
1423  CollectorOpts.CollectMacro = true;
1424  runSymbolCollector(Header.code(), Main.code());
1425  EXPECT_THAT(
1426  Symbols,
1427  UnorderedElementsAre(
1428  QName("p"), QName("t"),
1429  AllOf(QName("X"), DeclURI(TestHeaderURI),
1431  AllOf(Labeled("MAC(x)"), RefCount(0),
1432 
1433  DeclRange(Header.range("mac")), VisibleOutsideFile()),
1434  AllOf(Labeled("USED(y)"), RefCount(1),
1435  DeclRange(Header.range("used")), VisibleOutsideFile()),
1436  AllOf(Labeled("MAIN"), RefCount(0), DeclRange(Main.range("main")),
1437  Not(VisibleOutsideFile()))));
1438 }
1439 
1440 TEST_F(SymbolCollectorTest, DeprecatedSymbols) {
1441  const std::string Header = R"(
1442  void TestClangc() __attribute__((deprecated("", "")));
1443  void TestClangd();
1444  )";
1445  runSymbolCollector(Header, /**/ "");
1446  EXPECT_THAT(Symbols, UnorderedElementsAre(
1447  AllOf(QName("TestClangc"), Deprecated()),
1448  AllOf(QName("TestClangd"), Not(Deprecated()))));
1449 }
1450 
1451 TEST_F(SymbolCollectorTest, ImplementationDetail) {
1452  const std::string Header = R"(
1453  #define DECL_NAME(x, y) x##_##y##_Decl
1454  #define DECL(x, y) class DECL_NAME(x, y) {};
1455  DECL(X, Y); // X_Y_Decl
1456 
1457  class Public {};
1458  )";
1459  runSymbolCollector(Header, /**/ "");
1460  EXPECT_THAT(Symbols,
1461  UnorderedElementsAre(
1462  AllOf(QName("X_Y_Decl"), ImplementationDetail()),
1463  AllOf(QName("Public"), Not(ImplementationDetail()))));
1464 }
1465 
1466 TEST_F(SymbolCollectorTest, UsingDecl) {
1467  const char *Header = R"(
1468  void foo();
1469  namespace std {
1470  using ::foo;
1471  })";
1472  runSymbolCollector(Header, /**/ "");
1473  EXPECT_THAT(Symbols, Contains(QName("std::foo")));
1474 }
1475 
1476 TEST_F(SymbolCollectorTest, CBuiltins) {
1477  // In C, printf in stdio.h is a redecl of an implicit builtin.
1478  const char *Header = R"(
1479  extern int printf(const char*, ...);
1480  )";
1481  runSymbolCollector(Header, /**/ "", {"-xc"});
1482  EXPECT_THAT(Symbols, Contains(QName("printf")));
1483 }
1484 
1485 TEST_F(SymbolCollectorTest, InvalidSourceLoc) {
1486  const char *Header = R"(
1487  void operator delete(void*)
1488  __attribute__((__externally_visible__));)";
1489  runSymbolCollector(Header, /**/ "");
1490  EXPECT_THAT(Symbols, Contains(QName("operator delete")));
1491 }
1492 
1493 TEST_F(SymbolCollectorTest, BadUTF8) {
1494  // Extracted from boost/spirit/home/support/char_encoding/iso8859_1.hpp
1495  // This looks like UTF-8 and fools clang, but has high-ISO-8859-1 comments.
1496  const char *Header = "int PUNCT = 0;\n"
1497  "int types[] = { /* \xa1 */PUNCT };";
1498  CollectorOpts.RefFilter = RefKind::All;
1499  CollectorOpts.RefsInHeaders = true;
1500  runSymbolCollector(Header, "");
1501  EXPECT_THAT(Symbols, Contains(QName("types")));
1502  EXPECT_THAT(Symbols, Contains(QName("PUNCT")));
1503  // Reference is stored, although offset within line is not reliable.
1504  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "PUNCT").ID, _)));
1505 }
1506 
1507 } // namespace
1508 } // namespace clangd
1509 } // namespace clang
std::unique_ptr< CommentHandler > collectIWYUHeaderMaps(CanonicalIncludes *Includes)
Returns a CommentHandler that parses pragma comment on include files to determine when we should incl...
std::string Code
MATCHER_P(Named, N, "")
std::string HeaderName
SymbolCollector::Options CollectorOpts
std::string FileName
llvm::IntrusiveRefCntPtr< llvm::vfs::InMemoryFileSystem > InMemoryFileSystem
std::string TestHeaderName
bool isInsideMainFile(SourceLocation Loc, const SourceManager &SM)
Returns true iff Loc is inside the main file.
Definition: SourceCode.cpp:421
Symbol is visible to other files (not e.g. a static helper function).
Definition: Symbol.h:125
const NamedDecl & findUnqualifiedDecl(ParsedAST &AST, llvm::StringRef Name)
Definition: TestTU.cpp:206
std::string TestHeaderURI
SymbolID ID
The ID of the symbol.
Definition: Symbol.h:38
std::unique_ptr< CompilerInvocation > CI
std::string MainFile
TEST_F(BackgroundIndexTest, NoCrashOnErrorFile)
Symbol is an implementation detail.
Definition: Symbol.h:123
SymbolCollector::Options COpts
std::string QName
static TestTU withHeaderCode(llvm::StringRef HeaderCode)
Definition: TestTU.h:41
Whether or not this symbol is meant to be used for the code completion.
Definition: Symbol.h:119
std::string testPath(PathRef File, llvm::sys::path::Style Style)
Definition: TestFS.cpp:82
MATCHER(Declared, "")
static constexpr llvm::StringLiteral Name
const char * testRoot()
Definition: TestFS.cpp:74
RelationSlab Relations
std::shared_ptr< SymbolCollector > Collector
SymbolSlab Symbols
std::string ReturnType
Position Pos
Definition: SourceCode.cpp:649
CodeCompletionBuilder Builder
int line
Line position in a document (zero-based).
Definition: Protocol.h:146
int character
Character offset on a line in a document (zero-based).
Definition: Protocol.h:151
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:196
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
unsigned References
const Symbol & findSymbol(const SymbolSlab &Slab, llvm::StringRef QName)
Definition: TestTU.cpp:144
CharSourceRange Range
SourceRange for the file name.
std::string IncludeHeader
std::string TestFileName
std::string TestFileURI
Indicates if the symbol is deprecated.
Definition: Symbol.h:121
RefSlab Refs
std::unique_ptr< GlobalCompilationDatabase > Base
SymbolOrigin Origin
Where this symbol came from. Usually an index provides a constant value.
Definition: Symbol.h:61
const NamedDecl & findDecl(ParsedAST &AST, llvm::StringRef QName)
Definition: TestTU.cpp:165
CommentHandler * PragmaHandler