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 | |
13 | namespace clang { |
14 | namespace clangd { |
15 | namespace { |
16 | |
17 | TWEAK_TEST(ExtractVariable); |
18 | |
19 | TEST_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 | 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 argument |
131 | void f(int b = [[1]]) { |
132 | // empty selection |
133 | int a = ^1 ^+ ^2; |
134 | // void expressions |
135 | auto i = new int, j = new int; |
136 | [[[[delete i]], delete j]]; |
137 | [[v]](); |
138 | // if |
139 | 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 loop |
145 | for(int a = 1, b = 2, c = 3; a > [[b + c]]; [[a++]]) |
146 | a = [[a + 1]]; |
147 | // lambda |
148 | auto lamb = [&[[a]], &[[b]]](int r = [[1]]) {return 1;}; |
149 | // assignment |
150 | xyz([[a = 5]]); |
151 | xyz([[a *= 5]]); |
152 | // Variable DeclRefExpr |
153 | a = [[b]]; |
154 | a = [[xyz()]]; |
155 | // expression statement of type void |
156 | [[v()]]; |
157 | while (a) |
158 | [[++a]]; |
159 | // label statement |
160 | goto label; |
161 | label: |
162 | a = [[1]]; |
163 | |
164 | // lambdas: captures |
165 | 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 scope |
174 | [](int x = [[10]]){}; |
175 | [](auto h = [[ [i = [](){}](){} ]]) {}; |
176 | |
177 | // lambdas: default args |
178 | // Extracting from capture initializers is usually fine, |
179 | // but not if the capture itself is nested inside a default argument |
180 | [](auto h = [i = [[ [](){} ]]](){}) {}; |
181 | [](auto h = [i = [[ 42 ]]](){}) {}; |
182 | |
183 | // lambdas: scope |
184 | 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 binding |
194 | 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 extraction |
203 | // in trailing return type or in a decltype used |
204 | // by a parameter |
205 | 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 extraction |
224 | // in requires clause or defaulted explicit template parameters |
225 | 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 strings |
238 | std::vector<std::pair<std::string, std::string>> InputOutputs = { |
239 | // extraction from variable declaration/assignment |
240 | {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 case |
247 | /*{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 | // Macros |
270 | {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 macro |
284 | {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 testing |
305 | {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 | // MemberExpr |
312 | {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 DeclRefExpr |
323 | {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 of |
331 | // DeclStmt don't cover the attribute. |
332 | |
333 | // Binary subexpressions |
334 | {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 support |
353 | {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 associativity |
367 | // 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 expressions |
385 | {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 boundaries |
608 | // FIXME: it'd be nice to do this someday (in a safe way) |
609 | {R"cpp(#define ECHO(X) X |
610 | void f() { |
611 | int x = 1 + [[ECHO(2 + 3) + 4]] + 5; |
612 | })cpp" , |
613 | R"cpp(#define ECHO(X) X |
614 | void f() { |
615 | auto placeholder = 1 + ECHO(2 + 3) + 4; int x = placeholder + 5; |
616 | })cpp" }, |
617 | {R"cpp(#define ECHO(X) X |
618 | void f() { |
619 | int x = 1 + [[ECHO(2) + ECHO(3) + 4]] + 5; |
620 | })cpp" , |
621 | R"cpp(#define ECHO(X) X |
622 | 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 Pointers |
633 | {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 types |
656 | {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 Foo |
675 | - (void)setMethod1:(int)a; |
676 | - (int)method1; |
677 | @property int prop1; |
678 | @end |
679 | @implementation Foo |
680 | - (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 Foo |
691 | @property int prop1; |
692 | @end |
693 | @implementation Foo |
694 | - (void)method { |
695 | int x = [[self.prop1]] + 1; |
696 | } |
697 | @end)cpp" , |
698 | R"cpp(__attribute__((objc_root_class)) |
699 | @interface Foo |
700 | @property int prop1; |
701 | @end |
702 | @implementation Foo |
703 | - (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 Foo |
710 | - (int)method1; |
711 | @end |
712 | @implementation Foo |
713 | - (void)method { |
714 | int x = [[self.method1]] + 1; |
715 | } |
716 | @end)cpp" , |
717 | R"cpp(__attribute__((objc_root_class)) |
718 | @interface Foo |
719 | - (int)method1; |
720 | @end |
721 | @implementation Foo |
722 | - (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 | } // namespace |
733 | } // namespace clangd |
734 | } // namespace clang |
735 | |