clang-tools 20.0.0git
ExtractVariableTests.cpp
Go to the documentation of this file.
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-exception
6//
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 statement
27 return [[[[t.b[[a]]r]]([[t.z]])]];
28 }
29 void f() {
30 int a = 5 + [[4 * [[[[xyz]]()]]]];
31 // multivariable initialization
32 if(1)
33 int x = [[1]] + 1, y = a + [[1]], a = [[1]] + 2, z = a + 1;
34 // if without else
35 if([[1]])
36 a = [[1]] + 1;
37 // if with else
38 if(a < [[3]])
39 if(a == [[4]])
40 a = [[5]] + 1;
41 else
42 a = [[5]] + 1;
43 else if (a < [[4]])
44 a = [[4]] + 1;
45 else
46 a = [[5]] + 1;
47 // for loop
48 for(a = [[1]] + 1; a > [[[[3]] + [[4]]]]; a++)
49 a = [[2]] + 1;
50 // while
51 while(a < [[1]])
52 a = [[1]] + 1;
53 // do while
54 do
55 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 crash
71 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 Foo
95 @end
96 @implementation Foo
97 - (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 crash
106 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 }
120 int z = [[1]];
121 } t;
122 int x = [[1 + 2]];
123 int y;
124 y = [[1 + 2]];
125 return [[t]].bar([[t]].z);
126 }
127 void v() { return; }
128
129 // function default argument
130 void f(int b = [[1]]) {
131 // empty selection
132 int a = ^1 ^+ ^2;
133 // void expressions
134 auto i = new int, j = new int;
135 [[[[delete i]], delete j]];
136 [[v]]();
137 // if
138 if(1)
139 int x = 1, y = a + 1, a = 1, z = [[a + 1]];
140 if(int a = 1)
141 if([[a + 1]] == 4)
142 a = [[[[a]] +]] 1;
143 // for loop
144 for(int a = 1, b = 2, c = 3; a > [[b + c]]; [[a++]])
145 a = [[a + 1]];
146 // lambda
147 auto lamb = [&[[a]], &[[b]]](int r = [[1]]) {return 1;};
148 // assignment
149 xyz([[a = 5]]);
150 xyz([[a *= 5]]);
151 // Variable DeclRefExpr
152 a = [[b]];
153 a = [[xyz()]];
154 // expression statement of type void
155 [[v()]];
156 while (a)
157 [[++a]];
158 // label statement
159 goto label;
160 label:
161 a = [[1]];
162
163 // lambdas: captures
164 int x = 0;
165 [ [[=]] ](){};
166 [ [[&]] ](){};
167 [ [[x]] ](){};
168 [ [[&x]] ](){};
169 [y = [[x]] ](){};
170 [ [[y = x]] ](){};
171
172 // lambdas: default args, cannot extract into function-local scope
173 [](int x = [[10]]){};
174 [](auto h = [[ [i = [](){}](){} ]]) {};
175
176 // lambdas: default args
177 // Extracting from capture initializers is usually fine,
178 // but not if the capture itself is nested inside a default argument
179 [](auto h = [i = [[ [](){} ]]](){}) {};
180 [](auto h = [i = [[ 42 ]]](){}) {};
181
182 // lambdas: scope
183 if (int a = 1)
184 if ([[ [&](){ return a + 1; } ]]() == 4)
185 a = a + 1;
186
187 for (int c = 0; [[ [&]() { return c < b; } ]](); ++c) {
188 }
189 for (int c = 0; [[ [&]() { return c < b; } () ]]; ++c) {
190 }
191
192 // lambdas: scope with structured binding
193 struct Coordinates {
194 int x{};
195 int y{};
196 };
197 Coordinates c{};
198 if (const auto [x, y] = c; x > y)
199 auto f = [[ [&]() { return x + y; } ]];
200
201 // lambdas: referencing outside variables that block extraction
202 // in trailing return type or in a decltype used
203 // by a parameter
204 if (int a = 1)
205 if ([[ []() -> decltype(a) { return 1; } ]] () == 4)
206 a = a + 1;
207 if (int a = 1)
208 if ([[ [](int x = decltype(a){}) { return 1; } ]] () == 4)
209 a = a + 1;
210 if (int a = 1)
211 if ([[ [](decltype(a) x) { return 1; } ]] (42) == 4)
212 a = a + 1;
213 }
214 )cpp";
215 EXPECT_UNAVAILABLE(UnavailableCases);
216
217 ExtraArgs = {"-std=c++20"};
218 const char *UnavailableCasesCXX20 = R"cpp(
219 template <typename T>
220 concept Constraint = requires (T t) { true; };
221 void foo() {
222 // lambdas: referencing outside variables that block extraction
223 // in requires clause or defaulted explicit template parameters
224 if (int a = 1)
225 if ([[ [](auto b) requires (Constraint<decltype(a)> && Constraint<decltype(b)>) { return true; } ]] (a))
226 a = a + 1;
227
228 if (int a = 1)
229 if ([[ []<typename T = decltype(a)>(T b) { return true; } ]] (a))
230 a = a + 1;
231 }
232 )cpp";
233 EXPECT_UNAVAILABLE(UnavailableCasesCXX20);
234 ExtraArgs.clear();
235
236 // vector of pairs of input and output strings
237 std::vector<std::pair<std::string, std::string>> InputOutputs = {
238 // extraction from variable declaration/assignment
239 {R"cpp(void varDecl() {
240 int a = 5 * (4 + (3 [[- 1)]]);
241 })cpp",
242 R"cpp(void varDecl() {
243 auto placeholder = (3 - 1); int a = 5 * (4 + placeholder);
244 })cpp"},
245 // FIXME: extraction from switch case
246 /*{R"cpp(void f(int a) {
247 if(1)
248 while(a < 1)
249 switch (1) {
250 case 1:
251 a = [[1 + 2]];
252 break;
253 default:
254 break;
255 }
256 })cpp",
257 R"cpp(void f(int a) {
258 auto placeholder = 1 + 2; if(1)
259 while(a < 1)
260 switch (1) {
261 case 1:
262 a = placeholder;
263 break;
264 default:
265 break;
266 }
267 })cpp"},*/
268 // Macros
269 {R"cpp(#define PLUS(x) x++
270 void f(int a) {
271 int y = PLUS([[1+a]]);
272 })cpp",
273 /*FIXME: It should be extracted like this.
274 R"cpp(#define PLUS(x) x++
275 void f(int a) {
276 auto placeholder = 1+a; int y = PLUS(placeholder);
277 })cpp"},*/
278 R"cpp(#define PLUS(x) x++
279 void f(int a) {
280 auto placeholder = PLUS(1+a); int y = placeholder;
281 })cpp"},
282 // ensure InsertionPoint isn't inside a macro
283 {R"cpp(#define LOOP(x) while (1) {a = x;}
284 void f(int a) {
285 if(1)
286 LOOP(5 + [[3]])
287 })cpp",
288 R"cpp(#define LOOP(x) while (1) {a = x;}
289 void f(int a) {
290 auto placeholder = 3; if(1)
291 LOOP(5 + placeholder)
292 })cpp"},
293 {R"cpp(#define LOOP(x) do {x;} while(1);
294 void f(int a) {
295 if(1)
296 LOOP(5 + [[3]])
297 })cpp",
298 R"cpp(#define LOOP(x) do {x;} while(1);
299 void f(int a) {
300 auto placeholder = 3; if(1)
301 LOOP(5 + placeholder)
302 })cpp"},
303 // attribute testing
304 {R"cpp(void f(int a) {
305 [ [gsl::suppress("type")] ] for (;;) a = [[1]] + 1;
306 })cpp",
307 R"cpp(void f(int a) {
308 auto placeholder = 1; [ [gsl::suppress("type")] ] for (;;) a = placeholder + 1;
309 })cpp"},
310 // MemberExpr
311 {R"cpp(class T {
312 T f() {
313 return [[T().f()]].f();
314 }
315 };)cpp",
316 R"cpp(class T {
317 T f() {
318 auto placeholder = T().f(); return placeholder.f();
319 }
320 };)cpp"},
321 // Function DeclRefExpr
322 {R"cpp(int f() {
323 return [[f]]();
324 })cpp",
325 R"cpp(int f() {
326 auto placeholder = f(); return placeholder;
327 })cpp"},
328 // FIXME: Wrong result for \[\[clang::uninitialized\]\] int b = [[1]];
329 // since the attr is inside the DeclStmt and the bounds of
330 // DeclStmt don't cover the attribute.
331
332 // Binary subexpressions
333 {R"cpp(void f() {
334 int x = 1 + [[2 + 3 + 4]] + 5;
335 })cpp",
336 R"cpp(void f() {
337 auto placeholder = 2 + 3 + 4; int x = 1 + placeholder + 5;
338 })cpp"},
339 {R"cpp(void f() {
340 int x = [[1 + 2 + 3]] + 4 + 5;
341 })cpp",
342 R"cpp(void f() {
343 auto placeholder = 1 + 2 + 3; int x = placeholder + 4 + 5;
344 })cpp"},
345 {R"cpp(void f() {
346 int x = 1 + 2 + [[3 + 4 + 5]];
347 })cpp",
348 R"cpp(void f() {
349 auto placeholder = 3 + 4 + 5; int x = 1 + 2 + placeholder;
350 })cpp"},
351 // Non-associative operations have no special support
352 {R"cpp(void f() {
353 int x = 1 - [[2 - 3 - 4]] - 5;
354 })cpp",
355 R"cpp(void f() {
356 auto placeholder = 1 - 2 - 3 - 4; int x = placeholder - 5;
357 })cpp"},
358 // A mix of associative operators isn't associative.
359 {R"cpp(void f() {
360 int x = 0 + 1 * [[2 + 3]] * 4 + 5;
361 })cpp",
362 R"cpp(void f() {
363 auto placeholder = 1 * 2 + 3 * 4; int x = 0 + placeholder + 5;
364 })cpp"},
365 // Overloaded operators are supported, we assume associativity
366 // as if they were built-in.
367 {R"cpp(struct S {
368 S(int);
369 };
370 S operator+(S, S);
371
372 void f() {
373 S x = S(1) + [[S(2) + S(3) + S(4)]] + S(5);
374 })cpp",
375 R"cpp(struct S {
376 S(int);
377 };
378 S operator+(S, S);
379
380 void f() {
381 auto placeholder = S(2) + S(3) + S(4); S x = S(1) + placeholder + S(5);
382 })cpp"},
383 // lambda expressions
384 {R"cpp(template <typename T> void f(T) {}
385 void f2() {
386 f([[ [](){ return 42; }]]);
387 }
388 )cpp",
389 R"cpp(template <typename T> void f(T) {}
390 void f2() {
391 auto placeholder = [](){ return 42; }; f( placeholder);
392 }
393 )cpp"},
394 {R"cpp(template <typename T> void f(T) {}
395 void f2() {
396 f([x = [[40 + 2]] ](){ return 42; });
397 }
398 )cpp",
399 R"cpp(template <typename T> void f(T) {}
400 void f2() {
401 auto placeholder = 40 + 2; f([x = placeholder ](){ return 42; });
402 }
403 )cpp"},
404 {R"cpp(auto foo(int VarA) {
405 return [VarA]() {
406 return [[ [VarA, VarC = 42 + VarA](int VarB) { return VarA + VarB + VarC; }]];
407 };
408 }
409 )cpp",
410 R"cpp(auto foo(int VarA) {
411 return [VarA]() {
412 auto placeholder = [VarA, VarC = 42 + VarA](int VarB) { return VarA + VarB + VarC; }; return placeholder;
413 };
414 }
415 )cpp"},
416 {R"cpp(template <typename T> void f(T) {}
417 void f2(int var) {
418 f([[ [&var](){ auto internal_val = 42; return var + internal_val; }]]);
419 }
420 )cpp",
421 R"cpp(template <typename T> void f(T) {}
422 void f2(int var) {
423 auto placeholder = [&var](){ auto internal_val = 42; return var + internal_val; }; f( placeholder);
424 }
425 )cpp"},
426 {R"cpp(template <typename T> void f(T) { }
427 struct A {
428 void f2(int& var) {
429 auto local_var = 42;
430 f([[ [&var, &local_var, this]() {
431 auto internal_val = 42;
432 return var + local_var + internal_val + member;
433 }]]);
434 }
435
436 int member = 42;
437};
438 )cpp",
439 R"cpp(template <typename T> void f(T) { }
440 struct A {
441 void f2(int& var) {
442 auto local_var = 42;
443 auto placeholder = [&var, &local_var, this]() {
444 auto internal_val = 42;
445 return var + local_var + internal_val + member;
446 }; f( placeholder);
447 }
448
449 int member = 42;
450};
451 )cpp"},
452 {R"cpp(void f() { auto x = +[[ [](){ return 42; }]]; })cpp",
453 R"cpp(void f() { auto placeholder = [](){ return 42; }; auto x = + placeholder; })cpp"},
454 {R"cpp(
455 template <typename T>
456 auto sink(T f) { return f(); }
457 int bar() {
458 return sink([[ []() { return 42; }]]);
459 }
460 )cpp",
461 R"cpp(
462 template <typename T>
463 auto sink(T f) { return f(); }
464 int bar() {
465 auto placeholder = []() { return 42; }; return sink( placeholder);
466 }
467 )cpp"},
468 {R"cpp(
469 int main() {
470 if (int a = 1) {
471 if ([[ [&](){ return a + 1; } ]]() == 4)
472 a = a + 1;
473 }
474 })cpp",
475 R"cpp(
476 int main() {
477 if (int a = 1) {
478 auto placeholder = [&](){ return a + 1; }; if ( placeholder () == 4)
479 a = a + 1;
480 }
481 })cpp"},
482 {R"cpp(
483 int main() {
484 if (int a = 1) {
485 if ([[ [&](){ return a + 1; }() ]] == 4)
486 a = a + 1;
487 }
488 })cpp",
489 R"cpp(
490 int main() {
491 if (int a = 1) {
492 auto placeholder = [&](){ return a + 1; }(); if ( placeholder == 4)
493 a = a + 1;
494 }
495 })cpp"},
496 {R"cpp(
497 int func() { return 0; }
498 int main() {
499 [[func()]];
500 })cpp",
501 R"cpp(
502 int func() { return 0; }
503 int main() {
504 auto placeholder = func();
505 })cpp"},
506 {R"cpp(
507 template <typename T>
508 auto call(T t) { return t(); }
509
510 int main() {
511 return [[ call([](){ int a = 1; return a + 1; }) ]] + 5;
512 })cpp",
513 R"cpp(
514 template <typename T>
515 auto call(T t) { return t(); }
516
517 int main() {
518 auto placeholder = call([](){ int a = 1; return a + 1; }); return placeholder + 5;
519 })cpp"},
520 {R"cpp(
521 class Foo {
522 int bar() {
523 return [f = [[ [this](int g) { return g + x; } ]] ]() { return 42; }();
524 }
525 int x;
526 };
527 )cpp",
528 R"cpp(
529 class Foo {
530 int bar() {
531 auto placeholder = [this](int g) { return g + x; }; return [f = placeholder ]() { return 42; }();
532 }
533 int x;
534 };
535 )cpp"},
536 {R"cpp(
537 int main() {
538 return [[ []() { return 42; }() ]];
539 })cpp",
540 R"cpp(
541 int main() {
542 auto placeholder = []() { return 42; }(); return placeholder ;
543 })cpp"},
544 {R"cpp(
545 template <typename ...Ts>
546 void foo(Ts ...args) {
547 auto x = +[[ [&args...]() {} ]];
548 }
549 )cpp",
550 R"cpp(
551 template <typename ...Ts>
552 void foo(Ts ...args) {
553 auto placeholder = [&args...]() {}; auto x = + placeholder ;
554 }
555 )cpp"},
556 {R"cpp(
557 struct Coordinates {
558 int x{};
559 int y{};
560 };
561
562 int main() {
563 Coordinates c = {};
564 const auto [x, y] = c;
565 auto f = [[ [&]() { return x + y; } ]]();
566 }
567 )cpp",
568 R"cpp(
569 struct Coordinates {
570 int x{};
571 int y{};
572 };
573
574 int main() {
575 Coordinates c = {};
576 const auto [x, y] = c;
577 auto placeholder = [&]() { return x + y; }; auto f = placeholder ();
578 }
579 )cpp"},
580 {R"cpp(
581 struct Coordinates {
582 int x{};
583 int y{};
584 };
585
586 int main() {
587 Coordinates c = {};
588 if (const auto [x, y] = c; x > y) {
589 auto f = [[ [&]() { return x + y; } ]]();
590 }
591 }
592 )cpp",
593 R"cpp(
594 struct Coordinates {
595 int x{};
596 int y{};
597 };
598
599 int main() {
600 Coordinates c = {};
601 if (const auto [x, y] = c; x > y) {
602 auto placeholder = [&]() { return x + y; }; auto f = placeholder ();
603 }
604 }
605 )cpp"},
606 // Don't try to analyze across macro boundaries
607 // FIXME: it'd be nice to do this someday (in a safe way)
608 {R"cpp(#define ECHO(X) X
609 void f() {
610 int x = 1 + [[ECHO(2 + 3) + 4]] + 5;
611 })cpp",
612 R"cpp(#define ECHO(X) X
613 void f() {
614 auto placeholder = 1 + ECHO(2 + 3) + 4; int x = placeholder + 5;
615 })cpp"},
616 {R"cpp(#define ECHO(X) X
617 void f() {
618 int x = 1 + [[ECHO(2) + ECHO(3) + 4]] + 5;
619 })cpp",
620 R"cpp(#define ECHO(X) X
621 void f() {
622 auto placeholder = 1 + ECHO(2) + ECHO(3) + 4; int x = placeholder + 5;
623 })cpp"},
624 };
625 for (const auto &IO : InputOutputs) {
626 EXPECT_EQ(IO.second, apply(IO.first)) << IO.first;
627 }
628
629 ExtraArgs = {"-xc"};
630 InputOutputs = {
631 // Function Pointers
632 {R"cpp(struct Handlers {
633 void (*handlerFunc)(int);
634 };
635 void runFunction(void (*func)(int)) {}
636 void f(struct Handlers *handler) {
637 runFunction([[handler->handlerFunc]]);
638 })cpp",
639 R"cpp(struct Handlers {
640 void (*handlerFunc)(int);
641 };
642 void runFunction(void (*func)(int)) {}
643 void f(struct Handlers *handler) {
644 void (*placeholder)(int) = handler->handlerFunc; runFunction(placeholder);
645 })cpp"},
646 {R"cpp(int (*foo(char))(int);
647 void bar() {
648 (void)[[foo('c')]];
649 })cpp",
650 R"cpp(int (*foo(char))(int);
651 void bar() {
652 int (*placeholder)(int) = foo('c'); (void)placeholder;
653 })cpp"},
654 // Arithmetic on typedef types preserves typedef types
655 {R"cpp(typedef long NSInteger;
656 void varDecl() {
657 NSInteger a = 2 * 5;
658 NSInteger b = [[a * 7]] + 3;
659 })cpp",
660 R"cpp(typedef long NSInteger;
661 void varDecl() {
662 NSInteger a = 2 * 5;
663 NSInteger placeholder = a * 7; NSInteger b = placeholder + 3;
664 })cpp"},
665 };
666 for (const auto &IO : InputOutputs) {
667 EXPECT_EQ(IO.second, apply(IO.first)) << IO.first;
668 }
669
670 ExtraArgs = {"-xobjective-c"};
671 EXPECT_UNAVAILABLE(R"cpp(
672 __attribute__((objc_root_class))
673 @interface Foo
674 - (void)setMethod1:(int)a;
675 - (int)method1;
676 @property int prop1;
677 @end
678 @implementation Foo
679 - (void)method {
680 [[self.method1]] = 1;
681 [[self.method1]] += 1;
682 [[self.prop1]] = 1;
683 [[self.prop1]] += 1;
684 }
685 @end)cpp");
686 InputOutputs = {
687 // Support ObjC property references (explicit property getter).
688 {R"cpp(__attribute__((objc_root_class))
689 @interface Foo
690 @property int prop1;
691 @end
692 @implementation Foo
693 - (void)method {
694 int x = [[self.prop1]] + 1;
695 }
696 @end)cpp",
697 R"cpp(__attribute__((objc_root_class))
698 @interface Foo
699 @property int prop1;
700 @end
701 @implementation Foo
702 - (void)method {
703 int placeholder = self.prop1; int x = placeholder + 1;
704 }
705 @end)cpp"},
706 // Support ObjC property references (implicit property getter).
707 {R"cpp(__attribute__((objc_root_class))
708 @interface Foo
709 - (int)method1;
710 @end
711 @implementation Foo
712 - (void)method {
713 int x = [[self.method1]] + 1;
714 }
715 @end)cpp",
716 R"cpp(__attribute__((objc_root_class))
717 @interface Foo
718 - (int)method1;
719 @end
720 @implementation Foo
721 - (void)method {
722 int placeholder = self.method1; int x = placeholder + 1;
723 }
724 @end)cpp"},
725 };
726 for (const auto &IO : InputOutputs) {
727 EXPECT_EQ(IO.second, apply(IO.first)) << IO.first;
728 }
729}
730
731} // namespace
732} // namespace clangd
733} // namespace clang
#define TWEAK_TEST(TweakID)
Definition: TweakTesting.h:107
#define EXPECT_AVAILABLE(MarkedCode)
Definition: TweakTesting.h:123
#define EXPECT_UNAVAILABLE(MarkedCode)
Definition: TweakTesting.h:124
TEST_F(BackgroundIndexTest, NoCrashOnErrorFile)
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//