| //===-- 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 <initializer_list> |
| |
| 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<const char *> Flags; |
| |
| // Asserts that `Code` has a marked selection of a node `NodeType`, |
| // and returns allTargetDecls() as PrintedDecl structs. |
| // Use via EXPECT_DECLS(). |
| std::vector<PrintedDecl> 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<PrintedDecl> 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<PrintedDecl>({__VA_ARGS__}))) \ |
| << Code |
| using ExpectedDecls = std::vector<PrintedDecl>; |
| |
| 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 <typename T> |
| 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}); |
| |
| // 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"; |
| // 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<template<typename> class T> |
| void foo() { [[T<int>]] x; } |
| )cpp"; |
| EXPECT_DECLS("TemplateSpecializationTypeLoc", "template <typename> 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 <typename... E> |
| struct S { |
| static const int size = sizeof...([[E]]); |
| }; |
| )cpp"; |
| // FIXME: We don't do a good job printing TemplateTypeParmDecls, apparently! |
| EXPECT_DECLS("SizeOfPackExpr", ""); |
| } |
| |
| TEST_F(TargetDeclTest, ClassTemplate) { |
| Code = R"cpp( |
| // Implicit specialization. |
| template<int x> 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<int x> class Foo{}; |
| template<> class Foo<42>{}; |
| [[Foo<42>]] B; |
| )cpp"; |
| EXPECT_DECLS("TemplateSpecializationTypeLoc", "template<> class Foo<42>"); |
| |
| Code = R"cpp( |
| // Partial specialization. |
| template<typename T> class Foo{}; |
| template<typename T> class Foo<T*>{}; |
| [[Foo<int*>]] B; |
| )cpp"; |
| EXPECT_DECLS("TemplateSpecializationTypeLoc", |
| {"template<> class Foo<int *>", Rel::TemplateInstantiation}, |
| {"template <typename T> class Foo<T *>", Rel::TemplatePattern}); |
| |
| Code = R"cpp( |
| // Class template argument deduction |
| template <typename T> |
| 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<typename T> bool foo(T) { return false; }; |
| bool x = [[foo]](42); |
| )cpp"; |
| EXPECT_DECLS("DeclRefExpr", |
| {"template<> bool foo<int>(int)", Rel::TemplateInstantiation}, |
| {"bool foo(T)", Rel::TemplatePattern}); |
| |
| Code = R"cpp( |
| // Explicit specialization. |
| template<typename T> bool foo(T) { return false; }; |
| template<> bool foo<int>(int) { return false; }; |
| bool x = [[foo]](42); |
| )cpp"; |
| EXPECT_DECLS("DeclRefExpr", "template<> bool foo<int>(int)"); |
| } |
| |
| TEST_F(TargetDeclTest, VariableTemplate) { |
| // Pretty-printer doesn't do a very good job of variable templates :-( |
| Code = R"cpp( |
| // Implicit specialization. |
| template<typename T> int foo; |
| int x = [[foo]]<char>; |
| )cpp"; |
| EXPECT_DECLS("DeclRefExpr", {"int foo", Rel::TemplateInstantiation}, |
| {"int foo", Rel::TemplatePattern}); |
| |
| Code = R"cpp( |
| // Explicit specialization. |
| template<typename T> int foo; |
| template <> bool foo<char>; |
| int x = [[foo]]<char>; |
| )cpp"; |
| EXPECT_DECLS("DeclRefExpr", "bool foo"); |
| |
| Code = R"cpp( |
| // Partial specialization. |
| template<typename T> int foo; |
| template<typename T> bool foo<T*>; |
| bool x = [[foo]]<char*>; |
| )cpp"; |
| EXPECT_DECLS("DeclRefExpr", {"bool foo", Rel::TemplateInstantiation}, |
| {"bool foo", Rel::TemplatePattern}); |
| } |
| |
| TEST_F(TargetDeclTest, TypeAliasTemplate) { |
| Code = R"cpp( |
| template<typename T, int X> class SmallVector {}; |
| template<typename U> using TinyVector = SmallVector<U, 1>; |
| [[TinyVector<int>]] X; |
| )cpp"; |
| EXPECT_DECLS("TemplateSpecializationTypeLoc", |
| {"template<> class SmallVector<int, 1>", |
| Rel::TemplateInstantiation | Rel::Underlying}, |
| {"class SmallVector", Rel::TemplatePattern | Rel::Underlying}, |
| {"using TinyVector = SmallVector<U, 1>", |
| Rel::Alias | Rel::TemplatePattern}); |
| } |
| |
| TEST_F(TargetDeclTest, MemberOfTemplate) { |
| Code = R"cpp( |
| template <typename T> struct Foo { |
| int x(T); |
| }; |
| int y = Foo<int>().[[x]](42); |
| )cpp"; |
| EXPECT_DECLS("MemberExpr", {"int x(int)", Rel::TemplateInstantiation}, |
| {"int x(T)", Rel::TemplatePattern}); |
| |
| Code = R"cpp( |
| template <typename T> struct Foo { |
| template <typename U> |
| int x(T, U); |
| }; |
| int y = Foo<char>().[[x]]('c', 42); |
| )cpp"; |
| EXPECT_DECLS("MemberExpr", |
| {"template<> int x<int>(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 <class T> |
| 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 <class T> |
| 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<Foo>]] 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<FunctionTemplateDecl>(TestDecl)) |
| TestDecl = T->getTemplatedDecl(); |
| auto &Func = llvm::cast<FunctionDecl>(*TestDecl); |
| |
| std::vector<ReferenceLoc> Refs; |
| findExplicitReferences(Func.getBody(), [&Refs](ReferenceLoc R) { |
| Refs.push_back(std::move(R)); |
| }); |
| |
| 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</*Code*/ llvm::StringRef, /*References*/ llvm::StringRef> 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 <class T> struct vector { using value_type = T; }; |
| template <> struct vector<bool> { using value_type = bool; }; |
| void foo() { |
| $0^vector<int> $1^vi; |
| $2^vector<bool> $3^vb; |
| } |
| )cpp", |
| "0: targets = {vector<int>}\n" |
| "1: targets = {vi}, decl\n" |
| "2: targets = {vector<bool>}\n" |
| "3: targets = {vb}, decl\n"}, |
| // Template type aliases. |
| {R"cpp( |
| template <class T> struct vector { using value_type = T; }; |
| template <> struct vector<bool> { using value_type = bool; }; |
| template <class T> using valias = vector<T>; |
| void foo() { |
| $0^valias<int> $1^vi; |
| $2^valias<bool> $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 <class T> |
| 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 <class T> |
| 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 <class T> |
| 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 <int I> |
| void foo() { |
| int $0^x = $1^I; |
| } |
| )cpp", |
| "0: targets = {x}, decl\n" |
| "1: targets = {I}\n"}, |
| // Template template parameters. |
| {R"cpp( |
| template <class T> struct vector {}; |
| |
| template <template<class> class TT, template<class> class ...TP> |
| void foo() { |
| $0^TT<int> $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 <int(*)()> struct wrapper {}; |
| |
| template <int(*FuncParam)()> |
| 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 <typename... E> |
| 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 <typename T> |
| struct Test { |
| Test(T); |
| }; |
| void foo() { |
| $0^Test $1^a(5); |
| } |
| )cpp", |
| "0: targets = {Test}\n" |
| "1: targets = {a}, decl\n"}}; |
| |
| for (const auto &C : Cases) { |
| llvm::StringRef ExpectedCode = C.first; |
| llvm::StringRef ExpectedRefs = C.second; |
| |
| auto Actual = |
| annotateReferencesInFoo(llvm::Annotations(ExpectedCode).code()); |
| EXPECT_EQ(ExpectedCode, Actual.AnnotatedCode); |
| EXPECT_EQ(ExpectedRefs, Actual.DumpedReferences) << ExpectedCode; |
| } |
| } |
| |
| } // namespace |
| } // namespace clangd |
| } // namespace clang |