brintos

brintos / llvm-project-archived public Read only

0
0
Text · 24.4 KiB · 7689bf3 Raw
980 lines · cpp
1//===-- DefineOutline.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 "TestFS.h"10#include "TweakTesting.h"11#include "gmock/gmock.h"12#include "gtest/gtest.h"13 14namespace clang {15namespace clangd {16namespace {17 18using ::testing::UnorderedElementsAre;19 20TWEAK_TEST(DefineOutline);21 22TEST_F(DefineOutlineTest, TriggersOnFunctionDecl) {23  FileName = "Test.cpp";24  // Not available for free function unless in a header file.25  EXPECT_UNAVAILABLE(R"cpp(26    [[void [[f^o^o]]() [[{27      return;28    }]]]])cpp");29 30  // Available in soure file.31  EXPECT_AVAILABLE(R"cpp(32    struct Foo {33      void f^oo() {}34    };35  )cpp");36 37  // Available within named namespace in source file.38  EXPECT_AVAILABLE(R"cpp(39    namespace N {40      struct Foo {41        void f^oo() {}42      };43    } // namespace N44  )cpp");45 46  // Available within anonymous namespace in source file.47  EXPECT_AVAILABLE(R"cpp(48    namespace {49      struct Foo {50        void f^oo() {}51      };52    } // namespace53  )cpp");54 55  // Not available for out-of-line method.56  EXPECT_UNAVAILABLE(R"cpp(57    class Bar {58      void baz();59    };60 61    [[void [[Bar::[[b^a^z]]]]() [[{62      return;63    }]]]])cpp");64 65  FileName = "Test.hpp";66  // Not available unless function name or fully body is selected.67  EXPECT_UNAVAILABLE(R"cpp(68    // Not a definition69    vo^i[[d^ ^f]]^oo();70 71    [[vo^id ]]foo[[()]] {[[72      [[(void)(5+3);73      return;]]74    }]])cpp");75 76  // Available even if there are no implementation files.77  EXPECT_AVAILABLE(R"cpp(78    [[void [[f^o^o]]() [[{79      return;80    }]]]])cpp");81 82  // Not available for out-of-line methods.83  EXPECT_UNAVAILABLE(R"cpp(84    class Bar {85      void baz();86    };87 88    [[void [[Bar::[[b^a^z]]]]() [[{89      return;90    }]]]])cpp");91 92  // Basic check for function body and signature.93  EXPECT_AVAILABLE(R"cpp(94    class Bar {95      [[void [[f^o^o^]]() [[{ return; }]]]]96    };97 98    void foo();99    [[void [[f^o^o]]() [[{100      return;101    }]]]])cpp");102 103  // Not available on defaulted/deleted members.104  EXPECT_UNAVAILABLE(R"cpp(105    class Foo {106      Fo^o() = default;107      F^oo(const Foo&) = delete;108    };)cpp");109 110  // Not available within templated classes with unnamed parameters, as it is111  // hard to spell class name out-of-line in such cases.112  EXPECT_UNAVAILABLE(R"cpp(113    template <typename> struct Foo { void fo^o(){} };114    )cpp");115 116  // Not available on function template specializations and free function117  // templates.118  EXPECT_UNAVAILABLE(R"cpp(119    template <typename T> void fo^o() {}120    template <> void fo^o<int>() {}121  )cpp");122 123  // Not available on methods of unnamed classes.124  EXPECT_UNAVAILABLE(R"cpp(125    struct Foo {126      struct { void b^ar() {} } Bar;127    };128  )cpp");129 130  // Not available on methods of named classes with unnamed parent in parents131  // nesting.132  EXPECT_UNAVAILABLE(R"cpp(133    struct Foo {134      struct {135        struct Bar { void b^ar() {} };136      } Baz;137    };138  )cpp");139 140  // Not available on definitions in header file within unnamed namespaces141  EXPECT_UNAVAILABLE(R"cpp(142    namespace {143      struct Foo {144        void f^oo() {}145      };146    } // namespace147  )cpp");148}149 150TEST_F(DefineOutlineTest, FailsWithoutSource) {151  FileName = "Test.hpp";152  llvm::StringRef Test = "void fo^o() { return; }";153  llvm::StringRef Expected =154      "fail: Couldn't find a suitable implementation file.";155  EXPECT_EQ(apply(Test), Expected);156}157 158TEST_F(DefineOutlineTest, ApplyTest) {159  ExtraFiles["Test.cpp"] = "";160  FileName = "Test.hpp";161 162  struct {163    llvm::StringRef Test;164    llvm::StringRef ExpectedHeader;165    llvm::StringRef ExpectedSource;166  } Cases[] = {167      // Simple check168      {169          "void fo^o() { return; }",170          "void foo() ;",171          "void foo() { return; }",172      },173      // Inline specifier.174      {175          "inline void fo^o() { return; }",176          " void foo() ;",177          " void foo() { return; }",178      },179      // Default args.180      {181          "void fo^o(int x, int y = 5, int = 2, int (*foo)(int) = nullptr) {}",182          "void foo(int x, int y = 5, int = 2, int (*foo)(int) = nullptr) ;",183          "void foo(int x, int y , int , int (*foo)(int) ) {}",184      },185      {186          "struct Bar{Bar();}; void fo^o(Bar x = {}) {}",187          "struct Bar{Bar();}; void foo(Bar x = {}) ;",188          "void foo(Bar x ) {}",189      },190      // Constructors191      {192          R"cpp(193            class Foo {public: Foo(); Foo(int);};194            class Bar {195              Ba^r() {}196              Bar(int x) : f1(x) {}197              Foo f1;198              Foo f2 = 2;199            };)cpp",200          R"cpp(201            class Foo {public: Foo(); Foo(int);};202            class Bar {203              Bar() ;204              Bar(int x) : f1(x) {}205              Foo f1;206              Foo f2 = 2;207            };)cpp",208          "Bar::Bar() {}\n",209      },210      // Ctor with initializer.211      {212          R"cpp(213            class Foo {public: Foo(); Foo(int);};214            class Bar {215              Bar() {}216              B^ar(int x) : f1(x), f2(3) {}217              Foo f1;218              Foo f2 = 2;219            };)cpp",220          R"cpp(221            class Foo {public: Foo(); Foo(int);};222            class Bar {223              Bar() {}224              Bar(int x) ;225              Foo f1;226              Foo f2 = 2;227            };)cpp",228          "Bar::Bar(int x) : f1(x), f2(3) {}\n",229      },230      // Ctor initializer with attribute.231      {232          R"cpp(233              template <typename T> class Foo {234                F^oo(T z) __attribute__((weak)) : bar(2){}235                int bar;236              };)cpp",237          R"cpp(238              template <typename T> class Foo {239                Foo(T z) __attribute__((weak)) ;240                int bar;241              };template <typename T>242inline Foo<T>::Foo(T z) __attribute__((weak)) : bar(2){}243)cpp",244          ""},245      // Virt specifiers.246      {247          R"cpp(248            struct A {249              virtual void f^oo() {}250            };)cpp",251          R"cpp(252            struct A {253              virtual void foo() ;254            };)cpp",255          " void A::foo() {}\n",256      },257      {258          R"cpp(259            struct A {260              virtual virtual void virtual f^oo() {}261            };)cpp",262          R"cpp(263            struct A {264              virtual virtual void virtual foo() ;265            };)cpp",266          "  void  A::foo() {}\n",267      },268      {269          R"cpp(270            struct A {271              virtual void foo() = 0;272            };273            struct B : A {274              void fo^o() override {}275            };)cpp",276          R"cpp(277            struct A {278              virtual void foo() = 0;279            };280            struct B : A {281              void foo() override ;282            };)cpp",283          "void B::foo()  {}\n",284      },285      {286          R"cpp(287            struct A {288              virtual void foo() = 0;289            };290            struct B : A {291              void fo^o() final {}292            };)cpp",293          R"cpp(294            struct A {295              virtual void foo() = 0;296            };297            struct B : A {298              void foo() final ;299            };)cpp",300          "void B::foo()  {}\n",301      },302      {303          R"cpp(304            struct A {305              virtual void foo() = 0;306            };307            struct B : A {308              void fo^o() final override {}309            };)cpp",310          R"cpp(311            struct A {312              virtual void foo() = 0;313            };314            struct B : A {315              void foo() final override ;316            };)cpp",317          "void B::foo()   {}\n",318      },319      {320          R"cpp(321            struct A {322              static void fo^o() {}323            };)cpp",324          R"cpp(325            struct A {326              static void foo() ;327            };)cpp",328          " void A::foo() {}\n",329      },330      {331          R"cpp(332            struct A {333              static static void fo^o() {}334            };)cpp",335          R"cpp(336            struct A {337              static static void foo() ;338            };)cpp",339          "  void A::foo() {}\n",340      },341      {342          R"cpp(343            struct Foo {344              explicit Fo^o(int) {}345            };)cpp",346          R"cpp(347            struct Foo {348              explicit Foo(int) ;349            };)cpp",350          " Foo::Foo(int) {}\n",351      },352      {353          R"cpp(354            struct Foo {355              explicit explicit Fo^o(int) {}356            };)cpp",357          R"cpp(358            struct Foo {359              explicit explicit Foo(int) ;360            };)cpp",361          "  Foo::Foo(int) {}\n",362      },363      {364          R"cpp(365            struct A {366              inline void f^oo(int) {}367            };)cpp",368          R"cpp(369            struct A {370               void foo(int) ;371            };)cpp",372          " void A::foo(int) {}\n",373      },374      // Complex class template375      {376          R"cpp(377            template <typename T, typename ...U> struct O1 {378              template <class V, int A> struct O2 {379                enum E { E1, E2 };380                struct I {381                  E f^oo(T, U..., V, E) { return E1; }382                };383              };384            };)cpp",385          R"cpp(386            template <typename T, typename ...U> struct O1 {387              template <class V, int A> struct O2 {388                enum E { E1, E2 };389                struct I {390                  E foo(T, U..., V, E) ;391                };392              };393            };template <typename T, typename ...U>394template <class V, int A>395inline typename O1<T, U...>::template O2<V, A>::E O1<T, U...>::template O2<V, A>::I::foo(T, U..., V, E) { return E1; }396)cpp",397          ""},398      // Destructors399      {400          "class A { ~A^(){} };",401          "class A { ~A(); };",402          "A::~A(){} ",403      },404 405      // Member template406      {407          R"cpp(408            struct Foo {409              template <typename T, typename, bool B = true>410              T ^bar() { return {}; }411            };)cpp",412          R"cpp(413            struct Foo {414              template <typename T, typename, bool B = true>415              T bar() ;416            };template <typename T, typename, bool B>417inline T Foo::bar() { return {}; }418)cpp",419          ""},420 421      // Class template with member template422      {423          R"cpp(424            template <typename T> struct Foo {425              template <typename U, bool> T ^bar(const T& t, const U& u) { return {}; }426            };)cpp",427          R"cpp(428            template <typename T> struct Foo {429              template <typename U, bool> T bar(const T& t, const U& u) ;430            };template <typename T>431template <typename U, bool>432inline T Foo<T>::bar(const T& t, const U& u) { return {}; }433)cpp",434          ""},435  };436  for (const auto &Case : Cases) {437    SCOPED_TRACE(Case.Test);438    llvm::StringMap<std::string> EditedFiles;439    EXPECT_EQ(apply(Case.Test, &EditedFiles), Case.ExpectedHeader);440    if (Case.ExpectedSource.empty()) {441      EXPECT_TRUE(EditedFiles.empty());442    } else {443      EXPECT_THAT(EditedFiles, testing::ElementsAre(FileWithContents(444                                   testPath("Test.cpp"), Case.ExpectedSource)));445    }446  }447}448 449TEST_F(DefineOutlineTest, InCppFile) {450  FileName = "Test.cpp";451  struct {452    llvm::StringRef Test;453    llvm::StringRef ExpectedSource;454  } Cases[] = {455      {456          R"cpp(457            namespace foo {458            namespace {459            struct Foo { void ba^r() {} };460            struct Bar { void foo(); };461            void Bar::foo() {}462            }463            }464        )cpp",465          R"cpp(466            namespace foo {467            namespace {468            struct Foo { void bar() ; };void Foo::bar() {} 469            struct Bar { void foo(); };470            void Bar::foo() {}471            }472            }473        )cpp"},474  };475 476  for (const auto &Case : Cases) {477    SCOPED_TRACE(Case.Test);478    EXPECT_EQ(apply(Case.Test, nullptr), Case.ExpectedSource);479  }480}481 482TEST_F(DefineOutlineTest, HandleMacros) {483  llvm::StringMap<std::string> EditedFiles;484  ExtraFiles["Test.cpp"] = "";485  FileName = "Test.hpp";486  ExtraArgs.push_back("-DVIRTUAL=virtual");487  ExtraArgs.push_back("-DOVER=override");488 489  struct {490    llvm::StringRef Test;491    llvm::StringRef ExpectedHeader;492    llvm::StringRef ExpectedSource;493  } Cases[] = {494      {R"cpp(495          #define BODY { return; }496          void f^oo()BODY)cpp",497       R"cpp(498          #define BODY { return; }499          void foo();)cpp",500       "void foo()BODY"},501 502      {R"cpp(503          #define BODY return;504          void f^oo(){BODY})cpp",505       R"cpp(506          #define BODY return;507          void foo();)cpp",508       "void foo(){BODY}"},509 510      {R"cpp(511          #define TARGET void foo()512          [[TARGET]]{ return; })cpp",513       R"cpp(514          #define TARGET void foo()515          TARGET;)cpp",516       "TARGET{ return; }"},517 518      {R"cpp(519          #define TARGET foo520          void [[TARGET]](){ return; })cpp",521       R"cpp(522          #define TARGET foo523          void TARGET();)cpp",524       "void TARGET(){ return; }"},525      {R"cpp(#define VIRT virtual526          struct A {527            VIRT void f^oo() {}528          };)cpp",529       R"cpp(#define VIRT virtual530          struct A {531            VIRT void foo() ;532          };)cpp",533       " void A::foo() {}\n"},534      {R"cpp(535          struct A {536            VIRTUAL void f^oo() {}537          };)cpp",538       R"cpp(539          struct A {540            VIRTUAL void foo() ;541          };)cpp",542       " void A::foo() {}\n"},543      {R"cpp(544          struct A {545            virtual void foo() = 0;546          };547          struct B : A {548            void fo^o() OVER {}549          };)cpp",550       R"cpp(551          struct A {552            virtual void foo() = 0;553          };554          struct B : A {555            void foo() OVER ;556          };)cpp",557       "void B::foo()  {}\n"},558      {R"cpp(#define STUPID_MACRO(X) virtual559          struct A {560            STUPID_MACRO(sizeof sizeof int) void f^oo() {}561          };)cpp",562       R"cpp(#define STUPID_MACRO(X) virtual563          struct A {564            STUPID_MACRO(sizeof sizeof int) void foo() ;565          };)cpp",566       " void A::foo() {}\n"},567      {R"cpp(#define STAT static568          struct A {569            STAT void f^oo() {}570          };)cpp",571       R"cpp(#define STAT static572          struct A {573            STAT void foo() ;574          };)cpp",575       " void A::foo() {}\n"},576      {R"cpp(#define STUPID_MACRO(X) static577          struct A {578            STUPID_MACRO(sizeof sizeof int) void f^oo() {}579          };)cpp",580       R"cpp(#define STUPID_MACRO(X) static581          struct A {582            STUPID_MACRO(sizeof sizeof int) void foo() ;583          };)cpp",584       " void A::foo() {}\n"},585  };586  for (const auto &Case : Cases) {587    SCOPED_TRACE(Case.Test);588    EXPECT_EQ(apply(Case.Test, &EditedFiles), Case.ExpectedHeader);589    EXPECT_THAT(EditedFiles, testing::ElementsAre(FileWithContents(590                                 testPath("Test.cpp"), Case.ExpectedSource)));591  }592}593 594TEST_F(DefineOutlineTest, QualifyReturnValue) {595  FileName = "Test.hpp";596  ExtraFiles["Test.cpp"] = "";597 598  struct {599    llvm::StringRef Test;600    llvm::StringRef ExpectedHeader;601    llvm::StringRef ExpectedSource;602  } Cases[] = {603      {R"cpp(604        namespace a { class Foo{}; }605        using namespace a;606        Foo fo^o() { return {}; })cpp",607       R"cpp(608        namespace a { class Foo{}; }609        using namespace a;610        Foo foo() ;)cpp",611       "a::Foo foo() { return {}; }"},612      {R"cpp(613        namespace a {614          class Foo {615            class Bar {};616            Bar fo^o() { return {}; }617          };618        })cpp",619       R"cpp(620        namespace a {621          class Foo {622            class Bar {};623            Bar foo() ;624          };625        })cpp",626       "a::Foo::Bar a::Foo::foo() { return {}; }\n"},627      {R"cpp(628        class Foo {};629        Foo fo^o() { return {}; })cpp",630       R"cpp(631        class Foo {};632        Foo foo() ;)cpp",633       "Foo foo() { return {}; }"},634  };635  llvm::StringMap<std::string> EditedFiles;636  for (auto &Case : Cases) {637    apply(Case.Test, &EditedFiles);638    EXPECT_EQ(apply(Case.Test, &EditedFiles), Case.ExpectedHeader);639    EXPECT_THAT(EditedFiles, testing::ElementsAre(FileWithContents(640                                 testPath("Test.cpp"), Case.ExpectedSource)));641  }642}643 644TEST_F(DefineOutlineTest, QualifyFunctionName) {645  FileName = "Test.hpp";646  struct {647    llvm::StringRef TestHeader;648    llvm::StringRef TestSource;649    llvm::StringRef ExpectedHeader;650    llvm::StringRef ExpectedSource;651  } Cases[] = {652      {653          R"cpp(654            namespace a {655              namespace b {656                class Foo {657                  void fo^o() {}658                };659              }660            })cpp",661          "",662          R"cpp(663            namespace a {664              namespace b {665                class Foo {666                  void foo() ;667                };668              }669            })cpp",670          "void a::b::Foo::foo() {}\n",671      },672      {673          "namespace a { namespace b { void f^oo() {} } }",674          "namespace a{}",675          "namespace a { namespace b { void foo() ; } }",676          "namespace a{void b::foo() {} }",677      },678      {679          "namespace a { namespace b { void f^oo() {} } }",680          "using namespace a;",681          "namespace a { namespace b { void foo() ; } }",682          // FIXME: Take using namespace directives in the source file into683          // account. This can be spelled as b::foo instead.684          "using namespace a;void a::b::foo() {} ",685      },686      {687          "namespace a { class A { ~A^(){} }; }",688          "",689          "namespace a { class A { ~A(); }; }",690          "a::A::~A(){} ",691      },692      {693          "namespace a { class A { ~A^(){} }; }",694          "namespace a{}",695          "namespace a { class A { ~A(); }; }",696          "namespace a{A::~A(){} }",697      },698  };699  llvm::StringMap<std::string> EditedFiles;700  for (auto &Case : Cases) {701    ExtraFiles["Test.cpp"] = std::string(Case.TestSource);702    EXPECT_EQ(apply(Case.TestHeader, &EditedFiles), Case.ExpectedHeader);703    EXPECT_THAT(EditedFiles, testing::ElementsAre(FileWithContents(704                                 testPath("Test.cpp"), Case.ExpectedSource)))705        << Case.TestHeader;706  }707}708 709TEST_F(DefineOutlineTest, FailsMacroSpecifier) {710  FileName = "Test.hpp";711  ExtraFiles["Test.cpp"] = "";712  ExtraArgs.push_back("-DFINALOVER=final override");713 714  std::pair<StringRef, StringRef> Cases[] = {715      {716          R"cpp(717          #define VIRT virtual void718          struct A {719            VIRT fo^o() {}720          };)cpp",721          "fail: define outline: couldn't remove `virtual` keyword."},722      {723          R"cpp(724          #define OVERFINAL final override725          struct A {726            virtual void foo() {}727          };728          struct B : A {729            void fo^o() OVERFINAL {}730          };)cpp",731          "fail: define outline: Can't move out of line as function has a "732          "macro `override` specifier.\ndefine outline: Can't move out of line "733          "as function has a macro `final` specifier."},734      {735          R"cpp(736          struct A {737            virtual void foo() {}738          };739          struct B : A {740            void fo^o() FINALOVER {}741          };)cpp",742          "fail: define outline: Can't move out of line as function has a "743          "macro `override` specifier.\ndefine outline: Can't move out of line "744          "as function has a macro `final` specifier."},745  };746  for (const auto &Case : Cases) {747    EXPECT_EQ(apply(Case.first), Case.second);748  }749}750 751TWEAK_WORKSPACE_TEST(DefineOutline);752 753// Test that DefineOutline's use of getCorrespondingHeaderOrSource()754// to find the source file corresponding to a header file in which the755// tweak is invoked is working as intended.756TEST_F(DefineOutlineWorkspaceTest, FindsCorrespondingSource) {757  llvm::Annotations HeaderBefore(R"cpp(758class A {759  void bar();760  void f^oo(){}761};762)cpp");763  std::string SourceBefore(R"cpp(764#include "a.hpp"765void A::bar(){}766)cpp");767  std::string HeaderAfter = R"cpp(768class A {769  void bar();770  void foo();771};772)cpp";773  std::string SourceAfter = R"cpp(774#include "a.hpp"775void A::bar(){}void A::foo(){}776 777)cpp";778  Workspace.addSource("a.hpp", HeaderBefore.code());779  Workspace.addMainFile("a.cpp", SourceBefore);780  auto Result = apply("a.hpp", {HeaderBefore.point(), HeaderBefore.point()});781  EXPECT_THAT(Result,782              AllOf(withStatus("success"),783                    editedFiles(UnorderedElementsAre(784                        FileWithContents(testPath("a.hpp"), HeaderAfter),785                        FileWithContents(testPath("a.cpp"), SourceAfter)))));786}787 788// Test that the definition is inserted in a sensible location789// under various circumstances.790// Note that the formatting looks a little off here and there,791// which is because in contrast to the actual tweak, the test procedure792// does not run clang-format on the resulting code.793TEST_F(DefineOutlineWorkspaceTest, SensibleInsertionLocations) {794  const struct {795    llvm::StringRef HeaderBefore;796    llvm::StringRef SourceBefore;797    llvm::StringRef HeaderAfter;798    llvm::StringRef SourceAfter;799  } Cases[] = {800      // Criterion 1: Distance801      {802          R"cpp(803struct Foo {804  void ignored1();     // Too far away805  void ignored2();     // No definition806  void ignored3() {}   // Defined inline807  void fo^o() {}808  void neighbor();809};810)cpp",811          R"cpp(812#include "a.hpp"813void Foo::ignored1() {}814void Foo::neighbor() {}815)cpp",816          R"cpp(817struct Foo {818  void ignored1();     // Too far away819  void ignored2();     // No definition820  void ignored3() {}   // Defined inline821  void foo() ;822  void neighbor();823};824)cpp",825          R"cpp(826#include "a.hpp"827void Foo::ignored1() {}828void Foo::foo() {}829void Foo::neighbor() {}830)cpp"},831 832      // Criterion 2: Prefer preceding833      {834          R"cpp(835struct Foo {836  void neighbor();837  void fo^o() {}838  void ignored();839};840)cpp",841          R"cpp(842#include "a.hpp"843void Foo::neighbor() {}844void Foo::ignored() {}845)cpp",846          R"cpp(847struct Foo {848  void neighbor();849  void foo() ;850  void ignored();851};852)cpp",853          R"cpp(854#include "a.hpp"855void Foo::neighbor() {}void Foo::foo() {}856 857void Foo::ignored() {}858)cpp"},859 860      // Like above, but with a namespace861      {862          R"cpp(863namespace NS {864struct Foo {865  void neighbor();866  void fo^o() {}867  void ignored();868};869}870)cpp",871          R"cpp(872#include "a.hpp"873namespace NS {874void Foo::neighbor() {}875void Foo::ignored() {}876}877)cpp",878          R"cpp(879namespace NS {880struct Foo {881  void neighbor();882  void foo() ;883  void ignored();884};885}886)cpp",887          R"cpp(888#include "a.hpp"889namespace NS {890void Foo::neighbor() {}void Foo::foo() {}891 892void Foo::ignored() {}893}894)cpp"},895 896      // Like above, but there is no namespace at the definition site897      {898          R"cpp(899namespace NS {900struct Foo {901  void neighbor();902  void fo^o() {}903  void ignored();904};905}906)cpp",907          R"cpp(908#include "a.hpp"909void NS::Foo::neighbor() {}910void NS::Foo::ignored() {}911)cpp",912          R"cpp(913namespace NS {914struct Foo {915  void neighbor();916  void foo() ;917  void ignored();918};919}920)cpp",921          R"cpp(922#include "a.hpp"923void NS::Foo::neighbor() {}void NS::Foo::foo() {}924 925void NS::Foo::ignored() {}926)cpp"},927 928      // Neighbor's definition is in header929      {930          R"cpp(931struct Foo {932  void fo^o() {}933  void neighbor();934  void ignored();935};936inline void Foo::neighbor() {}937)cpp",938          R"cpp(939#include "a.hpp"940void Foo::ignored() {}941)cpp",942          R"cpp(943struct Foo {944  void foo() ;945  void neighbor();946  void ignored();947};948inline void Foo::foo() {}949inline void Foo::neighbor() {}950)cpp",951          {}},952 953  };954 955  for (const auto &Case : Cases) {956    Workspace = {};957    llvm::Annotations Hdr(Case.HeaderBefore);958    Workspace.addSource("a.hpp", Hdr.code());959    Workspace.addMainFile("a.cpp", Case.SourceBefore);960    auto Result = apply("a.hpp", {Hdr.point(), Hdr.point()});961    if (Case.SourceAfter.empty()) {962      EXPECT_THAT(Result,963                  AllOf(withStatus("success"),964                        editedFiles(UnorderedElementsAre(FileWithContents(965                            testPath("a.hpp"), Case.HeaderAfter)))));966 967    } else {968      EXPECT_THAT(969          Result,970          AllOf(withStatus("success"),971                editedFiles(UnorderedElementsAre(972                    FileWithContents(testPath("a.hpp"), Case.HeaderAfter),973                    FileWithContents(testPath("a.cpp"), Case.SourceAfter)))));974    }975  }976}977} // namespace978} // namespace clangd979} // namespace clang980