forked from OSchip/llvm-project
473 lines
8.8 KiB
C++
473 lines
8.8 KiB
C++
//===-- AddUsingTests.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 "Config.h"
|
|
#include "TestTU.h"
|
|
#include "TweakTesting.h"
|
|
#include "gmock/gmock-matchers.h"
|
|
#include "gmock/gmock.h"
|
|
#include "gtest/gtest.h"
|
|
|
|
namespace clang {
|
|
namespace clangd {
|
|
namespace {
|
|
|
|
TWEAK_TEST(AddUsing);
|
|
|
|
TEST_F(AddUsingTest, Prepare) {
|
|
Config Cfg;
|
|
Cfg.Style.FullyQualifiedNamespaces.push_back("ban");
|
|
WithContextValue WithConfig(Config::Key, std::move(Cfg));
|
|
|
|
const std::string Header = R"cpp(
|
|
#define NS(name) one::two::name
|
|
namespace ban { void foo() {} }
|
|
namespace banana { void foo() {} }
|
|
namespace one {
|
|
void oo() {}
|
|
template<typename TT> class tt {};
|
|
namespace two {
|
|
enum ee {};
|
|
void ff() {}
|
|
class cc {
|
|
public:
|
|
struct st {};
|
|
static void mm() {}
|
|
cc operator|(const cc& x) const { return x; }
|
|
};
|
|
}
|
|
})cpp";
|
|
|
|
EXPECT_AVAILABLE(Header + "void fun() { o^n^e^:^:^t^w^o^:^:^f^f(); }");
|
|
EXPECT_AVAILABLE(Header + "void fun() { o^n^e^::^o^o(); }");
|
|
EXPECT_AVAILABLE(Header + "void fun() { o^n^e^:^:^t^w^o^:^:^e^e E; }");
|
|
EXPECT_AVAILABLE(Header + "void fun() { o^n^e^:^:^t^w^o:^:^c^c C; }");
|
|
EXPECT_UNAVAILABLE(Header +
|
|
"void fun() { o^n^e^:^:^t^w^o^:^:^c^c^:^:^m^m(); }");
|
|
EXPECT_UNAVAILABLE(Header +
|
|
"void fun() { o^n^e^:^:^t^w^o^:^:^c^c^:^:^s^t inst; }");
|
|
EXPECT_UNAVAILABLE(Header +
|
|
"void fun() { o^n^e^:^:^t^w^o^:^:^c^c^:^:^s^t inst; }");
|
|
EXPECT_UNAVAILABLE(Header + "void fun() { N^S(c^c) inst; }");
|
|
// This used to crash. Ideally we would support this case, but for now we just
|
|
// test that we don't crash.
|
|
EXPECT_UNAVAILABLE(Header +
|
|
"template<typename TT> using foo = one::tt<T^T>;");
|
|
// Test that we don't crash or misbehave on unnamed DeclRefExpr.
|
|
EXPECT_UNAVAILABLE(Header +
|
|
"void fun() { one::two::cc() ^| one::two::cc(); }");
|
|
// Do not offer code action when operating on a banned namespace.
|
|
EXPECT_UNAVAILABLE(Header + "void fun() { ban::fo^o(); }");
|
|
EXPECT_UNAVAILABLE(Header + "void fun() { ::ban::fo^o(); }");
|
|
EXPECT_AVAILABLE(Header + "void fun() { banana::fo^o(); }");
|
|
|
|
// Do not offer code action on typo-corrections.
|
|
EXPECT_UNAVAILABLE(Header + "/*error-ok*/c^c C;");
|
|
|
|
// NestedNameSpecifier, but no namespace.
|
|
EXPECT_UNAVAILABLE(Header + "class Foo {}; class F^oo foo;");
|
|
|
|
// Check that we do not trigger in header files.
|
|
FileName = "test.h";
|
|
ExtraArgs.push_back("-xc++-header"); // .h file is treated a C by default.
|
|
EXPECT_UNAVAILABLE(Header + "void fun() { one::two::f^f(); }");
|
|
FileName = "test.hpp";
|
|
EXPECT_UNAVAILABLE(Header + "void fun() { one::two::f^f(); }");
|
|
}
|
|
|
|
TEST_F(AddUsingTest, Apply) {
|
|
FileName = "test.cpp";
|
|
struct {
|
|
llvm::StringRef TestSource;
|
|
llvm::StringRef ExpectedSource;
|
|
} Cases[]{{
|
|
// Function, no other using, namespace.
|
|
R"cpp(
|
|
#include "test.hpp"
|
|
namespace {
|
|
void fun() {
|
|
^o^n^e^:^:^t^w^o^:^:^f^f();
|
|
}
|
|
})cpp",
|
|
R"cpp(
|
|
#include "test.hpp"
|
|
namespace {using one::two::ff;
|
|
|
|
void fun() {
|
|
ff();
|
|
}
|
|
})cpp",
|
|
},
|
|
// Type, no other using, namespace.
|
|
{
|
|
R"cpp(
|
|
#include "test.hpp"
|
|
namespace {
|
|
void fun() {
|
|
::on^e::t^wo::c^c inst;
|
|
}
|
|
})cpp",
|
|
R"cpp(
|
|
#include "test.hpp"
|
|
namespace {using ::one::two::cc;
|
|
|
|
void fun() {
|
|
cc inst;
|
|
}
|
|
})cpp",
|
|
},
|
|
// Type, no other using, no namespace.
|
|
{
|
|
R"cpp(
|
|
#include "test.hpp"
|
|
|
|
void fun() {
|
|
on^e::t^wo::e^e inst;
|
|
})cpp",
|
|
R"cpp(
|
|
#include "test.hpp"
|
|
|
|
using one::two::ee;
|
|
|
|
void fun() {
|
|
ee inst;
|
|
})cpp"},
|
|
// Function, other usings.
|
|
{
|
|
R"cpp(
|
|
#include "test.hpp"
|
|
|
|
using one::two::cc;
|
|
using one::two::ee;
|
|
|
|
namespace {
|
|
void fun() {
|
|
one::two::f^f();
|
|
}
|
|
})cpp",
|
|
R"cpp(
|
|
#include "test.hpp"
|
|
|
|
using one::two::cc;
|
|
using one::two::ff;using one::two::ee;
|
|
|
|
namespace {
|
|
void fun() {
|
|
ff();
|
|
}
|
|
})cpp",
|
|
},
|
|
// Function, other usings inside namespace.
|
|
{
|
|
R"cpp(
|
|
#include "test.hpp"
|
|
|
|
using one::two::cc;
|
|
|
|
namespace {
|
|
|
|
using one::two::ff;
|
|
|
|
void fun() {
|
|
o^ne::o^o();
|
|
}
|
|
})cpp",
|
|
R"cpp(
|
|
#include "test.hpp"
|
|
|
|
using one::two::cc;
|
|
|
|
namespace {
|
|
|
|
using one::oo;using one::two::ff;
|
|
|
|
void fun() {
|
|
oo();
|
|
}
|
|
})cpp"},
|
|
// Using comes after cursor.
|
|
{
|
|
R"cpp(
|
|
#include "test.hpp"
|
|
|
|
namespace {
|
|
|
|
void fun() {
|
|
one::t^wo::ff();
|
|
}
|
|
|
|
using one::two::cc;
|
|
|
|
})cpp",
|
|
R"cpp(
|
|
#include "test.hpp"
|
|
|
|
namespace {using one::two::ff;
|
|
|
|
|
|
void fun() {
|
|
ff();
|
|
}
|
|
|
|
using one::two::cc;
|
|
|
|
})cpp"},
|
|
// Pointer type.
|
|
{R"cpp(
|
|
#include "test.hpp"
|
|
|
|
void fun() {
|
|
one::two::c^c *p;
|
|
})cpp",
|
|
R"cpp(
|
|
#include "test.hpp"
|
|
|
|
using one::two::cc;
|
|
|
|
void fun() {
|
|
cc *p;
|
|
})cpp"},
|
|
// Namespace declared via macro.
|
|
{R"cpp(
|
|
#include "test.hpp"
|
|
#define NS_BEGIN(name) namespace name {
|
|
|
|
NS_BEGIN(foo)
|
|
|
|
void fun() {
|
|
one::two::f^f();
|
|
}
|
|
})cpp",
|
|
R"cpp(
|
|
#include "test.hpp"
|
|
#define NS_BEGIN(name) namespace name {
|
|
|
|
using one::two::ff;
|
|
|
|
NS_BEGIN(foo)
|
|
|
|
void fun() {
|
|
ff();
|
|
}
|
|
})cpp"},
|
|
// Inside macro argument.
|
|
{R"cpp(
|
|
#include "test.hpp"
|
|
#define CALL(name) name()
|
|
|
|
void fun() {
|
|
CALL(one::t^wo::ff);
|
|
})cpp",
|
|
R"cpp(
|
|
#include "test.hpp"
|
|
#define CALL(name) name()
|
|
|
|
using one::two::ff;
|
|
|
|
void fun() {
|
|
CALL(ff);
|
|
})cpp"},
|
|
// Parent namespace != lexical parent namespace
|
|
{R"cpp(
|
|
#include "test.hpp"
|
|
namespace foo { void fun(); }
|
|
|
|
void foo::fun() {
|
|
one::two::f^f();
|
|
})cpp",
|
|
R"cpp(
|
|
#include "test.hpp"
|
|
using one::two::ff;
|
|
|
|
namespace foo { void fun(); }
|
|
|
|
void foo::fun() {
|
|
ff();
|
|
})cpp"},
|
|
// If all other using are fully qualified, add ::
|
|
{R"cpp(
|
|
#include "test.hpp"
|
|
|
|
using ::one::two::cc;
|
|
using ::one::two::ee;
|
|
|
|
void fun() {
|
|
one::two::f^f();
|
|
})cpp",
|
|
R"cpp(
|
|
#include "test.hpp"
|
|
|
|
using ::one::two::cc;
|
|
using ::one::two::ff;using ::one::two::ee;
|
|
|
|
void fun() {
|
|
ff();
|
|
})cpp"},
|
|
// Make sure we don't add :: if it's already there
|
|
{R"cpp(
|
|
#include "test.hpp"
|
|
|
|
using ::one::two::cc;
|
|
using ::one::two::ee;
|
|
|
|
void fun() {
|
|
::one::two::f^f();
|
|
})cpp",
|
|
R"cpp(
|
|
#include "test.hpp"
|
|
|
|
using ::one::two::cc;
|
|
using ::one::two::ff;using ::one::two::ee;
|
|
|
|
void fun() {
|
|
ff();
|
|
})cpp"},
|
|
// If even one using doesn't start with ::, do not add it
|
|
{R"cpp(
|
|
#include "test.hpp"
|
|
|
|
using ::one::two::cc;
|
|
using one::two::ee;
|
|
|
|
void fun() {
|
|
one::two::f^f();
|
|
})cpp",
|
|
R"cpp(
|
|
#include "test.hpp"
|
|
|
|
using ::one::two::cc;
|
|
using one::two::ff;using one::two::ee;
|
|
|
|
void fun() {
|
|
ff();
|
|
})cpp"},
|
|
// using alias; insert using for the spelled name.
|
|
{R"cpp(
|
|
#include "test.hpp"
|
|
|
|
void fun() {
|
|
one::u^u u;
|
|
})cpp",
|
|
R"cpp(
|
|
#include "test.hpp"
|
|
|
|
using one::uu;
|
|
|
|
void fun() {
|
|
uu u;
|
|
})cpp"},
|
|
// using namespace.
|
|
{R"cpp(
|
|
#include "test.hpp"
|
|
using namespace one;
|
|
namespace {
|
|
two::c^c C;
|
|
})cpp",
|
|
R"cpp(
|
|
#include "test.hpp"
|
|
using namespace one;
|
|
namespace {using two::cc;
|
|
|
|
cc C;
|
|
})cpp"},
|
|
// Type defined in main file, make sure using is after that.
|
|
{R"cpp(
|
|
namespace xx {
|
|
struct yy {};
|
|
}
|
|
|
|
x^x::yy X;
|
|
)cpp",
|
|
R"cpp(
|
|
namespace xx {
|
|
struct yy {};
|
|
}
|
|
|
|
using xx::yy;
|
|
|
|
yy X;
|
|
)cpp"},
|
|
// Type defined in main file via "using", insert after that.
|
|
{R"cpp(
|
|
#include "test.hpp"
|
|
|
|
namespace xx {
|
|
using yy = one::two::cc;
|
|
}
|
|
|
|
x^x::yy X;
|
|
)cpp",
|
|
R"cpp(
|
|
#include "test.hpp"
|
|
|
|
namespace xx {
|
|
using yy = one::two::cc;
|
|
}
|
|
|
|
using xx::yy;
|
|
|
|
yy X;
|
|
)cpp"},
|
|
// Using must come after function definition.
|
|
{R"cpp(
|
|
namespace xx {
|
|
void yy();
|
|
}
|
|
|
|
void fun() {
|
|
x^x::yy();
|
|
}
|
|
)cpp",
|
|
R"cpp(
|
|
namespace xx {
|
|
void yy();
|
|
}
|
|
|
|
using xx::yy;
|
|
|
|
void fun() {
|
|
yy();
|
|
}
|
|
)cpp"},
|
|
// Existing using with non-namespace part.
|
|
{R"cpp(
|
|
#include "test.hpp"
|
|
using one::two::ee::ee_one;
|
|
one::t^wo::cc c;
|
|
)cpp",
|
|
R"cpp(
|
|
#include "test.hpp"
|
|
using one::two::cc;using one::two::ee::ee_one;
|
|
cc c;
|
|
)cpp"}};
|
|
llvm::StringMap<std::string> EditedFiles;
|
|
for (const auto &Case : Cases) {
|
|
for (const auto &SubCase : expandCases(Case.TestSource)) {
|
|
ExtraFiles["test.hpp"] = R"cpp(
|
|
namespace one {
|
|
void oo() {}
|
|
namespace two {
|
|
enum ee {ee_one};
|
|
void ff() {}
|
|
class cc {
|
|
public:
|
|
struct st { struct nested {}; };
|
|
static void mm() {}
|
|
};
|
|
}
|
|
using uu = two::cc;
|
|
})cpp";
|
|
EXPECT_EQ(apply(SubCase, &EditedFiles), Case.ExpectedSource);
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace clangd
|
|
} // namespace clang
|