Files
TheRock/compiler/amd-llvm/clang-tools-extra/clangd/unittests/tweaks/DefineOutlineTests.cpp
T
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

1044 lines
26 KiB
C++

//===-- DefineOutline.cpp ---------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "TestFS.h"
#include "TweakTesting.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
namespace clang {
namespace clangd {
namespace {
using ::testing::UnorderedElementsAre;
TWEAK_TEST(DefineOutline);
TEST_F(DefineOutlineTest, TriggersOnFunctionDecl) {
FileName = "Test.cpp";
// Not available for free function unless in a header file.
EXPECT_UNAVAILABLE(R"cpp(
[[void [[f^o^o]]() [[{
return;
}]]]])cpp");
// Available in soure file.
EXPECT_AVAILABLE(R"cpp(
struct Foo {
void f^oo() {}
};
)cpp");
// Available within named namespace in source file.
EXPECT_AVAILABLE(R"cpp(
namespace N {
struct Foo {
void f^oo() {}
};
} // namespace N
)cpp");
// Available within anonymous namespace in source file.
EXPECT_AVAILABLE(R"cpp(
namespace {
struct Foo {
void f^oo() {}
};
} // namespace
)cpp");
// Not available for out-of-line method.
EXPECT_UNAVAILABLE(R"cpp(
class Bar {
void baz();
};
[[void [[Bar::[[b^a^z]]]]() [[{
return;
}]]]])cpp");
FileName = "Test.hpp";
// Not available unless function name or fully body is selected.
EXPECT_UNAVAILABLE(R"cpp(
[[vo^id ]]foo[[()]] {[[
[[(void)(5+3);
return;]]
}]])cpp");
// Available even if there are no implementation files.
EXPECT_AVAILABLE(R"cpp(
[[void [[f^o^o]]() [[{
return;
}]]]])cpp");
// Not available for out-of-line methods.
EXPECT_UNAVAILABLE(R"cpp(
class Bar {
void baz();
};
[[void [[Bar::[[b^a^z]]]]() [[{
return;
}]]]])cpp");
// Basic check for function body and signature.
EXPECT_AVAILABLE(R"cpp(
class Bar {
[[void [[f^o^o^]]() [[{ return; }]]]]
};
void foo();
[[void [[f^o^o]]() [[{
return;
}]]]])cpp");
// Not available on deleted members.
EXPECT_UNAVAILABLE(R"cpp(
class Foo {
F^oo(const Foo&) = delete;
};)cpp");
// Not available within templated classes with unnamed parameters, as it is
// hard to spell class name out-of-line in such cases.
EXPECT_UNAVAILABLE(R"cpp(
template <typename> struct Foo { void fo^o(){} };
)cpp");
// Not available on function template specializations and free function
// templates.
EXPECT_UNAVAILABLE(R"cpp(
template <typename T> void fo^o() {}
template <> void fo^o<int>() {}
)cpp");
// Not available on methods of unnamed classes.
EXPECT_UNAVAILABLE(R"cpp(
struct Foo {
struct { void b^ar() {} } Bar;
};
)cpp");
// Not available on methods of named classes with unnamed parent in parents
// nesting.
EXPECT_UNAVAILABLE(R"cpp(
struct Foo {
struct {
struct Bar { void b^ar() {} };
} Baz;
};
)cpp");
// Not available on definitions in header file within unnamed namespaces
EXPECT_UNAVAILABLE(R"cpp(
namespace {
struct Foo {
void f^oo() {}
};
} // namespace
)cpp");
}
TEST_F(DefineOutlineTest, FailsWithoutSource) {
FileName = "Test.hpp";
llvm::StringRef Test = "void fo^o() { return; }";
llvm::StringRef Expected =
"fail: Couldn't find a suitable implementation file.";
EXPECT_EQ(apply(Test), Expected);
}
TEST_F(DefineOutlineTest, ApplyTest) {
ExtraFiles["Test.cpp"] = "";
FileName = "Test.hpp";
struct {
llvm::StringRef Test;
llvm::StringRef ExpectedHeader;
llvm::StringRef ExpectedSource;
} Cases[] = {
// Simple check: Move
{
"void fo^o() { return; }",
"void foo() ;",
"void foo() { return; }",
},
// Simple check: Create
{
"void fo^o(int i = 5);",
"void foo(int i = 5);",
"void foo(int i ) {}",
},
// Simple check: Create with return value
{
"int fo^o();",
"int foo();",
"int foo() { return {}; }",
},
// Inline specifier.
{
"inline void fo^o() { return; }",
" void foo() ;",
" void foo() { return; }",
},
// Default args.
{
"void fo^o(int x, int y = 5, int = 2, int (*foo)(int) = nullptr) {}",
"void foo(int x, int y = 5, int = 2, int (*foo)(int) = nullptr) ;",
"void foo(int x, int y , int , int (*foo)(int) ) {}",
},
{
"struct Bar{Bar();}; void fo^o(Bar x = {}) {}",
"struct Bar{Bar();}; void foo(Bar x = {}) ;",
"void foo(Bar x ) {}",
},
// Constructors
{
R"cpp(
class Foo {public: Foo(); Foo(int);};
class Bar {
Ba^r() {}
Bar(int x) : f1(x) {}
Foo f1;
Foo f2 = 2;
};)cpp",
R"cpp(
class Foo {public: Foo(); Foo(int);};
class Bar {
Bar() ;
Bar(int x) : f1(x) {}
Foo f1;
Foo f2 = 2;
};)cpp",
"Bar::Bar() {}\n",
},
// Ctor with initializer.
{
R"cpp(
class Foo {public: Foo(); Foo(int);};
class Bar {
Bar() {}
B^ar(int x) : f1(x), f2(3) {}
Foo f1;
Foo f2 = 2;
};)cpp",
R"cpp(
class Foo {public: Foo(); Foo(int);};
class Bar {
Bar() {}
Bar(int x) ;
Foo f1;
Foo f2 = 2;
};)cpp",
"Bar::Bar(int x) : f1(x), f2(3) {}\n",
},
// Ctor initializer with attribute.
{
R"cpp(
template <typename T> class Foo {
F^oo(T z) __attribute__((weak)) : bar(2){}
int bar;
};)cpp",
R"cpp(
template <typename T> class Foo {
Foo(T z) __attribute__((weak)) ;
int bar;
};template <typename T>
inline Foo<T>::Foo(T z) __attribute__((weak)) : bar(2){}
)cpp",
""},
// Default ctor
{
R"cpp(
class Foo {
F^oo() = default;
};)cpp",
R"cpp(
class Foo {
Foo() ;
};)cpp",
"Foo::Foo() = default;",
},
// Virt specifiers.
{
R"cpp(
struct A {
virtual void f^oo() {}
};)cpp",
R"cpp(
struct A {
virtual void foo() ;
};)cpp",
" void A::foo() {}\n",
},
{
R"cpp(
struct A {
virtual virtual void virtual f^oo() {}
};)cpp",
R"cpp(
struct A {
virtual virtual void virtual foo() ;
};)cpp",
" void A::foo() {}\n",
},
{
R"cpp(
struct A {
virtual void foo() = 0;
};
struct B : A {
void fo^o() override {}
};)cpp",
R"cpp(
struct A {
virtual void foo() = 0;
};
struct B : A {
void foo() override ;
};)cpp",
"void B::foo() {}\n",
},
{
R"cpp(
struct A {
virtual void foo() = 0;
};
struct B : A {
void fo^o() final {}
};)cpp",
R"cpp(
struct A {
virtual void foo() = 0;
};
struct B : A {
void foo() final ;
};)cpp",
"void B::foo() {}\n",
},
{
R"cpp(
struct A {
virtual void foo() = 0;
};
struct B : A {
void fo^o() final override {}
};)cpp",
R"cpp(
struct A {
virtual void foo() = 0;
};
struct B : A {
void foo() final override ;
};)cpp",
"void B::foo() {}\n",
},
{
R"cpp(
struct A {
static void fo^o() {}
};)cpp",
R"cpp(
struct A {
static void foo() ;
};)cpp",
" void A::foo() {}\n",
},
{
R"cpp(
struct A {
static static void fo^o() {}
};)cpp",
R"cpp(
struct A {
static static void foo() ;
};)cpp",
" void A::foo() {}\n",
},
{
R"cpp(
struct Foo {
explicit Fo^o(int) {}
};)cpp",
R"cpp(
struct Foo {
explicit Foo(int) ;
};)cpp",
" Foo::Foo(int) {}\n",
},
{
R"cpp(
struct Foo {
explicit explicit Fo^o(int) {}
};)cpp",
R"cpp(
struct Foo {
explicit explicit Foo(int) ;
};)cpp",
" Foo::Foo(int) {}\n",
},
{
R"cpp(
struct A {
inline void f^oo(int) {}
};)cpp",
R"cpp(
struct A {
void foo(int) ;
};)cpp",
" void A::foo(int) {}\n",
},
// Complex class template
{
R"cpp(
template <typename T, typename ...U> struct O1 {
template <class V, int A> struct O2 {
enum E { E1, E2 };
struct I {
E f^oo(T, U..., V, E) { return E1; }
};
};
};)cpp",
R"cpp(
template <typename T, typename ...U> struct O1 {
template <class V, int A> struct O2 {
enum E { E1, E2 };
struct I {
E foo(T, U..., V, E) ;
};
};
};template <typename T, typename ...U>
template <class V, int A>
inline typename O1<T, U...>::template O2<V, A>::E O1<T, U...>::template O2<V, A>::I::foo(T, U..., V, E) { return E1; }
)cpp",
""},
// Destructors
{
"class A { ~A^(){} };",
"class A { ~A(); };",
"A::~A(){} ",
},
// Member template
{
R"cpp(
struct Foo {
template <typename T, typename, bool B = true>
T ^bar() { return {}; }
};)cpp",
R"cpp(
struct Foo {
template <typename T, typename, bool B = true>
T bar() ;
};template <typename T, typename, bool B>
inline T Foo::bar() { return {}; }
)cpp",
""},
// Class template with member template
{
R"cpp(
template <typename T> struct Foo {
template <typename U, bool> T ^bar(const T& t, const U& u) { return {}; }
};)cpp",
R"cpp(
template <typename T> struct Foo {
template <typename U, bool> T bar(const T& t, const U& u) ;
};template <typename T>
template <typename U, bool>
inline T Foo<T>::bar(const T& t, const U& u) { return {}; }
)cpp",
""},
};
for (const auto &Case : Cases) {
SCOPED_TRACE(Case.Test);
llvm::StringMap<std::string> EditedFiles;
EXPECT_EQ(apply(Case.Test, &EditedFiles), Case.ExpectedHeader);
if (Case.ExpectedSource.empty()) {
EXPECT_TRUE(EditedFiles.empty());
} else {
EXPECT_THAT(EditedFiles, testing::ElementsAre(FileWithContents(
testPath("Test.cpp"), Case.ExpectedSource)));
}
}
}
TEST_F(DefineOutlineTest, InCppFile) {
FileName = "Test.cpp";
struct {
llvm::StringRef Test;
llvm::StringRef ExpectedSource;
} Cases[] = {
{
R"cpp(
namespace foo {
namespace {
struct Foo { void ba^r() {} };
struct Bar { void foo(); };
void Bar::foo() {}
}
}
)cpp",
R"cpp(
namespace foo {
namespace {
struct Foo { void bar() ; };void Foo::bar() {}
struct Bar { void foo(); };
void Bar::foo() {}
}
}
)cpp"},
};
for (const auto &Case : Cases) {
SCOPED_TRACE(Case.Test);
EXPECT_EQ(apply(Case.Test, nullptr), Case.ExpectedSource);
}
}
TEST_F(DefineOutlineTest, HandleMacros) {
llvm::StringMap<std::string> EditedFiles;
ExtraFiles["Test.cpp"] = "";
FileName = "Test.hpp";
ExtraArgs.push_back("-DVIRTUAL=virtual");
ExtraArgs.push_back("-DOVER=override");
struct {
llvm::StringRef Test;
llvm::StringRef ExpectedHeader;
llvm::StringRef ExpectedSource;
} Cases[] = {
{R"cpp(
#define BODY { return; }
void f^oo()BODY)cpp",
R"cpp(
#define BODY { return; }
void foo();)cpp",
"void foo()BODY"},
{R"cpp(
#define BODY return;
void f^oo(){BODY})cpp",
R"cpp(
#define BODY return;
void foo();)cpp",
"void foo(){BODY}"},
{R"cpp(
#define TARGET void foo()
[[TARGET]]{ return; })cpp",
R"cpp(
#define TARGET void foo()
TARGET;)cpp",
"TARGET{ return; }"},
{R"cpp(
#define TARGET foo
void [[TARGET]](){ return; })cpp",
R"cpp(
#define TARGET foo
void TARGET();)cpp",
"void TARGET(){ return; }"},
{R"cpp(#define VIRT virtual
struct A {
VIRT void f^oo() {}
};)cpp",
R"cpp(#define VIRT virtual
struct A {
VIRT void foo() ;
};)cpp",
" void A::foo() {}\n"},
{R"cpp(
struct A {
VIRTUAL void f^oo() {}
};)cpp",
R"cpp(
struct A {
VIRTUAL void foo() ;
};)cpp",
" void A::foo() {}\n"},
{R"cpp(
struct A {
virtual void foo() = 0;
};
struct B : A {
void fo^o() OVER {}
};)cpp",
R"cpp(
struct A {
virtual void foo() = 0;
};
struct B : A {
void foo() OVER ;
};)cpp",
"void B::foo() {}\n"},
{R"cpp(#define STUPID_MACRO(X) virtual
struct A {
STUPID_MACRO(sizeof sizeof int) void f^oo() {}
};)cpp",
R"cpp(#define STUPID_MACRO(X) virtual
struct A {
STUPID_MACRO(sizeof sizeof int) void foo() ;
};)cpp",
" void A::foo() {}\n"},
{R"cpp(#define STAT static
struct A {
STAT void f^oo() {}
};)cpp",
R"cpp(#define STAT static
struct A {
STAT void foo() ;
};)cpp",
" void A::foo() {}\n"},
{R"cpp(#define STUPID_MACRO(X) static
struct A {
STUPID_MACRO(sizeof sizeof int) void f^oo() {}
};)cpp",
R"cpp(#define STUPID_MACRO(X) static
struct A {
STUPID_MACRO(sizeof sizeof int) void foo() ;
};)cpp",
" void A::foo() {}\n"},
};
for (const auto &Case : Cases) {
SCOPED_TRACE(Case.Test);
EXPECT_EQ(apply(Case.Test, &EditedFiles), Case.ExpectedHeader);
EXPECT_THAT(EditedFiles, testing::ElementsAre(FileWithContents(
testPath("Test.cpp"), Case.ExpectedSource)));
}
}
TEST_F(DefineOutlineTest, QualifyReturnValue) {
FileName = "Test.hpp";
ExtraFiles["Test.cpp"] = "";
struct {
llvm::StringRef Test;
llvm::StringRef ExpectedHeader;
llvm::StringRef ExpectedSource;
} Cases[] = {
{R"cpp(
namespace a { class Foo{}; }
using namespace a;
Foo fo^o() { return {}; })cpp",
R"cpp(
namespace a { class Foo{}; }
using namespace a;
Foo foo() ;)cpp",
"a::Foo foo() { return {}; }"},
{R"cpp(
namespace a {
class Foo {
class Bar {};
Bar fo^o() { return {}; }
};
})cpp",
R"cpp(
namespace a {
class Foo {
class Bar {};
Bar foo() ;
};
})cpp",
"a::Foo::Bar a::Foo::foo() { return {}; }\n"},
{R"cpp(
class Foo {};
Foo fo^o() { return {}; })cpp",
R"cpp(
class Foo {};
Foo foo() ;)cpp",
"Foo foo() { return {}; }"},
{R"cpp(
template <typename T> class Expected {};
class Foo {
class Bar {};
Expected<Bar> fu^nc() { return {}; }
};)cpp",
R"cpp(
template <typename T> class Expected {};
class Foo {
class Bar {};
Expected<Bar> func() ;
};)cpp",
"Expected<Foo::Bar> Foo::func() { return {}; }\n"},
};
llvm::StringMap<std::string> EditedFiles;
for (auto &Case : Cases) {
apply(Case.Test, &EditedFiles);
EXPECT_EQ(apply(Case.Test, &EditedFiles), Case.ExpectedHeader);
EXPECT_THAT(EditedFiles, testing::ElementsAre(FileWithContents(
testPath("Test.cpp"), Case.ExpectedSource)));
}
}
TEST_F(DefineOutlineTest, QualifyFunctionName) {
FileName = "Test.hpp";
struct {
llvm::StringRef TestHeader;
llvm::StringRef TestSource;
llvm::StringRef ExpectedHeader;
llvm::StringRef ExpectedSource;
} Cases[] = {
{
R"cpp(
namespace a {
namespace b {
class Foo {
void fo^o() {}
};
}
})cpp",
"",
R"cpp(
namespace a {
namespace b {
class Foo {
void foo() ;
};
}
})cpp",
"void a::b::Foo::foo() {}\n",
},
{
"namespace a { namespace b { void f^oo() {} } }",
"namespace a{}",
"namespace a { namespace b { void foo() ; } }",
"namespace a{void b::foo() {} }",
},
{
"namespace a { namespace b { void f^oo() {} } }",
"using namespace a;",
"namespace a { namespace b { void foo() ; } }",
// FIXME: Take using namespace directives in the source file into
// account. This can be spelled as b::foo instead.
"using namespace a;void a::b::foo() {} ",
},
{
"namespace a { class A { ~A^(){} }; }",
"",
"namespace a { class A { ~A(); }; }",
"a::A::~A(){} ",
},
{
"namespace a { class A { ~A^(){} }; }",
"namespace a{}",
"namespace a { class A { ~A(); }; }",
"namespace a{A::~A(){} }",
},
};
llvm::StringMap<std::string> EditedFiles;
for (auto &Case : Cases) {
ExtraFiles["Test.cpp"] = std::string(Case.TestSource);
EXPECT_EQ(apply(Case.TestHeader, &EditedFiles), Case.ExpectedHeader);
EXPECT_THAT(EditedFiles, testing::ElementsAre(FileWithContents(
testPath("Test.cpp"), Case.ExpectedSource)))
<< Case.TestHeader;
}
}
TEST_F(DefineOutlineTest, FailsMacroSpecifier) {
FileName = "Test.hpp";
ExtraFiles["Test.cpp"] = "";
ExtraArgs.push_back("-DFINALOVER=final override");
std::pair<StringRef, StringRef> Cases[] = {
{
R"cpp(
#define VIRT virtual void
struct A {
VIRT fo^o() {}
};)cpp",
"fail: define outline: couldn't remove `virtual` keyword."},
{
R"cpp(
#define OVERFINAL final override
struct A {
virtual void foo() {}
};
struct B : A {
void fo^o() OVERFINAL {}
};)cpp",
"fail: define outline: Can't move out of line as function has a "
"macro `override` specifier.\ndefine outline: Can't move out of line "
"as function has a macro `final` specifier."},
{
R"cpp(
struct A {
virtual void foo() {}
};
struct B : A {
void fo^o() FINALOVER {}
};)cpp",
"fail: define outline: Can't move out of line as function has a "
"macro `override` specifier.\ndefine outline: Can't move out of line "
"as function has a macro `final` specifier."},
};
for (const auto &Case : Cases) {
EXPECT_EQ(apply(Case.first), Case.second);
}
}
TWEAK_WORKSPACE_TEST(DefineOutline);
// Test that DefineOutline's use of getCorrespondingHeaderOrSource()
// to find the source file corresponding to a header file in which the
// tweak is invoked is working as intended.
TEST_F(DefineOutlineWorkspaceTest, FindsCorrespondingSource) {
llvm::Annotations HeaderBefore(R"cpp(
class A {
void bar();
void f^oo(){}
};
)cpp");
std::string SourceBefore(R"cpp(
#include "a.hpp"
void A::bar(){}
)cpp");
std::string HeaderAfter = R"cpp(
class A {
void bar();
void foo();
};
)cpp";
std::string SourceAfter = R"cpp(
#include "a.hpp"
void A::bar(){}void A::foo(){}
)cpp";
Workspace.addSource("a.hpp", HeaderBefore.code());
Workspace.addMainFile("a.cpp", SourceBefore);
auto Result = apply("a.hpp", {HeaderBefore.point(), HeaderBefore.point()});
EXPECT_THAT(Result,
AllOf(withStatus("success"),
editedFiles(UnorderedElementsAre(
FileWithContents(testPath("a.hpp"), HeaderAfter),
FileWithContents(testPath("a.cpp"), SourceAfter)))));
}
// Test that the definition is inserted in a sensible location
// under various circumstances.
// Note that the formatting looks a little off here and there,
// which is because in contrast to the actual tweak, the test procedure
// does not run clang-format on the resulting code.
TEST_F(DefineOutlineWorkspaceTest, SensibleInsertionLocations) {
const struct {
llvm::StringRef HeaderBefore;
llvm::StringRef SourceBefore;
llvm::StringRef HeaderAfter;
llvm::StringRef SourceAfter;
} Cases[] = {
// Criterion 1: Distance
{
R"cpp(
struct Foo {
void ignored1(); // Too far away
void ignored2(); // No definition
void ignored3() {} // Defined inline
void fo^o() {}
void neighbor();
};
)cpp",
R"cpp(
#include "a.hpp"
void Foo::ignored1() {}
void Foo::neighbor() {}
)cpp",
R"cpp(
struct Foo {
void ignored1(); // Too far away
void ignored2(); // No definition
void ignored3() {} // Defined inline
void foo() ;
void neighbor();
};
)cpp",
R"cpp(
#include "a.hpp"
void Foo::ignored1() {}
void Foo::foo() {}
void Foo::neighbor() {}
)cpp"},
// Criterion 2: Prefer preceding
{
R"cpp(
struct Foo {
void neighbor();
void fo^o() {}
void ignored();
};
)cpp",
R"cpp(
#include "a.hpp"
void Foo::neighbor() {}
void Foo::ignored() {}
)cpp",
R"cpp(
struct Foo {
void neighbor();
void foo() ;
void ignored();
};
)cpp",
R"cpp(
#include "a.hpp"
void Foo::neighbor() {}void Foo::foo() {}
void Foo::ignored() {}
)cpp"},
// Like above, but with a namespace
{
R"cpp(
namespace NS {
struct Foo {
void neighbor();
void fo^o() {}
void ignored();
};
}
)cpp",
R"cpp(
#include "a.hpp"
namespace NS {
void Foo::neighbor() {}
void Foo::ignored() {}
}
)cpp",
R"cpp(
namespace NS {
struct Foo {
void neighbor();
void foo() ;
void ignored();
};
}
)cpp",
R"cpp(
#include "a.hpp"
namespace NS {
void Foo::neighbor() {}void Foo::foo() {}
void Foo::ignored() {}
}
)cpp"},
// Like above, but there is no namespace at the definition site
{
R"cpp(
namespace NS {
struct Foo {
void neighbor();
void fo^o() {}
void ignored();
};
}
)cpp",
R"cpp(
#include "a.hpp"
void NS::Foo::neighbor() {}
void NS::Foo::ignored() {}
)cpp",
R"cpp(
namespace NS {
struct Foo {
void neighbor();
void foo() ;
void ignored();
};
}
)cpp",
R"cpp(
#include "a.hpp"
void NS::Foo::neighbor() {}void NS::Foo::foo() {}
void NS::Foo::ignored() {}
)cpp"},
// Neighbor's definition is in header
{
R"cpp(
struct Foo {
void fo^o() {}
void neighbor();
void ignored();
};
inline void Foo::neighbor() {}
)cpp",
R"cpp(
#include "a.hpp"
void Foo::ignored() {}
)cpp",
R"cpp(
struct Foo {
void foo() ;
void neighbor();
void ignored();
};
inline void Foo::foo() {}
inline void Foo::neighbor() {}
)cpp",
{}},
// Adjacent definition with `= default`
{
R"cpp(
struct Foo {
void ignored1();
Foo();
void fun^c() {}
void ignored2();
};
)cpp",
R"cpp(
#include "a.hpp"
void Foo::ignored1() {}
Foo::Foo() = default;
void Foo::ignored2() {}
)cpp",
R"cpp(
struct Foo {
void ignored1();
Foo();
void func() ;
void ignored2();
};
)cpp",
R"cpp(
#include "a.hpp"
void Foo::ignored1() {}
Foo::Foo() = default;void Foo::func() {}
void Foo::ignored2() {}
)cpp"},
};
for (const auto &Case : Cases) {
Workspace = {};
llvm::Annotations Hdr(Case.HeaderBefore);
Workspace.addSource("a.hpp", Hdr.code());
Workspace.addMainFile("a.cpp", Case.SourceBefore);
auto Result = apply("a.hpp", {Hdr.point(), Hdr.point()});
if (Case.SourceAfter.empty()) {
EXPECT_THAT(Result,
AllOf(withStatus("success"),
editedFiles(UnorderedElementsAre(FileWithContents(
testPath("a.hpp"), Case.HeaderAfter)))));
} else {
EXPECT_THAT(
Result,
AllOf(withStatus("success"),
editedFiles(UnorderedElementsAre(
FileWithContents(testPath("a.hpp"), Case.HeaderAfter),
FileWithContents(testPath("a.cpp"), Case.SourceAfter)))));
}
}
}
} // namespace
} // namespace clangd
} // namespace clang