teak-llvm/clang-tools-extra/clangd/unittests/FindTargetTests.cpp
Sam McCall d3260bf5b2 [clangd] Errors in TestTU cause test failures unless suppressed with error-ok.
Summary:
The historic behavior of TestTU is to gather diagnostics and otherwise ignore
them. So if a test has a syntax error, and doesn't assert diagnostics, it
silently misbehaves.
This can be annoying when developing tests, as evidenced by various tests
gaining "assert no diagnostics" where that's not really the point of the test.

This patch aims to make that default behavior. For the first error
(not warning), TestTU will call ADD_FAILURE().

This can be suppressed with a comment containing "error-ok". For now that will
suppress any errors in the TU. We can make this stricter later -verify style.
(-verify itself is hard to reuse because of DiagnosticConsumer interfaces...)
A magic-comment was chosen over a TestTU option because of table-driven tests.

In addition to the behavior change, this patch:
  - adds //error-ok where we're knowingly testing invalid code
    (e.g. for diagnostics, crash-resilience, or token-level tests)
  - fixes a bunch of errors in the checked-in tests, mostly trivial (missing ;)
  - removes a bunch of now-redundant instances of "assert no diagnostics"

Reviewers: kadircet

Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, usaxena95, cfe-commits

Tags: #clang

Differential Revision: https://reviews.llvm.org/D73199
2020-01-24 11:16:27 +01:00

1142 lines
32 KiB
C++

//===-- FindTargetTests.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();
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", "");
Code = R"cpp(
template <typename T>
class Foo {
void f([[Foo]] x);
};
)cpp";
EXPECT_DECLS("InjectedClassNameTypeLoc", "class Foo");
}
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, Concept) {
Code = R"cpp(
template <typename T>
concept Fooable = requires (T t) { t.foo(); };
template <typename T> requires [[Fooable]]<T>
void bar(T t) {
t.foo();
}
)cpp";
Flags.push_back("-std=c++2a");
EXPECT_DECLS(
"ConceptSpecializationExpr",
// FIXME: Should we truncate the pretty-printed form of a concept decl
// somewhere?
{"template <typename T> concept Fooable = requires (T t) { t.foo(); };"});
}
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 or namespace '::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++2a");
auto AST = TU.build();
auto *TestDecl = &findDecl(AST, "foo");
if (auto *T = llvm::dyn_cast<FunctionTemplateDecl>(TestDecl))
TestDecl = T->getTemplatedDecl();
std::vector<ReferenceLoc> Refs;
if (const auto *Func = llvm::dyn_cast<FunctionDecl>(TestDecl))
findExplicitReferences(Func->getBody(), [&Refs](ReferenceLoc R) {
Refs.push_back(std::move(R));
});
else if (const auto *NS = llvm::dyn_cast<NamespaceDecl>(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</*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"},
// Injected class name.
{R"cpp(
namespace foo {
template <typename $0^T>
class $1^Bar {
~$2^Bar();
void $3^f($4^Bar);
};
}
)cpp",
"0: targets = {foo::Bar::T}, decl\n"
"1: targets = {foo::Bar}, decl\n"
"2: targets = {foo::Bar}\n"
"3: targets = {foo::Bar::f}, decl\n"
"4: targets = {foo::Bar}\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"},
// User-defined conversion operator.
{R"cpp(
void foo() {
class $0^Bar {};
class $1^Foo {
public:
// FIXME: This should have only one reference to Bar.
$2^operator $3^$4^Bar();
};
$5^Foo $6^f;
$7^f.$8^operator $9^Bar();
}
)cpp",
"0: targets = {Bar}, decl\n"
"1: targets = {Foo}, decl\n"
"2: targets = {foo()::Foo::operator Bar}, decl\n"
"3: targets = {Bar}\n"
"4: targets = {Bar}\n"
"5: targets = {Foo}\n"
"6: targets = {f}, decl\n"
"7: targets = {f}\n"
"8: targets = {foo()::Foo::operator Bar}\n"
"9: targets = {Bar}\n"},
// Destructor.
{R"cpp(
void foo() {
class $0^Foo {
public:
~$1^Foo() {}
void $2^destructMe() {
this->~$3^Foo();
}
};
$4^Foo $5^f;
$6^f.~ /*...*/ $7^Foo();
}
)cpp",
"0: targets = {Foo}, decl\n"
// FIXME: It's better to target destructor's FunctionDecl instead of
// the type itself (similar to constructor).
"1: targets = {Foo}\n"
"2: targets = {foo()::Foo::destructMe}, decl\n"
"3: targets = {Foo}\n"
"4: targets = {Foo}\n"
"5: targets = {f}, decl\n"
"6: targets = {f}\n"
"7: targets = {Foo}\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"},
// Templates
{R"cpp(
namespace foo {
template <typename $0^T>
class $1^Bar {};
}
)cpp",
"0: targets = {foo::Bar::T}, decl\n"
"1: targets = {foo::Bar}, decl\n"},
// Templates
{R"cpp(
namespace foo {
template <typename $0^T>
void $1^func();
}
)cpp",
"0: targets = {T}, decl\n"
"1: targets = {foo::func}, decl\n"},
// Templates
{R"cpp(
namespace foo {
template <typename $0^T>
$1^T $2^x;
}
)cpp",
"0: targets = {foo::T}, decl\n"
"1: targets = {foo::T}\n"
"2: targets = {foo::x}, decl\n"},
// Templates
{R"cpp(
template<typename T> class vector {};
namespace foo {
template <typename $0^T>
using $1^V = $2^vector<$3^T>;
}
)cpp",
"0: targets = {foo::T}, decl\n"
"1: targets = {foo::V}, decl\n"
"2: targets = {vector}\n"
"3: targets = {foo::T}\n"},
// Concept
{
R"cpp(
template <typename T>
concept Drawable = requires (T t) { t.draw(); };
namespace foo {
template <typename $0^T> requires $1^Drawable<$2^T>
void $3^bar($4^T $5^t) {
$6^t.draw();
}
}
)cpp",
"0: targets = {T}, decl\n"
"1: targets = {Drawable}\n"
"2: targets = {T}\n"
"3: targets = {foo::bar}, decl\n"
"4: targets = {T}\n"
"5: targets = {t}, decl\n"
"6: targets = {t}\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