blob: 5862d4938d4a284b70d22c885fcdaa53b58a3348 [file] [log] [blame]
//===-- 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 placeholder = (3 - 1); int a = 5 * (4 + placeholder);
})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 placeholder = 1 + 2; if(1)
while(a < 1)
switch (1) {
case 1:
a = placeholder;
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 placeholder = 1+a; int y = PLUS(placeholder);
})cpp"},*/
R"cpp(#define PLUS(x) x++
void f(int a) {
auto placeholder = PLUS(1+a); int y = placeholder;
})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 placeholder = 3; if(1)
LOOP(5 + placeholder)
})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 placeholder = 3; if(1)
LOOP(5 + placeholder)
})cpp"},
// attribute testing
{R"cpp(void f(int a) {
[ [gsl::suppress("type")] ] for (;;) a = [[1]] + 1;
})cpp",
R"cpp(void f(int a) {
auto placeholder = 1; [ [gsl::suppress("type")] ] for (;;) a = placeholder + 1;
})cpp"},
// MemberExpr
{R"cpp(class T {
T f() {
return [[T().f()]].f();
}
};)cpp",
R"cpp(class T {
T f() {
auto placeholder = T().f(); return placeholder.f();
}
};)cpp"},
// Function DeclRefExpr
{R"cpp(int f() {
return [[f]]();
})cpp",
R"cpp(int f() {
auto placeholder = f(); return placeholder;
})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 placeholder = 2 + 3 + 4; int x = 1 + placeholder + 5;
})cpp"},
{R"cpp(void f() {
int x = [[1 + 2 + 3]] + 4 + 5;
})cpp",
R"cpp(void f() {
auto placeholder = 1 + 2 + 3; int x = placeholder + 4 + 5;
})cpp"},
{R"cpp(void f() {
int x = 1 + 2 + [[3 + 4 + 5]];
})cpp",
R"cpp(void f() {
auto placeholder = 3 + 4 + 5; int x = 1 + 2 + placeholder;
})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 placeholder = 1 - 2 - 3 - 4; int x = placeholder - 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 placeholder = 1 * 2 + 3 * 4; int x = 0 + placeholder + 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 placeholder = S(2) + S(3) + S(4); S x = S(1) + placeholder + 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 placeholder = 1 + ECHO(2 + 3) + 4; int x = placeholder + 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 placeholder = 1 + ECHO(2) + ECHO(3) + 4; int x = placeholder + 5;
})cpp"},
};
for (const auto &IO : InputOutputs) {
EXPECT_EQ(IO.second, apply(IO.first)) << IO.first;
}
}
} // namespace
} // namespace clangd
} // namespace clang