//===-- FindSymbolsTests.cpp -------------------------*- C++ -*------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "FindTarget.h" #include "Selection.h" #include "TestTU.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclTemplate.h" #include "clang/Basic/SourceLocation.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Casting.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Testing/Support/Annotations.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include namespace clang { namespace clangd { namespace { // A referenced Decl together with its DeclRelationSet, for assertions. // // There's no great way to assert on the "content" of a Decl in the general case // that's both expressive and unambiguous (e.g. clearly distinguishes between // templated decls and their specializations). // // We use the result of pretty-printing the decl, with the {body} truncated. struct PrintedDecl { PrintedDecl(const char *Name, DeclRelationSet Relations = {}) : Name(Name), Relations(Relations) {} PrintedDecl(const NamedDecl *D, DeclRelationSet Relations = {}) : Relations(Relations) { std::string S; llvm::raw_string_ostream OS(S); D->print(OS); llvm::StringRef FirstLine = llvm::StringRef(OS.str()).take_until([](char C) { return C == '\n'; }); FirstLine = FirstLine.rtrim(" {"); Name = FirstLine.rtrim(" {"); } std::string Name; DeclRelationSet Relations; }; bool operator==(const PrintedDecl &L, const PrintedDecl &R) { return std::tie(L.Name, L.Relations) == std::tie(R.Name, R.Relations); } llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const PrintedDecl &D) { return OS << D.Name << " Rel=" << D.Relations; } // The test cases in for targetDecl() take the form // - a piece of code (Code = "...") // - Code should have a single AST node marked as a [[range]] // - an EXPECT_DECLS() assertion that verify the type of node selected, and // all the decls that targetDecl() considers it to reference // Despite the name, these cases actually test allTargetDecls() for brevity. class TargetDeclTest : public ::testing::Test { protected: using Rel = DeclRelation; std::string Code; std::vector Flags; // Asserts that `Code` has a marked selection of a node `NodeType`, // and returns allTargetDecls() as PrintedDecl structs. // Use via EXPECT_DECLS(). std::vector assertNodeAndPrintDecls(const char *NodeType) { llvm::Annotations A(Code); auto TU = TestTU::withCode(A.code()); TU.ExtraArgs = Flags; auto AST = TU.build(); EXPECT_THAT(AST.getDiagnostics(), ::testing::IsEmpty()) << Code; llvm::Annotations::Range R = A.range(); SelectionTree Selection(AST.getASTContext(), AST.getTokens(), R.Begin, R.End); const SelectionTree::Node *N = Selection.commonAncestor(); if (!N) { ADD_FAILURE() << "No node selected!\n" << Code; return {}; } EXPECT_EQ(N->kind(), NodeType) << Selection; std::vector ActualDecls; for (const auto &Entry : allTargetDecls(N->ASTNode)) ActualDecls.emplace_back(Entry.first, Entry.second); return ActualDecls; } }; // This is a macro to preserve line numbers in assertion failures. // It takes the expected decls as varargs to work around comma-in-macro issues. #define EXPECT_DECLS(NodeType, ...) \ EXPECT_THAT(assertNodeAndPrintDecls(NodeType), \ ::testing::UnorderedElementsAreArray( \ std::vector({__VA_ARGS__}))) \ << Code using ExpectedDecls = std::vector; TEST_F(TargetDeclTest, Exprs) { Code = R"cpp( int f(); int x = [[f]](); )cpp"; EXPECT_DECLS("DeclRefExpr", "int f()"); Code = R"cpp( struct S { S operator+(S) const; }; auto X = S() [[+]] S(); )cpp"; EXPECT_DECLS("DeclRefExpr", "S operator+(S) const"); Code = R"cpp( int foo(); int s = foo[[()]]; )cpp"; EXPECT_DECLS("CallExpr", "int foo()"); Code = R"cpp( struct X { void operator()(int n); }; void test() { X x; x[[(123)]]; } )cpp"; EXPECT_DECLS("CXXOperatorCallExpr", "void operator()(int n)"); } TEST_F(TargetDeclTest, UsingDecl) { Code = R"cpp( namespace foo { int f(int); int f(char); } using foo::f; int x = [[f]](42); )cpp"; // f(char) is not referenced! EXPECT_DECLS("DeclRefExpr", {"using foo::f", Rel::Alias}, {"int f(int)", Rel::Underlying}); Code = R"cpp( namespace foo { int f(int); int f(char); } [[using foo::f]]; )cpp"; // All overloads are referenced. EXPECT_DECLS("UsingDecl", {"using foo::f", Rel::Alias}, {"int f(int)", Rel::Underlying}, {"int f(char)", Rel::Underlying}); Code = R"cpp( struct X { int foo(); }; struct Y : X { using X::foo; }; int x = Y().[[foo]](); )cpp"; EXPECT_DECLS("MemberExpr", {"using X::foo", Rel::Alias}, {"int foo()", Rel::Underlying}); } TEST_F(TargetDeclTest, ConstructorInitList) { Code = R"cpp( struct X { int a; X() : [[a]](42) {} }; )cpp"; EXPECT_DECLS("CXXCtorInitializer", "int a"); Code = R"cpp( struct X { X() : [[X]](1) {} X(int); }; )cpp"; EXPECT_DECLS("RecordTypeLoc", "struct X"); } TEST_F(TargetDeclTest, DesignatedInit) { Flags = {"-xc"}; // array designators are a C99 extension. Code = R"c( struct X { int a; }; struct Y { int b; struct X c[2]; }; struct Y y = { .c[0].[[a]] = 1 }; )c"; EXPECT_DECLS("DesignatedInitExpr", "int a"); } TEST_F(TargetDeclTest, NestedNameSpecifier) { Code = R"cpp( namespace a { namespace b { int c; } } int x = a::[[b::]]c; )cpp"; EXPECT_DECLS("NestedNameSpecifierLoc", "namespace b"); Code = R"cpp( namespace a { struct X { enum { y }; }; } int x = a::[[X::]]y; )cpp"; EXPECT_DECLS("NestedNameSpecifierLoc", "struct X"); Code = R"cpp( template int x = [[T::]]y; )cpp"; // FIXME: We don't do a good job printing TemplateTypeParmDecls, apparently! EXPECT_DECLS("NestedNameSpecifierLoc", ""); Code = R"cpp( namespace a { int x; } namespace b = a; int y = [[b]]::x; )cpp"; EXPECT_DECLS("NestedNameSpecifierLoc", {"namespace b = a", Rel::Alias}, {"namespace a", Rel::Underlying}); } TEST_F(TargetDeclTest, Types) { Code = R"cpp( struct X{}; [[X]] x; )cpp"; EXPECT_DECLS("RecordTypeLoc", "struct X"); Code = R"cpp( struct S{}; typedef S X; [[X]] x; )cpp"; EXPECT_DECLS("TypedefTypeLoc", {"typedef S X", Rel::Alias}, {"struct S", Rel::Underlying}); Code = R"cpp( namespace ns { struct S{}; } typedef ns::S X; [[X]] x; )cpp"; EXPECT_DECLS("TypedefTypeLoc", {"typedef ns::S X", Rel::Alias}, {"struct S", Rel::Underlying}); // FIXME: Auto-completion in a template requires disabling delayed template // parsing. Flags = {"-fno-delayed-template-parsing"}; Code = R"cpp( template void foo() { [[T]] x; } )cpp"; // FIXME: We don't do a good job printing TemplateTypeParmDecls, apparently! EXPECT_DECLS("TemplateTypeParmTypeLoc", ""); Flags.clear(); // FIXME: Auto-completion in a template requires disabling delayed template // parsing. Flags = {"-fno-delayed-template-parsing"}; Code = R"cpp( template class T> void foo() { [[T]] x; } )cpp"; EXPECT_DECLS("TemplateSpecializationTypeLoc", "template class T"); Flags.clear(); Code = R"cpp( struct S{}; S X; [[decltype]](X) Y; )cpp"; EXPECT_DECLS("DecltypeTypeLoc", {"struct S", Rel::Underlying}); Code = R"cpp( struct S{}; [[auto]] X = S{}; )cpp"; // FIXME: deduced type missing in AST. https://llvm.org/PR42914 EXPECT_DECLS("AutoTypeLoc"); Code = R"cpp( template struct S { static const int size = sizeof...([[E]]); }; )cpp"; // FIXME: We don't do a good job printing TemplateTypeParmDecls, apparently! EXPECT_DECLS("SizeOfPackExpr", ""); Code = R"cpp( template class Foo { void f([[Foo]] x); }; )cpp"; EXPECT_DECLS("InjectedClassNameTypeLoc", "class Foo"); } TEST_F(TargetDeclTest, ClassTemplate) { Code = R"cpp( // Implicit specialization. template class Foo{}; [[Foo<42>]] B; )cpp"; EXPECT_DECLS("TemplateSpecializationTypeLoc", {"template<> class Foo<42>", Rel::TemplateInstantiation}, {"class Foo", Rel::TemplatePattern}); Code = R"cpp( // Explicit specialization. template class Foo{}; template<> class Foo<42>{}; [[Foo<42>]] B; )cpp"; EXPECT_DECLS("TemplateSpecializationTypeLoc", "template<> class Foo<42>"); Code = R"cpp( // Partial specialization. template class Foo{}; template class Foo{}; [[Foo]] B; )cpp"; EXPECT_DECLS("TemplateSpecializationTypeLoc", {"template<> class Foo", Rel::TemplateInstantiation}, {"template class Foo", Rel::TemplatePattern}); Code = R"cpp( // Class template argument deduction template struct Test { Test(T); }; void foo() { [[Test]] a(5); } )cpp"; Flags.push_back("-std=c++17"); EXPECT_DECLS("DeducedTemplateSpecializationTypeLoc", {"struct Test", Rel::TemplatePattern}); } TEST_F(TargetDeclTest, FunctionTemplate) { Code = R"cpp( // Implicit specialization. template bool foo(T) { return false; }; bool x = [[foo]](42); )cpp"; EXPECT_DECLS("DeclRefExpr", {"template<> bool foo(int)", Rel::TemplateInstantiation}, {"bool foo(T)", Rel::TemplatePattern}); Code = R"cpp( // Explicit specialization. template bool foo(T) { return false; }; template<> bool foo(int) { return false; }; bool x = [[foo]](42); )cpp"; EXPECT_DECLS("DeclRefExpr", "template<> bool foo(int)"); } TEST_F(TargetDeclTest, VariableTemplate) { // Pretty-printer doesn't do a very good job of variable templates :-( Code = R"cpp( // Implicit specialization. template int foo; int x = [[foo]]; )cpp"; EXPECT_DECLS("DeclRefExpr", {"int foo", Rel::TemplateInstantiation}, {"int foo", Rel::TemplatePattern}); Code = R"cpp( // Explicit specialization. template int foo; template <> bool foo; int x = [[foo]]; )cpp"; EXPECT_DECLS("DeclRefExpr", "bool foo"); Code = R"cpp( // Partial specialization. template int foo; template bool foo; bool x = [[foo]]; )cpp"; EXPECT_DECLS("DeclRefExpr", {"bool foo", Rel::TemplateInstantiation}, {"bool foo", Rel::TemplatePattern}); } TEST_F(TargetDeclTest, TypeAliasTemplate) { Code = R"cpp( template class SmallVector {}; template using TinyVector = SmallVector; [[TinyVector]] X; )cpp"; EXPECT_DECLS("TemplateSpecializationTypeLoc", {"template<> class SmallVector", Rel::TemplateInstantiation | Rel::Underlying}, {"class SmallVector", Rel::TemplatePattern | Rel::Underlying}, {"using TinyVector = SmallVector", Rel::Alias | Rel::TemplatePattern}); } TEST_F(TargetDeclTest, MemberOfTemplate) { Code = R"cpp( template struct Foo { int x(T); }; int y = Foo().[[x]](42); )cpp"; EXPECT_DECLS("MemberExpr", {"int x(int)", Rel::TemplateInstantiation}, {"int x(T)", Rel::TemplatePattern}); Code = R"cpp( template struct Foo { template int x(T, U); }; int y = Foo().[[x]]('c', 42); )cpp"; EXPECT_DECLS("MemberExpr", {"template<> int x(char, int)", Rel::TemplateInstantiation}, {"int x(T, U)", Rel::TemplatePattern}); } TEST_F(TargetDeclTest, Lambda) { Code = R"cpp( void foo(int x = 42) { auto l = [ [[x]] ]{ return x + 1; }; }; )cpp"; EXPECT_DECLS("DeclRefExpr", "int x = 42"); // It seems like this should refer to another var, with the outer param being // an underlying decl. But it doesn't seem to exist. Code = R"cpp( void foo(int x = 42) { auto l = [x]{ return [[x]] + 1; }; }; )cpp"; EXPECT_DECLS("DeclRefExpr", "int x = 42"); Code = R"cpp( void foo() { auto l = [x = 1]{ return [[x]] + 1; }; }; )cpp"; // FIXME: why both auto and int? EXPECT_DECLS("DeclRefExpr", "auto int x = 1"); } TEST_F(TargetDeclTest, OverloadExpr) { // FIXME: Auto-completion in a template requires disabling delayed template // parsing. Flags = {"-fno-delayed-template-parsing"}; Code = R"cpp( void func(int*); void func(char*); template void foo(T t) { [[func]](t); }; )cpp"; EXPECT_DECLS("UnresolvedLookupExpr", "void func(int *)", "void func(char *)"); Code = R"cpp( struct X { void func(int*); void func(char*); }; template void foo(X x, T t) { x.[[func]](t); }; )cpp"; EXPECT_DECLS("UnresolvedMemberExpr", "void func(int *)", "void func(char *)"); } TEST_F(TargetDeclTest, ObjC) { Flags = {"-xobjective-c"}; Code = R"cpp( @interface Foo {} -(void)bar; @end void test(Foo *f) { [f [[bar]] ]; } )cpp"; EXPECT_DECLS("ObjCMessageExpr", "- (void)bar"); Code = R"cpp( @interface Foo { @public int bar; } @end int test(Foo *f) { return [[f->bar]]; } )cpp"; EXPECT_DECLS("ObjCIvarRefExpr", "int bar"); Code = R"cpp( @interface Foo {} -(int) x; -(void) setX:(int)x; @end void test(Foo *f) { [[f.x]] = 42; } )cpp"; EXPECT_DECLS("ObjCPropertyRefExpr", "- (void)setX:(int)x"); Code = R"cpp( @interface I {} @property(retain) I* x; @property(retain) I* y; @end void test(I *f) { [[f.x]].y = 0; } )cpp"; EXPECT_DECLS("OpaqueValueExpr", "@property(atomic, retain, readwrite) I *x"); Code = R"cpp( @protocol Foo @end id test() { return [[@protocol(Foo)]]; } )cpp"; EXPECT_DECLS("ObjCProtocolExpr", "@protocol Foo"); Code = R"cpp( @interface Foo @end void test([[Foo]] *p); )cpp"; EXPECT_DECLS("ObjCInterfaceTypeLoc", "@interface Foo"); Code = R"cpp( @protocol Foo @end void test([[id]] p); )cpp"; EXPECT_DECLS("ObjCObjectTypeLoc", "@protocol Foo"); Code = R"cpp( @class C; @protocol Foo @end void test(C<[[Foo]]> *p); )cpp"; // FIXME: there's no AST node corresponding to 'Foo', so we're stuck. EXPECT_DECLS("ObjCObjectTypeLoc"); } class FindExplicitReferencesTest : public ::testing::Test { protected: struct AllRefs { std::string AnnotatedCode; std::string DumpedReferences; }; /// Parses \p Code, finds function '::foo' and annotates its body with results /// of findExplicitReferecnces. /// See actual tests for examples of annotation format. AllRefs annotateReferencesInFoo(llvm::StringRef Code) { TestTU TU; TU.Code = Code; // FIXME: Auto-completion in a template requires disabling delayed template // parsing. TU.ExtraArgs.push_back("-fno-delayed-template-parsing"); TU.ExtraArgs.push_back("-std=c++17"); auto AST = TU.build(); for (auto &D : AST.getDiagnostics()) { if (D.Severity > DiagnosticsEngine::Warning) ADD_FAILURE() << D << Code; } auto *TestDecl = &findDecl(AST, "foo"); if (auto *T = llvm::dyn_cast(TestDecl)) TestDecl = T->getTemplatedDecl(); std::vector Refs; if (const auto *Func = llvm::dyn_cast(TestDecl)) findExplicitReferences(Func->getBody(), [&Refs](ReferenceLoc R) { Refs.push_back(std::move(R)); }); else if (const auto *NS = llvm::dyn_cast(TestDecl)) findExplicitReferences(NS, [&Refs, &NS](ReferenceLoc R) { // Avoid adding the namespace foo decl to the results. if (R.Targets.size() == 1 && R.Targets.front() == NS) return; Refs.push_back(std::move(R)); }); else ADD_FAILURE() << "Failed to find ::foo decl for test"; auto &SM = AST.getSourceManager(); llvm::sort(Refs, [&](const ReferenceLoc &L, const ReferenceLoc &R) { return SM.isBeforeInTranslationUnit(L.NameLoc, R.NameLoc); }); std::string AnnotatedCode; unsigned NextCodeChar = 0; for (unsigned I = 0; I < Refs.size(); ++I) { auto &R = Refs[I]; SourceLocation Pos = R.NameLoc; assert(Pos.isValid()); if (Pos.isMacroID()) // FIXME: figure out how to show macro locations. Pos = SM.getExpansionLoc(Pos); assert(Pos.isFileID()); FileID File; unsigned Offset; std::tie(File, Offset) = SM.getDecomposedLoc(Pos); if (File == SM.getMainFileID()) { // Print the reference in a source code. assert(NextCodeChar <= Offset); AnnotatedCode += Code.substr(NextCodeChar, Offset - NextCodeChar); AnnotatedCode += "$" + std::to_string(I) + "^"; NextCodeChar = Offset; } } AnnotatedCode += Code.substr(NextCodeChar); std::string DumpedReferences; for (unsigned I = 0; I < Refs.size(); ++I) DumpedReferences += llvm::formatv("{0}: {1}\n", I, Refs[I]); return AllRefs{std::move(AnnotatedCode), std::move(DumpedReferences)}; } }; TEST_F(FindExplicitReferencesTest, All) { std::pair Cases[] = {// Simple expressions. {R"cpp( int global; int func(); void foo(int param) { $0^global = $1^param + $2^func(); } )cpp", "0: targets = {global}\n" "1: targets = {param}\n" "2: targets = {func}\n"}, {R"cpp( struct X { int a; }; void foo(X x) { $0^x.$1^a = 10; } )cpp", "0: targets = {x}\n" "1: targets = {X::a}\n"}, // Namespaces and aliases. {R"cpp( namespace ns {} namespace alias = ns; void foo() { using namespace $0^ns; using namespace $1^alias; } )cpp", "0: targets = {ns}\n" "1: targets = {alias}\n"}, // Using declarations. {R"cpp( namespace ns { int global; } void foo() { using $0^ns::$1^global; } )cpp", "0: targets = {ns}\n" "1: targets = {ns::global}, qualifier = 'ns::'\n"}, // Simple types. {R"cpp( struct Struct { int a; }; using Typedef = int; void foo() { $0^Struct $1^x; $2^Typedef $3^y; static_cast<$4^Struct*>(0); } )cpp", "0: targets = {Struct}\n" "1: targets = {x}, decl\n" "2: targets = {Typedef}\n" "3: targets = {y}, decl\n" "4: targets = {Struct}\n"}, // Name qualifiers. {R"cpp( namespace a { namespace b { struct S { typedef int type; }; } } void foo() { $0^a::$1^b::$2^S $3^x; using namespace $4^a::$5^b; $6^S::$7^type $8^y; } )cpp", "0: targets = {a}\n" "1: targets = {a::b}, qualifier = 'a::'\n" "2: targets = {a::b::S}, qualifier = 'a::b::'\n" "3: targets = {x}, decl\n" "4: targets = {a}\n" "5: targets = {a::b}, qualifier = 'a::'\n" "6: targets = {a::b::S}\n" "7: targets = {a::b::S::type}, qualifier = 'struct S::'\n" "8: targets = {y}, decl\n"}, // Simple templates. {R"cpp( template struct vector { using value_type = T; }; template <> struct vector { using value_type = bool; }; void foo() { $0^vector $1^vi; $2^vector $3^vb; } )cpp", "0: targets = {vector}\n" "1: targets = {vi}, decl\n" "2: targets = {vector}\n" "3: targets = {vb}, decl\n"}, // Template type aliases. {R"cpp( template struct vector { using value_type = T; }; template <> struct vector { using value_type = bool; }; template using valias = vector; void foo() { $0^valias $1^vi; $2^valias $3^vb; } )cpp", "0: targets = {valias}\n" "1: targets = {vi}, decl\n" "2: targets = {valias}\n" "3: targets = {vb}, decl\n"}, // MemberExpr should know their using declaration. {R"cpp( struct X { void func(int); }; struct Y : X { using X::func; }; void foo(Y y) { $0^y.$1^func(1); } )cpp", "0: targets = {y}\n" "1: targets = {Y::func}\n"}, // DeclRefExpr should know their using declaration. {R"cpp( namespace ns { void bar(int); } using ns::bar; void foo() { $0^bar(10); } )cpp", "0: targets = {bar}\n"}, // References from a macro. {R"cpp( #define FOO a #define BAR b void foo(int a, int b) { $0^FOO+$1^BAR; } )cpp", "0: targets = {a}\n" "1: targets = {b}\n"}, // No references from implicit nodes. {R"cpp( struct vector { int *begin(); int *end(); }; void foo() { for (int $0^x : $1^vector()) { $2^x = 10; } } )cpp", "0: targets = {x}, decl\n" "1: targets = {vector}\n" "2: targets = {x}\n"}, // Handle UnresolvedLookupExpr. {R"cpp( namespace ns1 { void func(char*); } namespace ns2 { void func(int*); } using namespace ns1; using namespace ns2; template void foo(T t) { $0^func($1^t); } )cpp", "0: targets = {ns1::func, ns2::func}\n" "1: targets = {t}\n"}, // Handle UnresolvedMemberExpr. {R"cpp( struct X { void func(char*); void func(int*); }; template void foo(X x, T t) { $0^x.$1^func($2^t); } )cpp", "0: targets = {x}\n" "1: targets = {X::func, X::func}\n" "2: targets = {t}\n"}, // Type template parameters. {R"cpp( template void foo() { static_cast<$0^T>(0); $1^T(); $2^T $3^t; } )cpp", "0: targets = {T}\n" "1: targets = {T}\n" "2: targets = {T}\n" "3: targets = {t}, decl\n"}, // Non-type template parameters. {R"cpp( template void foo() { int $0^x = $1^I; } )cpp", "0: targets = {x}, decl\n" "1: targets = {I}\n"}, // Template template parameters. {R"cpp( template struct vector {}; template class TT, template class ...TP> void foo() { $0^TT $1^x; $2^foo<$3^TT>(); $4^foo<$5^vector>(); $6^foo<$7^TP...>(); } )cpp", "0: targets = {TT}\n" "1: targets = {x}, decl\n" "2: targets = {foo}\n" "3: targets = {TT}\n" "4: targets = {foo}\n" "5: targets = {vector}\n" "6: targets = {foo}\n" "7: targets = {TP}\n"}, // Non-type template parameters with declarations. {R"cpp( int func(); template struct wrapper {}; template void foo() { $0^wrapper<$1^func> $2^w; $3^FuncParam(); } )cpp", "0: targets = {wrapper<&func>}\n" "1: targets = {func}\n" "2: targets = {w}, decl\n" "3: targets = {FuncParam}\n"}, // declaration references. {R"cpp( namespace ns {} class S {}; void foo() { class $0^Foo { $1^Foo(); ~$2^Foo(); int $3^field; }; int $4^Var; enum $5^E { $6^ABC }; typedef int $7^INT; using $8^INT2 = int; namespace $9^NS = $10^ns; } )cpp", "0: targets = {Foo}, decl\n" "1: targets = {foo()::Foo::Foo}, decl\n" "2: targets = {Foo}\n" "3: targets = {foo()::Foo::field}, decl\n" "4: targets = {Var}, decl\n" "5: targets = {E}, decl\n" "6: targets = {foo()::ABC}, decl\n" "7: targets = {INT}, decl\n" "8: targets = {INT2}, decl\n" "9: targets = {NS}, decl\n" "10: targets = {ns}\n"}, // cxx constructor initializer. {R"cpp( class Base {}; void foo() { // member initializer class $0^X { int $1^abc; $2^X(): $3^abc() {} }; // base initializer class $4^Derived : public $5^Base { $6^Base $7^B; $8^Derived() : $9^Base() {} }; // delegating initializer class $10^Foo { $11^Foo(int); $12^Foo(): $13^Foo(111) {} }; } )cpp", "0: targets = {X}, decl\n" "1: targets = {foo()::X::abc}, decl\n" "2: targets = {foo()::X::X}, decl\n" "3: targets = {foo()::X::abc}\n" "4: targets = {Derived}, decl\n" "5: targets = {Base}\n" "6: targets = {Base}\n" "7: targets = {foo()::Derived::B}, decl\n" "8: targets = {foo()::Derived::Derived}, decl\n" "9: targets = {Base}\n" "10: targets = {Foo}, decl\n" "11: targets = {foo()::Foo::Foo}, decl\n" "12: targets = {foo()::Foo::Foo}, decl\n" "13: targets = {Foo}\n"}, // Anonymous entities should not be reported. { R"cpp( void foo() { class {} $0^x; int (*$1^fptr)(int $2^a, int) = nullptr; } )cpp", "0: targets = {x}, decl\n" "1: targets = {fptr}, decl\n" "2: targets = {a}, decl\n"}, // Namespace aliases should be handled properly. { R"cpp( namespace ns { struct Type {}; } namespace alias = ns; namespace rec_alias = alias; void foo() { $0^ns::$1^Type $2^a; $3^alias::$4^Type $5^b; $6^rec_alias::$7^Type $8^c; } )cpp", "0: targets = {ns}\n" "1: targets = {ns::Type}, qualifier = 'ns::'\n" "2: targets = {a}, decl\n" "3: targets = {alias}\n" "4: targets = {ns::Type}, qualifier = 'alias::'\n" "5: targets = {b}, decl\n" "6: targets = {rec_alias}\n" "7: targets = {ns::Type}, qualifier = 'rec_alias::'\n" "8: targets = {c}, decl\n"}, // Handle SizeOfPackExpr. { R"cpp( template void foo() { constexpr int $0^size = sizeof...($1^E); }; )cpp", "0: targets = {size}, decl\n" "1: targets = {E}\n"}, // Class template argument deduction { R"cpp( template struct Test { Test(T); }; void foo() { $0^Test $1^a(5); } )cpp", "0: targets = {Test}\n" "1: targets = {a}, decl\n"}, // unknown template name should not crash. // duplicate $1$2 is fixed on master. {R"cpp( template