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 //===----------------------------------------------------------------------===// 8 9 #include "TestTU.h" 10 #include "TweakTesting.h" 11 #include "gmock/gmock-matchers.h" 12 #include "gmock/gmock.h" 13 #include "gtest/gtest.h" 14 15 using ::testing::HasSubstr; 16 using ::testing::StartsWith; 17 18 namespace clang { 19 namespace clangd { 20 namespace { 21 22 TWEAK_TEST(ExtractFunction); 23 24 TEST_F(ExtractFunctionTest, FunctionTest) { 25 Context = Function; 26 27 // Root statements should have common parent. 28 EXPECT_EQ(apply("for(;;) [[1+2; 1+2;]]"), "unavailable"); 29 // Expressions aren't extracted. 30 EXPECT_EQ(apply("int x = 0; [[x++;]]"), "unavailable"); 31 // We don't support extraction from lambdas. 32 EXPECT_EQ(apply("auto lam = [](){ [[int x;]] }; "), "unavailable"); 33 // Partial statements aren't extracted. 34 EXPECT_THAT(apply("int [[x = 0]];"), "unavailable"); 35 // FIXME: Support hoisting. 36 EXPECT_THAT(apply(" [[int a = 5;]] a++; "), "unavailable"); 37 38 // Ensure that end of Zone and Beginning of PostZone being adjacent doesn't 39 // lead to break being included in the extraction zone. 40 EXPECT_THAT(apply("for(;;) { [[int x;]]break; }"), HasSubstr("extracted")); 41 // FIXME: ExtractFunction should be unavailable inside loop construct 42 // initializer/condition. 43 EXPECT_THAT(apply(" for([[int i = 0;]];);"), HasSubstr("extracted")); 44 // Extract certain return 45 EXPECT_THAT(apply(" if(true) [[{ return; }]] "), HasSubstr("extracted")); 46 // Don't extract uncertain return 47 EXPECT_THAT(apply(" if(true) [[if (false) return;]] "), 48 StartsWith("unavailable")); 49 EXPECT_THAT( 50 apply("#define RETURN_IF_ERROR(x) if (x) return\nRETU^RN_IF_ERROR(4);"), 51 StartsWith("unavailable")); 52 53 FileName = "a.c"; 54 EXPECT_THAT(apply(" for([[int i = 0;]];);"), HasSubstr("unavailable")); 55 } 56 57 TEST_F(ExtractFunctionTest, FileTest) { 58 // Check all parameters are in order 59 std::string ParameterCheckInput = R"cpp( 60 struct Foo { 61 int x; 62 }; 63 void f(int a) { 64 int b; 65 int *ptr = &a; 66 Foo foo; 67 [[a += foo.x + b; 68 *ptr++;]] 69 })cpp"; 70 std::string ParameterCheckOutput = R"cpp( 71 struct Foo { 72 int x; 73 }; 74 void extracted(int &a, int &b, int * &ptr, Foo &foo) { 75 a += foo.x + b; 76 *ptr++; 77 } 78 void f(int a) { 79 int b; 80 int *ptr = &a; 81 Foo foo; 82 extracted(a, b, ptr, foo); 83 })cpp"; 84 EXPECT_EQ(apply(ParameterCheckInput), ParameterCheckOutput); 85 86 // Check const qualifier 87 std::string ConstCheckInput = R"cpp( 88 void f(const int c) { 89 [[while(c) {}]] 90 })cpp"; 91 std::string ConstCheckOutput = R"cpp( 92 void extracted(const int &c) { 93 while(c) {} 94 } 95 void f(const int c) { 96 extracted(c); 97 })cpp"; 98 EXPECT_EQ(apply(ConstCheckInput), ConstCheckOutput); 99 100 // Don't extract when we need to make a function as a parameter. 101 EXPECT_THAT(apply("void f() { [[int a; f();]] }"), StartsWith("fail")); 102 103 // We don't extract from methods for now since they may involve multi-file 104 // edits 105 std::string MethodFailInput = R"cpp( 106 class T { 107 void f() { 108 [[int x;]] 109 } 110 }; 111 )cpp"; 112 EXPECT_EQ(apply(MethodFailInput), "unavailable"); 113 114 // We don't extract from templated functions for now as templates are hard 115 // to deal with. 116 std::string TemplateFailInput = R"cpp( 117 template<typename T> 118 void f() { 119 [[int x;]] 120 } 121 )cpp"; 122 EXPECT_EQ(apply(TemplateFailInput), "unavailable"); 123 124 std::string MacroInput = R"cpp( 125 #define F(BODY) void f() { BODY } 126 F ([[int x = 0;]]) 127 )cpp"; 128 std::string MacroOutput = R"cpp( 129 #define F(BODY) void f() { BODY } 130 void extracted() { 131 int x = 0; 132 } 133 F (extracted();) 134 )cpp"; 135 EXPECT_EQ(apply(MacroInput), MacroOutput); 136 137 // Shouldn't crash. 138 EXPECT_EQ(apply("void f([[int a]]);"), "unavailable"); 139 // Don't extract if we select the entire function body (CompoundStmt). 140 std::string CompoundFailInput = R"cpp( 141 void f() [[{ 142 int a; 143 }]] 144 )cpp"; 145 EXPECT_EQ(apply(CompoundFailInput), "unavailable"); 146 } 147 148 TEST_F(ExtractFunctionTest, ControlFlow) { 149 Context = Function; 150 // We should be able to extract break/continue with a parent loop/switch. 151 EXPECT_THAT(apply(" [[for(;;) if(1) break;]] "), HasSubstr("extracted")); 152 EXPECT_THAT(apply(" for(;;) [[while(1) break;]] "), HasSubstr("extracted")); 153 EXPECT_THAT(apply(" [[switch(1) { break; }]]"), HasSubstr("extracted")); 154 EXPECT_THAT(apply(" [[while(1) switch(1) { continue; }]]"), 155 HasSubstr("extracted")); 156 // Don't extract break and continue without a loop/switch parent. 157 EXPECT_THAT(apply(" for(;;) [[if(1) continue;]] "), StartsWith("fail")); 158 EXPECT_THAT(apply(" while(1) [[if(1) break;]] "), StartsWith("fail")); 159 EXPECT_THAT(apply(" switch(1) { [[break;]] }"), StartsWith("fail")); 160 EXPECT_THAT(apply(" for(;;) { [[while(1) break; break;]] }"), 161 StartsWith("fail")); 162 } 163 164 TEST_F(ExtractFunctionTest, ExistingReturnStatement) { 165 Context = File; 166 const char *Before = R"cpp( 167 bool lucky(int N); 168 int getNum(bool Superstitious, int Min, int Max) { 169 if (Superstitious) [[{ 170 for (int I = Min; I <= Max; ++I) 171 if (lucky(I)) 172 return I; 173 return -1; 174 }]] else { 175 return (Min + Max) / 2; 176 } 177 } 178 )cpp"; 179 // FIXME: min/max should be by value. 180 // FIXME: avoid emitting redundant braces 181 const char *After = R"cpp( 182 bool lucky(int N); 183 int extracted(int &Min, int &Max) { 184 { 185 for (int I = Min; I <= Max; ++I) 186 if (lucky(I)) 187 return I; 188 return -1; 189 } 190 } 191 int getNum(bool Superstitious, int Min, int Max) { 192 if (Superstitious) return extracted(Min, Max); else { 193 return (Min + Max) / 2; 194 } 195 } 196 )cpp"; 197 EXPECT_EQ(apply(Before), After); 198 } 199 200 } // namespace 201 } // namespace clangd 202 } // namespace clang 203