forked from OSchip/llvm-project
470 lines
13 KiB
C++
470 lines
13 KiB
C++
//===-- 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 "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 Decl *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");
|
|
}
|
|
|
|
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});
|
|
|
|
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", "");
|
|
|
|
Code = R"cpp(
|
|
template<template<typename> class T>
|
|
void foo() { [[T<int>]] x; }
|
|
)cpp";
|
|
EXPECT_DECLS("TemplateSpecializationTypeLoc", "template <typename> class T");
|
|
|
|
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");
|
|
}
|
|
|
|
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<type-parameter-0-0 *>",
|
|
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, 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 Foo {}
|
|
@property int x;
|
|
@end
|
|
void test(Foo *f) {
|
|
[[f.x]] = 42;
|
|
}
|
|
)cpp";
|
|
EXPECT_DECLS("ObjCPropertyRefExpr",
|
|
"@property(atomic, assign, unsafe_unretained, readwrite) int 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");
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace clangd
|
|
} // namespace clang
|