forked from OSchip/llvm-project
[clangd] Split tweak tests into one file per tweak.
No changes to the tests themselves, other than some auto -> const auto diagnostic fixes and formatting. Differential Revision: https://reviews.llvm.org/D92939
This commit is contained in:
parent
1a2147fead
commit
5934a79196
|
@ -93,8 +93,6 @@ add_unittest(ClangdUnitTests ClangdTests
|
|||
TestTU.cpp
|
||||
TestWorkspace.cpp
|
||||
TypeHierarchyTests.cpp
|
||||
TweakTests.cpp
|
||||
TweakTesting.cpp
|
||||
URITests.cpp
|
||||
XRefsTests.cpp
|
||||
${CMAKE_CURRENT_BINARY_DIR}/DecisionForestRuntimeTest.cpp
|
||||
|
@ -108,6 +106,26 @@ add_unittest(ClangdUnitTests ClangdTests
|
|||
support/TestTracer.cpp
|
||||
support/TraceTests.cpp
|
||||
|
||||
tweaks/AddUsingTests.cpp
|
||||
tweaks/AnnotateHighlightingsTests.cpp
|
||||
tweaks/DefineInlineTests.cpp
|
||||
tweaks/DefineOutlineTests.cpp
|
||||
tweaks/DumpASTTests.cpp
|
||||
tweaks/DumpRecordLayoutTests.cpp
|
||||
tweaks/DumpSymbolTests.cpp
|
||||
tweaks/ExpandAutoTypeTests.cpp
|
||||
tweaks/ExpandMacroTests.cpp
|
||||
tweaks/ExtractFunctionTests.cpp
|
||||
tweaks/ExtractVariableTests.cpp
|
||||
tweaks/ObjCLocalizeStringLiteralTests.cpp
|
||||
tweaks/PopulateSwitchTests.cpp
|
||||
tweaks/RawStringLiteralTests.cpp
|
||||
tweaks/RemoveUsingNamespaceTests.cpp
|
||||
tweaks/ShowSelectionTreeTests.cpp
|
||||
tweaks/SwapIfBranchesTests.cpp
|
||||
tweaks/TweakTesting.cpp
|
||||
tweaks/TweakTests.cpp
|
||||
|
||||
${REMOTE_TEST_SOURCES}
|
||||
|
||||
$<TARGET_OBJECTS:obj.clangDaemonTweaks>
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,472 @@
|
|||
//===-- 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
|
|
@ -0,0 +1,34 @@
|
|||
//===-- AnnotateHighlightingsTests.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 "gtest/gtest.h"
|
||||
|
||||
namespace clang {
|
||||
namespace clangd {
|
||||
namespace {
|
||||
|
||||
TWEAK_TEST(AnnotateHighlightings);
|
||||
|
||||
TEST_F(AnnotateHighlightingsTest, Test) {
|
||||
EXPECT_AVAILABLE("^vo^id^ ^f(^) {^}^"); // available everywhere.
|
||||
EXPECT_AVAILABLE("[[int a; int b;]]");
|
||||
EXPECT_EQ("void /* entity.name.function.cpp */f() {}", apply("void ^f() {}"));
|
||||
|
||||
EXPECT_EQ(apply("[[void f1(); void f2();]]"),
|
||||
"void /* entity.name.function.cpp */f1(); "
|
||||
"void /* entity.name.function.cpp */f2();");
|
||||
|
||||
EXPECT_EQ(apply("void f1(); void f2() {^}"),
|
||||
"void f1(); "
|
||||
"void /* entity.name.function.cpp */f2() {}");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace clangd
|
||||
} // namespace clang
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,541 @@
|
|||
//===-- DefineOutline.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 "TestTU.h"
|
||||
#include "TweakTesting.h"
|
||||
#include "gmock/gmock-matchers.h"
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using ::testing::ElementsAre;
|
||||
|
||||
namespace clang {
|
||||
namespace clangd {
|
||||
namespace {
|
||||
|
||||
TWEAK_TEST(DefineOutline);
|
||||
|
||||
TEST_F(DefineOutlineTest, TriggersOnFunctionDecl) {
|
||||
FileName = "Test.cpp";
|
||||
// Not available unless in a header file.
|
||||
EXPECT_UNAVAILABLE(R"cpp(
|
||||
[[void [[f^o^o]]() [[{
|
||||
return;
|
||||
}]]]])cpp");
|
||||
|
||||
FileName = "Test.hpp";
|
||||
// Not available unless function name or fully body is selected.
|
||||
EXPECT_UNAVAILABLE(R"cpp(
|
||||
// Not a definition
|
||||
vo^i[[d^ ^f]]^oo();
|
||||
|
||||
[[vo^id ]]foo[[()]] {[[
|
||||
[[(void)(5+3);
|
||||
return;]]
|
||||
}]])cpp");
|
||||
|
||||
// Available even if there are no implementation files.
|
||||
EXPECT_AVAILABLE(R"cpp(
|
||||
[[void [[f^o^o]]() [[{
|
||||
return;
|
||||
}]]]])cpp");
|
||||
|
||||
// Not available for out-of-line methods.
|
||||
EXPECT_UNAVAILABLE(R"cpp(
|
||||
class Bar {
|
||||
void baz();
|
||||
};
|
||||
|
||||
[[void [[Bar::[[b^a^z]]]]() [[{
|
||||
return;
|
||||
}]]]])cpp");
|
||||
|
||||
// Basic check for function body and signature.
|
||||
EXPECT_AVAILABLE(R"cpp(
|
||||
class Bar {
|
||||
[[void [[f^o^o^]]() [[{ return; }]]]]
|
||||
};
|
||||
|
||||
void foo();
|
||||
[[void [[f^o^o]]() [[{
|
||||
return;
|
||||
}]]]])cpp");
|
||||
|
||||
// Not available on defaulted/deleted members.
|
||||
EXPECT_UNAVAILABLE(R"cpp(
|
||||
class Foo {
|
||||
Fo^o() = default;
|
||||
F^oo(const Foo&) = delete;
|
||||
};)cpp");
|
||||
|
||||
// Not available within templated classes, as it is hard to spell class name
|
||||
// out-of-line in such cases.
|
||||
EXPECT_UNAVAILABLE(R"cpp(
|
||||
template <typename> struct Foo { void fo^o(){} };
|
||||
)cpp");
|
||||
|
||||
// Not available on function templates and specializations, as definition must
|
||||
// be visible to all translation units.
|
||||
EXPECT_UNAVAILABLE(R"cpp(
|
||||
template <typename> void fo^o() {};
|
||||
template <> void fo^o<int>() {};
|
||||
)cpp");
|
||||
}
|
||||
|
||||
TEST_F(DefineOutlineTest, FailsWithoutSource) {
|
||||
FileName = "Test.hpp";
|
||||
llvm::StringRef Test = "void fo^o() { return; }";
|
||||
llvm::StringRef Expected =
|
||||
"fail: Couldn't find a suitable implementation file.";
|
||||
EXPECT_EQ(apply(Test), Expected);
|
||||
}
|
||||
|
||||
TEST_F(DefineOutlineTest, ApplyTest) {
|
||||
llvm::StringMap<std::string> EditedFiles;
|
||||
ExtraFiles["Test.cpp"] = "";
|
||||
FileName = "Test.hpp";
|
||||
// Template body is not parsed until instantiation time on windows, which
|
||||
// results in arbitrary failures as function body becomes NULL.
|
||||
ExtraArgs.push_back("-fno-delayed-template-parsing");
|
||||
|
||||
struct {
|
||||
llvm::StringRef Test;
|
||||
llvm::StringRef ExpectedHeader;
|
||||
llvm::StringRef ExpectedSource;
|
||||
} Cases[] = {
|
||||
// Simple check
|
||||
{
|
||||
"void fo^o() { return; }",
|
||||
"void foo() ;",
|
||||
"void foo() { return; }",
|
||||
},
|
||||
// Default args.
|
||||
{
|
||||
"void fo^o(int x, int y = 5, int = 2, int (*foo)(int) = nullptr) {}",
|
||||
"void foo(int x, int y = 5, int = 2, int (*foo)(int) = nullptr) ;",
|
||||
"void foo(int x, int y , int , int (*foo)(int) ) {}",
|
||||
},
|
||||
// Constructors
|
||||
{
|
||||
R"cpp(
|
||||
class Foo {public: Foo(); Foo(int);};
|
||||
class Bar {
|
||||
Ba^r() {}
|
||||
Bar(int x) : f1(x) {}
|
||||
Foo f1;
|
||||
Foo f2 = 2;
|
||||
};)cpp",
|
||||
R"cpp(
|
||||
class Foo {public: Foo(); Foo(int);};
|
||||
class Bar {
|
||||
Bar() ;
|
||||
Bar(int x) : f1(x) {}
|
||||
Foo f1;
|
||||
Foo f2 = 2;
|
||||
};)cpp",
|
||||
"Bar::Bar() {}\n",
|
||||
},
|
||||
// Ctor with initializer.
|
||||
{
|
||||
R"cpp(
|
||||
class Foo {public: Foo(); Foo(int);};
|
||||
class Bar {
|
||||
Bar() {}
|
||||
B^ar(int x) : f1(x), f2(3) {}
|
||||
Foo f1;
|
||||
Foo f2 = 2;
|
||||
};)cpp",
|
||||
R"cpp(
|
||||
class Foo {public: Foo(); Foo(int);};
|
||||
class Bar {
|
||||
Bar() {}
|
||||
Bar(int x) ;
|
||||
Foo f1;
|
||||
Foo f2 = 2;
|
||||
};)cpp",
|
||||
"Bar::Bar(int x) : f1(x), f2(3) {}\n",
|
||||
},
|
||||
// Ctor initializer with attribute.
|
||||
{
|
||||
R"cpp(
|
||||
class Foo {
|
||||
F^oo(int z) __attribute__((weak)) : bar(2){}
|
||||
int bar;
|
||||
};)cpp",
|
||||
R"cpp(
|
||||
class Foo {
|
||||
Foo(int z) __attribute__((weak)) ;
|
||||
int bar;
|
||||
};)cpp",
|
||||
"Foo::Foo(int z) __attribute__((weak)) : bar(2){}\n",
|
||||
},
|
||||
// Virt specifiers.
|
||||
{
|
||||
R"cpp(
|
||||
struct A {
|
||||
virtual void f^oo() {}
|
||||
};)cpp",
|
||||
R"cpp(
|
||||
struct A {
|
||||
virtual void foo() ;
|
||||
};)cpp",
|
||||
" void A::foo() {}\n",
|
||||
},
|
||||
{
|
||||
R"cpp(
|
||||
struct A {
|
||||
virtual virtual void virtual f^oo() {}
|
||||
};)cpp",
|
||||
R"cpp(
|
||||
struct A {
|
||||
virtual virtual void virtual foo() ;
|
||||
};)cpp",
|
||||
" void A::foo() {}\n",
|
||||
},
|
||||
{
|
||||
R"cpp(
|
||||
struct A {
|
||||
virtual void foo() = 0;
|
||||
};
|
||||
struct B : A {
|
||||
void fo^o() override {}
|
||||
};)cpp",
|
||||
R"cpp(
|
||||
struct A {
|
||||
virtual void foo() = 0;
|
||||
};
|
||||
struct B : A {
|
||||
void foo() override ;
|
||||
};)cpp",
|
||||
"void B::foo() {}\n",
|
||||
},
|
||||
{
|
||||
R"cpp(
|
||||
struct A {
|
||||
virtual void foo() = 0;
|
||||
};
|
||||
struct B : A {
|
||||
void fo^o() final {}
|
||||
};)cpp",
|
||||
R"cpp(
|
||||
struct A {
|
||||
virtual void foo() = 0;
|
||||
};
|
||||
struct B : A {
|
||||
void foo() final ;
|
||||
};)cpp",
|
||||
"void B::foo() {}\n",
|
||||
},
|
||||
{
|
||||
R"cpp(
|
||||
struct A {
|
||||
virtual void foo() = 0;
|
||||
};
|
||||
struct B : A {
|
||||
void fo^o() final override {}
|
||||
};)cpp",
|
||||
R"cpp(
|
||||
struct A {
|
||||
virtual void foo() = 0;
|
||||
};
|
||||
struct B : A {
|
||||
void foo() final override ;
|
||||
};)cpp",
|
||||
"void B::foo() {}\n",
|
||||
},
|
||||
{
|
||||
R"cpp(
|
||||
struct A {
|
||||
static void fo^o() {}
|
||||
};)cpp",
|
||||
R"cpp(
|
||||
struct A {
|
||||
static void foo() ;
|
||||
};)cpp",
|
||||
" void A::foo() {}\n",
|
||||
},
|
||||
{
|
||||
R"cpp(
|
||||
struct A {
|
||||
static static void fo^o() {}
|
||||
};)cpp",
|
||||
R"cpp(
|
||||
struct A {
|
||||
static static void foo() ;
|
||||
};)cpp",
|
||||
" void A::foo() {}\n",
|
||||
},
|
||||
};
|
||||
for (const auto &Case : Cases) {
|
||||
SCOPED_TRACE(Case.Test);
|
||||
EXPECT_EQ(apply(Case.Test, &EditedFiles), Case.ExpectedHeader);
|
||||
EXPECT_THAT(EditedFiles, testing::ElementsAre(FileWithContents(
|
||||
testPath("Test.cpp"), Case.ExpectedSource)));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DefineOutlineTest, HandleMacros) {
|
||||
llvm::StringMap<std::string> EditedFiles;
|
||||
ExtraFiles["Test.cpp"] = "";
|
||||
FileName = "Test.hpp";
|
||||
ExtraArgs.push_back("-DVIRTUAL=virtual");
|
||||
ExtraArgs.push_back("-DOVER=override");
|
||||
|
||||
struct {
|
||||
llvm::StringRef Test;
|
||||
llvm::StringRef ExpectedHeader;
|
||||
llvm::StringRef ExpectedSource;
|
||||
} Cases[] = {
|
||||
{R"cpp(
|
||||
#define BODY { return; }
|
||||
void f^oo()BODY)cpp",
|
||||
R"cpp(
|
||||
#define BODY { return; }
|
||||
void foo();)cpp",
|
||||
"void foo()BODY"},
|
||||
|
||||
{R"cpp(
|
||||
#define BODY return;
|
||||
void f^oo(){BODY})cpp",
|
||||
R"cpp(
|
||||
#define BODY return;
|
||||
void foo();)cpp",
|
||||
"void foo(){BODY}"},
|
||||
|
||||
{R"cpp(
|
||||
#define TARGET void foo()
|
||||
[[TARGET]]{ return; })cpp",
|
||||
R"cpp(
|
||||
#define TARGET void foo()
|
||||
TARGET;)cpp",
|
||||
"TARGET{ return; }"},
|
||||
|
||||
{R"cpp(
|
||||
#define TARGET foo
|
||||
void [[TARGET]](){ return; })cpp",
|
||||
R"cpp(
|
||||
#define TARGET foo
|
||||
void TARGET();)cpp",
|
||||
"void TARGET(){ return; }"},
|
||||
{R"cpp(#define VIRT virtual
|
||||
struct A {
|
||||
VIRT void f^oo() {}
|
||||
};)cpp",
|
||||
R"cpp(#define VIRT virtual
|
||||
struct A {
|
||||
VIRT void foo() ;
|
||||
};)cpp",
|
||||
" void A::foo() {}\n"},
|
||||
{R"cpp(
|
||||
struct A {
|
||||
VIRTUAL void f^oo() {}
|
||||
};)cpp",
|
||||
R"cpp(
|
||||
struct A {
|
||||
VIRTUAL void foo() ;
|
||||
};)cpp",
|
||||
" void A::foo() {}\n"},
|
||||
{R"cpp(
|
||||
struct A {
|
||||
virtual void foo() = 0;
|
||||
};
|
||||
struct B : A {
|
||||
void fo^o() OVER {}
|
||||
};)cpp",
|
||||
R"cpp(
|
||||
struct A {
|
||||
virtual void foo() = 0;
|
||||
};
|
||||
struct B : A {
|
||||
void foo() OVER ;
|
||||
};)cpp",
|
||||
"void B::foo() {}\n"},
|
||||
{R"cpp(#define STUPID_MACRO(X) virtual
|
||||
struct A {
|
||||
STUPID_MACRO(sizeof sizeof int) void f^oo() {}
|
||||
};)cpp",
|
||||
R"cpp(#define STUPID_MACRO(X) virtual
|
||||
struct A {
|
||||
STUPID_MACRO(sizeof sizeof int) void foo() ;
|
||||
};)cpp",
|
||||
" void A::foo() {}\n"},
|
||||
{R"cpp(#define STAT static
|
||||
struct A {
|
||||
STAT void f^oo() {}
|
||||
};)cpp",
|
||||
R"cpp(#define STAT static
|
||||
struct A {
|
||||
STAT void foo() ;
|
||||
};)cpp",
|
||||
" void A::foo() {}\n"},
|
||||
{R"cpp(#define STUPID_MACRO(X) static
|
||||
struct A {
|
||||
STUPID_MACRO(sizeof sizeof int) void f^oo() {}
|
||||
};)cpp",
|
||||
R"cpp(#define STUPID_MACRO(X) static
|
||||
struct A {
|
||||
STUPID_MACRO(sizeof sizeof int) void foo() ;
|
||||
};)cpp",
|
||||
" void A::foo() {}\n"},
|
||||
};
|
||||
for (const auto &Case : Cases) {
|
||||
SCOPED_TRACE(Case.Test);
|
||||
EXPECT_EQ(apply(Case.Test, &EditedFiles), Case.ExpectedHeader);
|
||||
EXPECT_THAT(EditedFiles, testing::ElementsAre(FileWithContents(
|
||||
testPath("Test.cpp"), Case.ExpectedSource)));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DefineOutlineTest, QualifyReturnValue) {
|
||||
FileName = "Test.hpp";
|
||||
ExtraFiles["Test.cpp"] = "";
|
||||
|
||||
struct {
|
||||
llvm::StringRef Test;
|
||||
llvm::StringRef ExpectedHeader;
|
||||
llvm::StringRef ExpectedSource;
|
||||
} Cases[] = {
|
||||
{R"cpp(
|
||||
namespace a { class Foo{}; }
|
||||
using namespace a;
|
||||
Foo fo^o() { return {}; })cpp",
|
||||
R"cpp(
|
||||
namespace a { class Foo{}; }
|
||||
using namespace a;
|
||||
Foo foo() ;)cpp",
|
||||
"a::Foo foo() { return {}; }"},
|
||||
{R"cpp(
|
||||
namespace a {
|
||||
class Foo {
|
||||
class Bar {};
|
||||
Bar fo^o() { return {}; }
|
||||
};
|
||||
})cpp",
|
||||
R"cpp(
|
||||
namespace a {
|
||||
class Foo {
|
||||
class Bar {};
|
||||
Bar foo() ;
|
||||
};
|
||||
})cpp",
|
||||
"a::Foo::Bar a::Foo::foo() { return {}; }\n"},
|
||||
{R"cpp(
|
||||
class Foo {};
|
||||
Foo fo^o() { return {}; })cpp",
|
||||
R"cpp(
|
||||
class Foo {};
|
||||
Foo foo() ;)cpp",
|
||||
"Foo foo() { return {}; }"},
|
||||
};
|
||||
llvm::StringMap<std::string> EditedFiles;
|
||||
for (auto &Case : Cases) {
|
||||
apply(Case.Test, &EditedFiles);
|
||||
EXPECT_EQ(apply(Case.Test, &EditedFiles), Case.ExpectedHeader);
|
||||
EXPECT_THAT(EditedFiles, testing::ElementsAre(FileWithContents(
|
||||
testPath("Test.cpp"), Case.ExpectedSource)));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DefineOutlineTest, QualifyFunctionName) {
|
||||
FileName = "Test.hpp";
|
||||
struct {
|
||||
llvm::StringRef TestHeader;
|
||||
llvm::StringRef TestSource;
|
||||
llvm::StringRef ExpectedHeader;
|
||||
llvm::StringRef ExpectedSource;
|
||||
} Cases[] = {
|
||||
{
|
||||
R"cpp(
|
||||
namespace a {
|
||||
namespace b {
|
||||
class Foo {
|
||||
void fo^o() {}
|
||||
};
|
||||
}
|
||||
})cpp",
|
||||
"",
|
||||
R"cpp(
|
||||
namespace a {
|
||||
namespace b {
|
||||
class Foo {
|
||||
void foo() ;
|
||||
};
|
||||
}
|
||||
})cpp",
|
||||
"void a::b::Foo::foo() {}\n",
|
||||
},
|
||||
{
|
||||
"namespace a { namespace b { void f^oo() {} } }",
|
||||
"namespace a{}",
|
||||
"namespace a { namespace b { void foo() ; } }",
|
||||
"namespace a{void b::foo() {} }",
|
||||
},
|
||||
{
|
||||
"namespace a { namespace b { void f^oo() {} } }",
|
||||
"using namespace a;",
|
||||
"namespace a { namespace b { void foo() ; } }",
|
||||
// FIXME: Take using namespace directives in the source file into
|
||||
// account. This can be spelled as b::foo instead.
|
||||
"using namespace a;void a::b::foo() {} ",
|
||||
},
|
||||
};
|
||||
llvm::StringMap<std::string> EditedFiles;
|
||||
for (auto &Case : Cases) {
|
||||
ExtraFiles["Test.cpp"] = std::string(Case.TestSource);
|
||||
EXPECT_EQ(apply(Case.TestHeader, &EditedFiles), Case.ExpectedHeader);
|
||||
EXPECT_THAT(EditedFiles, testing::ElementsAre(FileWithContents(
|
||||
testPath("Test.cpp"), Case.ExpectedSource)))
|
||||
<< Case.TestHeader;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DefineOutlineTest, FailsMacroSpecifier) {
|
||||
FileName = "Test.hpp";
|
||||
ExtraFiles["Test.cpp"] = "";
|
||||
ExtraArgs.push_back("-DFINALOVER=final override");
|
||||
|
||||
std::pair<StringRef, StringRef> Cases[] = {
|
||||
{
|
||||
R"cpp(
|
||||
#define VIRT virtual void
|
||||
struct A {
|
||||
VIRT fo^o() {}
|
||||
};)cpp",
|
||||
"fail: define outline: couldn't remove `virtual` keyword."},
|
||||
{
|
||||
R"cpp(
|
||||
#define OVERFINAL final override
|
||||
struct A {
|
||||
virtual void foo() {}
|
||||
};
|
||||
struct B : A {
|
||||
void fo^o() OVERFINAL {}
|
||||
};)cpp",
|
||||
"fail: define outline: Can't move out of line as function has a "
|
||||
"macro `override` specifier.\ndefine outline: Can't move out of line "
|
||||
"as function has a macro `final` specifier."},
|
||||
{
|
||||
R"cpp(
|
||||
struct A {
|
||||
virtual void foo() {}
|
||||
};
|
||||
struct B : A {
|
||||
void fo^o() FINALOVER {}
|
||||
};)cpp",
|
||||
"fail: define outline: Can't move out of line as function has a "
|
||||
"macro `override` specifier.\ndefine outline: Can't move out of line "
|
||||
"as function has a macro `final` specifier."},
|
||||
};
|
||||
for (const auto &Case : Cases) {
|
||||
EXPECT_EQ(apply(Case.first), Case.second);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace clangd
|
||||
} // namespace clang
|
|
@ -0,0 +1,36 @@
|
|||
//===-- DumpASTTests.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 "gmock/gmock-matchers.h"
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using ::testing::AllOf;
|
||||
using ::testing::HasSubstr;
|
||||
using ::testing::StartsWith;
|
||||
|
||||
namespace clang {
|
||||
namespace clangd {
|
||||
namespace {
|
||||
|
||||
TWEAK_TEST(DumpAST);
|
||||
|
||||
TEST_F(DumpASTTest, Test) {
|
||||
EXPECT_AVAILABLE("^int f^oo() { re^turn 2 ^+ 2; }");
|
||||
EXPECT_UNAVAILABLE("/*c^omment*/ int foo() { return 2 ^ + 2; }");
|
||||
EXPECT_THAT(apply("int x = 2 ^+ 2;"),
|
||||
AllOf(StartsWith("message:"), HasSubstr("BinaryOperator"),
|
||||
HasSubstr("'+'"), HasSubstr("|-IntegerLiteral"),
|
||||
HasSubstr("<col:9> 'int' 2\n`-IntegerLiteral"),
|
||||
HasSubstr("<col:13> 'int' 2")));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace clangd
|
||||
} // namespace clang
|
|
@ -0,0 +1,37 @@
|
|||
//===-- DumpRecordLayoutTests.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 "gmock/gmock-matchers.h"
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using ::testing::AllOf;
|
||||
using ::testing::HasSubstr;
|
||||
using ::testing::StartsWith;
|
||||
|
||||
namespace clang {
|
||||
namespace clangd {
|
||||
namespace {
|
||||
|
||||
TWEAK_TEST(DumpRecordLayout);
|
||||
|
||||
TEST_F(DumpRecordLayoutTest, Test) {
|
||||
EXPECT_AVAILABLE("^s^truct ^X ^{ int x; ^};");
|
||||
EXPECT_THAT("struct X { int ^a; };", Not(isAvailable()));
|
||||
EXPECT_THAT("struct ^X;", Not(isAvailable()));
|
||||
EXPECT_THAT("template <typename T> struct ^X { T t; };", Not(isAvailable()));
|
||||
EXPECT_THAT("enum ^X {};", Not(isAvailable()));
|
||||
|
||||
EXPECT_THAT(apply("struct ^X { int x; int y; };"),
|
||||
AllOf(StartsWith("message:"), HasSubstr("0 | int x")));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace clangd
|
||||
} // namespace clang
|
|
@ -0,0 +1,35 @@
|
|||
//===-- DumpSymbolTests.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 "gmock/gmock-matchers.h"
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include <string>
|
||||
|
||||
using ::testing::AllOf;
|
||||
using ::testing::HasSubstr;
|
||||
using ::testing::StartsWith;
|
||||
|
||||
namespace clang {
|
||||
namespace clangd {
|
||||
namespace {
|
||||
|
||||
TWEAK_TEST(DumpSymbol);
|
||||
|
||||
TEST_F(DumpSymbolTest, Test) {
|
||||
std::string ID = R"("id":"CA2EBE44A1D76D2A")";
|
||||
std::string USR = R"("usr":"c:@F@foo#")";
|
||||
EXPECT_THAT(apply("void f^oo();"),
|
||||
AllOf(StartsWith("message:"), testing::HasSubstr(ID),
|
||||
testing::HasSubstr(USR)));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace clangd
|
||||
} // namespace clang
|
|
@ -0,0 +1,82 @@
|
|||
//===-- ExpandAutoTypeTests.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 "TestTU.h"
|
||||
#include "TweakTesting.h"
|
||||
#include "gmock/gmock-matchers.h"
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using ::testing::StartsWith;
|
||||
|
||||
namespace clang {
|
||||
namespace clangd {
|
||||
namespace {
|
||||
|
||||
TWEAK_TEST(ExpandAutoType);
|
||||
|
||||
TEST_F(ExpandAutoTypeTest, Test) {
|
||||
Header = R"cpp(
|
||||
namespace ns {
|
||||
struct Class {
|
||||
struct Nested {};
|
||||
};
|
||||
void Func();
|
||||
}
|
||||
inline namespace inl_ns {
|
||||
namespace {
|
||||
struct Visible {};
|
||||
}
|
||||
}
|
||||
)cpp";
|
||||
|
||||
EXPECT_AVAILABLE("^a^u^t^o^ i = 0;");
|
||||
EXPECT_UNAVAILABLE("auto ^i^ ^=^ ^0^;^");
|
||||
|
||||
// check primitive type
|
||||
EXPECT_EQ(apply("[[auto]] i = 0;"), "int i = 0;");
|
||||
EXPECT_EQ(apply("au^to i = 0;"), "int i = 0;");
|
||||
// check classes and namespaces
|
||||
EXPECT_EQ(apply("^auto C = ns::Class::Nested();"),
|
||||
"ns::Class::Nested C = ns::Class::Nested();");
|
||||
// check that namespaces are shortened
|
||||
EXPECT_EQ(apply("namespace ns { void f() { ^auto C = Class(); } }"),
|
||||
"namespace ns { void f() { Class C = Class(); } }");
|
||||
// undefined functions should not be replaced
|
||||
EXPECT_THAT(apply("au^to x = doesnt_exist(); // error-ok"),
|
||||
StartsWith("fail: Could not deduce type for 'auto' type"));
|
||||
// function pointers should not be replaced
|
||||
EXPECT_THAT(apply("au^to x = &ns::Func;"),
|
||||
StartsWith("fail: Could not expand type of function pointer"));
|
||||
// lambda types are not replaced
|
||||
EXPECT_UNAVAILABLE("au^to x = []{};");
|
||||
// inline namespaces
|
||||
EXPECT_EQ(apply("au^to x = inl_ns::Visible();"),
|
||||
"Visible x = inl_ns::Visible();");
|
||||
// local class
|
||||
EXPECT_EQ(apply("namespace x { void y() { struct S{}; ^auto z = S(); } }"),
|
||||
"namespace x { void y() { struct S{}; S z = S(); } }");
|
||||
// replace array types
|
||||
EXPECT_EQ(apply(R"cpp(au^to x = "test";)cpp"),
|
||||
R"cpp(const char * x = "test";)cpp");
|
||||
|
||||
EXPECT_UNAVAILABLE("dec^ltype(au^to) x = 10;");
|
||||
// expanding types in structured bindings is syntactically invalid.
|
||||
EXPECT_UNAVAILABLE("const ^auto &[x,y] = (int[]){1,2};");
|
||||
|
||||
// FIXME: Auto-completion in a template requires disabling delayed template
|
||||
// parsing.
|
||||
ExtraArgs.push_back("-fno-delayed-template-parsing");
|
||||
// unknown types in a template should not be replaced
|
||||
EXPECT_THAT(apply("template <typename T> void x() { ^auto y = T::z(); }"),
|
||||
StartsWith("fail: Could not deduce type for 'auto' type"));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace clangd
|
||||
} // namespace clang
|
|
@ -0,0 +1,55 @@
|
|||
//===-- ExpandMacroTests.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 "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(ExpandMacro);
|
||||
|
||||
TEST_F(ExpandMacroTest, Test) {
|
||||
Header = R"cpp(
|
||||
// error-ok: not real c++, just token manipulation
|
||||
#define FOO 1 2 3
|
||||
#define FUNC(X) X+X+X
|
||||
#define EMPTY
|
||||
#define EMPTY_FN(X)
|
||||
)cpp";
|
||||
|
||||
// Available on macro names, not available anywhere else.
|
||||
EXPECT_AVAILABLE("^F^O^O^ BAR ^F^O^O^");
|
||||
EXPECT_AVAILABLE("^F^U^N^C^(1)");
|
||||
EXPECT_UNAVAILABLE("^#^d^efine^ ^XY^Z 1 ^2 ^3^");
|
||||
EXPECT_UNAVAILABLE("FOO ^B^A^R^ FOO ^");
|
||||
EXPECT_UNAVAILABLE("FUNC(^1^)^");
|
||||
|
||||
// Works as expected on object-like macros.
|
||||
EXPECT_EQ(apply("^FOO BAR FOO"), "1 2 3 BAR FOO");
|
||||
EXPECT_EQ(apply("FOO BAR ^FOO"), "FOO BAR 1 2 3");
|
||||
// And function-like macros.
|
||||
EXPECT_EQ(apply("F^UNC(2)"), "2 + 2 + 2");
|
||||
|
||||
// Works on empty macros.
|
||||
EXPECT_EQ(apply("int a ^EMPTY;"), "int a ;");
|
||||
EXPECT_EQ(apply("int a ^EMPTY_FN(1 2 3);"), "int a ;");
|
||||
EXPECT_EQ(apply("int a = 123 ^EMPTY EMPTY_FN(1);"),
|
||||
"int a = 123 EMPTY_FN(1);");
|
||||
EXPECT_EQ(apply("int a = 123 ^EMPTY_FN(1) EMPTY;"), "int a = 123 EMPTY;");
|
||||
EXPECT_EQ(apply("int a = 123 EMPTY_FN(1) ^EMPTY;"),
|
||||
"int a = 123 EMPTY_FN(1) ;");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace clangd
|
||||
} // namespace clang
|
|
@ -0,0 +1,202 @@
|
|||
//===-- ExtractFunctionTests.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 "TestTU.h"
|
||||
#include "TweakTesting.h"
|
||||
#include "gmock/gmock-matchers.h"
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using ::testing::HasSubstr;
|
||||
using ::testing::StartsWith;
|
||||
|
||||
namespace clang {
|
||||
namespace clangd {
|
||||
namespace {
|
||||
|
||||
TWEAK_TEST(ExtractFunction);
|
||||
|
||||
TEST_F(ExtractFunctionTest, FunctionTest) {
|
||||
Context = Function;
|
||||
|
||||
// Root statements should have common parent.
|
||||
EXPECT_EQ(apply("for(;;) [[1+2; 1+2;]]"), "unavailable");
|
||||
// Expressions aren't extracted.
|
||||
EXPECT_EQ(apply("int x = 0; [[x++;]]"), "unavailable");
|
||||
// We don't support extraction from lambdas.
|
||||
EXPECT_EQ(apply("auto lam = [](){ [[int x;]] }; "), "unavailable");
|
||||
// Partial statements aren't extracted.
|
||||
EXPECT_THAT(apply("int [[x = 0]];"), "unavailable");
|
||||
// FIXME: Support hoisting.
|
||||
EXPECT_THAT(apply(" [[int a = 5;]] a++; "), "unavailable");
|
||||
|
||||
// Ensure that end of Zone and Beginning of PostZone being adjacent doesn't
|
||||
// lead to break being included in the extraction zone.
|
||||
EXPECT_THAT(apply("for(;;) { [[int x;]]break; }"), HasSubstr("extracted"));
|
||||
// FIXME: ExtractFunction should be unavailable inside loop construct
|
||||
// initializer/condition.
|
||||
EXPECT_THAT(apply(" for([[int i = 0;]];);"), HasSubstr("extracted"));
|
||||
// Extract certain return
|
||||
EXPECT_THAT(apply(" if(true) [[{ return; }]] "), HasSubstr("extracted"));
|
||||
// Don't extract uncertain return
|
||||
EXPECT_THAT(apply(" if(true) [[if (false) return;]] "),
|
||||
StartsWith("unavailable"));
|
||||
EXPECT_THAT(
|
||||
apply("#define RETURN_IF_ERROR(x) if (x) return\nRETU^RN_IF_ERROR(4);"),
|
||||
StartsWith("unavailable"));
|
||||
|
||||
FileName = "a.c";
|
||||
EXPECT_THAT(apply(" for([[int i = 0;]];);"), HasSubstr("unavailable"));
|
||||
}
|
||||
|
||||
TEST_F(ExtractFunctionTest, FileTest) {
|
||||
// Check all parameters are in order
|
||||
std::string ParameterCheckInput = R"cpp(
|
||||
struct Foo {
|
||||
int x;
|
||||
};
|
||||
void f(int a) {
|
||||
int b;
|
||||
int *ptr = &a;
|
||||
Foo foo;
|
||||
[[a += foo.x + b;
|
||||
*ptr++;]]
|
||||
})cpp";
|
||||
std::string ParameterCheckOutput = R"cpp(
|
||||
struct Foo {
|
||||
int x;
|
||||
};
|
||||
void extracted(int &a, int &b, int * &ptr, Foo &foo) {
|
||||
a += foo.x + b;
|
||||
*ptr++;
|
||||
}
|
||||
void f(int a) {
|
||||
int b;
|
||||
int *ptr = &a;
|
||||
Foo foo;
|
||||
extracted(a, b, ptr, foo);
|
||||
})cpp";
|
||||
EXPECT_EQ(apply(ParameterCheckInput), ParameterCheckOutput);
|
||||
|
||||
// Check const qualifier
|
||||
std::string ConstCheckInput = R"cpp(
|
||||
void f(const int c) {
|
||||
[[while(c) {}]]
|
||||
})cpp";
|
||||
std::string ConstCheckOutput = R"cpp(
|
||||
void extracted(const int &c) {
|
||||
while(c) {}
|
||||
}
|
||||
void f(const int c) {
|
||||
extracted(c);
|
||||
})cpp";
|
||||
EXPECT_EQ(apply(ConstCheckInput), ConstCheckOutput);
|
||||
|
||||
// Don't extract when we need to make a function as a parameter.
|
||||
EXPECT_THAT(apply("void f() { [[int a; f();]] }"), StartsWith("fail"));
|
||||
|
||||
// We don't extract from methods for now since they may involve multi-file
|
||||
// edits
|
||||
std::string MethodFailInput = R"cpp(
|
||||
class T {
|
||||
void f() {
|
||||
[[int x;]]
|
||||
}
|
||||
};
|
||||
)cpp";
|
||||
EXPECT_EQ(apply(MethodFailInput), "unavailable");
|
||||
|
||||
// We don't extract from templated functions for now as templates are hard
|
||||
// to deal with.
|
||||
std::string TemplateFailInput = R"cpp(
|
||||
template<typename T>
|
||||
void f() {
|
||||
[[int x;]]
|
||||
}
|
||||
)cpp";
|
||||
EXPECT_EQ(apply(TemplateFailInput), "unavailable");
|
||||
|
||||
std::string MacroInput = R"cpp(
|
||||
#define F(BODY) void f() { BODY }
|
||||
F ([[int x = 0;]])
|
||||
)cpp";
|
||||
std::string MacroOutput = R"cpp(
|
||||
#define F(BODY) void f() { BODY }
|
||||
void extracted() {
|
||||
int x = 0;
|
||||
}
|
||||
F (extracted();)
|
||||
)cpp";
|
||||
EXPECT_EQ(apply(MacroInput), MacroOutput);
|
||||
|
||||
// Shouldn't crash.
|
||||
EXPECT_EQ(apply("void f([[int a]]);"), "unavailable");
|
||||
// Don't extract if we select the entire function body (CompoundStmt).
|
||||
std::string CompoundFailInput = R"cpp(
|
||||
void f() [[{
|
||||
int a;
|
||||
}]]
|
||||
)cpp";
|
||||
EXPECT_EQ(apply(CompoundFailInput), "unavailable");
|
||||
}
|
||||
|
||||
TEST_F(ExtractFunctionTest, ControlFlow) {
|
||||
Context = Function;
|
||||
// We should be able to extract break/continue with a parent loop/switch.
|
||||
EXPECT_THAT(apply(" [[for(;;) if(1) break;]] "), HasSubstr("extracted"));
|
||||
EXPECT_THAT(apply(" for(;;) [[while(1) break;]] "), HasSubstr("extracted"));
|
||||
EXPECT_THAT(apply(" [[switch(1) { break; }]]"), HasSubstr("extracted"));
|
||||
EXPECT_THAT(apply(" [[while(1) switch(1) { continue; }]]"),
|
||||
HasSubstr("extracted"));
|
||||
// Don't extract break and continue without a loop/switch parent.
|
||||
EXPECT_THAT(apply(" for(;;) [[if(1) continue;]] "), StartsWith("fail"));
|
||||
EXPECT_THAT(apply(" while(1) [[if(1) break;]] "), StartsWith("fail"));
|
||||
EXPECT_THAT(apply(" switch(1) { [[break;]] }"), StartsWith("fail"));
|
||||
EXPECT_THAT(apply(" for(;;) { [[while(1) break; break;]] }"),
|
||||
StartsWith("fail"));
|
||||
}
|
||||
|
||||
TEST_F(ExtractFunctionTest, ExistingReturnStatement) {
|
||||
Context = File;
|
||||
const char *Before = R"cpp(
|
||||
bool lucky(int N);
|
||||
int getNum(bool Superstitious, int Min, int Max) {
|
||||
if (Superstitious) [[{
|
||||
for (int I = Min; I <= Max; ++I)
|
||||
if (lucky(I))
|
||||
return I;
|
||||
return -1;
|
||||
}]] else {
|
||||
return (Min + Max) / 2;
|
||||
}
|
||||
}
|
||||
)cpp";
|
||||
// FIXME: min/max should be by value.
|
||||
// FIXME: avoid emitting redundant braces
|
||||
const char *After = R"cpp(
|
||||
bool lucky(int N);
|
||||
int extracted(int &Min, int &Max) {
|
||||
{
|
||||
for (int I = Min; I <= Max; ++I)
|
||||
if (lucky(I))
|
||||
return I;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
int getNum(bool Superstitious, int Min, int Max) {
|
||||
if (Superstitious) return extracted(Min, Max); else {
|
||||
return (Min + Max) / 2;
|
||||
}
|
||||
}
|
||||
)cpp";
|
||||
EXPECT_EQ(apply(Before), After);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace clangd
|
||||
} // namespace clang
|
|
@ -0,0 +1,300 @@
|
|||
//===-- ExtractVariableTests.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 "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(ExtractVariable);
|
||||
|
||||
TEST_F(ExtractVariableTest, Test) {
|
||||
const char *AvailableCases = R"cpp(
|
||||
int xyz(int a = 1) {
|
||||
struct T {
|
||||
int bar(int a = 1);
|
||||
int z;
|
||||
} t;
|
||||
// return statement
|
||||
return [[[[t.b[[a]]r]](t.z)]];
|
||||
}
|
||||
void f() {
|
||||
int a = [[5 +]] [[4 * [[[[xyz]]()]]]];
|
||||
// multivariable initialization
|
||||
if(1)
|
||||
int x = [[1]], y = [[a + 1]], a = [[1]], z = a + 1;
|
||||
// if without else
|
||||
if([[1]])
|
||||
a = [[1]] + 1;
|
||||
// if with else
|
||||
if(a < [[3]])
|
||||
if(a == [[4]])
|
||||
a = [[5]] + 1;
|
||||
else
|
||||
a = [[5]] + 1;
|
||||
else if (a < [[4]])
|
||||
a = [[4]] + 1;
|
||||
else
|
||||
a = [[5]] + 1;
|
||||
// for loop
|
||||
for(a = [[1]] + 1; a > [[[[3]] + [[4]]]]; a++)
|
||||
a = [[2]] + 1;
|
||||
// while
|
||||
while(a < [[1]])
|
||||
a = [[1]] + 1;
|
||||
// do while
|
||||
do
|
||||
a = [[1]] + 1;
|
||||
while(a < [[3]]);
|
||||
}
|
||||
)cpp";
|
||||
EXPECT_AVAILABLE(AvailableCases);
|
||||
|
||||
ExtraArgs = {"-xc"};
|
||||
const char *AvailableButC = R"cpp(
|
||||
void foo() {
|
||||
int x = [[1]];
|
||||
})cpp";
|
||||
EXPECT_UNAVAILABLE(AvailableButC);
|
||||
ExtraArgs = {};
|
||||
|
||||
const char *NoCrashCases = R"cpp(
|
||||
// error-ok: broken code, but shouldn't crash
|
||||
template<typename T, typename ...Args>
|
||||
struct Test<T, Args...> {
|
||||
Test(const T &v) :val[[(^]]) {}
|
||||
T val;
|
||||
};
|
||||
)cpp";
|
||||
EXPECT_UNAVAILABLE(NoCrashCases);
|
||||
|
||||
const char *UnavailableCases = R"cpp(
|
||||
int xyz(int a = [[1]]) {
|
||||
struct T {
|
||||
int bar(int a = [[1]]);
|
||||
int z = [[1]];
|
||||
} t;
|
||||
return [[t]].bar([[[[t]].z]]);
|
||||
}
|
||||
void v() { return; }
|
||||
// function default argument
|
||||
void f(int b = [[1]]) {
|
||||
// empty selection
|
||||
int a = ^1 ^+ ^2;
|
||||
// void expressions
|
||||
auto i = new int, j = new int;
|
||||
[[[[delete i]], delete j]];
|
||||
[[v]]();
|
||||
// if
|
||||
if(1)
|
||||
int x = 1, y = a + 1, a = 1, z = [[a + 1]];
|
||||
if(int a = 1)
|
||||
if([[a + 1]] == 4)
|
||||
a = [[[[a]] +]] 1;
|
||||
// for loop
|
||||
for(int a = 1, b = 2, c = 3; a > [[b + c]]; [[a++]])
|
||||
a = [[a + 1]];
|
||||
// lambda
|
||||
auto lamb = [&[[a]], &[[b]]](int r = [[1]]) {return 1;};
|
||||
// assignment
|
||||
xyz([[a = 5]]);
|
||||
xyz([[a *= 5]]);
|
||||
// Variable DeclRefExpr
|
||||
a = [[b]];
|
||||
a = [[xyz()]];
|
||||
// statement expression
|
||||
[[xyz()]];
|
||||
while (a)
|
||||
[[++a]];
|
||||
// label statement
|
||||
goto label;
|
||||
label:
|
||||
a = [[1]];
|
||||
}
|
||||
)cpp";
|
||||
EXPECT_UNAVAILABLE(UnavailableCases);
|
||||
|
||||
// vector of pairs of input and output strings
|
||||
const std::vector<std::pair<std::string, std::string>> InputOutputs = {
|
||||
// extraction from variable declaration/assignment
|
||||
{R"cpp(void varDecl() {
|
||||
int a = 5 * (4 + (3 [[- 1)]]);
|
||||
})cpp",
|
||||
R"cpp(void varDecl() {
|
||||
auto dummy = (3 - 1); int a = 5 * (4 + dummy);
|
||||
})cpp"},
|
||||
// FIXME: extraction from switch case
|
||||
/*{R"cpp(void f(int a) {
|
||||
if(1)
|
||||
while(a < 1)
|
||||
switch (1) {
|
||||
case 1:
|
||||
a = [[1 + 2]];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
})cpp",
|
||||
R"cpp(void f(int a) {
|
||||
auto dummy = 1 + 2; if(1)
|
||||
while(a < 1)
|
||||
switch (1) {
|
||||
case 1:
|
||||
a = dummy;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
})cpp"},*/
|
||||
// Macros
|
||||
{R"cpp(#define PLUS(x) x++
|
||||
void f(int a) {
|
||||
int y = PLUS([[1+a]]);
|
||||
})cpp",
|
||||
/*FIXME: It should be extracted like this.
|
||||
R"cpp(#define PLUS(x) x++
|
||||
void f(int a) {
|
||||
auto dummy = 1+a; int y = PLUS(dummy);
|
||||
})cpp"},*/
|
||||
R"cpp(#define PLUS(x) x++
|
||||
void f(int a) {
|
||||
auto dummy = PLUS(1+a); int y = dummy;
|
||||
})cpp"},
|
||||
// ensure InsertionPoint isn't inside a macro
|
||||
{R"cpp(#define LOOP(x) while (1) {a = x;}
|
||||
void f(int a) {
|
||||
if(1)
|
||||
LOOP(5 + [[3]])
|
||||
})cpp",
|
||||
R"cpp(#define LOOP(x) while (1) {a = x;}
|
||||
void f(int a) {
|
||||
auto dummy = 3; if(1)
|
||||
LOOP(5 + dummy)
|
||||
})cpp"},
|
||||
{R"cpp(#define LOOP(x) do {x;} while(1);
|
||||
void f(int a) {
|
||||
if(1)
|
||||
LOOP(5 + [[3]])
|
||||
})cpp",
|
||||
R"cpp(#define LOOP(x) do {x;} while(1);
|
||||
void f(int a) {
|
||||
auto dummy = 3; if(1)
|
||||
LOOP(5 + dummy)
|
||||
})cpp"},
|
||||
// attribute testing
|
||||
{R"cpp(void f(int a) {
|
||||
[ [gsl::suppress("type")] ] for (;;) a = [[1]] + 1;
|
||||
})cpp",
|
||||
R"cpp(void f(int a) {
|
||||
auto dummy = 1; [ [gsl::suppress("type")] ] for (;;) a = dummy + 1;
|
||||
})cpp"},
|
||||
// MemberExpr
|
||||
{R"cpp(class T {
|
||||
T f() {
|
||||
return [[T().f()]].f();
|
||||
}
|
||||
};)cpp",
|
||||
R"cpp(class T {
|
||||
T f() {
|
||||
auto dummy = T().f(); return dummy.f();
|
||||
}
|
||||
};)cpp"},
|
||||
// Function DeclRefExpr
|
||||
{R"cpp(int f() {
|
||||
return [[f]]();
|
||||
})cpp",
|
||||
R"cpp(int f() {
|
||||
auto dummy = f(); return dummy;
|
||||
})cpp"},
|
||||
// FIXME: Wrong result for \[\[clang::uninitialized\]\] int b = [[1]];
|
||||
// since the attr is inside the DeclStmt and the bounds of
|
||||
// DeclStmt don't cover the attribute.
|
||||
|
||||
// Binary subexpressions
|
||||
{R"cpp(void f() {
|
||||
int x = 1 + [[2 + 3 + 4]] + 5;
|
||||
})cpp",
|
||||
R"cpp(void f() {
|
||||
auto dummy = 2 + 3 + 4; int x = 1 + dummy + 5;
|
||||
})cpp"},
|
||||
{R"cpp(void f() {
|
||||
int x = [[1 + 2 + 3]] + 4 + 5;
|
||||
})cpp",
|
||||
R"cpp(void f() {
|
||||
auto dummy = 1 + 2 + 3; int x = dummy + 4 + 5;
|
||||
})cpp"},
|
||||
{R"cpp(void f() {
|
||||
int x = 1 + 2 + [[3 + 4 + 5]];
|
||||
})cpp",
|
||||
R"cpp(void f() {
|
||||
auto dummy = 3 + 4 + 5; int x = 1 + 2 + dummy;
|
||||
})cpp"},
|
||||
// Non-associative operations have no special support
|
||||
{R"cpp(void f() {
|
||||
int x = 1 - [[2 - 3 - 4]] - 5;
|
||||
})cpp",
|
||||
R"cpp(void f() {
|
||||
auto dummy = 1 - 2 - 3 - 4; int x = dummy - 5;
|
||||
})cpp"},
|
||||
// A mix of associative operators isn't associative.
|
||||
{R"cpp(void f() {
|
||||
int x = 0 + 1 * [[2 + 3]] * 4 + 5;
|
||||
})cpp",
|
||||
R"cpp(void f() {
|
||||
auto dummy = 1 * 2 + 3 * 4; int x = 0 + dummy + 5;
|
||||
})cpp"},
|
||||
// Overloaded operators are supported, we assume associativity
|
||||
// as if they were built-in.
|
||||
{R"cpp(struct S {
|
||||
S(int);
|
||||
};
|
||||
S operator+(S, S);
|
||||
|
||||
void f() {
|
||||
S x = S(1) + [[S(2) + S(3) + S(4)]] + S(5);
|
||||
})cpp",
|
||||
R"cpp(struct S {
|
||||
S(int);
|
||||
};
|
||||
S operator+(S, S);
|
||||
|
||||
void f() {
|
||||
auto dummy = S(2) + S(3) + S(4); S x = S(1) + dummy + S(5);
|
||||
})cpp"},
|
||||
// Don't try to analyze across macro boundaries
|
||||
// FIXME: it'd be nice to do this someday (in a safe way)
|
||||
{R"cpp(#define ECHO(X) X
|
||||
void f() {
|
||||
int x = 1 + [[ECHO(2 + 3) + 4]] + 5;
|
||||
})cpp",
|
||||
R"cpp(#define ECHO(X) X
|
||||
void f() {
|
||||
auto dummy = 1 + ECHO(2 + 3) + 4; int x = dummy + 5;
|
||||
})cpp"},
|
||||
{R"cpp(#define ECHO(X) X
|
||||
void f() {
|
||||
int x = 1 + [[ECHO(2) + ECHO(3) + 4]] + 5;
|
||||
})cpp",
|
||||
R"cpp(#define ECHO(X) X
|
||||
void f() {
|
||||
auto dummy = 1 + ECHO(2) + ECHO(3) + 4; int x = dummy + 5;
|
||||
})cpp"},
|
||||
};
|
||||
for (const auto &IO : InputOutputs) {
|
||||
EXPECT_EQ(IO.second, apply(IO.first)) << IO.first;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace clangd
|
||||
} // namespace clang
|
|
@ -0,0 +1,41 @@
|
|||
//===-- ObjCLocalizeStringLiteralTests.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 "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(ObjCLocalizeStringLiteral);
|
||||
|
||||
TEST_F(ObjCLocalizeStringLiteralTest, Test) {
|
||||
ExtraArgs.push_back("-x");
|
||||
ExtraArgs.push_back("objective-c");
|
||||
|
||||
// Ensure the action can be initiated in the string literal.
|
||||
EXPECT_AVAILABLE(R"(id x = ^[[@[[^"^t^est^"]]]];)");
|
||||
|
||||
// Ensure that the action can't be initiated in other places.
|
||||
EXPECT_UNAVAILABLE(R"([[i^d ^[[x]] ^= @"test";^]])");
|
||||
|
||||
// Ensure that the action is not available for regular C strings.
|
||||
EXPECT_UNAVAILABLE(R"(const char * x= "^test";)");
|
||||
|
||||
const char *Input = R"(id x = [[@"test"]];)";
|
||||
const char *Output = R"(id x = NSLocalizedString(@"test", @"");)";
|
||||
EXPECT_EQ(apply(Input), Output);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace clangd
|
||||
} // namespace clang
|
|
@ -0,0 +1,213 @@
|
|||
//===-- PopulateSwitchTest.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 "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(PopulateSwitch);
|
||||
|
||||
TEST_F(PopulateSwitchTest, Test) {
|
||||
struct Case {
|
||||
CodeContext Context;
|
||||
llvm::StringRef TestSource;
|
||||
llvm::StringRef ExpectedSource;
|
||||
};
|
||||
|
||||
Case Cases[]{
|
||||
{
|
||||
// No enumerators
|
||||
Function,
|
||||
R""(enum Enum {}; ^switch ((Enum)0) {})"",
|
||||
"unavailable",
|
||||
},
|
||||
{
|
||||
// All enumerators already in switch (unscoped)
|
||||
Function,
|
||||
R""(enum Enum {A,B}; ^switch (A) {case A:break;case B:break;})"",
|
||||
"unavailable",
|
||||
},
|
||||
{
|
||||
// All enumerators already in switch (scoped)
|
||||
Function,
|
||||
R""(
|
||||
enum class Enum {A,B};
|
||||
^switch (Enum::A) {case Enum::A:break;case Enum::B:break;}
|
||||
)"",
|
||||
"unavailable",
|
||||
},
|
||||
{
|
||||
// Default case in switch
|
||||
Function,
|
||||
R""(
|
||||
enum class Enum {A,B};
|
||||
^switch (Enum::A) {default:break;}
|
||||
)"",
|
||||
"unavailable",
|
||||
},
|
||||
{
|
||||
// GNU range in switch
|
||||
Function,
|
||||
R""(
|
||||
enum class Enum {A,B};
|
||||
^switch (Enum::A) {case Enum::A ... Enum::B:break;}
|
||||
)"",
|
||||
"unavailable",
|
||||
},
|
||||
{
|
||||
// Value dependent case expression
|
||||
File,
|
||||
R""(
|
||||
enum class Enum {A,B};
|
||||
template<Enum Value>
|
||||
void function() {
|
||||
^switch (Enum::A) {case Value:break;}
|
||||
}
|
||||
)"",
|
||||
"unavailable",
|
||||
},
|
||||
{
|
||||
// Body not CompoundStmt
|
||||
Function,
|
||||
R""(enum Enum {A}; ^switch (A);)"",
|
||||
"unavailable",
|
||||
},
|
||||
{
|
||||
// Selection on switch token
|
||||
Function,
|
||||
R""(enum Enum {A}; ^switch (A) {})"",
|
||||
R""(enum Enum {A}; switch (A) {case A:break;})"",
|
||||
},
|
||||
{
|
||||
// Selection on switch condition
|
||||
Function,
|
||||
R""(enum Enum {A}; switch (^A) {})"",
|
||||
R""(enum Enum {A}; switch (A) {case A:break;})"",
|
||||
},
|
||||
{
|
||||
// Selection in switch body
|
||||
Function,
|
||||
R""(enum Enum {A}; switch (A) {^})"",
|
||||
R""(enum Enum {A}; switch (A) {case A:break;})"",
|
||||
},
|
||||
{
|
||||
// Scoped enumeration
|
||||
Function,
|
||||
R""(enum class Enum {A}; ^switch (Enum::A) {})"",
|
||||
R""(enum class Enum {A}; switch (Enum::A) {case Enum::A:break;})"",
|
||||
},
|
||||
{
|
||||
// Scoped enumeration with multiple enumerators
|
||||
Function,
|
||||
R""(
|
||||
enum class Enum {A,B};
|
||||
^switch (Enum::A) {}
|
||||
)"",
|
||||
R""(
|
||||
enum class Enum {A,B};
|
||||
switch (Enum::A) {case Enum::A:case Enum::B:break;}
|
||||
)"",
|
||||
},
|
||||
{
|
||||
// Only filling in missing enumerators (unscoped)
|
||||
Function,
|
||||
R""(
|
||||
enum Enum {A,B,C};
|
||||
^switch (A) {case B:break;}
|
||||
)"",
|
||||
R""(
|
||||
enum Enum {A,B,C};
|
||||
switch (A) {case B:break;case A:case C:break;}
|
||||
)"",
|
||||
},
|
||||
{
|
||||
// Only filling in missing enumerators,
|
||||
// even when using integer literals
|
||||
Function,
|
||||
R""(
|
||||
enum Enum {A,B=1,C};
|
||||
^switch (A) {case 1:break;}
|
||||
)"",
|
||||
R""(
|
||||
enum Enum {A,B=1,C};
|
||||
switch (A) {case 1:break;case A:case C:break;}
|
||||
)"",
|
||||
},
|
||||
{
|
||||
// Only filling in missing enumerators (scoped)
|
||||
Function,
|
||||
R""(
|
||||
enum class Enum {A,B,C};
|
||||
^switch (Enum::A)
|
||||
{case Enum::B:break;}
|
||||
)"",
|
||||
R""(
|
||||
enum class Enum {A,B,C};
|
||||
switch (Enum::A)
|
||||
{case Enum::B:break;case Enum::A:case Enum::C:break;}
|
||||
)"",
|
||||
},
|
||||
{
|
||||
// Scoped enumerations in namespace
|
||||
File,
|
||||
R""(
|
||||
namespace ns { enum class Enum {A}; }
|
||||
void function() { ^switch (ns::Enum::A) {} }
|
||||
)"",
|
||||
R""(
|
||||
namespace ns { enum class Enum {A}; }
|
||||
void function() { switch (ns::Enum::A) {case ns::Enum::A:break;} }
|
||||
)"",
|
||||
},
|
||||
{
|
||||
// Unscoped enumerations in namespace
|
||||
File,
|
||||
R""(
|
||||
namespace ns { enum Enum {A}; }
|
||||
void function() { ^switch (ns::A) {} }
|
||||
)"",
|
||||
R""(
|
||||
namespace ns { enum Enum {A}; }
|
||||
void function() { switch (ns::A) {case ns::A:break;} }
|
||||
)"",
|
||||
},
|
||||
{
|
||||
// Duplicated constant names
|
||||
Function,
|
||||
R""(enum Enum {A,B,b=B}; ^switch (A) {})"",
|
||||
R""(enum Enum {A,B,b=B}; switch (A) {case A:case B:break;})"",
|
||||
},
|
||||
{
|
||||
// Duplicated constant names all in switch
|
||||
Function,
|
||||
R""(enum Enum {A,B,b=B}; ^switch (A) {case A:case B:break;})"",
|
||||
"unavailable",
|
||||
},
|
||||
{
|
||||
// Enum is dependent type
|
||||
File,
|
||||
R""(template<typename T> void f() {enum Enum {A}; ^switch (A) {}})"",
|
||||
"unavailable",
|
||||
},
|
||||
};
|
||||
|
||||
for (const auto &Case : Cases) {
|
||||
Context = Case.Context;
|
||||
EXPECT_EQ(apply(Case.TestSource), Case.ExpectedSource);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace clangd
|
||||
} // namespace clang
|
|
@ -0,0 +1,42 @@
|
|||
//===-- RawStringLiteralTests.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 "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(RawStringLiteral);
|
||||
|
||||
TEST_F(RawStringLiteralTest, Test) {
|
||||
Context = Expression;
|
||||
EXPECT_AVAILABLE(R"cpp(^"^f^o^o^\^n^")cpp");
|
||||
EXPECT_AVAILABLE(R"cpp(R"(multi )" ^"token " "str\ning")cpp");
|
||||
EXPECT_UNAVAILABLE(R"cpp(^"f^o^o^o")cpp"); // no chars need escaping
|
||||
EXPECT_UNAVAILABLE(R"cpp(R"(multi )" ^"token " u8"str\ning")cpp"); // nonascii
|
||||
EXPECT_UNAVAILABLE(R"cpp(^R^"^(^multi )" "token " "str\ning")cpp"); // raw
|
||||
EXPECT_UNAVAILABLE(R"cpp(^"token\n" __FILE__)cpp"); // chunk is macro
|
||||
EXPECT_UNAVAILABLE(R"cpp(^"a\r\n";)cpp"); // forbidden escape char
|
||||
|
||||
const char *Input = R"cpp(R"(multi
|
||||
token)" "\nst^ring\n" "literal")cpp";
|
||||
const char *Output = R"cpp(R"(multi
|
||||
token
|
||||
string
|
||||
literal)")cpp";
|
||||
EXPECT_EQ(apply(Input), Output);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace clangd
|
||||
} // namespace clang
|
|
@ -0,0 +1,237 @@
|
|||
//===-- RemoveUsingNamespaceTest.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 "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(RemoveUsingNamespace);
|
||||
|
||||
TEST_F(RemoveUsingNamespaceTest, All) {
|
||||
std::pair<llvm::StringRef /*Input*/, llvm::StringRef /*Expected*/> Cases[] = {
|
||||
{// Remove all occurrences of ns. Qualify only unqualified.
|
||||
R"cpp(
|
||||
namespace ns1 { struct vector {}; }
|
||||
namespace ns2 { struct map {}; }
|
||||
using namespace n^s1;
|
||||
using namespace ns2;
|
||||
using namespace ns1;
|
||||
int main() {
|
||||
ns1::vector v1;
|
||||
vector v2;
|
||||
map m1;
|
||||
}
|
||||
)cpp",
|
||||
R"cpp(
|
||||
namespace ns1 { struct vector {}; }
|
||||
namespace ns2 { struct map {}; }
|
||||
|
||||
using namespace ns2;
|
||||
|
||||
int main() {
|
||||
ns1::vector v1;
|
||||
ns1::vector v2;
|
||||
map m1;
|
||||
}
|
||||
)cpp"},
|
||||
{// Ident to be qualified is a macro arg.
|
||||
R"cpp(
|
||||
#define DECLARE(x, y) x y
|
||||
namespace ns { struct vector {}; }
|
||||
using namespace n^s;
|
||||
int main() {
|
||||
DECLARE(ns::vector, v1);
|
||||
DECLARE(vector, v2);
|
||||
}
|
||||
)cpp",
|
||||
R"cpp(
|
||||
#define DECLARE(x, y) x y
|
||||
namespace ns { struct vector {}; }
|
||||
|
||||
int main() {
|
||||
DECLARE(ns::vector, v1);
|
||||
DECLARE(ns::vector, v2);
|
||||
}
|
||||
)cpp"},
|
||||
{// Nested namespace: Fully qualify ident from inner ns.
|
||||
R"cpp(
|
||||
namespace aa { namespace bb { struct map {}; }}
|
||||
using namespace aa::b^b;
|
||||
int main() {
|
||||
map m;
|
||||
}
|
||||
)cpp",
|
||||
R"cpp(
|
||||
namespace aa { namespace bb { struct map {}; }}
|
||||
|
||||
int main() {
|
||||
aa::bb::map m;
|
||||
}
|
||||
)cpp"},
|
||||
{// Nested namespace: Fully qualify ident from inner ns.
|
||||
R"cpp(
|
||||
namespace aa { namespace bb { struct map {}; }}
|
||||
using namespace a^a;
|
||||
int main() {
|
||||
bb::map m;
|
||||
}
|
||||
)cpp",
|
||||
R"cpp(
|
||||
namespace aa { namespace bb { struct map {}; }}
|
||||
|
||||
int main() {
|
||||
aa::bb::map m;
|
||||
}
|
||||
)cpp"},
|
||||
{// Typedef.
|
||||
R"cpp(
|
||||
namespace aa { namespace bb { struct map {}; }}
|
||||
using namespace a^a;
|
||||
typedef bb::map map;
|
||||
int main() { map M; }
|
||||
)cpp",
|
||||
R"cpp(
|
||||
namespace aa { namespace bb { struct map {}; }}
|
||||
|
||||
typedef aa::bb::map map;
|
||||
int main() { map M; }
|
||||
)cpp"},
|
||||
{// FIXME: Nested namespaces: Not aware of using ns decl of outer ns.
|
||||
R"cpp(
|
||||
namespace aa { namespace bb { struct map {}; }}
|
||||
using name[[space aa::b]]b;
|
||||
using namespace aa;
|
||||
int main() {
|
||||
map m;
|
||||
}
|
||||
)cpp",
|
||||
R"cpp(
|
||||
namespace aa { namespace bb { struct map {}; }}
|
||||
|
||||
using namespace aa;
|
||||
int main() {
|
||||
aa::bb::map m;
|
||||
}
|
||||
)cpp"},
|
||||
{// Does not qualify ident from inner namespace.
|
||||
R"cpp(
|
||||
namespace aa { namespace bb { struct map {}; }}
|
||||
using namespace aa::bb;
|
||||
using namespace a^a;
|
||||
int main() {
|
||||
map m;
|
||||
}
|
||||
)cpp",
|
||||
R"cpp(
|
||||
namespace aa { namespace bb { struct map {}; }}
|
||||
using namespace aa::bb;
|
||||
|
||||
int main() {
|
||||
map m;
|
||||
}
|
||||
)cpp"},
|
||||
{// Available only for top level namespace decl.
|
||||
R"cpp(
|
||||
namespace aa {
|
||||
namespace bb { struct map {}; }
|
||||
using namespace b^b;
|
||||
}
|
||||
int main() { aa::map m; }
|
||||
)cpp",
|
||||
"unavailable"},
|
||||
{// FIXME: Unavailable for namespaces containing using-namespace decl.
|
||||
R"cpp(
|
||||
namespace aa {
|
||||
namespace bb { struct map {}; }
|
||||
using namespace bb;
|
||||
}
|
||||
using namespace a^a;
|
||||
int main() {
|
||||
map m;
|
||||
}
|
||||
)cpp",
|
||||
"unavailable"},
|
||||
{R"cpp(
|
||||
namespace a::b { struct Foo {}; }
|
||||
using namespace a;
|
||||
using namespace a::[[b]];
|
||||
using namespace b;
|
||||
int main() { Foo F;}
|
||||
)cpp",
|
||||
R"cpp(
|
||||
namespace a::b { struct Foo {}; }
|
||||
using namespace a;
|
||||
|
||||
|
||||
int main() { a::b::Foo F;}
|
||||
)cpp"},
|
||||
{R"cpp(
|
||||
namespace a::b { struct Foo {}; }
|
||||
using namespace a;
|
||||
using namespace a::b;
|
||||
using namespace [[b]];
|
||||
int main() { Foo F;}
|
||||
)cpp",
|
||||
R"cpp(
|
||||
namespace a::b { struct Foo {}; }
|
||||
using namespace a;
|
||||
|
||||
|
||||
int main() { b::Foo F;}
|
||||
)cpp"},
|
||||
{// Enumerators.
|
||||
R"cpp(
|
||||
namespace tokens {
|
||||
enum Token {
|
||||
comma, identifier, numeric
|
||||
};
|
||||
}
|
||||
using namespace tok^ens;
|
||||
int main() {
|
||||
auto x = comma;
|
||||
}
|
||||
)cpp",
|
||||
R"cpp(
|
||||
namespace tokens {
|
||||
enum Token {
|
||||
comma, identifier, numeric
|
||||
};
|
||||
}
|
||||
|
||||
int main() {
|
||||
auto x = tokens::comma;
|
||||
}
|
||||
)cpp"},
|
||||
{// inline namespaces.
|
||||
R"cpp(
|
||||
namespace std { inline namespace ns1 { inline namespace ns2 { struct vector {}; }}}
|
||||
using namespace st^d;
|
||||
int main() {
|
||||
vector V;
|
||||
}
|
||||
)cpp",
|
||||
R"cpp(
|
||||
namespace std { inline namespace ns1 { inline namespace ns2 { struct vector {}; }}}
|
||||
|
||||
int main() {
|
||||
std::vector V;
|
||||
}
|
||||
)cpp"}};
|
||||
for (auto C : Cases)
|
||||
EXPECT_EQ(C.second, apply(C.first)) << C.first;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace clangd
|
||||
} // namespace clang
|
|
@ -0,0 +1,48 @@
|
|||
//===-- ShowSelectionTreeTests.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 "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(ShowSelectionTree);
|
||||
|
||||
TEST_F(ShowSelectionTreeTest, Test) {
|
||||
EXPECT_AVAILABLE("^int f^oo() { re^turn 2 ^+ 2; }");
|
||||
EXPECT_AVAILABLE("/*c^omment*/ int foo() { return 2 ^ + 2; }");
|
||||
|
||||
const char *Output = R"(message:
|
||||
TranslationUnitDecl
|
||||
VarDecl int x = fcall(2 + 2)
|
||||
.CallExpr fcall(2 + 2)
|
||||
ImplicitCastExpr fcall
|
||||
.DeclRefExpr fcall
|
||||
.BinaryOperator 2 + 2
|
||||
*IntegerLiteral 2
|
||||
)";
|
||||
EXPECT_EQ(apply("int fcall(int); int x = fca[[ll(2 +]]2);"), Output);
|
||||
|
||||
Output = R"(message:
|
||||
TranslationUnitDecl
|
||||
FunctionDecl void x()
|
||||
CompoundStmt { …
|
||||
ForStmt for (;;) …
|
||||
*BreakStmt break;
|
||||
)";
|
||||
EXPECT_EQ(apply("void x() { for (;;) br^eak; }"), Output);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace clangd
|
||||
} // namespace clang
|
|
@ -0,0 +1,48 @@
|
|||
//===-- SwapIfBranchesTests.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 "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(SwapIfBranches);
|
||||
|
||||
TEST_F(SwapIfBranchesTest, Test) {
|
||||
Context = Function;
|
||||
EXPECT_EQ(apply("^if (true) {return;} else {(void)0;}"),
|
||||
"if (true) {(void)0;} else {return;}");
|
||||
EXPECT_EQ(apply("^if (/*error-ok*/) {return;} else {(void)0;}"),
|
||||
"if (/*error-ok*/) {(void)0;} else {return;}")
|
||||
<< "broken condition";
|
||||
EXPECT_AVAILABLE("^i^f^^(^t^r^u^e^) { return; } ^e^l^s^e^ { return; }");
|
||||
EXPECT_UNAVAILABLE("if (true) {^return ^;^ } else { ^return^;^ }");
|
||||
// Available in subexpressions of the condition;
|
||||
EXPECT_THAT("if(2 + [[2]] + 2) { return; } else {return;}", isAvailable());
|
||||
// But not as part of the branches.
|
||||
EXPECT_THAT("if(2 + 2 + 2) { [[return]]; } else { return; }",
|
||||
Not(isAvailable()));
|
||||
// Range covers the "else" token, so available.
|
||||
EXPECT_THAT("if(2 + 2 + 2) { return[[; } else {return;]]}", isAvailable());
|
||||
// Not available in compound statements in condition.
|
||||
EXPECT_THAT("if([]{return [[true]];}()) { return; } else { return; }",
|
||||
Not(isAvailable()));
|
||||
// Not available if both sides aren't braced.
|
||||
EXPECT_THAT("^if (1) return; else { return; }", Not(isAvailable()));
|
||||
// Only one if statement is supported!
|
||||
EXPECT_THAT("[[if(1){}else{}if(2){}else{}]]", Not(isAvailable()));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace clangd
|
||||
} // namespace clang
|
|
@ -25,12 +25,12 @@ using Context = TweakTest::CodeContext;
|
|||
|
||||
std::pair<llvm::StringRef, llvm::StringRef> wrapping(Context Ctx) {
|
||||
switch (Ctx) {
|
||||
case TweakTest::File:
|
||||
return {"",""};
|
||||
case TweakTest::Function:
|
||||
return {"void wrapperFunction(){\n", "\n}"};
|
||||
case TweakTest::Expression:
|
||||
return {"auto expressionWrapper(){return\n", "\n;}"};
|
||||
case TweakTest::File:
|
||||
return {"", ""};
|
||||
case TweakTest::Function:
|
||||
return {"void wrapperFunction(){\n", "\n}"};
|
||||
case TweakTest::Expression:
|
||||
return {"auto expressionWrapper(){return\n", "\n;}"};
|
||||
}
|
||||
llvm_unreachable("Unknown TweakTest::CodeContext enum");
|
||||
}
|
||||
|
@ -46,7 +46,8 @@ llvm::StringRef unwrap(Context Ctx, llvm::StringRef Outer) {
|
|||
// Don't allow the begin/end wrapping to overlap!
|
||||
if (Outer.startswith(Wrapping.first) && Outer.endswith(Wrapping.second) &&
|
||||
Outer.size() >= Wrapping.first.size() + Wrapping.second.size())
|
||||
return Outer.drop_front(Wrapping.first.size()).drop_back(Wrapping.second.size());
|
||||
return Outer.drop_front(Wrapping.first.size())
|
||||
.drop_back(Wrapping.second.size());
|
||||
return Outer;
|
||||
}
|
||||
|
||||
|
@ -157,11 +158,11 @@ std::vector<std::string> TweakTest::expandCases(llvm::StringRef MarkedCode) {
|
|||
Annotations Test(MarkedCode);
|
||||
llvm::StringRef Code = Test.code();
|
||||
std::vector<std::string> Cases;
|
||||
for (const auto& Point : Test.points()) {
|
||||
for (const auto &Point : Test.points()) {
|
||||
size_t Offset = llvm::cantFail(positionToOffset(Code, Point));
|
||||
Cases.push_back((Code.substr(0, Offset) + "^" + Code.substr(Offset)).str());
|
||||
}
|
||||
for (const auto& Range : Test.ranges()) {
|
||||
for (const auto &Range : Test.ranges()) {
|
||||
size_t Begin = llvm::cantFail(positionToOffset(Code, Range.start));
|
||||
size_t End = llvm::cantFail(positionToOffset(Code, Range.end));
|
||||
Cases.push_back((Code.substr(0, Begin) + "[[" +
|
|
@ -100,6 +100,10 @@ protected:
|
|||
::testing::Matcher<llvm::StringRef> isAvailable() const;
|
||||
};
|
||||
|
||||
MATCHER_P2(FileWithContents, FileName, Contents, "") {
|
||||
return arg.first() == FileName && arg.second == Contents;
|
||||
}
|
||||
|
||||
#define TWEAK_TEST(TweakID) \
|
||||
class TweakID##Test : public ::clang::clangd::TweakTest { \
|
||||
protected: \
|
|
@ -0,0 +1,66 @@
|
|||
//===-- TweakTests.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 "TestFS.h"
|
||||
#include "TestTU.h"
|
||||
#include "TweakTesting.h"
|
||||
#include "refactor/Tweak.h"
|
||||
#include "clang/Basic/Diagnostic.h"
|
||||
#include "clang/Basic/DiagnosticIDs.h"
|
||||
#include "clang/Basic/DiagnosticOptions.h"
|
||||
#include "clang/Basic/FileManager.h"
|
||||
#include "clang/Basic/FileSystemOptions.h"
|
||||
#include "clang/Basic/LLVM.h"
|
||||
#include "clang/Basic/SourceLocation.h"
|
||||
#include "clang/Basic/SourceManager.h"
|
||||
#include "clang/Rewrite/Core/Rewriter.h"
|
||||
#include "clang/Tooling/Core/Replacement.h"
|
||||
#include "llvm/ADT/IntrusiveRefCntPtr.h"
|
||||
#include "llvm/ADT/StringExtras.h"
|
||||
#include "llvm/ADT/StringMap.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/Support/Error.h"
|
||||
#include "llvm/Support/MemoryBuffer.h"
|
||||
#include "llvm/Support/VirtualFileSystem.h"
|
||||
#include "llvm/Testing/Support/Error.h"
|
||||
#include "gmock/gmock-matchers.h"
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace clang {
|
||||
namespace clangd {
|
||||
namespace {
|
||||
|
||||
TEST(FileEdits, AbsolutePath) {
|
||||
auto RelPaths = {"a.h", "foo.cpp", "test/test.cpp"};
|
||||
|
||||
llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> MemFS(
|
||||
new llvm::vfs::InMemoryFileSystem);
|
||||
MemFS->setCurrentWorkingDirectory(testRoot());
|
||||
for (const auto *Path : RelPaths)
|
||||
MemFS->addFile(Path, 0, llvm::MemoryBuffer::getMemBuffer("", Path));
|
||||
FileManager FM(FileSystemOptions(), MemFS);
|
||||
DiagnosticsEngine DE(new DiagnosticIDs, new DiagnosticOptions);
|
||||
SourceManager SM(DE, FM);
|
||||
|
||||
for (const auto *Path : RelPaths) {
|
||||
auto FID = SM.createFileID(*FM.getFile(Path), SourceLocation(),
|
||||
clang::SrcMgr::C_User);
|
||||
auto Res = Tweak::Effect::fileEdit(SM, FID, tooling::Replacements());
|
||||
ASSERT_THAT_EXPECTED(Res, llvm::Succeeded());
|
||||
EXPECT_EQ(Res->first, testPath(Path));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace clangd
|
||||
} // namespace clang
|
Loading…
Reference in New Issue