[AMDGPU][AsmParser][NFC] Translate parsed MIMG instructions to MCInsts automatically.
[llvm-project.git] / clang-tools-extra / clangd / unittests / tweaks / ExtractFunctionTests.cpp
blobdec63d454d52c6f71e911f4215cd9d8bf9280967
1 //===-- ExtractFunctionTests.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 //===----------------------------------------------------------------------===//
9 #include "TweakTesting.h"
10 #include "gmock/gmock.h"
11 #include "gtest/gtest.h"
13 using ::testing::HasSubstr;
14 using ::testing::StartsWith;
16 namespace clang {
17 namespace clangd {
18 namespace {
20 TWEAK_TEST(ExtractFunction);
22 TEST_F(ExtractFunctionTest, FunctionTest) {
23 Context = Function;
25 // Root statements should have common parent.
26 EXPECT_EQ(apply("for(;;) [[1+2; 1+2;]]"), "unavailable");
27 // Expressions aren't extracted.
28 EXPECT_EQ(apply("int x = 0; [[x++;]]"), "unavailable");
29 // We don't support extraction from lambdas.
30 EXPECT_EQ(apply("auto lam = [](){ [[int x;]] }; "), "unavailable");
31 // Partial statements aren't extracted.
32 EXPECT_THAT(apply("int [[x = 0]];"), "unavailable");
33 // FIXME: Support hoisting.
34 EXPECT_THAT(apply(" [[int a = 5;]] a++; "), "unavailable");
36 // Ensure that end of Zone and Beginning of PostZone being adjacent doesn't
37 // lead to break being included in the extraction zone.
38 EXPECT_THAT(apply("for(;;) { [[int x;]]break; }"), HasSubstr("extracted"));
39 // FIXME: ExtractFunction should be unavailable inside loop construct
40 // initializer/condition.
41 EXPECT_THAT(apply(" for([[int i = 0;]];);"), HasSubstr("extracted"));
42 // Extract certain return
43 EXPECT_THAT(apply(" if(true) [[{ return; }]] "), HasSubstr("extracted"));
44 // Don't extract uncertain return
45 EXPECT_THAT(apply(" if(true) [[if (false) return;]] "),
46 StartsWith("unavailable"));
47 EXPECT_THAT(
48 apply("#define RETURN_IF_ERROR(x) if (x) return\nRETU^RN_IF_ERROR(4);"),
49 StartsWith("unavailable"));
51 FileName = "a.c";
52 EXPECT_THAT(apply(" for([[int i = 0;]];);"), HasSubstr("unavailable"));
55 TEST_F(ExtractFunctionTest, FileTest) {
56 // Check all parameters are in order
57 std::string ParameterCheckInput = R"cpp(
58 struct Foo {
59 int x;
61 void f(int a) {
62 int b;
63 int *ptr = &a;
64 Foo foo;
65 [[a += foo.x + b;
66 *ptr++;]]
67 })cpp";
68 std::string ParameterCheckOutput = R"cpp(
69 struct Foo {
70 int x;
72 void extracted(int &a, int &b, int * &ptr, Foo &foo) {
73 a += foo.x + b;
74 *ptr++;
76 void f(int a) {
77 int b;
78 int *ptr = &a;
79 Foo foo;
80 extracted(a, b, ptr, foo);
81 })cpp";
82 EXPECT_EQ(apply(ParameterCheckInput), ParameterCheckOutput);
84 // Check const qualifier
85 std::string ConstCheckInput = R"cpp(
86 void f(const int c) {
87 [[while(c) {}]]
88 })cpp";
89 std::string ConstCheckOutput = R"cpp(
90 void extracted(const int &c) {
91 while(c) {}
93 void f(const int c) {
94 extracted(c);
95 })cpp";
96 EXPECT_EQ(apply(ConstCheckInput), ConstCheckOutput);
98 // Check const qualifier with namespace
99 std::string ConstNamespaceCheckInput = R"cpp(
100 namespace X { struct Y { int z; }; }
101 int f(const X::Y &y) {
102 [[return y.z + y.z;]]
103 })cpp";
104 std::string ConstNamespaceCheckOutput = R"cpp(
105 namespace X { struct Y { int z; }; }
106 int extracted(const X::Y &y) {
107 return y.z + y.z;
109 int f(const X::Y &y) {
110 return extracted(y);
111 })cpp";
112 EXPECT_EQ(apply(ConstNamespaceCheckInput), ConstNamespaceCheckOutput);
114 // Don't extract when we need to make a function as a parameter.
115 EXPECT_THAT(apply("void f() { [[int a; f();]] }"), StartsWith("fail"));
117 std::string MethodInput = R"cpp(
118 class T {
119 void f() {
120 [[int x;]]
123 )cpp";
124 std::string MethodCheckOutput = R"cpp(
125 class T {
126 void extracted() {
127 int x;
129 void f() {
130 extracted();
133 )cpp";
134 EXPECT_EQ(apply(MethodInput), MethodCheckOutput);
136 std::string OutOfLineMethodInput = R"cpp(
137 class T {
138 void f();
141 void T::f() {
142 [[int x;]]
144 )cpp";
145 std::string OutOfLineMethodCheckOutput = R"cpp(
146 class T {
147 void extracted();
148 void f();
151 void T::extracted() {
152 int x;
154 void T::f() {
155 extracted();
157 )cpp";
158 EXPECT_EQ(apply(OutOfLineMethodInput), OutOfLineMethodCheckOutput);
160 // We don't extract from templated functions for now as templates are hard
161 // to deal with.
162 std::string TemplateFailInput = R"cpp(
163 template<typename T>
164 void f() {
165 [[int x;]]
167 )cpp";
168 EXPECT_EQ(apply(TemplateFailInput), "unavailable");
170 std::string MacroInput = R"cpp(
171 #define F(BODY) void f() { BODY }
172 F ([[int x = 0;]])
173 )cpp";
174 std::string MacroOutput = R"cpp(
175 #define F(BODY) void f() { BODY }
176 void extracted() {
177 int x = 0;
179 F (extracted();)
180 )cpp";
181 EXPECT_EQ(apply(MacroInput), MacroOutput);
183 // Shouldn't crash.
184 EXPECT_EQ(apply("void f([[int a]]);"), "unavailable");
185 EXPECT_EQ(apply("void f(int a = [[1]]);"), "unavailable");
186 // Don't extract if we select the entire function body (CompoundStmt).
187 std::string CompoundFailInput = R"cpp(
188 void f() [[{
189 int a;
191 )cpp";
192 EXPECT_EQ(apply(CompoundFailInput), "unavailable");
195 TEST_F(ExtractFunctionTest, DifferentHeaderSourceTest) {
196 Header = R"cpp(
197 class SomeClass {
198 void f();
200 )cpp";
202 std::string OutOfLineSource = R"cpp(
203 void SomeClass::f() {
204 [[int x;]]
206 )cpp";
208 std::string OutOfLineSourceOutputCheck = R"cpp(
209 void SomeClass::extracted() {
210 int x;
212 void SomeClass::f() {
213 extracted();
215 )cpp";
217 std::string HeaderOutputCheck = R"cpp(
218 class SomeClass {
219 void extracted();
220 void f();
222 )cpp";
224 llvm::StringMap<std::string> EditedFiles;
226 EXPECT_EQ(apply(OutOfLineSource, &EditedFiles), OutOfLineSourceOutputCheck);
227 EXPECT_EQ(EditedFiles.begin()->second, HeaderOutputCheck);
230 TEST_F(ExtractFunctionTest, DifferentFilesNestedTest) {
231 Header = R"cpp(
232 class T {
233 class SomeClass {
234 void f();
237 )cpp";
239 std::string NestedOutOfLineSource = R"cpp(
240 void T::SomeClass::f() {
241 [[int x;]]
243 )cpp";
245 std::string NestedOutOfLineSourceOutputCheck = R"cpp(
246 void T::SomeClass::extracted() {
247 int x;
249 void T::SomeClass::f() {
250 extracted();
252 )cpp";
254 std::string NestedHeaderOutputCheck = R"cpp(
255 class T {
256 class SomeClass {
257 void extracted();
258 void f();
261 )cpp";
263 llvm::StringMap<std::string> EditedFiles;
265 EXPECT_EQ(apply(NestedOutOfLineSource, &EditedFiles),
266 NestedOutOfLineSourceOutputCheck);
267 EXPECT_EQ(EditedFiles.begin()->second, NestedHeaderOutputCheck);
270 TEST_F(ExtractFunctionTest, ConstexprDifferentHeaderSourceTest) {
271 Header = R"cpp(
272 class SomeClass {
273 constexpr void f() const;
275 )cpp";
277 std::string OutOfLineSource = R"cpp(
278 constexpr void SomeClass::f() const {
279 [[int x;]]
281 )cpp";
283 std::string OutOfLineSourceOutputCheck = R"cpp(
284 constexpr void SomeClass::extracted() const {
285 int x;
287 constexpr void SomeClass::f() const {
288 extracted();
290 )cpp";
292 std::string HeaderOutputCheck = R"cpp(
293 class SomeClass {
294 constexpr void extracted() const;
295 constexpr void f() const;
297 )cpp";
299 llvm::StringMap<std::string> EditedFiles;
301 EXPECT_EQ(apply(OutOfLineSource, &EditedFiles), OutOfLineSourceOutputCheck);
302 EXPECT_NE(EditedFiles.begin(), EditedFiles.end())
303 << "The header should be edited and receives the declaration of the new "
304 "function";
306 if (EditedFiles.begin() != EditedFiles.end()) {
307 EXPECT_EQ(EditedFiles.begin()->second, HeaderOutputCheck);
311 TEST_F(ExtractFunctionTest, ConstevalDifferentHeaderSourceTest) {
312 ExtraArgs.push_back("--std=c++20");
313 Header = R"cpp(
314 class SomeClass {
315 consteval void f() const;
317 )cpp";
319 std::string OutOfLineSource = R"cpp(
320 consteval void SomeClass::f() const {
321 [[int x;]]
323 )cpp";
325 std::string OutOfLineSourceOutputCheck = R"cpp(
326 consteval void SomeClass::extracted() const {
327 int x;
329 consteval void SomeClass::f() const {
330 extracted();
332 )cpp";
334 std::string HeaderOutputCheck = R"cpp(
335 class SomeClass {
336 consteval void extracted() const;
337 consteval void f() const;
339 )cpp";
341 llvm::StringMap<std::string> EditedFiles;
343 EXPECT_EQ(apply(OutOfLineSource, &EditedFiles), OutOfLineSourceOutputCheck);
344 EXPECT_NE(EditedFiles.begin(), EditedFiles.end())
345 << "The header should be edited and receives the declaration of the new "
346 "function";
348 if (EditedFiles.begin() != EditedFiles.end()) {
349 EXPECT_EQ(EditedFiles.begin()->second, HeaderOutputCheck);
353 TEST_F(ExtractFunctionTest, ConstDifferentHeaderSourceTest) {
354 Header = R"cpp(
355 class SomeClass {
356 void f() const;
358 )cpp";
360 std::string OutOfLineSource = R"cpp(
361 void SomeClass::f() const {
362 [[int x;]]
364 )cpp";
366 std::string OutOfLineSourceOutputCheck = R"cpp(
367 void SomeClass::extracted() const {
368 int x;
370 void SomeClass::f() const {
371 extracted();
373 )cpp";
375 std::string HeaderOutputCheck = R"cpp(
376 class SomeClass {
377 void extracted() const;
378 void f() const;
380 )cpp";
382 llvm::StringMap<std::string> EditedFiles;
384 EXPECT_EQ(apply(OutOfLineSource, &EditedFiles), OutOfLineSourceOutputCheck);
385 EXPECT_NE(EditedFiles.begin(), EditedFiles.end())
386 << "The header should be edited and receives the declaration of the new "
387 "function";
389 if (EditedFiles.begin() != EditedFiles.end()) {
390 EXPECT_EQ(EditedFiles.begin()->second, HeaderOutputCheck);
394 TEST_F(ExtractFunctionTest, StaticDifferentHeaderSourceTest) {
395 Header = R"cpp(
396 class SomeClass {
397 static void f();
399 )cpp";
401 std::string OutOfLineSource = R"cpp(
402 void SomeClass::f() {
403 [[int x;]]
405 )cpp";
407 std::string OutOfLineSourceOutputCheck = R"cpp(
408 void SomeClass::extracted() {
409 int x;
411 void SomeClass::f() {
412 extracted();
414 )cpp";
416 std::string HeaderOutputCheck = R"cpp(
417 class SomeClass {
418 static void extracted();
419 static void f();
421 )cpp";
423 llvm::StringMap<std::string> EditedFiles;
425 EXPECT_EQ(apply(OutOfLineSource, &EditedFiles), OutOfLineSourceOutputCheck);
426 EXPECT_NE(EditedFiles.begin(), EditedFiles.end())
427 << "The header should be edited and receives the declaration of the new "
428 "function";
430 if (EditedFiles.begin() != EditedFiles.end()) {
431 EXPECT_EQ(EditedFiles.begin()->second, HeaderOutputCheck);
435 TEST_F(ExtractFunctionTest, DifferentContextHeaderSourceTest) {
436 Header = R"cpp(
437 namespace ns{
438 class A {
439 class C {
440 public:
441 class RType {};
444 class T {
445 class SomeClass {
446 static C::RType f();
450 } // ns
451 )cpp";
453 std::string OutOfLineSource = R"cpp(
454 ns::A::C::RType ns::A::T::SomeClass::f() {
455 [[A::C::RType x;
456 return x;]]
458 )cpp";
460 std::string OutOfLineSourceOutputCheck = R"cpp(
461 ns::A::C::RType ns::A::T::SomeClass::extracted() {
462 A::C::RType x;
463 return x;
465 ns::A::C::RType ns::A::T::SomeClass::f() {
466 return extracted();
468 )cpp";
470 std::string HeaderOutputCheck = R"cpp(
471 namespace ns{
472 class A {
473 class C {
474 public:
475 class RType {};
478 class T {
479 class SomeClass {
480 static ns::A::C::RType extracted();
481 static C::RType f();
485 } // ns
486 )cpp";
488 llvm::StringMap<std::string> EditedFiles;
490 EXPECT_EQ(apply(OutOfLineSource, &EditedFiles), OutOfLineSourceOutputCheck);
491 EXPECT_EQ(EditedFiles.begin()->second, HeaderOutputCheck);
494 TEST_F(ExtractFunctionTest, DifferentSyntacticContextNamespace) {
495 std::string OutOfLineSource = R"cpp(
496 namespace ns {
497 void f();
500 void ns::f() {
501 [[int x;]]
503 )cpp";
505 std::string OutOfLineSourceOutputCheck = R"cpp(
506 namespace ns {
507 void extracted();
508 void f();
511 void ns::extracted() {
512 int x;
514 void ns::f() {
515 extracted();
517 )cpp";
519 EXPECT_EQ(apply(OutOfLineSource), OutOfLineSourceOutputCheck);
522 TEST_F(ExtractFunctionTest, ControlFlow) {
523 Context = Function;
524 // We should be able to extract break/continue with a parent loop/switch.
525 EXPECT_THAT(apply(" [[for(;;) if(1) break;]] "), HasSubstr("extracted"));
526 EXPECT_THAT(apply(" for(;;) [[while(1) break;]] "), HasSubstr("extracted"));
527 EXPECT_THAT(apply(" [[switch(1) { break; }]]"), HasSubstr("extracted"));
528 EXPECT_THAT(apply(" [[while(1) switch(1) { continue; }]]"),
529 HasSubstr("extracted"));
530 // Don't extract break and continue without a loop/switch parent.
531 EXPECT_THAT(apply(" for(;;) [[if(1) continue;]] "), StartsWith("fail"));
532 EXPECT_THAT(apply(" while(1) [[if(1) break;]] "), StartsWith("fail"));
533 EXPECT_THAT(apply(" switch(1) { [[break;]] }"), StartsWith("fail"));
534 EXPECT_THAT(apply(" for(;;) { [[while(1) break; break;]] }"),
535 StartsWith("fail"));
538 TEST_F(ExtractFunctionTest, ExistingReturnStatement) {
539 Context = File;
540 const char *Before = R"cpp(
541 bool lucky(int N);
542 int getNum(bool Superstitious, int Min, int Max) {
543 if (Superstitious) [[{
544 for (int I = Min; I <= Max; ++I)
545 if (lucky(I))
546 return I;
547 return -1;
548 }]] else {
549 return (Min + Max) / 2;
552 )cpp";
553 // FIXME: min/max should be by value.
554 // FIXME: avoid emitting redundant braces
555 const char *After = R"cpp(
556 bool lucky(int N);
557 int extracted(int &Min, int &Max) {
559 for (int I = Min; I <= Max; ++I)
560 if (lucky(I))
561 return I;
562 return -1;
565 int getNum(bool Superstitious, int Min, int Max) {
566 if (Superstitious) return extracted(Min, Max); else {
567 return (Min + Max) / 2;
570 )cpp";
571 EXPECT_EQ(apply(Before), After);
574 } // namespace
575 } // namespace clangd
576 } // namespace clang