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 // Check const qualifier with namespace 101 std::string ConstNamespaceCheckInput = R"cpp( 102 namespace X { struct Y { int z; }; } 103 int f(const X::Y &y) { 104 [[return y.z + y.z;]] 105 })cpp"; 106 std::string ConstNamespaceCheckOutput = R"cpp( 107 namespace X { struct Y { int z; }; } 108 int extracted(const X::Y &y) { 109 return y.z + y.z; 110 } 111 int f(const X::Y &y) { 112 return extracted(y); 113 })cpp"; 114 EXPECT_EQ(apply(ConstNamespaceCheckInput), ConstNamespaceCheckOutput); 115 116 // Don't extract when we need to make a function as a parameter. 117 EXPECT_THAT(apply("void f() { [[int a; f();]] }"), StartsWith("fail")); 118 119 // We don't extract from methods for now since they may involve multi-file 120 // edits 121 std::string MethodFailInput = R"cpp( 122 class T { 123 void f() { 124 [[int x;]] 125 } 126 }; 127 )cpp"; 128 EXPECT_EQ(apply(MethodFailInput), "unavailable"); 129 130 // We don't extract from templated functions for now as templates are hard 131 // to deal with. 132 std::string TemplateFailInput = R"cpp( 133 template<typename T> 134 void f() { 135 [[int x;]] 136 } 137 )cpp"; 138 EXPECT_EQ(apply(TemplateFailInput), "unavailable"); 139 140 std::string MacroInput = R"cpp( 141 #define F(BODY) void f() { BODY } 142 F ([[int x = 0;]]) 143 )cpp"; 144 std::string MacroOutput = R"cpp( 145 #define F(BODY) void f() { BODY } 146 void extracted() { 147 int x = 0; 148 } 149 F (extracted();) 150 )cpp"; 151 EXPECT_EQ(apply(MacroInput), MacroOutput); 152 153 // Shouldn't crash. 154 EXPECT_EQ(apply("void f([[int a]]);"), "unavailable"); 155 // Don't extract if we select the entire function body (CompoundStmt). 156 std::string CompoundFailInput = R"cpp( 157 void f() [[{ 158 int a; 159 }]] 160 )cpp"; 161 EXPECT_EQ(apply(CompoundFailInput), "unavailable"); 162 } 163 164 TEST_F(ExtractFunctionTest, ControlFlow) { 165 Context = Function; 166 // We should be able to extract break/continue with a parent loop/switch. 167 EXPECT_THAT(apply(" [[for(;;) if(1) break;]] "), HasSubstr("extracted")); 168 EXPECT_THAT(apply(" for(;;) [[while(1) break;]] "), HasSubstr("extracted")); 169 EXPECT_THAT(apply(" [[switch(1) { break; }]]"), HasSubstr("extracted")); 170 EXPECT_THAT(apply(" [[while(1) switch(1) { continue; }]]"), 171 HasSubstr("extracted")); 172 // Don't extract break and continue without a loop/switch parent. 173 EXPECT_THAT(apply(" for(;;) [[if(1) continue;]] "), StartsWith("fail")); 174 EXPECT_THAT(apply(" while(1) [[if(1) break;]] "), StartsWith("fail")); 175 EXPECT_THAT(apply(" switch(1) { [[break;]] }"), StartsWith("fail")); 176 EXPECT_THAT(apply(" for(;;) { [[while(1) break; break;]] }"), 177 StartsWith("fail")); 178 } 179 180 TEST_F(ExtractFunctionTest, ExistingReturnStatement) { 181 Context = File; 182 const char *Before = R"cpp( 183 bool lucky(int N); 184 int getNum(bool Superstitious, int Min, int Max) { 185 if (Superstitious) [[{ 186 for (int I = Min; I <= Max; ++I) 187 if (lucky(I)) 188 return I; 189 return -1; 190 }]] else { 191 return (Min + Max) / 2; 192 } 193 } 194 )cpp"; 195 // FIXME: min/max should be by value. 196 // FIXME: avoid emitting redundant braces 197 const char *After = R"cpp( 198 bool lucky(int N); 199 int extracted(int &Min, int &Max) { 200 { 201 for (int I = Min; I <= Max; ++I) 202 if (lucky(I)) 203 return I; 204 return -1; 205 } 206 } 207 int getNum(bool Superstitious, int Min, int Max) { 208 if (Superstitious) return extracted(Min, Max); else { 209 return (Min + Max) / 2; 210 } 211 } 212 )cpp"; 213 EXPECT_EQ(apply(Before), After); 214 } 215 216 } // namespace 217 } // namespace clangd 218 } // namespace clang 219