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 | } |
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 | // statement expression |
155 | [[xyz()]]; |
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 | template <typename T> |
498 | auto call(T t) { return t(); } |
499 | |
500 | int main() { |
501 | return [[ call([](){ int a = 1; return a + 1; }) ]] + 5; |
502 | })cpp" , |
503 | R"cpp( |
504 | template <typename T> |
505 | auto call(T t) { return t(); } |
506 | |
507 | int main() { |
508 | auto placeholder = call([](){ int a = 1; return a + 1; }); return placeholder + 5; |
509 | })cpp" }, |
510 | {R"cpp( |
511 | class Foo { |
512 | int bar() { |
513 | return [f = [[ [this](int g) { return g + x; } ]] ]() { return 42; }(); |
514 | } |
515 | int x; |
516 | }; |
517 | )cpp" , |
518 | R"cpp( |
519 | class Foo { |
520 | int bar() { |
521 | auto placeholder = [this](int g) { return g + x; }; return [f = placeholder ]() { return 42; }(); |
522 | } |
523 | int x; |
524 | }; |
525 | )cpp" }, |
526 | {R"cpp( |
527 | int main() { |
528 | return [[ []() { return 42; }() ]]; |
529 | })cpp" , |
530 | R"cpp( |
531 | int main() { |
532 | auto placeholder = []() { return 42; }(); return placeholder ; |
533 | })cpp" }, |
534 | {R"cpp( |
535 | template <typename ...Ts> |
536 | void foo(Ts ...args) { |
537 | auto x = +[[ [&args...]() {} ]]; |
538 | } |
539 | )cpp" , |
540 | R"cpp( |
541 | template <typename ...Ts> |
542 | void foo(Ts ...args) { |
543 | auto placeholder = [&args...]() {}; auto x = + placeholder ; |
544 | } |
545 | )cpp" }, |
546 | {R"cpp( |
547 | struct Coordinates { |
548 | int x{}; |
549 | int y{}; |
550 | }; |
551 | |
552 | int main() { |
553 | Coordinates c = {}; |
554 | const auto [x, y] = c; |
555 | auto f = [[ [&]() { return x + y; } ]](); |
556 | } |
557 | )cpp" , |
558 | R"cpp( |
559 | struct Coordinates { |
560 | int x{}; |
561 | int y{}; |
562 | }; |
563 | |
564 | int main() { |
565 | Coordinates c = {}; |
566 | const auto [x, y] = c; |
567 | auto placeholder = [&]() { return x + y; }; auto f = placeholder (); |
568 | } |
569 | )cpp" }, |
570 | {R"cpp( |
571 | struct Coordinates { |
572 | int x{}; |
573 | int y{}; |
574 | }; |
575 | |
576 | int main() { |
577 | Coordinates c = {}; |
578 | if (const auto [x, y] = c; x > y) { |
579 | auto f = [[ [&]() { return x + y; } ]](); |
580 | } |
581 | } |
582 | )cpp" , |
583 | R"cpp( |
584 | struct Coordinates { |
585 | int x{}; |
586 | int y{}; |
587 | }; |
588 | |
589 | int main() { |
590 | Coordinates c = {}; |
591 | if (const auto [x, y] = c; x > y) { |
592 | auto placeholder = [&]() { return x + y; }; auto f = placeholder (); |
593 | } |
594 | } |
595 | )cpp" }, |
596 | // Don't try to analyze across macro boundaries |
597 | // FIXME: it'd be nice to do this someday (in a safe way) |
598 | {R"cpp(#define ECHO(X) X |
599 | void f() { |
600 | int x = 1 + [[ECHO(2 + 3) + 4]] + 5; |
601 | })cpp" , |
602 | R"cpp(#define ECHO(X) X |
603 | void f() { |
604 | auto placeholder = 1 + ECHO(2 + 3) + 4; int x = placeholder + 5; |
605 | })cpp" }, |
606 | {R"cpp(#define ECHO(X) X |
607 | void f() { |
608 | int x = 1 + [[ECHO(2) + ECHO(3) + 4]] + 5; |
609 | })cpp" , |
610 | R"cpp(#define ECHO(X) X |
611 | void f() { |
612 | auto placeholder = 1 + ECHO(2) + ECHO(3) + 4; int x = placeholder + 5; |
613 | })cpp" }, |
614 | }; |
615 | for (const auto &IO : InputOutputs) { |
616 | EXPECT_EQ(IO.second, apply(IO.first)) << IO.first; |
617 | } |
618 | |
619 | ExtraArgs = {"-xc" }; |
620 | InputOutputs = { |
621 | // Function Pointers |
622 | {R"cpp(struct Handlers { |
623 | void (*handlerFunc)(int); |
624 | }; |
625 | void runFunction(void (*func)(int)) {} |
626 | void f(struct Handlers *handler) { |
627 | runFunction([[handler->handlerFunc]]); |
628 | })cpp" , |
629 | R"cpp(struct Handlers { |
630 | void (*handlerFunc)(int); |
631 | }; |
632 | void runFunction(void (*func)(int)) {} |
633 | void f(struct Handlers *handler) { |
634 | void (*placeholder)(int) = handler->handlerFunc; runFunction(placeholder); |
635 | })cpp" }, |
636 | {R"cpp(int (*foo(char))(int); |
637 | void bar() { |
638 | (void)[[foo('c')]]; |
639 | })cpp" , |
640 | R"cpp(int (*foo(char))(int); |
641 | void bar() { |
642 | int (*placeholder)(int) = foo('c'); (void)placeholder; |
643 | })cpp" }, |
644 | // Arithmetic on typedef types preserves typedef types |
645 | {R"cpp(typedef long NSInteger; |
646 | void varDecl() { |
647 | NSInteger a = 2 * 5; |
648 | NSInteger b = [[a * 7]] + 3; |
649 | })cpp" , |
650 | R"cpp(typedef long NSInteger; |
651 | void varDecl() { |
652 | NSInteger a = 2 * 5; |
653 | NSInteger placeholder = a * 7; NSInteger b = placeholder + 3; |
654 | })cpp" }, |
655 | }; |
656 | for (const auto &IO : InputOutputs) { |
657 | EXPECT_EQ(IO.second, apply(IO.first)) << IO.first; |
658 | } |
659 | |
660 | ExtraArgs = {"-xobjective-c" }; |
661 | EXPECT_UNAVAILABLE(R"cpp( |
662 | __attribute__((objc_root_class)) |
663 | @interface Foo |
664 | - (void)setMethod1:(int)a; |
665 | - (int)method1; |
666 | @property int prop1; |
667 | @end |
668 | @implementation Foo |
669 | - (void)method { |
670 | [[self.method1]] = 1; |
671 | [[self.method1]] += 1; |
672 | [[self.prop1]] = 1; |
673 | [[self.prop1]] += 1; |
674 | } |
675 | @end)cpp" ); |
676 | InputOutputs = { |
677 | // Support ObjC property references (explicit property getter). |
678 | {R"cpp(__attribute__((objc_root_class)) |
679 | @interface Foo |
680 | @property int prop1; |
681 | @end |
682 | @implementation Foo |
683 | - (void)method { |
684 | int x = [[self.prop1]] + 1; |
685 | } |
686 | @end)cpp" , |
687 | R"cpp(__attribute__((objc_root_class)) |
688 | @interface Foo |
689 | @property int prop1; |
690 | @end |
691 | @implementation Foo |
692 | - (void)method { |
693 | int placeholder = self.prop1; int x = placeholder + 1; |
694 | } |
695 | @end)cpp" }, |
696 | // Support ObjC property references (implicit property getter). |
697 | {R"cpp(__attribute__((objc_root_class)) |
698 | @interface Foo |
699 | - (int)method1; |
700 | @end |
701 | @implementation Foo |
702 | - (void)method { |
703 | int x = [[self.method1]] + 1; |
704 | } |
705 | @end)cpp" , |
706 | R"cpp(__attribute__((objc_root_class)) |
707 | @interface Foo |
708 | - (int)method1; |
709 | @end |
710 | @implementation Foo |
711 | - (void)method { |
712 | int placeholder = self.method1; int x = placeholder + 1; |
713 | } |
714 | @end)cpp" }, |
715 | }; |
716 | for (const auto &IO : InputOutputs) { |
717 | EXPECT_EQ(IO.second, apply(IO.first)) << IO.first; |
718 | } |
719 | } |
720 | |
721 | } // namespace |
722 | } // namespace clangd |
723 | } // namespace clang |
724 | |