//===-- 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 struct Test { 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> 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