brintos

brintos / llvm-project-archived public Read only

0
0
Text · 15.0 KiB · eff4d0f Raw
636 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8 9#include "TweakTesting.h"10#include "gmock/gmock.h"11#include "gtest/gtest.h"12 13using ::testing::HasSubstr;14using ::testing::StartsWith;15 16namespace clang {17namespace clangd {18namespace {19 20TWEAK_TEST(ExtractFunction);21 22TEST_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't37  // 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 construct40  // initializer/condition.41  EXPECT_THAT(apply(" for([[int i = 0;]];);"), HasSubstr("extracted"));42  // Extract certain return43  EXPECT_THAT(apply(" if(true) [[{ return; }]] "), HasSubstr("extracted"));44  // Don't extract uncertain return45  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 55TEST_F(ExtractFunctionTest, FileTest) {56  // Check all parameters are in order57  std::string ParameterCheckInput = R"cpp(58struct Foo {59  int x;60};61void 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(69struct Foo {70  int x;71};72void extracted(int &a, int &b, int * &ptr, Foo &foo) {73a += foo.x + b;74  *ptr++;75}76void 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 qualifier85  std::string ConstCheckInput = R"cpp(86void f(const int c) {87  [[while(c) {}]]88})cpp";89  std::string ConstCheckOutput = R"cpp(90void extracted(const int &c) {91while(c) {}92}93void f(const int c) {94  extracted(c);95})cpp";96  EXPECT_EQ(apply(ConstCheckInput), ConstCheckOutput);97 98  // Check const qualifier with namespace99  std::string ConstNamespaceCheckInput = R"cpp(100namespace X { struct Y { int z; }; }101int f(const X::Y &y) {102  [[return y.z + y.z;]]103})cpp";104  std::string ConstNamespaceCheckOutput = R"cpp(105namespace X { struct Y { int z; }; }106int extracted(const X::Y &y) {107return y.z + y.z;108}109int 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  std::string MethodInput = R"cpp(118    class T {119      void f() {120        [[int x;]]121      }122    };123  )cpp";124  std::string MethodCheckOutput = R"cpp(125    class T {126      void extracted() {127int x;128}129void f() {130        extracted();131      }132    };133  )cpp";134  EXPECT_EQ(apply(MethodInput), MethodCheckOutput);135 136  std::string OutOfLineMethodInput = R"cpp(137    class T {138      void f();139    };140 141    void T::f() {142      [[int x;]]143    }144  )cpp";145  std::string OutOfLineMethodCheckOutput = R"cpp(146    class T {147      void extracted();148void f();149    };150 151    void T::extracted() {152int x;153}154void T::f() {155      extracted();156    }157  )cpp";158  EXPECT_EQ(apply(OutOfLineMethodInput), OutOfLineMethodCheckOutput);159 160  // We don't extract from templated functions for now as templates are hard161  // to deal with.162  std::string TemplateFailInput = R"cpp(163    template<typename T>164    void f() {165      [[int x;]]166    }167  )cpp";168  EXPECT_EQ(apply(TemplateFailInput), "unavailable");169 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() {177int x = 0;178}179F (extracted();)180  )cpp";181  EXPECT_EQ(apply(MacroInput), MacroOutput);182 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;190    }]]191  )cpp";192  EXPECT_EQ(apply(CompoundFailInput), "unavailable");193 194  ExtraArgs.push_back("-std=c++14");195  // FIXME: Expressions are currently not extracted196  EXPECT_EQ(apply(R"cpp(197                void call() { [[1+1]]; }198            )cpp"),199            "unavailable");200  // FIXME: Single expression statements are currently not extracted201  EXPECT_EQ(apply(R"cpp(202                void call() { [[1+1;]] }203            )cpp"),204            "unavailable");205}206 207TEST_F(ExtractFunctionTest, DifferentHeaderSourceTest) {208  Header = R"cpp(209    class SomeClass {210      void f();211    };212  )cpp";213 214  std::string OutOfLineSource = R"cpp(215    void SomeClass::f() {216      [[int x;]]217    }218  )cpp";219 220  std::string OutOfLineSourceOutputCheck = R"cpp(221    void SomeClass::extracted() {222int x;223}224void SomeClass::f() {225      extracted();226    }227  )cpp";228 229  std::string HeaderOutputCheck = R"cpp(230    class SomeClass {231      void extracted();232void f();233    };234  )cpp";235 236  llvm::StringMap<std::string> EditedFiles;237 238  EXPECT_EQ(apply(OutOfLineSource, &EditedFiles), OutOfLineSourceOutputCheck);239  EXPECT_EQ(EditedFiles.begin()->second, HeaderOutputCheck);240}241 242TEST_F(ExtractFunctionTest, DifferentFilesNestedTest) {243  Header = R"cpp(244    class T {245    class SomeClass {246      void f();247    };248    };249  )cpp";250 251  std::string NestedOutOfLineSource = R"cpp(252    void T::SomeClass::f() {253      [[int x;]]254    }255  )cpp";256 257  std::string NestedOutOfLineSourceOutputCheck = R"cpp(258    void T::SomeClass::extracted() {259int x;260}261void T::SomeClass::f() {262      extracted();263    }264  )cpp";265 266  std::string NestedHeaderOutputCheck = R"cpp(267    class T {268    class SomeClass {269      void extracted();270void f();271    };272    };273  )cpp";274 275  llvm::StringMap<std::string> EditedFiles;276 277  EXPECT_EQ(apply(NestedOutOfLineSource, &EditedFiles),278            NestedOutOfLineSourceOutputCheck);279  EXPECT_EQ(EditedFiles.begin()->second, NestedHeaderOutputCheck);280}281 282TEST_F(ExtractFunctionTest, ConstexprDifferentHeaderSourceTest) {283  Header = R"cpp(284    class SomeClass {285      constexpr void f() const;286    };287  )cpp";288 289  std::string OutOfLineSource = R"cpp(290    constexpr void SomeClass::f() const {291      [[int x;]]292    }293  )cpp";294 295  std::string OutOfLineSourceOutputCheck = R"cpp(296    constexpr void SomeClass::extracted() const {297int x;298}299constexpr void SomeClass::f() const {300      extracted();301    }302  )cpp";303 304  std::string HeaderOutputCheck = R"cpp(305    class SomeClass {306      constexpr void extracted() const;307constexpr void f() const;308    };309  )cpp";310 311  llvm::StringMap<std::string> EditedFiles;312 313  EXPECT_EQ(apply(OutOfLineSource, &EditedFiles), OutOfLineSourceOutputCheck);314  EXPECT_NE(EditedFiles.begin(), EditedFiles.end())315      << "The header should be edited and receives the declaration of the new "316         "function";317 318  if (EditedFiles.begin() != EditedFiles.end()) {319    EXPECT_EQ(EditedFiles.begin()->second, HeaderOutputCheck);320  }321}322 323TEST_F(ExtractFunctionTest, ConstevalDifferentHeaderSourceTest) {324  ExtraArgs.push_back("--std=c++20");325  Header = R"cpp(326    class SomeClass {327      consteval void f() const;328    };329  )cpp";330 331  std::string OutOfLineSource = R"cpp(332    consteval void SomeClass::f() const {333      [[int x;]]334    }335  )cpp";336 337  std::string OutOfLineSourceOutputCheck = R"cpp(338    consteval void SomeClass::extracted() const {339int x;340}341consteval void SomeClass::f() const {342      extracted();343    }344  )cpp";345 346  std::string HeaderOutputCheck = R"cpp(347    class SomeClass {348      consteval void extracted() const;349consteval void f() const;350    };351  )cpp";352 353  llvm::StringMap<std::string> EditedFiles;354 355  EXPECT_EQ(apply(OutOfLineSource, &EditedFiles), OutOfLineSourceOutputCheck);356  EXPECT_NE(EditedFiles.begin(), EditedFiles.end())357      << "The header should be edited and receives the declaration of the new "358         "function";359 360  if (EditedFiles.begin() != EditedFiles.end()) {361    EXPECT_EQ(EditedFiles.begin()->second, HeaderOutputCheck);362  }363}364 365TEST_F(ExtractFunctionTest, ConstDifferentHeaderSourceTest) {366  Header = R"cpp(367    class SomeClass {368      void f() const;369    };370  )cpp";371 372  std::string OutOfLineSource = R"cpp(373    void SomeClass::f() const {374      [[int x;]]375    }376  )cpp";377 378  std::string OutOfLineSourceOutputCheck = R"cpp(379    void SomeClass::extracted() const {380int x;381}382void SomeClass::f() const {383      extracted();384    }385  )cpp";386 387  std::string HeaderOutputCheck = R"cpp(388    class SomeClass {389      void extracted() const;390void f() const;391    };392  )cpp";393 394  llvm::StringMap<std::string> EditedFiles;395 396  EXPECT_EQ(apply(OutOfLineSource, &EditedFiles), OutOfLineSourceOutputCheck);397  EXPECT_NE(EditedFiles.begin(), EditedFiles.end())398      << "The header should be edited and receives the declaration of the new "399         "function";400 401  if (EditedFiles.begin() != EditedFiles.end()) {402    EXPECT_EQ(EditedFiles.begin()->second, HeaderOutputCheck);403  }404}405 406TEST_F(ExtractFunctionTest, StaticDifferentHeaderSourceTest) {407  Header = R"cpp(408    class SomeClass {409      static void f();410    };411  )cpp";412 413  std::string OutOfLineSource = R"cpp(414    void SomeClass::f() {415      [[int x;]]416    }417  )cpp";418 419  std::string OutOfLineSourceOutputCheck = R"cpp(420    void SomeClass::extracted() {421int x;422}423void SomeClass::f() {424      extracted();425    }426  )cpp";427 428  std::string HeaderOutputCheck = R"cpp(429    class SomeClass {430      static void extracted();431static void f();432    };433  )cpp";434 435  llvm::StringMap<std::string> EditedFiles;436 437  EXPECT_EQ(apply(OutOfLineSource, &EditedFiles), OutOfLineSourceOutputCheck);438  EXPECT_NE(EditedFiles.begin(), EditedFiles.end())439      << "The header should be edited and receives the declaration of the new "440         "function";441 442  if (EditedFiles.begin() != EditedFiles.end()) {443    EXPECT_EQ(EditedFiles.begin()->second, HeaderOutputCheck);444  }445}446 447TEST_F(ExtractFunctionTest, DifferentContextHeaderSourceTest) {448  Header = R"cpp(449    namespace ns{450    class A {451      class C {452      public:453        class RType {};454      };455 456      class T {457        class SomeClass {458          static C::RType f();459        };460      };461    };462    } // ns463  )cpp";464 465  std::string OutOfLineSource = R"cpp(466    ns::A::C::RType ns::A::T::SomeClass::f() {467      [[A::C::RType x;468      return x;]]469    }470  )cpp";471 472  std::string OutOfLineSourceOutputCheck = R"cpp(473    ns::A::C::RType ns::A::T::SomeClass::extracted() {474A::C::RType x;475      return x;476}477ns::A::C::RType ns::A::T::SomeClass::f() {478      return extracted();479    }480  )cpp";481 482  std::string HeaderOutputCheck = R"cpp(483    namespace ns{484    class A {485      class C {486      public:487        class RType {};488      };489 490      class T {491        class SomeClass {492          static ns::A::C::RType extracted();493static C::RType f();494        };495      };496    };497    } // ns498  )cpp";499 500  llvm::StringMap<std::string> EditedFiles;501 502  EXPECT_EQ(apply(OutOfLineSource, &EditedFiles), OutOfLineSourceOutputCheck);503  EXPECT_EQ(EditedFiles.begin()->second, HeaderOutputCheck);504}505 506TEST_F(ExtractFunctionTest, DifferentSyntacticContextNamespace) {507  std::string OutOfLineSource = R"cpp(508    namespace ns {509      void f();510    }511 512    void ns::f() {513      [[int x;]]514    }515  )cpp";516 517  std::string OutOfLineSourceOutputCheck = R"cpp(518    namespace ns {519      void extracted();520void f();521    }522 523    void ns::extracted() {524int x;525}526void ns::f() {527      extracted();528    }529  )cpp";530 531  EXPECT_EQ(apply(OutOfLineSource), OutOfLineSourceOutputCheck);532}533 534TEST_F(ExtractFunctionTest, ControlFlow) {535  Context = Function;536  // We should be able to extract break/continue with a parent loop/switch.537  EXPECT_THAT(apply(" [[for(;;) if(1) break;]] "), HasSubstr("extracted"));538  EXPECT_THAT(apply(" for(;;) [[while(1) break;]] "), HasSubstr("extracted"));539  EXPECT_THAT(apply(" [[switch(1) { break; }]]"), HasSubstr("extracted"));540  EXPECT_THAT(apply(" [[while(1) switch(1) { continue; }]]"),541              HasSubstr("extracted"));542  // Don't extract break and continue without a loop/switch parent.543  EXPECT_THAT(apply(" for(;;) [[if(1) continue;]] "), StartsWith("fail"));544  EXPECT_THAT(apply(" while(1) [[if(1) break;]] "), StartsWith("fail"));545  EXPECT_THAT(apply(" switch(1) { [[break;]] }"), StartsWith("fail"));546  EXPECT_THAT(apply(" for(;;) { [[while(1) break; break;]] }"),547              StartsWith("fail"));548}549 550TEST_F(ExtractFunctionTest, ExistingReturnStatement) {551  Context = File;552  const char *Before = R"cpp(553    bool lucky(int N);554    int getNum(bool Superstitious, int Min, int Max) {555      if (Superstitious) [[{556        for (int I = Min; I <= Max; ++I)557          if (lucky(I))558            return I;559        return -1;560      }]] else {561        return (Min + Max) / 2;562      }563    }564  )cpp";565  // FIXME: min/max should be by value.566  // FIXME: avoid emitting redundant braces567  const char *After = R"cpp(568    bool lucky(int N);569    int extracted(int &Min, int &Max) {570{571        for (int I = Min; I <= Max; ++I)572          if (lucky(I))573            return I;574        return -1;575      }576}577int getNum(bool Superstitious, int Min, int Max) {578      if (Superstitious) return extracted(Min, Max); else {579        return (Min + Max) / 2;580      }581    }582  )cpp";583  EXPECT_EQ(apply(Before), After);584}585 586TEST_F(ExtractFunctionTest, OverloadedOperators) {587  Context = File;588  std::string Before = R"cpp(struct A {589                int operator+(int x) { return x; }590              };591              A &operator<<(A &, int);592              A &operator|(A &, int);593 594              A stream{};595 596              void foo(int, int);597 598              int main() {599                [[foo(1, 2);600                foo(3, 4);601                stream << 42;602                stream + 42;603                stream | 42;604                foo(1, 2);605                foo(3, 4);]]606              })cpp";607  std::string After =608      R"cpp(struct A {609                int operator+(int x) { return x; }610              };611              A &operator<<(A &, int);612              A &operator|(A &, int);613 614              A stream{};615 616              void foo(int, int);617 618              void extracted() {619foo(1, 2);620                foo(3, 4);621                stream << 42;622                stream + 42;623                stream | 42;624                foo(1, 2);625                foo(3, 4);626}627int main() {628                extracted();629              })cpp";630  EXPECT_EQ(apply(Before), After);631}632 633} // namespace634} // namespace clangd635} // namespace clang636