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