735 lines · cpp
1//===-- ExtractVariableTests.cpp --------------------------------*- C++ -*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "TweakTesting.h"10#include "gmock/gmock.h"11#include "gtest/gtest.h"12 13namespace clang {14namespace clangd {15namespace {16 17TWEAK_TEST(ExtractVariable);18 19TEST_F(ExtractVariableTest, Test) {20 const char *AvailableCases = R"cpp(21 int xyz(int a = 1) {22 struct T {23 int bar(int a = 1);24 int z;25 } t;26 // return statement27 return [[[[t.b[[a]]r]]([[t.z]])]];28 }29 void f() {30 int a = 5 + [[4 * [[[[xyz]]()]]]];31 // multivariable initialization32 if(1)33 int x = [[1]] + 1, y = a + [[1]], a = [[1]] + 2, z = a + 1;34 // if without else35 if([[1]])36 a = [[1]] + 1;37 // if with else38 if(a < [[3]])39 if(a == [[4]])40 a = [[5]] + 1;41 else42 a = [[5]] + 1;43 else if (a < [[4]])44 a = [[4]] + 1;45 else46 a = [[5]] + 1;47 // for loop48 for(a = [[1]] + 1; a > [[[[3]] + [[4]]]]; a++)49 a = [[2]] + 1;50 // while51 while(a < [[1]])52 a = [[1]] + 1;53 // do while54 do55 a = [[1]] + 1;56 while(a < [[3]]);57 }58 )cpp";59 EXPECT_AVAILABLE(AvailableCases);60 61 ExtraArgs = {"-xc"};62 const char *AvailableC = R"cpp(63 void foo() {64 int x = [[1]] + 1;65 })cpp";66 EXPECT_AVAILABLE(AvailableC);67 68 ExtraArgs = {"-xc"};69 const char *NoCrashCasesC = R"cpp(70 // error-ok: broken code, but shouldn't crash71 int x = [[foo()]];72 )cpp";73 EXPECT_UNAVAILABLE(NoCrashCasesC);74 75 ExtraArgs = {"-xc"};76 const char *NoCrashDesignator = R"cpp(77 struct A {78 struct {79 int x;80 };81 };82 struct B {83 int y;84 };85 void foo(struct B *b) {86 struct A a = {.x=b[[->]]y};87 }88 )cpp";89 EXPECT_AVAILABLE(NoCrashDesignator);90 91 ExtraArgs = {"-xobjective-c"};92 const char *AvailableObjC = R"cpp(93 __attribute__((objc_root_class))94 @interface Foo95 @end96 @implementation Foo97 - (void)method {98 int x = [[1]] + 2;99 }100 @end)cpp";101 EXPECT_AVAILABLE(AvailableObjC);102 ExtraArgs = {};103 104 const char *NoCrashCases = R"cpp(105 // error-ok: broken code, but shouldn't crash106 template<typename T, typename ...Args>107 struct Test<T, Args...> {108 Test(const T &v) :val[[(^]]) {}109 T val;110 };111 )cpp";112 EXPECT_UNAVAILABLE(NoCrashCases);113 114 const char *UnavailableCases = R"cpp(115 int xyz(int a = [[1]]) {116 struct T {117 int bar(int a = [[1]]) {118 int b = [[z]];119 return 0;120 }121 int z = [[1]];122 } t;123 int x = [[1 + 2]];124 int y;125 y = [[1 + 2]];126 return [[t]].bar([[t]].z);127 }128 void v() { return; }129 130 // function default argument131 void f(int b = [[1]]) {132 // empty selection133 int a = ^1 ^+ ^2;134 // void expressions135 auto i = new int, j = new int;136 [[[[delete i]], delete j]];137 [[v]]();138 // if139 if(1)140 int x = 1, y = a + 1, a = 1, z = [[a + 1]];141 if(int a = 1)142 if([[a + 1]] == 4)143 a = [[[[a]] +]] 1;144 // for loop145 for(int a = 1, b = 2, c = 3; a > [[b + c]]; [[a++]])146 a = [[a + 1]];147 // lambda148 auto lamb = [&[[a]], &[[b]]](int r = [[1]]) {return 1;};149 // assignment150 xyz([[a = 5]]);151 xyz([[a *= 5]]);152 // Variable DeclRefExpr153 a = [[b]];154 a = [[xyz()]];155 // expression statement of type void156 [[v()]];157 while (a)158 [[++a]];159 // label statement160 goto label;161 label:162 a = [[1]];163 164 // lambdas: captures165 int x = 0;166 [ [[=]] ](){};167 [ [[&]] ](){};168 [ [[x]] ](){};169 [ [[&x]] ](){};170 [y = [[x]] ](){};171 [ [[y = x]] ](){};172 173 // lambdas: default args, cannot extract into function-local scope174 [](int x = [[10]]){};175 [](auto h = [[ [i = [](){}](){} ]]) {};176 177 // lambdas: default args178 // Extracting from capture initializers is usually fine,179 // but not if the capture itself is nested inside a default argument180 [](auto h = [i = [[ [](){} ]]](){}) {};181 [](auto h = [i = [[ 42 ]]](){}) {};182 183 // lambdas: scope184 if (int a = 1)185 if ([[ [&](){ return a + 1; } ]]() == 4)186 a = a + 1;187 188 for (int c = 0; [[ [&]() { return c < b; } ]](); ++c) {189 }190 for (int c = 0; [[ [&]() { return c < b; } () ]]; ++c) {191 }192 193 // lambdas: scope with structured binding194 struct Coordinates {195 int x{};196 int y{};197 };198 Coordinates c{};199 if (const auto [x, y] = c; x > y)200 auto f = [[ [&]() { return x + y; } ]];201 202 // lambdas: referencing outside variables that block extraction203 // in trailing return type or in a decltype used204 // by a parameter205 if (int a = 1)206 if ([[ []() -> decltype(a) { return 1; } ]] () == 4)207 a = a + 1;208 if (int a = 1)209 if ([[ [](int x = decltype(a){}) { return 1; } ]] () == 4)210 a = a + 1;211 if (int a = 1)212 if ([[ [](decltype(a) x) { return 1; } ]] (42) == 4)213 a = a + 1;214 }215 )cpp";216 EXPECT_UNAVAILABLE(UnavailableCases);217 218 ExtraArgs = {"-std=c++20"};219 const char *UnavailableCasesCXX20 = R"cpp(220 template <typename T>221 concept Constraint = requires (T t) { true; };222 void foo() {223 // lambdas: referencing outside variables that block extraction224 // in requires clause or defaulted explicit template parameters225 if (int a = 1)226 if ([[ [](auto b) requires (Constraint<decltype(a)> && Constraint<decltype(b)>) { return true; } ]] (a))227 a = a + 1;228 229 if (int a = 1)230 if ([[ []<typename T = decltype(a)>(T b) { return true; } ]] (a))231 a = a + 1;232 }233 )cpp";234 EXPECT_UNAVAILABLE(UnavailableCasesCXX20);235 ExtraArgs.clear();236 237 // vector of pairs of input and output strings238 std::vector<std::pair<std::string, std::string>> InputOutputs = {239 // extraction from variable declaration/assignment240 {R"cpp(void varDecl() {241 int a = 5 * (4 + (3 [[- 1)]]);242 })cpp",243 R"cpp(void varDecl() {244 auto placeholder = (3 - 1); int a = 5 * (4 + placeholder);245 })cpp"},246 // FIXME: extraction from switch case247 /*{R"cpp(void f(int a) {248 if(1)249 while(a < 1)250 switch (1) {251 case 1:252 a = [[1 + 2]];253 break;254 default:255 break;256 }257 })cpp",258 R"cpp(void f(int a) {259 auto placeholder = 1 + 2; if(1)260 while(a < 1)261 switch (1) {262 case 1:263 a = placeholder;264 break;265 default:266 break;267 }268 })cpp"},*/269 // Macros270 {R"cpp(#define PLUS(x) x++271 void f(int a) {272 int y = PLUS([[1+a]]);273 })cpp",274 /*FIXME: It should be extracted like this.275 R"cpp(#define PLUS(x) x++276 void f(int a) {277 auto placeholder = 1+a; int y = PLUS(placeholder);278 })cpp"},*/279 R"cpp(#define PLUS(x) x++280 void f(int a) {281 auto placeholder = PLUS(1+a); int y = placeholder;282 })cpp"},283 // ensure InsertionPoint isn't inside a macro284 {R"cpp(#define LOOP(x) while (1) {a = x;}285 void f(int a) {286 if(1)287 LOOP(5 + [[3]])288 })cpp",289 R"cpp(#define LOOP(x) while (1) {a = x;}290 void f(int a) {291 auto placeholder = 3; if(1)292 LOOP(5 + placeholder)293 })cpp"},294 {R"cpp(#define LOOP(x) do {x;} while(1);295 void f(int a) {296 if(1)297 LOOP(5 + [[3]])298 })cpp",299 R"cpp(#define LOOP(x) do {x;} while(1);300 void f(int a) {301 auto placeholder = 3; if(1)302 LOOP(5 + placeholder)303 })cpp"},304 // attribute testing305 {R"cpp(void f(int a) {306 [ [gsl::suppress("type")] ] for (;;) a = [[1]] + 1;307 })cpp",308 R"cpp(void f(int a) {309 auto placeholder = 1; [ [gsl::suppress("type")] ] for (;;) a = placeholder + 1;310 })cpp"},311 // MemberExpr312 {R"cpp(class T {313 T f() {314 return [[T().f()]].f();315 }316 };)cpp",317 R"cpp(class T {318 T f() {319 auto placeholder = T().f(); return placeholder.f();320 }321 };)cpp"},322 // Function DeclRefExpr323 {R"cpp(int f() {324 return [[f]]();325 })cpp",326 R"cpp(int f() {327 auto placeholder = f(); return placeholder;328 })cpp"},329 // FIXME: Wrong result for \[\[clang::uninitialized\]\] int b = [[1]];330 // since the attr is inside the DeclStmt and the bounds of331 // DeclStmt don't cover the attribute.332 333 // Binary subexpressions334 {R"cpp(void f() {335 int x = 1 + [[2 + 3 + 4]] + 5;336 })cpp",337 R"cpp(void f() {338 auto placeholder = 2 + 3 + 4; int x = 1 + placeholder + 5;339 })cpp"},340 {R"cpp(void f() {341 int x = [[1 + 2 + 3]] + 4 + 5;342 })cpp",343 R"cpp(void f() {344 auto placeholder = 1 + 2 + 3; int x = placeholder + 4 + 5;345 })cpp"},346 {R"cpp(void f() {347 int x = 1 + 2 + [[3 + 4 + 5]];348 })cpp",349 R"cpp(void f() {350 auto placeholder = 3 + 4 + 5; int x = 1 + 2 + placeholder;351 })cpp"},352 // Non-associative operations have no special support353 {R"cpp(void f() {354 int x = 1 - [[2 - 3 - 4]] - 5;355 })cpp",356 R"cpp(void f() {357 auto placeholder = 1 - 2 - 3 - 4; int x = placeholder - 5;358 })cpp"},359 // A mix of associative operators isn't associative.360 {R"cpp(void f() {361 int x = 0 + 1 * [[2 + 3]] * 4 + 5;362 })cpp",363 R"cpp(void f() {364 auto placeholder = 1 * 2 + 3 * 4; int x = 0 + placeholder + 5;365 })cpp"},366 // Overloaded operators are supported, we assume associativity367 // as if they were built-in.368 {R"cpp(struct S {369 S(int);370 };371 S operator+(S, S);372 373 void f() {374 S x = S(1) + [[S(2) + S(3) + S(4)]] + S(5);375 })cpp",376 R"cpp(struct S {377 S(int);378 };379 S operator+(S, S);380 381 void f() {382 auto placeholder = S(2) + S(3) + S(4); S x = S(1) + placeholder + S(5);383 })cpp"},384 // lambda expressions385 {R"cpp(template <typename T> void f(T) {}386 void f2() {387 f([[ [](){ return 42; }]]);388 }389 )cpp",390 R"cpp(template <typename T> void f(T) {}391 void f2() {392 auto placeholder = [](){ return 42; }; f( placeholder);393 }394 )cpp"},395 {R"cpp(template <typename T> void f(T) {}396 void f2() {397 f([x = [[40 + 2]] ](){ return 42; });398 }399 )cpp",400 R"cpp(template <typename T> void f(T) {}401 void f2() {402 auto placeholder = 40 + 2; f([x = placeholder ](){ return 42; });403 }404 )cpp"},405 {R"cpp(auto foo(int VarA) {406 return [VarA]() {407 return [[ [VarA, VarC = 42 + VarA](int VarB) { return VarA + VarB + VarC; }]];408 };409 }410 )cpp",411 R"cpp(auto foo(int VarA) {412 return [VarA]() {413 auto placeholder = [VarA, VarC = 42 + VarA](int VarB) { return VarA + VarB + VarC; }; return placeholder;414 };415 }416 )cpp"},417 {R"cpp(template <typename T> void f(T) {}418 void f2(int var) {419 f([[ [&var](){ auto internal_val = 42; return var + internal_val; }]]);420 }421 )cpp",422 R"cpp(template <typename T> void f(T) {}423 void f2(int var) {424 auto placeholder = [&var](){ auto internal_val = 42; return var + internal_val; }; f( placeholder);425 }426 )cpp"},427 {R"cpp(template <typename T> void f(T) { }428 struct A {429 void f2(int& var) {430 auto local_var = 42;431 f([[ [&var, &local_var, this]() {432 auto internal_val = 42;433 return var + local_var + internal_val + member;434 }]]);435 }436 437 int member = 42;438};439 )cpp",440 R"cpp(template <typename T> void f(T) { }441 struct A {442 void f2(int& var) {443 auto local_var = 42;444 auto placeholder = [&var, &local_var, this]() {445 auto internal_val = 42;446 return var + local_var + internal_val + member;447 }; f( placeholder);448 }449 450 int member = 42;451};452 )cpp"},453 {R"cpp(void f() { auto x = +[[ [](){ return 42; }]]; })cpp",454 R"cpp(void f() { auto placeholder = [](){ return 42; }; auto x = + placeholder; })cpp"},455 {R"cpp(456 template <typename T>457 auto sink(T f) { return f(); }458 int bar() {459 return sink([[ []() { return 42; }]]);460 }461 )cpp",462 R"cpp(463 template <typename T>464 auto sink(T f) { return f(); }465 int bar() {466 auto placeholder = []() { return 42; }; return sink( placeholder);467 }468 )cpp"},469 {R"cpp(470 int main() {471 if (int a = 1) {472 if ([[ [&](){ return a + 1; } ]]() == 4)473 a = a + 1;474 }475 })cpp",476 R"cpp(477 int main() {478 if (int a = 1) {479 auto placeholder = [&](){ return a + 1; }; if ( placeholder () == 4)480 a = a + 1;481 }482 })cpp"},483 {R"cpp(484 int main() {485 if (int a = 1) {486 if ([[ [&](){ return a + 1; }() ]] == 4)487 a = a + 1;488 }489 })cpp",490 R"cpp(491 int main() {492 if (int a = 1) {493 auto placeholder = [&](){ return a + 1; }(); if ( placeholder == 4)494 a = a + 1;495 }496 })cpp"},497 {R"cpp(498 int func() { return 0; }499 int main() {500 [[func()]];501 })cpp",502 R"cpp(503 int func() { return 0; }504 int main() {505 auto placeholder = func();506 })cpp"},507 {R"cpp(508 template <typename T>509 auto call(T t) { return t(); }510 511 int main() {512 return [[ call([](){ int a = 1; return a + 1; }) ]] + 5;513 })cpp",514 R"cpp(515 template <typename T>516 auto call(T t) { return t(); }517 518 int main() {519 auto placeholder = call([](){ int a = 1; return a + 1; }); return placeholder + 5;520 })cpp"},521 {R"cpp(522 class Foo {523 int bar() {524 return [f = [[ [this](int g) { return g + x; } ]] ]() { return 42; }();525 }526 int x;527 };528 )cpp",529 R"cpp(530 class Foo {531 int bar() {532 auto placeholder = [this](int g) { return g + x; }; return [f = placeholder ]() { return 42; }();533 }534 int x;535 };536 )cpp"},537 {R"cpp(538 int main() {539 return [[ []() { return 42; }() ]];540 })cpp",541 R"cpp(542 int main() {543 auto placeholder = []() { return 42; }(); return placeholder ;544 })cpp"},545 {R"cpp(546 template <typename ...Ts>547 void foo(Ts ...args) {548 auto x = +[[ [&args...]() {} ]];549 }550 )cpp",551 R"cpp(552 template <typename ...Ts>553 void foo(Ts ...args) {554 auto placeholder = [&args...]() {}; auto x = + placeholder ;555 }556 )cpp"},557 {R"cpp(558 struct Coordinates {559 int x{};560 int y{};561 };562 563 int main() {564 Coordinates c = {};565 const auto [x, y] = c;566 auto f = [[ [&]() { return x + y; } ]]();567 }568 )cpp",569 R"cpp(570 struct Coordinates {571 int x{};572 int y{};573 };574 575 int main() {576 Coordinates c = {};577 const auto [x, y] = c;578 auto placeholder = [&]() { return x + y; }; auto f = placeholder ();579 }580 )cpp"},581 {R"cpp(582 struct Coordinates {583 int x{};584 int y{};585 };586 587 int main() {588 Coordinates c = {};589 if (const auto [x, y] = c; x > y) {590 auto f = [[ [&]() { return x + y; } ]]();591 }592 }593 )cpp",594 R"cpp(595 struct Coordinates {596 int x{};597 int y{};598 };599 600 int main() {601 Coordinates c = {};602 if (const auto [x, y] = c; x > y) {603 auto placeholder = [&]() { return x + y; }; auto f = placeholder ();604 }605 }606 )cpp"},607 // Don't try to analyze across macro boundaries608 // FIXME: it'd be nice to do this someday (in a safe way)609 {R"cpp(#define ECHO(X) X610 void f() {611 int x = 1 + [[ECHO(2 + 3) + 4]] + 5;612 })cpp",613 R"cpp(#define ECHO(X) X614 void f() {615 auto placeholder = 1 + ECHO(2 + 3) + 4; int x = placeholder + 5;616 })cpp"},617 {R"cpp(#define ECHO(X) X618 void f() {619 int x = 1 + [[ECHO(2) + ECHO(3) + 4]] + 5;620 })cpp",621 R"cpp(#define ECHO(X) X622 void f() {623 auto placeholder = 1 + ECHO(2) + ECHO(3) + 4; int x = placeholder + 5;624 })cpp"},625 };626 for (const auto &IO : InputOutputs) {627 EXPECT_EQ(IO.second, apply(IO.first)) << IO.first;628 }629 630 ExtraArgs = {"-xc"};631 InputOutputs = {632 // Function Pointers633 {R"cpp(struct Handlers {634 void (*handlerFunc)(int);635 };636 void runFunction(void (*func)(int)) {}637 void f(struct Handlers *handler) {638 runFunction([[handler->handlerFunc]]);639 })cpp",640 R"cpp(struct Handlers {641 void (*handlerFunc)(int);642 };643 void runFunction(void (*func)(int)) {}644 void f(struct Handlers *handler) {645 void (*placeholder)(int) = handler->handlerFunc; runFunction(placeholder);646 })cpp"},647 {R"cpp(int (*foo(char))(int);648 void bar() {649 (void)[[foo('c')]];650 })cpp",651 R"cpp(int (*foo(char))(int);652 void bar() {653 int (*placeholder)(int) = foo('c'); (void)placeholder;654 })cpp"},655 // Arithmetic on typedef types preserves typedef types656 {R"cpp(typedef long NSInteger;657 void varDecl() {658 NSInteger a = 2 * 5;659 NSInteger b = [[a * 7]] + 3;660 })cpp",661 R"cpp(typedef long NSInteger;662 void varDecl() {663 NSInteger a = 2 * 5;664 NSInteger placeholder = a * 7; NSInteger b = placeholder + 3;665 })cpp"},666 };667 for (const auto &IO : InputOutputs) {668 EXPECT_EQ(IO.second, apply(IO.first)) << IO.first;669 }670 671 ExtraArgs = {"-xobjective-c"};672 EXPECT_UNAVAILABLE(R"cpp(673 __attribute__((objc_root_class))674 @interface Foo675 - (void)setMethod1:(int)a;676 - (int)method1;677 @property int prop1;678 @end679 @implementation Foo680 - (void)method {681 [[self.method1]] = 1;682 [[self.method1]] += 1;683 [[self.prop1]] = 1;684 [[self.prop1]] += 1;685 }686 @end)cpp");687 InputOutputs = {688 // Support ObjC property references (explicit property getter).689 {R"cpp(__attribute__((objc_root_class))690 @interface Foo691 @property int prop1;692 @end693 @implementation Foo694 - (void)method {695 int x = [[self.prop1]] + 1;696 }697 @end)cpp",698 R"cpp(__attribute__((objc_root_class))699 @interface Foo700 @property int prop1;701 @end702 @implementation Foo703 - (void)method {704 int placeholder = self.prop1; int x = placeholder + 1;705 }706 @end)cpp"},707 // Support ObjC property references (implicit property getter).708 {R"cpp(__attribute__((objc_root_class))709 @interface Foo710 - (int)method1;711 @end712 @implementation Foo713 - (void)method {714 int x = [[self.method1]] + 1;715 }716 @end)cpp",717 R"cpp(__attribute__((objc_root_class))718 @interface Foo719 - (int)method1;720 @end721 @implementation Foo722 - (void)method {723 int placeholder = self.method1; int x = placeholder + 1;724 }725 @end)cpp"},726 };727 for (const auto &IO : InputOutputs) {728 EXPECT_EQ(IO.second, apply(IO.first)) << IO.first;729 }730}731 732} // namespace733} // namespace clangd734} // namespace clang735