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 "TweakTesting.h" 10 #include "gmock/gmock.h" 11 #include "gtest/gtest.h" 12 13 using ::testing::HasSubstr; 14 using ::testing::StartsWith; 15 16 namespace clang { 17 namespace clangd { 18 namespace { 19 20 TWEAK_TEST(ExtractFunction); 21 22 TEST_F(ExtractFunctionTest, FunctionTest) { 23 Context = Function; 24 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"); 35 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")); 50 51 FileName = "a.c"; 52 EXPECT_THAT(apply(" for([[int i = 0;]];);"), HasSubstr("unavailable")); 53 } 54 55 TEST_F(ExtractFunctionTest, FileTest) { 56 // Check all parameters are in order 57 std::string ParameterCheckInput = R"cpp( 58 struct Foo { 59 int x; 60 }; 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; 71 }; 72 void extracted(int &a, int &b, int * &ptr, Foo &foo) { 73 a += foo.x + b; 74 *ptr++; 75 } 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); 83 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) {} 92 } 93 void f(const int c) { 94 extracted(c); 95 })cpp"; 96 EXPECT_EQ(apply(ConstCheckInput), ConstCheckOutput); 97 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; 108 } 109 int f(const X::Y &y) { 110 return extracted(y); 111 })cpp"; 112 EXPECT_EQ(apply(ConstNamespaceCheckInput), ConstNamespaceCheckOutput); 113 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")); 116 117 // We don't extract from methods for now since they may involve multi-file 118 // edits 119 std::string MethodFailInput = R"cpp( 120 class T { 121 void f() { 122 [[int x;]] 123 } 124 }; 125 )cpp"; 126 EXPECT_EQ(apply(MethodFailInput), "unavailable"); 127 128 // We don't extract from templated functions for now as templates are hard 129 // to deal with. 130 std::string TemplateFailInput = R"cpp( 131 template<typename T> 132 void f() { 133 [[int x;]] 134 } 135 )cpp"; 136 EXPECT_EQ(apply(TemplateFailInput), "unavailable"); 137 138 std::string MacroInput = R"cpp( 139 #define F(BODY) void f() { BODY } 140 F ([[int x = 0;]]) 141 )cpp"; 142 std::string MacroOutput = R"cpp( 143 #define F(BODY) void f() { BODY } 144 void extracted() { 145 int x = 0; 146 } 147 F (extracted();) 148 )cpp"; 149 EXPECT_EQ(apply(MacroInput), MacroOutput); 150 151 // Shouldn't crash. 152 EXPECT_EQ(apply("void f([[int a]]);"), "unavailable"); 153 // Don't extract if we select the entire function body (CompoundStmt). 154 std::string CompoundFailInput = R"cpp( 155 void f() [[{ 156 int a; 157 }]] 158 )cpp"; 159 EXPECT_EQ(apply(CompoundFailInput), "unavailable"); 160 } 161 162 TEST_F(ExtractFunctionTest, ControlFlow) { 163 Context = Function; 164 // We should be able to extract break/continue with a parent loop/switch. 165 EXPECT_THAT(apply(" [[for(;;) if(1) break;]] "), HasSubstr("extracted")); 166 EXPECT_THAT(apply(" for(;;) [[while(1) break;]] "), HasSubstr("extracted")); 167 EXPECT_THAT(apply(" [[switch(1) { break; }]]"), HasSubstr("extracted")); 168 EXPECT_THAT(apply(" [[while(1) switch(1) { continue; }]]"), 169 HasSubstr("extracted")); 170 // Don't extract break and continue without a loop/switch parent. 171 EXPECT_THAT(apply(" for(;;) [[if(1) continue;]] "), StartsWith("fail")); 172 EXPECT_THAT(apply(" while(1) [[if(1) break;]] "), StartsWith("fail")); 173 EXPECT_THAT(apply(" switch(1) { [[break;]] }"), StartsWith("fail")); 174 EXPECT_THAT(apply(" for(;;) { [[while(1) break; break;]] }"), 175 StartsWith("fail")); 176 } 177 178 TEST_F(ExtractFunctionTest, ExistingReturnStatement) { 179 Context = File; 180 const char *Before = R"cpp( 181 bool lucky(int N); 182 int getNum(bool Superstitious, int Min, int Max) { 183 if (Superstitious) [[{ 184 for (int I = Min; I <= Max; ++I) 185 if (lucky(I)) 186 return I; 187 return -1; 188 }]] else { 189 return (Min + Max) / 2; 190 } 191 } 192 )cpp"; 193 // FIXME: min/max should be by value. 194 // FIXME: avoid emitting redundant braces 195 const char *After = R"cpp( 196 bool lucky(int N); 197 int extracted(int &Min, int &Max) { 198 { 199 for (int I = Min; I <= Max; ++I) 200 if (lucky(I)) 201 return I; 202 return -1; 203 } 204 } 205 int getNum(bool Superstitious, int Min, int Max) { 206 if (Superstitious) return extracted(Min, Max); else { 207 return (Min + Max) / 2; 208 } 209 } 210 )cpp"; 211 EXPECT_EQ(apply(Before), After); 212 } 213 214 } // namespace 215 } // namespace clangd 216 } // namespace clang 217