996 lines
22 KiB
C++
996 lines
22 KiB
C++
//===-- DefineInlineTests.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 "TweakTesting.h"
|
|
#include "TestFS.h"
|
|
#include "gmock/gmock.h"
|
|
#include "gtest/gtest.h"
|
|
|
|
using ::testing::ElementsAre;
|
|
|
|
namespace clang {
|
|
namespace clangd {
|
|
namespace {
|
|
|
|
TWEAK_TEST(DefineInline);
|
|
|
|
TEST_F(DefineInlineTest, TriggersOnFunctionDecl) {
|
|
// Basic check for function body and signature.
|
|
EXPECT_AVAILABLE(R"cpp(
|
|
class Bar {
|
|
void baz();
|
|
};
|
|
|
|
[[void [[Bar::[[b^a^z]]]]() [[{
|
|
return;
|
|
}]]]]
|
|
|
|
void foo();
|
|
[[void [[f^o^o]]() [[{
|
|
return;
|
|
}]]]]
|
|
)cpp");
|
|
|
|
EXPECT_UNAVAILABLE(R"cpp(
|
|
// Not a definition
|
|
vo^i[[d^ ^f]]^oo();
|
|
|
|
[[vo^id ]]foo[[()]] {[[
|
|
[[(void)(5+3);
|
|
return;]]
|
|
}]]
|
|
|
|
// Definition with no body.
|
|
class Bar { Bar() = def^ault; };
|
|
)cpp");
|
|
}
|
|
|
|
TEST_F(DefineInlineTest, NoForwardDecl) {
|
|
Header = "void bar();";
|
|
EXPECT_UNAVAILABLE(R"cpp(
|
|
void bar() {
|
|
return;
|
|
}
|
|
// FIXME: Generate a decl in the header.
|
|
void fo^o() {
|
|
return;
|
|
})cpp");
|
|
}
|
|
|
|
TEST_F(DefineInlineTest, ReferencedDecls) {
|
|
EXPECT_AVAILABLE(R"cpp(
|
|
void bar();
|
|
void foo(int test);
|
|
|
|
void fo^o(int baz) {
|
|
int x = 10;
|
|
bar();
|
|
})cpp");
|
|
|
|
// Internal symbol usage.
|
|
Header = "void foo(int test);";
|
|
EXPECT_UNAVAILABLE(R"cpp(
|
|
void bar();
|
|
void fo^o(int baz) {
|
|
int x = 10;
|
|
bar();
|
|
})cpp");
|
|
|
|
// Becomes available after making symbol visible.
|
|
Header = "void bar();" + Header;
|
|
EXPECT_AVAILABLE(R"cpp(
|
|
void fo^o(int baz) {
|
|
int x = 10;
|
|
bar();
|
|
})cpp");
|
|
|
|
// FIXME: Move declaration below bar to make it visible.
|
|
Header.clear();
|
|
EXPECT_UNAVAILABLE(R"cpp(
|
|
void foo();
|
|
void bar();
|
|
|
|
void fo^o() {
|
|
bar();
|
|
})cpp");
|
|
|
|
// Order doesn't matter within a class.
|
|
EXPECT_AVAILABLE(R"cpp(
|
|
class Bar {
|
|
void foo();
|
|
void bar();
|
|
};
|
|
|
|
void Bar::fo^o() {
|
|
bar();
|
|
})cpp");
|
|
|
|
// FIXME: Perform include insertion to make symbol visible.
|
|
ExtraFiles["a.h"] = "void bar();";
|
|
Header = "void foo(int test);";
|
|
EXPECT_UNAVAILABLE(R"cpp(
|
|
#include "a.h"
|
|
void fo^o(int baz) {
|
|
int x = 10;
|
|
bar();
|
|
})cpp");
|
|
}
|
|
|
|
TEST_F(DefineInlineTest, TemplateSpec) {
|
|
EXPECT_UNAVAILABLE(R"cpp(
|
|
template <typename T> void foo();
|
|
template<> void foo<char>();
|
|
|
|
template<> void f^oo<int>() {
|
|
})cpp");
|
|
EXPECT_UNAVAILABLE(R"cpp(
|
|
template <typename T> void foo();
|
|
|
|
template<> void f^oo<int>() {
|
|
})cpp");
|
|
EXPECT_UNAVAILABLE(R"cpp(
|
|
template <typename T> struct Foo { void foo(); };
|
|
|
|
template <typename T> void Foo<T>::f^oo() {
|
|
})cpp");
|
|
EXPECT_AVAILABLE(R"cpp(
|
|
template <typename T> void foo();
|
|
void bar();
|
|
template <> void foo<int>();
|
|
|
|
template<> void f^oo<int>() {
|
|
bar();
|
|
})cpp");
|
|
EXPECT_UNAVAILABLE(R"cpp(
|
|
namespace bar {
|
|
template <typename T> void f^oo() {}
|
|
template void foo<int>();
|
|
})cpp");
|
|
}
|
|
|
|
TEST_F(DefineInlineTest, CheckForCanonDecl) {
|
|
EXPECT_UNAVAILABLE(R"cpp(
|
|
void foo();
|
|
|
|
void bar() {}
|
|
void f^oo() {
|
|
// This bar normally refers to the definition just above, but it is not
|
|
// visible from the forward declaration of foo.
|
|
bar();
|
|
})cpp");
|
|
// Make it available with a forward decl.
|
|
EXPECT_AVAILABLE(R"cpp(
|
|
void bar();
|
|
void foo();
|
|
|
|
void bar() {}
|
|
void f^oo() {
|
|
bar();
|
|
})cpp");
|
|
}
|
|
|
|
TEST_F(DefineInlineTest, UsingShadowDecls) {
|
|
EXPECT_UNAVAILABLE(R"cpp(
|
|
namespace ns1 { void foo(int); }
|
|
namespace ns2 { void foo(int*); }
|
|
template <typename T>
|
|
void bar();
|
|
|
|
using ns1::foo;
|
|
using ns2::foo;
|
|
|
|
template <typename T>
|
|
void b^ar() {
|
|
foo(T());
|
|
})cpp");
|
|
}
|
|
|
|
TEST_F(DefineInlineTest, TransformNestedNamespaces) {
|
|
auto *Test = R"cpp(
|
|
namespace a {
|
|
void bar();
|
|
namespace b {
|
|
void baz();
|
|
namespace c {
|
|
void aux();
|
|
}
|
|
}
|
|
}
|
|
|
|
void foo();
|
|
using namespace a;
|
|
using namespace b;
|
|
using namespace c;
|
|
void f^oo() {
|
|
bar();
|
|
a::bar();
|
|
|
|
baz();
|
|
b::baz();
|
|
a::b::baz();
|
|
|
|
aux();
|
|
c::aux();
|
|
b::c::aux();
|
|
a::b::c::aux();
|
|
})cpp";
|
|
auto *Expected = R"cpp(
|
|
namespace a {
|
|
void bar();
|
|
namespace b {
|
|
void baz();
|
|
namespace c {
|
|
void aux();
|
|
}
|
|
}
|
|
}
|
|
|
|
void foo(){
|
|
a::bar();
|
|
a::bar();
|
|
|
|
a::b::baz();
|
|
a::b::baz();
|
|
a::b::baz();
|
|
|
|
a::b::c::aux();
|
|
a::b::c::aux();
|
|
a::b::c::aux();
|
|
a::b::c::aux();
|
|
}
|
|
using namespace a;
|
|
using namespace b;
|
|
using namespace c;
|
|
)cpp";
|
|
EXPECT_EQ(apply(Test), Expected);
|
|
}
|
|
|
|
TEST_F(DefineInlineTest, TransformUsings) {
|
|
auto *Test = R"cpp(
|
|
namespace a { namespace b { namespace c { void aux(); } } }
|
|
|
|
void foo();
|
|
void f^oo() {
|
|
using namespace a;
|
|
using namespace b;
|
|
using namespace c;
|
|
using c::aux;
|
|
namespace d = c;
|
|
})cpp";
|
|
auto *Expected = R"cpp(
|
|
namespace a { namespace b { namespace c { void aux(); } } }
|
|
|
|
void foo(){
|
|
using namespace a;
|
|
using namespace a::b;
|
|
using namespace a::b::c;
|
|
using a::b::c::aux;
|
|
namespace d = a::b::c;
|
|
}
|
|
)cpp";
|
|
EXPECT_EQ(apply(Test), Expected);
|
|
}
|
|
|
|
TEST_F(DefineInlineTest, TransformDecls) {
|
|
auto *Test = R"cpp(
|
|
void foo();
|
|
void f^oo() {
|
|
class Foo {
|
|
public:
|
|
void foo();
|
|
int x;
|
|
};
|
|
|
|
enum En { Zero, One };
|
|
En x = Zero;
|
|
|
|
enum class EnClass { Zero, One };
|
|
EnClass y = EnClass::Zero;
|
|
})cpp";
|
|
auto *Expected = R"cpp(
|
|
void foo(){
|
|
class Foo {
|
|
public:
|
|
void foo();
|
|
int x;
|
|
};
|
|
|
|
enum En { Zero, One };
|
|
En x = Zero;
|
|
|
|
enum class EnClass { Zero, One };
|
|
EnClass y = EnClass::Zero;
|
|
}
|
|
)cpp";
|
|
EXPECT_EQ(apply(Test), Expected);
|
|
}
|
|
|
|
TEST_F(DefineInlineTest, TransformTemplDecls) {
|
|
auto *Test = R"cpp(
|
|
namespace a {
|
|
template <typename T> class Bar {
|
|
public:
|
|
void bar();
|
|
};
|
|
template <typename T> T bar;
|
|
template <typename T> void aux() {}
|
|
}
|
|
|
|
void foo();
|
|
|
|
using namespace a;
|
|
void f^oo() {
|
|
bar<Bar<int>>.bar();
|
|
aux<Bar<int>>();
|
|
})cpp";
|
|
auto *Expected = R"cpp(
|
|
namespace a {
|
|
template <typename T> class Bar {
|
|
public:
|
|
void bar();
|
|
};
|
|
template <typename T> T bar;
|
|
template <typename T> void aux() {}
|
|
}
|
|
|
|
void foo(){
|
|
a::bar<a::Bar<int>>.bar();
|
|
a::aux<a::Bar<int>>();
|
|
}
|
|
|
|
using namespace a;
|
|
)cpp";
|
|
EXPECT_EQ(apply(Test), Expected);
|
|
}
|
|
|
|
TEST_F(DefineInlineTest, TransformMembers) {
|
|
auto *Test = R"cpp(
|
|
class Foo {
|
|
void foo();
|
|
};
|
|
|
|
void Foo::f^oo() {
|
|
return;
|
|
})cpp";
|
|
auto *Expected = R"cpp(
|
|
class Foo {
|
|
void foo(){
|
|
return;
|
|
}
|
|
};
|
|
|
|
)cpp";
|
|
EXPECT_EQ(apply(Test), Expected);
|
|
|
|
ExtraFiles["a.h"] = R"cpp(
|
|
class Foo {
|
|
void foo();
|
|
};)cpp";
|
|
|
|
llvm::StringMap<std::string> EditedFiles;
|
|
Test = R"cpp(
|
|
#include "a.h"
|
|
void Foo::f^oo() {
|
|
return;
|
|
})cpp";
|
|
Expected = R"cpp(
|
|
#include "a.h"
|
|
)cpp";
|
|
EXPECT_EQ(apply(Test, &EditedFiles), Expected);
|
|
|
|
Expected = R"cpp(
|
|
class Foo {
|
|
void foo(){
|
|
return;
|
|
}
|
|
};)cpp";
|
|
EXPECT_THAT(EditedFiles,
|
|
ElementsAre(FileWithContents(testPath("a.h"), Expected)));
|
|
}
|
|
|
|
TEST_F(DefineInlineTest, TransformDependentTypes) {
|
|
auto *Test = R"cpp(
|
|
namespace a {
|
|
template <typename T> class Bar {};
|
|
}
|
|
|
|
template <typename T>
|
|
void foo();
|
|
|
|
using namespace a;
|
|
template <typename T>
|
|
void f^oo() {
|
|
Bar<T> B;
|
|
Bar<Bar<T>> q;
|
|
})cpp";
|
|
auto *Expected = R"cpp(
|
|
namespace a {
|
|
template <typename T> class Bar {};
|
|
}
|
|
|
|
template <typename T>
|
|
void foo(){
|
|
a::Bar<T> B;
|
|
a::Bar<a::Bar<T>> q;
|
|
}
|
|
|
|
using namespace a;
|
|
)cpp";
|
|
|
|
EXPECT_EQ(apply(Test), Expected);
|
|
}
|
|
|
|
TEST_F(DefineInlineTest, TransformFunctionTempls) {
|
|
// Check we select correct specialization decl.
|
|
std::pair<llvm::StringRef, llvm::StringRef> Cases[] = {
|
|
{R"cpp(
|
|
template <typename T>
|
|
void foo(T p);
|
|
|
|
template <>
|
|
void foo<int>(int p);
|
|
|
|
template <>
|
|
void foo<char>(char p);
|
|
|
|
template <>
|
|
void fo^o<int>(int p) {
|
|
return;
|
|
})cpp",
|
|
R"cpp(
|
|
template <typename T>
|
|
void foo(T p);
|
|
|
|
template <>
|
|
void foo<int>(int p){
|
|
return;
|
|
}
|
|
|
|
template <>
|
|
void foo<char>(char p);
|
|
|
|
)cpp"},
|
|
{// Make sure we are not selecting the first specialization all the time.
|
|
R"cpp(
|
|
template <typename T>
|
|
void foo(T p);
|
|
|
|
template <>
|
|
void foo<int>(int p);
|
|
|
|
template <>
|
|
void foo<char>(char p);
|
|
|
|
template <>
|
|
void fo^o<char>(char p) {
|
|
return;
|
|
})cpp",
|
|
R"cpp(
|
|
template <typename T>
|
|
void foo(T p);
|
|
|
|
template <>
|
|
void foo<int>(int p);
|
|
|
|
template <>
|
|
void foo<char>(char p){
|
|
return;
|
|
}
|
|
|
|
)cpp"},
|
|
{R"cpp(
|
|
template <typename T>
|
|
void foo(T p);
|
|
|
|
template <>
|
|
void foo<int>(int p);
|
|
|
|
template <typename T>
|
|
void fo^o(T p) {
|
|
return;
|
|
})cpp",
|
|
R"cpp(
|
|
template <typename T>
|
|
void foo(T p){
|
|
return;
|
|
}
|
|
|
|
template <>
|
|
void foo<int>(int p);
|
|
|
|
)cpp"},
|
|
};
|
|
for (const auto &Case : Cases)
|
|
EXPECT_EQ(apply(Case.first), Case.second) << Case.first;
|
|
}
|
|
|
|
TEST_F(DefineInlineTest, TransformTypeLocs) {
|
|
auto *Test = R"cpp(
|
|
namespace a {
|
|
template <typename T> class Bar {
|
|
public:
|
|
template <typename Q> class Baz {};
|
|
};
|
|
class Foo{};
|
|
}
|
|
|
|
void foo();
|
|
|
|
using namespace a;
|
|
void f^oo() {
|
|
Bar<int> B;
|
|
Foo foo;
|
|
a::Bar<Bar<int>>::Baz<Bar<int>> q;
|
|
})cpp";
|
|
auto *Expected = R"cpp(
|
|
namespace a {
|
|
template <typename T> class Bar {
|
|
public:
|
|
template <typename Q> class Baz {};
|
|
};
|
|
class Foo{};
|
|
}
|
|
|
|
void foo(){
|
|
a::Bar<int> B;
|
|
a::Foo foo;
|
|
a::Bar<a::Bar<int>>::Baz<a::Bar<int>> q;
|
|
}
|
|
|
|
using namespace a;
|
|
)cpp";
|
|
EXPECT_EQ(apply(Test), Expected);
|
|
}
|
|
|
|
TEST_F(DefineInlineTest, TransformDeclRefs) {
|
|
auto *Test = R"cpp(
|
|
namespace a {
|
|
template <typename T> class Bar {
|
|
public:
|
|
void foo();
|
|
static void bar();
|
|
int x;
|
|
static int y;
|
|
};
|
|
void bar();
|
|
void test();
|
|
}
|
|
|
|
void foo();
|
|
using namespace a;
|
|
void f^oo() {
|
|
a::Bar<int> B;
|
|
B.foo();
|
|
a::bar();
|
|
Bar<Bar<int>>::bar();
|
|
a::Bar<int>::bar();
|
|
B.x = Bar<int>::y;
|
|
Bar<int>::y = 3;
|
|
bar();
|
|
a::test();
|
|
})cpp";
|
|
auto *Expected = R"cpp(
|
|
namespace a {
|
|
template <typename T> class Bar {
|
|
public:
|
|
void foo();
|
|
static void bar();
|
|
int x;
|
|
static int y;
|
|
};
|
|
void bar();
|
|
void test();
|
|
}
|
|
|
|
void foo(){
|
|
a::Bar<int> B;
|
|
B.foo();
|
|
a::bar();
|
|
a::Bar<a::Bar<int>>::bar();
|
|
a::Bar<int>::bar();
|
|
B.x = a::Bar<int>::y;
|
|
a::Bar<int>::y = 3;
|
|
a::bar();
|
|
a::test();
|
|
}
|
|
using namespace a;
|
|
)cpp";
|
|
EXPECT_EQ(apply(Test), Expected);
|
|
}
|
|
|
|
TEST_F(DefineInlineTest, StaticMembers) {
|
|
auto *Test = R"cpp(
|
|
namespace ns { class X { static void foo(); void bar(); }; }
|
|
void ns::X::b^ar() {
|
|
foo();
|
|
})cpp";
|
|
auto *Expected = R"cpp(
|
|
namespace ns { class X { static void foo(); void bar(){
|
|
foo();
|
|
} }; }
|
|
)cpp";
|
|
EXPECT_EQ(apply(Test), Expected);
|
|
}
|
|
|
|
TEST_F(DefineInlineTest, TransformParamNames) {
|
|
std::pair<llvm::StringRef, llvm::StringRef> Cases[] = {
|
|
{R"cpp(
|
|
void foo(int, bool b, int T\
|
|
est);
|
|
void ^foo(int f, bool x, int z) {})cpp",
|
|
R"cpp(
|
|
void foo(int f, bool x, int z){}
|
|
)cpp"},
|
|
{R"cpp(
|
|
#define PARAM int Z
|
|
void foo(PARAM);
|
|
|
|
void ^foo(int X) {})cpp",
|
|
"fail: Cant rename parameter inside macro body."},
|
|
{R"cpp(
|
|
#define TYPE int
|
|
#define PARAM TYPE Z
|
|
#define BODY(x) 5 * (x) + 2
|
|
template <int P>
|
|
void foo(PARAM, TYPE Q, TYPE, TYPE W = BODY(P));
|
|
template <int x>
|
|
void ^foo(int Z, int b, int c, int d) {})cpp",
|
|
R"cpp(
|
|
#define TYPE int
|
|
#define PARAM TYPE Z
|
|
#define BODY(x) 5 * (x) + 2
|
|
template <int x>
|
|
void foo(PARAM, TYPE b, TYPE c, TYPE d = BODY(x)){}
|
|
)cpp"},
|
|
};
|
|
for (const auto &Case : Cases)
|
|
EXPECT_EQ(apply(Case.first), Case.second) << Case.first;
|
|
}
|
|
|
|
TEST_F(DefineInlineTest, TransformTemplParamNames) {
|
|
auto *Test = R"cpp(
|
|
struct Foo {
|
|
struct Bar {
|
|
template <class, class X,
|
|
template<typename> class, template<typename> class Y,
|
|
int, int Z>
|
|
void foo(X, Y<X>, int W = 5 * Z + 2);
|
|
};
|
|
};
|
|
|
|
template <class T, class U,
|
|
template<typename> class V, template<typename> class W,
|
|
int X, int Y>
|
|
void Foo::Bar::f^oo(U, W<U>, int Q) {})cpp";
|
|
auto *Expected = R"cpp(
|
|
struct Foo {
|
|
struct Bar {
|
|
template <class T, class U,
|
|
template<typename> class V, template<typename> class W,
|
|
int X, int Y>
|
|
void foo(U, W<U>, int Q = 5 * Y + 2){}
|
|
};
|
|
};
|
|
|
|
)cpp";
|
|
EXPECT_EQ(apply(Test), Expected);
|
|
}
|
|
|
|
TEST_F(DefineInlineTest, TransformInlineNamespaces) {
|
|
auto *Test = R"cpp(
|
|
namespace a { inline namespace b { namespace { struct Foo{}; } } }
|
|
void foo();
|
|
|
|
using namespace a;
|
|
void ^foo() {Foo foo;})cpp";
|
|
auto *Expected = R"cpp(
|
|
namespace a { inline namespace b { namespace { struct Foo{}; } } }
|
|
void foo(){a::Foo foo;}
|
|
|
|
using namespace a;
|
|
)cpp";
|
|
EXPECT_EQ(apply(Test), Expected);
|
|
}
|
|
|
|
TEST_F(DefineInlineTest, TokensBeforeSemicolon) {
|
|
std::pair<llvm::StringRef, llvm::StringRef> Cases[] = {
|
|
{R"cpp(
|
|
void foo() /*Comment -_-*/ /*Com 2*/ ;
|
|
void fo^o() { return ; })cpp",
|
|
R"cpp(
|
|
void foo() /*Comment -_-*/ /*Com 2*/ { return ; }
|
|
)cpp"},
|
|
|
|
{R"cpp(
|
|
void foo();
|
|
void fo^o() { return ; })cpp",
|
|
R"cpp(
|
|
void foo(){ return ; }
|
|
)cpp"},
|
|
|
|
{R"cpp(
|
|
#define SEMI ;
|
|
void foo() SEMI
|
|
void fo^o() { return ; })cpp",
|
|
"fail: Couldn't find semicolon for target declaration."},
|
|
};
|
|
for (const auto &Case : Cases)
|
|
EXPECT_EQ(apply(Case.first), Case.second) << Case.first;
|
|
}
|
|
|
|
TEST_F(DefineInlineTest, HandleMacros) {
|
|
EXPECT_UNAVAILABLE(R"cpp(
|
|
#define BODY { return; }
|
|
void foo();
|
|
void f^oo()BODY)cpp");
|
|
|
|
EXPECT_UNAVAILABLE(R"cpp(
|
|
#define BODY void foo(){ return; }
|
|
void foo();
|
|
[[BODY]])cpp");
|
|
|
|
std::pair<llvm::StringRef, llvm::StringRef> Cases[] = {
|
|
// We don't qualify declarations coming from macros.
|
|
{R"cpp(
|
|
#define BODY Foo
|
|
namespace a { class Foo{}; }
|
|
void foo();
|
|
using namespace a;
|
|
void f^oo(){BODY();})cpp",
|
|
R"cpp(
|
|
#define BODY Foo
|
|
namespace a { class Foo{}; }
|
|
void foo(){BODY();}
|
|
using namespace a;
|
|
)cpp"},
|
|
|
|
// Macro is not visible at declaration location, but we proceed.
|
|
{R"cpp(
|
|
void foo();
|
|
#define BODY return;
|
|
void f^oo(){BODY})cpp",
|
|
R"cpp(
|
|
void foo(){BODY}
|
|
#define BODY return;
|
|
)cpp"},
|
|
|
|
{R"cpp(
|
|
#define TARGET void foo()
|
|
TARGET;
|
|
void f^oo(){ return; })cpp",
|
|
R"cpp(
|
|
#define TARGET void foo()
|
|
TARGET{ return; }
|
|
)cpp"},
|
|
|
|
{R"cpp(
|
|
#define TARGET foo
|
|
void TARGET();
|
|
void f^oo(){ return; })cpp",
|
|
R"cpp(
|
|
#define TARGET foo
|
|
void TARGET(){ return; }
|
|
)cpp"},
|
|
};
|
|
for (const auto &Case : Cases)
|
|
EXPECT_EQ(apply(Case.first), Case.second) << Case.first;
|
|
}
|
|
|
|
TEST_F(DefineInlineTest, DropCommonNameSpecifiers) {
|
|
struct {
|
|
llvm::StringRef Test;
|
|
llvm::StringRef Expected;
|
|
} Cases[] = {
|
|
{R"cpp(
|
|
namespace a { namespace b { void aux(); } }
|
|
namespace ns1 {
|
|
void foo();
|
|
namespace qq { void test(); }
|
|
namespace ns2 {
|
|
void bar();
|
|
namespace ns3 { void baz(); }
|
|
}
|
|
}
|
|
|
|
using namespace a;
|
|
using namespace a::b;
|
|
using namespace ns1::qq;
|
|
void ns1::ns2::ns3::b^az() {
|
|
foo();
|
|
bar();
|
|
baz();
|
|
ns1::ns2::ns3::baz();
|
|
aux();
|
|
test();
|
|
})cpp",
|
|
R"cpp(
|
|
namespace a { namespace b { void aux(); } }
|
|
namespace ns1 {
|
|
void foo();
|
|
namespace qq { void test(); }
|
|
namespace ns2 {
|
|
void bar();
|
|
namespace ns3 { void baz(){
|
|
foo();
|
|
bar();
|
|
baz();
|
|
ns1::ns2::ns3::baz();
|
|
a::b::aux();
|
|
qq::test();
|
|
} }
|
|
}
|
|
}
|
|
|
|
using namespace a;
|
|
using namespace a::b;
|
|
using namespace ns1::qq;
|
|
)cpp"},
|
|
{R"cpp(
|
|
namespace ns1 {
|
|
namespace qq { struct Foo { struct Bar {}; }; using B = Foo::Bar; }
|
|
namespace ns2 { void baz(); }
|
|
}
|
|
|
|
using namespace ns1::qq;
|
|
void ns1::ns2::b^az() { Foo f; B b; })cpp",
|
|
R"cpp(
|
|
namespace ns1 {
|
|
namespace qq { struct Foo { struct Bar {}; }; using B = Foo::Bar; }
|
|
namespace ns2 { void baz(){ qq::Foo f; qq::B b; } }
|
|
}
|
|
|
|
using namespace ns1::qq;
|
|
)cpp"},
|
|
{R"cpp(
|
|
namespace ns1 {
|
|
namespace qq {
|
|
template<class T> struct Foo { template <class U> struct Bar {}; };
|
|
template<class T, class U>
|
|
using B = typename Foo<T>::template Bar<U>;
|
|
}
|
|
namespace ns2 { void baz(); }
|
|
}
|
|
|
|
using namespace ns1::qq;
|
|
void ns1::ns2::b^az() { B<int, bool> b; })cpp",
|
|
R"cpp(
|
|
namespace ns1 {
|
|
namespace qq {
|
|
template<class T> struct Foo { template <class U> struct Bar {}; };
|
|
template<class T, class U>
|
|
using B = typename Foo<T>::template Bar<U>;
|
|
}
|
|
namespace ns2 { void baz(){ qq::B<int, bool> b; } }
|
|
}
|
|
|
|
using namespace ns1::qq;
|
|
)cpp"},
|
|
};
|
|
for (const auto &Case : Cases)
|
|
EXPECT_EQ(apply(Case.Test), Case.Expected) << Case.Test;
|
|
}
|
|
|
|
TEST_F(DefineInlineTest, QualifyWithUsingDirectives) {
|
|
llvm::StringRef Test = R"cpp(
|
|
namespace a {
|
|
void bar();
|
|
namespace b { struct Foo{}; void aux(); }
|
|
namespace c { void cux(); }
|
|
}
|
|
using namespace a;
|
|
using X = b::Foo;
|
|
void foo();
|
|
|
|
using namespace b;
|
|
using namespace c;
|
|
void ^foo() {
|
|
cux();
|
|
bar();
|
|
X x;
|
|
aux();
|
|
using namespace c;
|
|
// FIXME: The last reference to cux() in body of foo should not be
|
|
// qualified, since there is a using directive inside the function body.
|
|
cux();
|
|
})cpp";
|
|
llvm::StringRef Expected = R"cpp(
|
|
namespace a {
|
|
void bar();
|
|
namespace b { struct Foo{}; void aux(); }
|
|
namespace c { void cux(); }
|
|
}
|
|
using namespace a;
|
|
using X = b::Foo;
|
|
void foo(){
|
|
c::cux();
|
|
bar();
|
|
X x;
|
|
b::aux();
|
|
using namespace c;
|
|
// FIXME: The last reference to cux() in body of foo should not be
|
|
// qualified, since there is a using directive inside the function body.
|
|
c::cux();
|
|
}
|
|
|
|
using namespace b;
|
|
using namespace c;
|
|
)cpp";
|
|
EXPECT_EQ(apply(Test), Expected) << Test;
|
|
}
|
|
|
|
TEST_F(DefineInlineTest, AddInline) {
|
|
llvm::StringMap<std::string> EditedFiles;
|
|
ExtraFiles["a.h"] = "void foo();";
|
|
apply(R"cpp(#include "a.h"
|
|
void fo^o() {})cpp",
|
|
&EditedFiles);
|
|
EXPECT_THAT(EditedFiles, testing::ElementsAre(FileWithContents(
|
|
testPath("a.h"), "inline void foo(){}")));
|
|
|
|
// Check we put inline before cv-qualifiers.
|
|
ExtraFiles["a.h"] = "const int foo();";
|
|
apply(R"cpp(#include "a.h"
|
|
const int fo^o() {})cpp",
|
|
&EditedFiles);
|
|
EXPECT_THAT(EditedFiles, testing::ElementsAre(FileWithContents(
|
|
testPath("a.h"), "inline const int foo(){}")));
|
|
|
|
// No double inline.
|
|
ExtraFiles["a.h"] = "inline void foo();";
|
|
apply(R"cpp(#include "a.h"
|
|
inline void fo^o() {})cpp",
|
|
&EditedFiles);
|
|
EXPECT_THAT(EditedFiles, testing::ElementsAre(FileWithContents(
|
|
testPath("a.h"), "inline void foo(){}")));
|
|
|
|
// Constexprs don't need "inline".
|
|
ExtraFiles["a.h"] = "constexpr void foo();";
|
|
apply(R"cpp(#include "a.h"
|
|
constexpr void fo^o() {})cpp",
|
|
&EditedFiles);
|
|
EXPECT_THAT(EditedFiles, testing::ElementsAre(FileWithContents(
|
|
testPath("a.h"), "constexpr void foo(){}")));
|
|
|
|
// Class members don't need "inline".
|
|
ExtraFiles["a.h"] = "struct Foo { void foo(); };";
|
|
apply(R"cpp(#include "a.h"
|
|
void Foo::fo^o() {})cpp",
|
|
&EditedFiles);
|
|
EXPECT_THAT(EditedFiles,
|
|
testing::ElementsAre(FileWithContents(
|
|
testPath("a.h"), "struct Foo { void foo(){} };")));
|
|
|
|
// Function template doesn't need to be "inline"d.
|
|
ExtraFiles["a.h"] = "template <typename T> void foo();";
|
|
apply(R"cpp(#include "a.h"
|
|
template <typename T>
|
|
void fo^o() {})cpp",
|
|
&EditedFiles);
|
|
EXPECT_THAT(EditedFiles,
|
|
testing::ElementsAre(FileWithContents(
|
|
testPath("a.h"), "template <typename T> void foo(){}")));
|
|
|
|
// Specializations needs to be marked "inline".
|
|
ExtraFiles["a.h"] = R"cpp(
|
|
template <typename T> void foo();
|
|
template <> void foo<int>();)cpp";
|
|
apply(R"cpp(#include "a.h"
|
|
template <>
|
|
void fo^o<int>() {})cpp",
|
|
&EditedFiles);
|
|
EXPECT_THAT(EditedFiles,
|
|
testing::ElementsAre(FileWithContents(testPath("a.h"),
|
|
R"cpp(
|
|
template <typename T> void foo();
|
|
template <> inline void foo<int>(){})cpp")));
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace clangd
|
|
} // namespace clang
|