1 //===-- ExtractFunctionTests.cpp --------------------------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 #include "TweakTesting.h"
10 #include "gmock/gmock.h"
11 #include "gtest/gtest.h"
13 using ::testing::HasSubstr
;
14 using ::testing::StartsWith
;
20 TWEAK_TEST(ExtractFunction
);
22 TEST_F(ExtractFunctionTest
, FunctionTest
) {
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"));
48 apply("#define RETURN_IF_ERROR(x) if (x) return\nRETU^RN_IF_ERROR(4);"),
49 StartsWith("unavailable"));
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(
68 std::string ParameterCheckOutput
= R
"cpp(
72 void extracted(int &a, int &b, int * &ptr, Foo &foo) {
80 extracted(a, b, ptr, foo);
82 EXPECT_EQ(apply(ParameterCheckInput
), ParameterCheckOutput
);
84 // Check const qualifier
85 std::string ConstCheckInput
= R
"cpp(
89 std::string ConstCheckOutput
= R
"cpp(
90 void extracted(const int &c) {
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;]]
104 std::string ConstNamespaceCheckOutput
= R
"cpp(
105 namespace X { struct Y { int z; }; }
106 int extracted(const X::Y &y) {
109 int f(const X::Y &y) {
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(
124 std::string MethodCheckOutput
= R
"cpp(
134 EXPECT_EQ(apply(MethodInput
), MethodCheckOutput
);
136 std::string OutOfLineMethodInput
= R
"cpp(
145 std::string OutOfLineMethodCheckOutput
= R
"cpp(
151 void T::extracted() {
158 EXPECT_EQ(apply(OutOfLineMethodInput
), OutOfLineMethodCheckOutput
);
160 // We don't extract from templated functions for now as templates are hard
162 std::string TemplateFailInput
= R
"cpp(
168 EXPECT_EQ(apply(TemplateFailInput
), "unavailable");
170 std::string MacroInput
= R
"cpp(
171 #define F(BODY) void f() { BODY }
174 std::string MacroOutput
= R
"cpp(
175 #define F(BODY) void f() { BODY }
181 EXPECT_EQ(apply(MacroInput
), MacroOutput
);
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(
192 EXPECT_EQ(apply(CompoundFailInput
), "unavailable");
195 TEST_F(ExtractFunctionTest
, DifferentHeaderSourceTest
) {
202 std::string OutOfLineSource
= R
"cpp(
203 void SomeClass::f() {
208 std::string OutOfLineSourceOutputCheck
= R
"cpp(
209 void SomeClass::extracted() {
212 void SomeClass::f() {
217 std::string HeaderOutputCheck
= R
"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
) {
239 std::string NestedOutOfLineSource
= R
"cpp(
240 void T::SomeClass::f() {
245 std::string NestedOutOfLineSourceOutputCheck
= R
"cpp(
246 void T::SomeClass::extracted() {
249 void T::SomeClass::f() {
254 std::string NestedHeaderOutputCheck
= R
"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
) {
273 constexpr void f() const;
277 std::string OutOfLineSource
= R
"cpp(
278 constexpr void SomeClass::f() const {
283 std::string OutOfLineSourceOutputCheck
= R
"cpp(
284 constexpr void SomeClass::extracted() const {
287 constexpr void SomeClass::f() const {
292 std::string HeaderOutputCheck
= R
"cpp(
294 constexpr void extracted() const;
295 constexpr void f() const;
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 "
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");
315 consteval void f() const;
319 std::string OutOfLineSource
= R
"cpp(
320 consteval void SomeClass::f() const {
325 std::string OutOfLineSourceOutputCheck
= R
"cpp(
326 consteval void SomeClass::extracted() const {
329 consteval void SomeClass::f() const {
334 std::string HeaderOutputCheck
= R
"cpp(
336 consteval void extracted() const;
337 consteval void f() const;
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 "
348 if (EditedFiles
.begin() != EditedFiles
.end()) {
349 EXPECT_EQ(EditedFiles
.begin()->second
, HeaderOutputCheck
);
353 TEST_F(ExtractFunctionTest
, ConstDifferentHeaderSourceTest
) {
360 std::string OutOfLineSource
= R
"cpp(
361 void SomeClass::f() const {
366 std::string OutOfLineSourceOutputCheck
= R
"cpp(
367 void SomeClass::extracted() const {
370 void SomeClass::f() const {
375 std::string HeaderOutputCheck
= R
"cpp(
377 void extracted() const;
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 "
389 if (EditedFiles
.begin() != EditedFiles
.end()) {
390 EXPECT_EQ(EditedFiles
.begin()->second
, HeaderOutputCheck
);
394 TEST_F(ExtractFunctionTest
, StaticDifferentHeaderSourceTest
) {
401 std::string OutOfLineSource
= R
"cpp(
402 void SomeClass::f() {
407 std::string OutOfLineSourceOutputCheck
= R
"cpp(
408 void SomeClass::extracted() {
411 void SomeClass::f() {
416 std::string HeaderOutputCheck
= R
"cpp(
418 static void extracted();
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 "
430 if (EditedFiles
.begin() != EditedFiles
.end()) {
431 EXPECT_EQ(EditedFiles
.begin()->second
, HeaderOutputCheck
);
435 TEST_F(ExtractFunctionTest
, DifferentContextHeaderSourceTest
) {
453 std::string OutOfLineSource
= R
"cpp(
454 ns::A::C::RType ns::A::T::SomeClass::f() {
460 std::string OutOfLineSourceOutputCheck
= R
"cpp(
461 ns::A::C::RType ns::A::T::SomeClass::extracted() {
465 ns::A::C::RType ns::A::T::SomeClass::f() {
470 std::string HeaderOutputCheck
= R
"cpp(
480 static ns::A::C::RType extracted();
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(
505 std::string OutOfLineSourceOutputCheck
= R
"cpp(
511 void ns::extracted() {
519 EXPECT_EQ(apply(OutOfLineSource
), OutOfLineSourceOutputCheck
);
522 TEST_F(ExtractFunctionTest
, ControlFlow
) {
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;]] }"),
538 TEST_F(ExtractFunctionTest
, ExistingReturnStatement
) {
540 const char *Before
= R
"cpp(
542 int getNum(bool Superstitious, int Min, int Max) {
543 if (Superstitious) [[{
544 for (int I = Min; I <= Max; ++I)
549 return (Min + Max) / 2;
553 // FIXME: min/max should be by value.
554 // FIXME: avoid emitting redundant braces
555 const char *After
= R
"cpp(
557 int extracted(int &Min, int &Max) {
559 for (int I = Min; I <= Max; ++I)
565 int getNum(bool Superstitious, int Min, int Max) {
566 if (Superstitious) return extracted(Min, Max); else {
567 return (Min + Max) / 2;
571 EXPECT_EQ(apply(Before
), After
);
575 } // namespace clangd