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