clang-tools  10.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 = HeaderCode;
119  File.Code = 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, NameReferences) {
704  CollectorOpts.RefFilter = RefKind::All;
705  CollectorOpts.RefsInHeaders = true;
706  Annotations Header(R"(
707  class [[Foo]] {
708  public:
709  [[Foo]]() {}
710  ~[[Foo]]() {}
711  };
712  )");
713  CollectorOpts.RefFilter = RefKind::All;
714  runSymbolCollector(Header.code(), "");
715  // When we find references for class Foo, we expect to see all
716  // constructor/destructor references.
717  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Foo").ID,
718  HaveRanges(Header.ranges()))));
719 }
720 
721 TEST_F(SymbolCollectorTest, RefsOnMacros) {
722  // Refs collected from SymbolCollector behave in the same way as
723  // AST-based xrefs.
724  CollectorOpts.RefFilter = RefKind::All;
725  CollectorOpts.RefsInHeaders = true;
726  Annotations Header(R"(
727  #define TYPE(X) X
728  #define FOO Foo
729  #define CAT(X, Y) X##Y
730  class [[Foo]] {};
731  void test() {
732  TYPE([[Foo]]) foo;
733  [[FOO]] foo2;
734  TYPE(TYPE([[Foo]])) foo3;
735  [[CAT]](Fo, o) foo4;
736  }
737  )");
738  CollectorOpts.RefFilter = RefKind::All;
739  runSymbolCollector(Header.code(), "");
740  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Foo").ID,
741  HaveRanges(Header.ranges()))));
742 }
743 
744 TEST_F(SymbolCollectorTest, HeaderAsMainFile) {
745  CollectorOpts.RefFilter = RefKind::All;
746  Annotations Header(R"(
747  class $Foo[[Foo]] {};
748 
749  void $Func[[Func]]() {
750  $Foo[[Foo]] fo;
751  }
752  )");
753  // The main file is normal .cpp file, we shouldn't collect any refs of symbols
754  // which are not declared in the preamble.
755  TestFileName = testPath("foo.cpp");
756  runSymbolCollector("", Header.code());
757  EXPECT_THAT(Refs, UnorderedElementsAre());
758 
759  // Run the .h file as main file, we should collect the refs.
760  TestFileName = testPath("foo.h");
761  runSymbolCollector("", Header.code(),
762  /*ExtraArgs=*/{"-xobjective-c++-header"});
763  EXPECT_THAT(Symbols, UnorderedElementsAre(QName("Foo"), QName("Func")));
764  EXPECT_THAT(Refs,
765  UnorderedElementsAre(Pair(findSymbol(Symbols, "Foo").ID,
766  HaveRanges(Header.ranges("Foo"))),
767  Pair(findSymbol(Symbols, "Func").ID,
768  HaveRanges(Header.ranges("Func")))));
769 
770  // Run the .hh file as main file (without "-x c++-header"), we should collect
771  // the refs as well.
772  TestFileName = testPath("foo.hh");
773  runSymbolCollector("", Header.code());
774  EXPECT_THAT(Symbols, UnorderedElementsAre(QName("Foo"), QName("Func")));
775  EXPECT_THAT(Refs,
776  UnorderedElementsAre(Pair(findSymbol(Symbols, "Foo").ID,
777  HaveRanges(Header.ranges("Foo"))),
778  Pair(findSymbol(Symbols, "Func").ID,
779  HaveRanges(Header.ranges("Func")))));
780 }
781 
782 TEST_F(SymbolCollectorTest, RefsInHeaders) {
783  CollectorOpts.RefFilter = RefKind::All;
784  CollectorOpts.RefsInHeaders = true;
785  CollectorOpts.CollectMacro = true;
786  Annotations Header(R"(
787  #define $macro[[MACRO]](x) (x+1)
788  class $foo[[Foo]] {};
789  )");
790  runSymbolCollector(Header.code(), "");
791  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Foo").ID,
792  HaveRanges(Header.ranges("foo")))));
793  EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "MACRO").ID,
794  HaveRanges(Header.ranges("macro")))));
795 }
796 
797 TEST_F(SymbolCollectorTest, Relations) {
798  std::string Header = R"(
799  class Base {};
800  class Derived : public Base {};
801  )";
802  runSymbolCollector(Header, /*Main=*/"");
803  const Symbol &Base = findSymbol(Symbols, "Base");
804  const Symbol &Derived = findSymbol(Symbols, "Derived");
805  EXPECT_THAT(Relations,
806  Contains(Relation{Base.ID, RelationKind::BaseOf, Derived.ID}));
807 }
808 
809 TEST_F(SymbolCollectorTest, CountReferences) {
810  const std::string Header = R"(
811  class W;
812  class X {};
813  class Y;
814  class Z {}; // not used anywhere
815  Y* y = nullptr; // used in header doesn't count
816  #define GLOBAL_Z(name) Z name;
817  )";
818  const std::string Main = R"(
819  W* w = nullptr;
820  W* w2 = nullptr; // only one usage counts
821  X x();
822  class V;
823  class Y{}; // definition doesn't count as a reference
824  V* v = nullptr;
825  GLOBAL_Z(z); // Not a reference to Z, we don't spell the type.
826  )";
827  CollectorOpts.CountReferences = true;
828  runSymbolCollector(Header, Main);
829  EXPECT_THAT(
830  Symbols,
831  UnorderedElementsAreArray(
832  {AllOf(QName("W"), RefCount(1)), AllOf(QName("X"), RefCount(1)),
833  AllOf(QName("Y"), RefCount(0)), AllOf(QName("Z"), RefCount(0)),
834  AllOf(QName("y"), RefCount(0)), AllOf(QName("z"), RefCount(0)),
835  AllOf(QName("x"), RefCount(0)), AllOf(QName("w"), RefCount(0)),
836  AllOf(QName("w2"), RefCount(0)), AllOf(QName("V"), RefCount(1)),
837  AllOf(QName("v"), RefCount(0))}));
838 }
839 
840 TEST_F(SymbolCollectorTest, SymbolRelativeNoFallback) {
841  runSymbolCollector("class Foo {};", /*Main=*/"");
842  EXPECT_THAT(Symbols, UnorderedElementsAre(
843  AllOf(QName("Foo"), DeclURI(TestHeaderURI))));
844 }
845 
846 TEST_F(SymbolCollectorTest, SymbolRelativeWithFallback) {
847  TestHeaderName = "x.h";
848  TestFileName = "x.cpp";
850  CollectorOpts.FallbackDir = testRoot();
851  runSymbolCollector("class Foo {};", /*Main=*/"");
852  EXPECT_THAT(Symbols, UnorderedElementsAre(
853  AllOf(QName("Foo"), DeclURI(TestHeaderURI))));
854 }
855 
856 TEST_F(SymbolCollectorTest, UnittestURIScheme) {
857  // Use test URI scheme from URITests.cpp
858  TestHeaderName = testPath("x.h");
859  TestFileName = testPath("x.cpp");
860  runSymbolCollector("class Foo {};", /*Main=*/"");
861  EXPECT_THAT(Symbols, UnorderedElementsAre(
862  AllOf(QName("Foo"), DeclURI("unittest:///x.h"))));
863 }
864 
865 TEST_F(SymbolCollectorTest, IncludeEnums) {
866  const std::string Header = R"(
867  enum {
868  Red
869  };
870  enum Color {
871  Green
872  };
873  enum class Color2 {
874  Yellow
875  };
876  namespace ns {
877  enum {
878  Black
879  };
880  }
881  )";
882  runSymbolCollector(Header, /*Main=*/"");
883  EXPECT_THAT(Symbols,
884  UnorderedElementsAre(
885  AllOf(QName("Red"), ForCodeCompletion(true)),
886  AllOf(QName("Color"), ForCodeCompletion(true)),
887  AllOf(QName("Green"), ForCodeCompletion(true)),
888  AllOf(QName("Color2"), ForCodeCompletion(true)),
889  AllOf(QName("Color2::Yellow"), ForCodeCompletion(false)),
890  AllOf(QName("ns"), ForCodeCompletion(true)),
891  AllOf(QName("ns::Black"), ForCodeCompletion(true))));
892 }
893 
894 TEST_F(SymbolCollectorTest, NamelessSymbols) {
895  const std::string Header = R"(
896  struct {
897  int a;
898  } Foo;
899  )";
900  runSymbolCollector(Header, /*Main=*/"");
901  EXPECT_THAT(Symbols, UnorderedElementsAre(QName("Foo"),
902  QName("(anonymous struct)::a")));
903 }
904 
905 TEST_F(SymbolCollectorTest, SymbolFormedFromRegisteredSchemeFromMacro) {
906 
907  Annotations Header(R"(
908  #define FF(name) \
909  class name##_Test {};
910 
911  $expansion[[FF]](abc);
912 
913  #define FF2() \
914  class $spelling[[Test]] {};
915 
916  FF2();
917  )");
918 
919  runSymbolCollector(Header.code(), /*Main=*/"");
920  EXPECT_THAT(Symbols,
921  UnorderedElementsAre(
922  AllOf(QName("abc_Test"), DeclRange(Header.range("expansion")),
923  DeclURI(TestHeaderURI)),
924  AllOf(QName("Test"), DeclRange(Header.range("spelling")),
925  DeclURI(TestHeaderURI))));
926 }
927 
928 TEST_F(SymbolCollectorTest, SymbolFormedByCLI) {
929  Annotations Header(R"(
930  #ifdef NAME
931  class $expansion[[NAME]] {};
932  #endif
933  )");
934  runSymbolCollector(Header.code(), /*Main=*/"", /*ExtraArgs=*/{"-DNAME=name"});
935  EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(
936  QName("name"), DeclRange(Header.range("expansion")),
937  DeclURI(TestHeaderURI))));
938 }
939 
940 TEST_F(SymbolCollectorTest, SymbolsInMainFile) {
941  const std::string Main = R"(
942  class Foo {};
943  void f1();
944  inline void f2() {}
945 
946  namespace {
947  void ff() {}
948  }
949  namespace foo {
950  namespace {
951  class Bar {};
952  }
953  }
954  void main_f() {}
955  void f1() {}
956  )";
957  runSymbolCollector(/*Header=*/"", Main);
958  EXPECT_THAT(Symbols, UnorderedElementsAre(
959  QName("Foo"), QName("f1"), QName("f2"), QName("ff"),
960  QName("foo"), QName("foo::Bar"), QName("main_f")));
961 }
962 
963 TEST_F(SymbolCollectorTest, Documentation) {
964  const std::string Header = R"(
965  // Doc Foo
966  class Foo {
967  // Doc f
968  int f();
969  };
970  )";
971  CollectorOpts.StoreAllDocumentation = false;
972  runSymbolCollector(Header, /* Main */ "");
973  EXPECT_THAT(Symbols,
974  UnorderedElementsAre(
975  AllOf(QName("Foo"), Doc("Doc Foo"), ForCodeCompletion(true)),
976  AllOf(QName("Foo::f"), Doc(""), ReturnType(""),
977  ForCodeCompletion(false))));
978 
979  CollectorOpts.StoreAllDocumentation = true;
980  runSymbolCollector(Header, /* Main */ "");
981  EXPECT_THAT(Symbols,
982  UnorderedElementsAre(
983  AllOf(QName("Foo"), Doc("Doc Foo"), ForCodeCompletion(true)),
984  AllOf(QName("Foo::f"), Doc("Doc f"), ReturnType(""),
985  ForCodeCompletion(false))));
986 }
987 
988 TEST_F(SymbolCollectorTest, ClassMembers) {
989  const std::string Header = R"(
990  class Foo {
991  void f() {}
992  void g();
993  static void sf() {}
994  static void ssf();
995  static int x;
996  };
997  )";
998  const std::string Main = R"(
999  void Foo::g() {}
1000  void Foo::ssf() {}
1001  )";
1002  runSymbolCollector(Header, Main);
1003  EXPECT_THAT(
1004  Symbols,
1005  UnorderedElementsAre(
1006  QName("Foo"),
1007  AllOf(QName("Foo::f"), ReturnType(""), ForCodeCompletion(false)),
1008  AllOf(QName("Foo::g"), ReturnType(""), ForCodeCompletion(false)),
1009  AllOf(QName("Foo::sf"), ReturnType(""), ForCodeCompletion(false)),
1010  AllOf(QName("Foo::ssf"), ReturnType(""), ForCodeCompletion(false)),
1011  AllOf(QName("Foo::x"), ReturnType(""), ForCodeCompletion(false))));
1012 }
1013 
1014 TEST_F(SymbolCollectorTest, Scopes) {
1015  const std::string Header = R"(
1016  namespace na {
1017  class Foo {};
1018  namespace nb {
1019  class Bar {};
1020  }
1021  }
1022  )";
1023  runSymbolCollector(Header, /*Main=*/"");
1024  EXPECT_THAT(Symbols,
1025  UnorderedElementsAre(QName("na"), QName("na::nb"),
1026  QName("na::Foo"), QName("na::nb::Bar")));
1027 }
1028 
1029 TEST_F(SymbolCollectorTest, ExternC) {
1030  const std::string Header = R"(
1031  extern "C" { class Foo {}; }
1032  namespace na {
1033  extern "C" { class Bar {}; }
1034  }
1035  )";
1036  runSymbolCollector(Header, /*Main=*/"");
1037  EXPECT_THAT(Symbols, UnorderedElementsAre(QName("na"), QName("Foo"),
1038  QName("na::Bar")));
1039 }
1040 
1041 TEST_F(SymbolCollectorTest, SkipInlineNamespace) {
1042  const std::string Header = R"(
1043  namespace na {
1044  inline namespace nb {
1045  class Foo {};
1046  }
1047  }
1048  namespace na {
1049  // This is still inlined.
1050  namespace nb {
1051  class Bar {};
1052  }
1053  }
1054  )";
1055  runSymbolCollector(Header, /*Main=*/"");
1056  EXPECT_THAT(Symbols,
1057  UnorderedElementsAre(QName("na"), QName("na::nb"),
1058  QName("na::Foo"), QName("na::Bar")));
1059 }
1060 
1061 TEST_F(SymbolCollectorTest, SymbolWithDocumentation) {
1062  const std::string Header = R"(
1063  namespace nx {
1064  /// Foo comment.
1065  int ff(int x, double y) { return 0; }
1066  }
1067  )";
1068  runSymbolCollector(Header, /*Main=*/"");
1069  EXPECT_THAT(
1070  Symbols,
1071  UnorderedElementsAre(
1072  QName("nx"), AllOf(QName("nx::ff"), Labeled("ff(int x, double y)"),
1073  ReturnType("int"), Doc("Foo comment."))));
1074 }
1075 
1076 TEST_F(SymbolCollectorTest, Snippet) {
1077  const std::string Header = R"(
1078  namespace nx {
1079  void f() {}
1080  int ff(int x, double y) { return 0; }
1081  }
1082  )";
1083  runSymbolCollector(Header, /*Main=*/"");
1084  EXPECT_THAT(Symbols,
1085  UnorderedElementsAre(
1086  QName("nx"),
1087  AllOf(QName("nx::f"), Labeled("f()"), Snippet("f()")),
1088  AllOf(QName("nx::ff"), Labeled("ff(int x, double y)"),
1089  Snippet("ff(${1:int x}, ${2:double y})"))));
1090 }
1091 
1092 TEST_F(SymbolCollectorTest, IncludeHeaderSameAsFileURI) {
1093  CollectorOpts.CollectIncludePath = true;
1094  runSymbolCollector("#pragma once\nclass Foo {};", /*Main=*/"");
1095  EXPECT_THAT(Symbols, UnorderedElementsAre(
1096  AllOf(QName("Foo"), DeclURI(TestHeaderURI))));
1097  EXPECT_THAT(Symbols.begin()->IncludeHeaders,
1098  UnorderedElementsAre(IncludeHeaderWithRef(TestHeaderURI, 1u)));
1099 }
1100 
1101 TEST_F(SymbolCollectorTest, CanonicalSTLHeader) {
1102  CollectorOpts.CollectIncludePath = true;
1103  CanonicalIncludes Includes;
1104  auto Language = LangOptions();
1105  Language.CPlusPlus = true;
1106  Includes.addSystemHeadersMapping(Language);
1107  CollectorOpts.Includes = &Includes;
1108  runSymbolCollector("namespace std { class string {}; }", /*Main=*/"");
1109  EXPECT_THAT(Symbols,
1110  Contains(AllOf(QName("std::string"), DeclURI(TestHeaderURI),
1111  IncludeHeader("<string>"))));
1112 }
1113 
1114 TEST_F(SymbolCollectorTest, IWYUPragma) {
1115  CollectorOpts.CollectIncludePath = true;
1116  CanonicalIncludes Includes;
1117  PragmaHandler = collectIWYUHeaderMaps(&Includes);
1118  CollectorOpts.Includes = &Includes;
1119  const std::string Header = R"(
1120  // IWYU pragma: private, include the/good/header.h
1121  class Foo {};
1122  )";
1123  runSymbolCollector(Header, /*Main=*/"");
1124  EXPECT_THAT(Symbols, UnorderedElementsAre(
1125  AllOf(QName("Foo"), DeclURI(TestHeaderURI),
1126  IncludeHeader("\"the/good/header.h\""))));
1127 }
1128 
1129 TEST_F(SymbolCollectorTest, IWYUPragmaWithDoubleQuotes) {
1130  CollectorOpts.CollectIncludePath = true;
1131  CanonicalIncludes Includes;
1132  PragmaHandler = collectIWYUHeaderMaps(&Includes);
1133  CollectorOpts.Includes = &Includes;
1134  const std::string Header = R"(
1135  // IWYU pragma: private, include "the/good/header.h"
1136  class Foo {};
1137  )";
1138  runSymbolCollector(Header, /*Main=*/"");
1139  EXPECT_THAT(Symbols, UnorderedElementsAre(
1140  AllOf(QName("Foo"), DeclURI(TestHeaderURI),
1141  IncludeHeader("\"the/good/header.h\""))));
1142 }
1143 
1144 TEST_F(SymbolCollectorTest, SkipIncFileWhenCanonicalizeHeaders) {
1145  CollectorOpts.CollectIncludePath = true;
1146  CanonicalIncludes Includes;
1147  Includes.addMapping(TestHeaderName, "<canonical>");
1148  CollectorOpts.Includes = &Includes;
1149  auto IncFile = testPath("test.inc");
1150  auto IncURI = URI::create(IncFile).toString();
1151  InMemoryFileSystem->addFile(IncFile, 0,
1152  llvm::MemoryBuffer::getMemBuffer("class X {};"));
1153  runSymbolCollector("#include \"test.inc\"\nclass Y {};", /*Main=*/"",
1154  /*ExtraArgs=*/{"-I", testRoot()});
1155  EXPECT_THAT(Symbols,
1156  UnorderedElementsAre(AllOf(QName("X"), DeclURI(IncURI),
1157  IncludeHeader("<canonical>")),
1158  AllOf(QName("Y"), DeclURI(TestHeaderURI),
1159  IncludeHeader("<canonical>"))));
1160 }
1161 
1162 TEST_F(SymbolCollectorTest, MainFileIsHeaderWhenSkipIncFile) {
1163  CollectorOpts.CollectIncludePath = true;
1164  // To make this case as hard as possible, we won't tell clang main is a
1165  // header. No extension, no -x c++-header.
1166  TestFileName = testPath("no_ext_main");
1167  TestFileURI = URI::create(TestFileName).toString();
1168  auto IncFile = testPath("test.inc");
1169  auto IncURI = URI::create(IncFile).toString();
1170  InMemoryFileSystem->addFile(IncFile, 0,
1171  llvm::MemoryBuffer::getMemBuffer("class X {};"));
1172  runSymbolCollector("", R"cpp(
1173  // Can't use #pragma once in a main file clang doesn't think is a header.
1174  #ifndef MAIN_H_
1175  #define MAIN_H_
1176  #include "test.inc"
1177  #endif
1178  )cpp",
1179  /*ExtraArgs=*/{"-I", testRoot()});
1180  EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(QName("X"), DeclURI(IncURI),
1182 }
1183 
1184 TEST_F(SymbolCollectorTest, IncFileInNonHeader) {
1185  CollectorOpts.CollectIncludePath = true;
1186  TestFileName = testPath("main.cc");
1187  TestFileURI = URI::create(TestFileName).toString();
1188  auto IncFile = testPath("test.inc");
1189  auto IncURI = URI::create(IncFile).toString();
1190  InMemoryFileSystem->addFile(IncFile, 0,
1191  llvm::MemoryBuffer::getMemBuffer("class X {};"));
1192  runSymbolCollector("", R"cpp(
1193  #include "test.inc"
1194  )cpp",
1195  /*ExtraArgs=*/{"-I", testRoot()});
1196  EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(QName("X"), DeclURI(IncURI),
1197  Not(IncludeHeader()))));
1198 }
1199 
1200 // Features that depend on header-guards are fragile. Header guards are only
1201 // recognized when the file ends, so we have to defer checking for them.
1202 TEST_F(SymbolCollectorTest, HeaderGuardDetected) {
1203  CollectorOpts.CollectIncludePath = true;
1204  CollectorOpts.CollectMacro = true;
1205  runSymbolCollector(R"cpp(
1206  #ifndef HEADER_GUARD_
1207  #define HEADER_GUARD_
1208 
1209  // Symbols are seen before the header guard is complete.
1210  #define MACRO
1211  int decl();
1212 
1213  #endif // Header guard is recognized here.
1214  )cpp",
1215  "");
1216  EXPECT_THAT(Symbols, Not(Contains(QName("HEADER_GUARD_"))));
1217  EXPECT_THAT(Symbols, Each(IncludeHeader()));
1218 }
1219 
1220 TEST_F(SymbolCollectorTest, NonModularHeader) {
1221  auto TU = TestTU::withHeaderCode("int x();");
1222  EXPECT_THAT(TU.headerSymbols(), ElementsAre(IncludeHeader()));
1223 
1224  // Files missing include guards aren't eligible for insertion.
1225  TU.ImplicitHeaderGuard = false;
1226  EXPECT_THAT(TU.headerSymbols(), ElementsAre(Not(IncludeHeader())));
1227 
1228  // We recognize some patterns of trying to prevent insertion.
1229  TU = TestTU::withHeaderCode(R"cpp(
1230 #ifndef SECRET
1231 #error "This file isn't safe to include directly"
1232 #endif
1233  int x();
1234  )cpp");
1235  TU.ExtraArgs.push_back("-DSECRET"); // *we're* able to include it.
1236  EXPECT_THAT(TU.headerSymbols(), ElementsAre(Not(IncludeHeader())));
1237 }
1238 
1239 TEST_F(SymbolCollectorTest, AvoidUsingFwdDeclsAsCanonicalDecls) {
1240  CollectorOpts.CollectIncludePath = true;
1241  Annotations Header(R"(
1242  #pragma once
1243  // Forward declarations of TagDecls.
1244  class C;
1245  struct S;
1246  union U;
1247 
1248  // Canonical declarations.
1249  class $cdecl[[C]] {};
1250  struct $sdecl[[S]] {};
1251  union $udecl[[U]] {int $xdecl[[x]]; bool $ydecl[[y]];};
1252  )");
1253  runSymbolCollector(Header.code(), /*Main=*/"");
1254  EXPECT_THAT(
1255  Symbols,
1256  UnorderedElementsAre(
1257  AllOf(QName("C"), DeclURI(TestHeaderURI),
1258  DeclRange(Header.range("cdecl")), IncludeHeader(TestHeaderURI),
1259  DefURI(TestHeaderURI), DefRange(Header.range("cdecl"))),
1260  AllOf(QName("S"), DeclURI(TestHeaderURI),
1261  DeclRange(Header.range("sdecl")), IncludeHeader(TestHeaderURI),
1262  DefURI(TestHeaderURI), DefRange(Header.range("sdecl"))),
1263  AllOf(QName("U"), DeclURI(TestHeaderURI),
1264  DeclRange(Header.range("udecl")), IncludeHeader(TestHeaderURI),
1265  DefURI(TestHeaderURI), DefRange(Header.range("udecl"))),
1266  AllOf(QName("U::x"), DeclURI(TestHeaderURI),
1267  DeclRange(Header.range("xdecl")), DefURI(TestHeaderURI),
1268  DefRange(Header.range("xdecl"))),
1269  AllOf(QName("U::y"), DeclURI(TestHeaderURI),
1270  DeclRange(Header.range("ydecl")), DefURI(TestHeaderURI),
1271  DefRange(Header.range("ydecl")))));
1272 }
1273 
1274 TEST_F(SymbolCollectorTest, ClassForwardDeclarationIsCanonical) {
1275  CollectorOpts.CollectIncludePath = true;
1276  runSymbolCollector(/*Header=*/"#pragma once\nclass X;",
1277  /*Main=*/"class X {};");
1278  EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(
1279  QName("X"), DeclURI(TestHeaderURI),
1281 }
1282 
1283 TEST_F(SymbolCollectorTest, UTF16Character) {
1284  // ö is 2-bytes.
1285  Annotations Header(/*Header=*/"class [[pörk]] {};");
1286  runSymbolCollector(Header.code(), /*Main=*/"");
1287  EXPECT_THAT(Symbols, UnorderedElementsAre(
1288  AllOf(QName("pörk"), DeclRange(Header.range()))));
1289 }
1290 
1291 TEST_F(SymbolCollectorTest, DoNotIndexSymbolsInFriendDecl) {
1292  Annotations Header(R"(
1293  namespace nx {
1294  class $z[[Z]] {};
1295  class X {
1296  friend class Y;
1297  friend class Z;
1298  friend void foo();
1299  friend void $bar[[bar]]() {}
1300  };
1301  class $y[[Y]] {};
1302  void $foo[[foo]]();
1303  }
1304  )");
1305  runSymbolCollector(Header.code(), /*Main=*/"");
1306 
1307  EXPECT_THAT(Symbols,
1308  UnorderedElementsAre(
1309  QName("nx"), QName("nx::X"),
1310  AllOf(QName("nx::Y"), DeclRange(Header.range("y"))),
1311  AllOf(QName("nx::Z"), DeclRange(Header.range("z"))),
1312  AllOf(QName("nx::foo"), DeclRange(Header.range("foo"))),
1313  AllOf(QName("nx::bar"), DeclRange(Header.range("bar")))));
1314 }
1315 
1316 TEST_F(SymbolCollectorTest, ReferencesInFriendDecl) {
1317  const std::string Header = R"(
1318  class X;
1319  class Y;
1320  )";
1321  const std::string Main = R"(
1322  class C {
1323  friend ::X;
1324  friend class Y;
1325  };
1326  )";
1327  CollectorOpts.CountReferences = true;
1328  runSymbolCollector(Header, Main);
1329  EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(QName("X"), RefCount(1)),
1330  AllOf(QName("Y"), RefCount(1)),
1331  AllOf(QName("C"), RefCount(0))));
1332 }
1333 
1334 TEST_F(SymbolCollectorTest, Origin) {
1336  runSymbolCollector("class Foo {};", /*Main=*/"");
1337  EXPECT_THAT(Symbols, UnorderedElementsAre(
1339 }
1340 
1341 TEST_F(SymbolCollectorTest, CollectMacros) {
1342  CollectorOpts.CollectIncludePath = true;
1343  Annotations Header(R"(
1344  #pragma once
1345  #define X 1
1346  #define $mac[[MAC]](x) int x
1347  #define $used[[USED]](y) float y;
1348 
1349  MAC(p);
1350  )");
1351 
1352  Annotations Main(R"(
1353  #define $main[[MAIN]] 1
1354  USED(t);
1355  )");
1356  CollectorOpts.CountReferences = true;
1357  CollectorOpts.CollectMacro = true;
1358  runSymbolCollector(Header.code(), Main.code());
1359  EXPECT_THAT(
1360  Symbols,
1361  UnorderedElementsAre(
1362  QName("p"), QName("t"),
1363  AllOf(QName("X"), DeclURI(TestHeaderURI),
1365  AllOf(Labeled("MAC(x)"), RefCount(0),
1366 
1367  DeclRange(Header.range("mac")), VisibleOutsideFile()),
1368  AllOf(Labeled("USED(y)"), RefCount(1),
1369  DeclRange(Header.range("used")), VisibleOutsideFile()),
1370  AllOf(Labeled("MAIN"), RefCount(0), DeclRange(Main.range("main")),
1371  Not(VisibleOutsideFile()))));
1372 }
1373 
1374 TEST_F(SymbolCollectorTest, DeprecatedSymbols) {
1375  const std::string Header = R"(
1376  void TestClangc() __attribute__((deprecated("", "")));
1377  void TestClangd();
1378  )";
1379  runSymbolCollector(Header, /**/ "");
1380  EXPECT_THAT(Symbols, UnorderedElementsAre(
1381  AllOf(QName("TestClangc"), Deprecated()),
1382  AllOf(QName("TestClangd"), Not(Deprecated()))));
1383 }
1384 
1385 TEST_F(SymbolCollectorTest, ImplementationDetail) {
1386  const std::string Header = R"(
1387  #define DECL_NAME(x, y) x##_##y##_Decl
1388  #define DECL(x, y) class DECL_NAME(x, y) {};
1389  DECL(X, Y); // X_Y_Decl
1390 
1391  class Public {};
1392  )";
1393  runSymbolCollector(Header, /**/ "");
1394  EXPECT_THAT(Symbols,
1395  UnorderedElementsAre(
1396  AllOf(QName("X_Y_Decl"), ImplementationDetail()),
1397  AllOf(QName("Public"), Not(ImplementationDetail()))));
1398 }
1399 
1400 TEST_F(SymbolCollectorTest, UsingDecl) {
1401  const char *Header = R"(
1402  void foo();
1403  namespace std {
1404  using ::foo;
1405  })";
1406  runSymbolCollector(Header, /**/ "");
1407  EXPECT_THAT(Symbols, Contains(QName("std::foo")));
1408 }
1409 
1410 TEST_F(SymbolCollectorTest, CBuiltins) {
1411  // In C, printf in stdio.h is a redecl of an implicit builtin.
1412  const char *Header = R"(
1413  extern int printf(const char*, ...);
1414  )";
1415  runSymbolCollector(Header, /**/ "", {"-xc"});
1416  EXPECT_THAT(Symbols, Contains(QName("printf")));
1417 }
1418 
1419 TEST_F(SymbolCollectorTest, InvalidSourceLoc) {
1420  const char *Header = R"(
1421  void operator delete(void*)
1422  __attribute__((__externally_visible__));)";
1423  runSymbolCollector(Header, /**/ "");
1424  EXPECT_THAT(Symbols, Contains(QName("operator delete")));
1425 }
1426 
1427 } // namespace
1428 } // namespace clangd
1429 } // 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:534
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:159
std::string TestHeaderURI
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:39
Whether or not this symbol is meant to be used for the code completion.
Definition: Symbol.h:119
std::string testPath(PathRef File)
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:772
int line
Line position in a document (zero-based).
Definition: Protocol.h:129
int character
Character offset on a line in a document (zero-based).
Definition: Protocol.h:134
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:197
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
unsigned References
const Symbol & findSymbol(const SymbolSlab &Slab, llvm::StringRef QName)
Definition: TestTU.cpp:97
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
llvm::StringMap< std::string > Files
const NamedDecl & findDecl(ParsedAST &AST, llvm::StringRef QName)
Definition: TestTU.cpp:118
CommentHandler * PragmaHandler