| //===-- DefineInlineTests.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 "TestTU.h" |
| #include "TweakTesting.h" |
| #include "gmock/gmock-matchers.h" |
| #include "gmock/gmock.h" |
| #include "gtest/gtest.h" |
| |
| using ::testing::ElementsAre; |
| |
| namespace clang { |
| namespace clangd { |
| namespace { |
| |
| TWEAK_TEST(DefineInline); |
| |
| TEST_F(DefineInlineTest, TriggersOnFunctionDecl) { |
| // Basic check for function body and signature. |
| EXPECT_AVAILABLE(R"cpp( |
| class Bar { |
| void baz(); |
| }; |
| |
| [[void [[Bar::[[b^a^z]]]]() [[{ |
| return; |
| }]]]] |
| |
| void foo(); |
| [[void [[f^o^o]]() [[{ |
| return; |
| }]]]] |
| )cpp"); |
| |
| EXPECT_UNAVAILABLE(R"cpp( |
| // Not a definition |
| vo^i[[d^ ^f]]^oo(); |
| |
| [[vo^id ]]foo[[()]] {[[ |
| [[(void)(5+3); |
| return;]] |
| }]] |
| |
| // Definition with no body. |
| class Bar { Bar() = def^ault; }; |
| )cpp"); |
| } |
| |
| TEST_F(DefineInlineTest, NoForwardDecl) { |
| Header = "void bar();"; |
| EXPECT_UNAVAILABLE(R"cpp( |
| void bar() { |
| return; |
| } |
| // FIXME: Generate a decl in the header. |
| void fo^o() { |
| return; |
| })cpp"); |
| } |
| |
| TEST_F(DefineInlineTest, ReferencedDecls) { |
| EXPECT_AVAILABLE(R"cpp( |
| void bar(); |
| void foo(int test); |
| |
| void fo^o(int baz) { |
| int x = 10; |
| bar(); |
| })cpp"); |
| |
| // Internal symbol usage. |
| Header = "void foo(int test);"; |
| EXPECT_UNAVAILABLE(R"cpp( |
| void bar(); |
| void fo^o(int baz) { |
| int x = 10; |
| bar(); |
| })cpp"); |
| |
| // Becomes available after making symbol visible. |
| Header = "void bar();" + Header; |
| EXPECT_AVAILABLE(R"cpp( |
| void fo^o(int baz) { |
| int x = 10; |
| bar(); |
| })cpp"); |
| |
| // FIXME: Move declaration below bar to make it visible. |
| Header.clear(); |
| EXPECT_UNAVAILABLE(R"cpp( |
| void foo(); |
| void bar(); |
| |
| void fo^o() { |
| bar(); |
| })cpp"); |
| |
| // Order doesn't matter within a class. |
| EXPECT_AVAILABLE(R"cpp( |
| class Bar { |
| void foo(); |
| void bar(); |
| }; |
| |
| void Bar::fo^o() { |
| bar(); |
| })cpp"); |
| |
| // FIXME: Perform include insertion to make symbol visible. |
| ExtraFiles["a.h"] = "void bar();"; |
| Header = "void foo(int test);"; |
| EXPECT_UNAVAILABLE(R"cpp( |
| #include "a.h" |
| void fo^o(int baz) { |
| int x = 10; |
| bar(); |
| })cpp"); |
| } |
| |
| TEST_F(DefineInlineTest, TemplateSpec) { |
| EXPECT_UNAVAILABLE(R"cpp( |
| template <typename T> void foo(); |
| template<> void foo<char>(); |
| |
| template<> void f^oo<int>() { |
| })cpp"); |
| EXPECT_UNAVAILABLE(R"cpp( |
| template <typename T> void foo(); |
| |
| template<> void f^oo<int>() { |
| })cpp"); |
| EXPECT_UNAVAILABLE(R"cpp( |
| template <typename T> struct Foo { void foo(); }; |
| |
| template <typename T> void Foo<T>::f^oo() { |
| })cpp"); |
| EXPECT_AVAILABLE(R"cpp( |
| template <typename T> void foo(); |
| void bar(); |
| template <> void foo<int>(); |
| |
| template<> void f^oo<int>() { |
| bar(); |
| })cpp"); |
| EXPECT_UNAVAILABLE(R"cpp( |
| namespace bar { |
| template <typename T> void f^oo() {} |
| template void foo<int>(); |
| })cpp"); |
| } |
| |
| TEST_F(DefineInlineTest, CheckForCanonDecl) { |
| EXPECT_UNAVAILABLE(R"cpp( |
| void foo(); |
| |
| void bar() {} |
| void f^oo() { |
| // This bar normally refers to the definition just above, but it is not |
| // visible from the forward declaration of foo. |
| bar(); |
| })cpp"); |
| // Make it available with a forward decl. |
| EXPECT_AVAILABLE(R"cpp( |
| void bar(); |
| void foo(); |
| |
| void bar() {} |
| void f^oo() { |
| bar(); |
| })cpp"); |
| } |
| |
| TEST_F(DefineInlineTest, UsingShadowDecls) { |
| EXPECT_UNAVAILABLE(R"cpp( |
| namespace ns1 { void foo(int); } |
| namespace ns2 { void foo(int*); } |
| template <typename T> |
| void bar(); |
| |
| using ns1::foo; |
| using ns2::foo; |
| |
| template <typename T> |
| void b^ar() { |
| foo(T()); |
| })cpp"); |
| } |
| |
| TEST_F(DefineInlineTest, TransformNestedNamespaces) { |
| auto Test = R"cpp( |
| namespace a { |
| void bar(); |
| namespace b { |
| void baz(); |
| namespace c { |
| void aux(); |
| } |
| } |
| } |
| |
| void foo(); |
| using namespace a; |
| using namespace b; |
| using namespace c; |
| void f^oo() { |
| bar(); |
| a::bar(); |
| |
| baz(); |
| b::baz(); |
| a::b::baz(); |
| |
| aux(); |
| c::aux(); |
| b::c::aux(); |
| a::b::c::aux(); |
| })cpp"; |
| auto Expected = R"cpp( |
| namespace a { |
| void bar(); |
| namespace b { |
| void baz(); |
| namespace c { |
| void aux(); |
| } |
| } |
| } |
| |
| void foo(){ |
| a::bar(); |
| a::bar(); |
| |
| a::b::baz(); |
| a::b::baz(); |
| a::b::baz(); |
| |
| a::b::c::aux(); |
| a::b::c::aux(); |
| a::b::c::aux(); |
| a::b::c::aux(); |
| } |
| using namespace a; |
| using namespace b; |
| using namespace c; |
| )cpp"; |
| EXPECT_EQ(apply(Test), Expected); |
| } |
| |
| TEST_F(DefineInlineTest, TransformUsings) { |
| auto Test = R"cpp( |
| namespace a { namespace b { namespace c { void aux(); } } } |
| |
| void foo(); |
| void f^oo() { |
| using namespace a; |
| using namespace b; |
| using namespace c; |
| using c::aux; |
| namespace d = c; |
| })cpp"; |
| auto Expected = R"cpp( |
| namespace a { namespace b { namespace c { void aux(); } } } |
| |
| void foo(){ |
| using namespace a; |
| using namespace a::b; |
| using namespace a::b::c; |
| using a::b::c::aux; |
| namespace d = a::b::c; |
| } |
| )cpp"; |
| EXPECT_EQ(apply(Test), Expected); |
| } |
| |
| TEST_F(DefineInlineTest, TransformDecls) { |
| auto Test = R"cpp( |
| void foo(); |
| void f^oo() { |
| class Foo { |
| public: |
| void foo(); |
| int x; |
| }; |
| |
| enum En { Zero, One }; |
| En x = Zero; |
| |
| enum class EnClass { Zero, One }; |
| EnClass y = EnClass::Zero; |
| })cpp"; |
| auto Expected = R"cpp( |
| void foo(){ |
| class Foo { |
| public: |
| void foo(); |
| int x; |
| }; |
| |
| enum En { Zero, One }; |
| En x = Zero; |
| |
| enum class EnClass { Zero, One }; |
| EnClass y = EnClass::Zero; |
| } |
| )cpp"; |
| EXPECT_EQ(apply(Test), Expected); |
| } |
| |
| TEST_F(DefineInlineTest, TransformTemplDecls) { |
| auto Test = R"cpp( |
| namespace a { |
| template <typename T> class Bar { |
| public: |
| void bar(); |
| }; |
| template <typename T> T bar; |
| template <typename T> void aux() {} |
| } |
| |
| void foo(); |
| |
| using namespace a; |
| void f^oo() { |
| bar<Bar<int>>.bar(); |
| aux<Bar<int>>(); |
| })cpp"; |
| auto Expected = R"cpp( |
| namespace a { |
| template <typename T> class Bar { |
| public: |
| void bar(); |
| }; |
| template <typename T> T bar; |
| template <typename T> void aux() {} |
| } |
| |
| void foo(){ |
| a::bar<a::Bar<int>>.bar(); |
| a::aux<a::Bar<int>>(); |
| } |
| |
| using namespace a; |
| )cpp"; |
| EXPECT_EQ(apply(Test), Expected); |
| } |
| |
| TEST_F(DefineInlineTest, TransformMembers) { |
| auto Test = R"cpp( |
| class Foo { |
| void foo(); |
| }; |
| |
| void Foo::f^oo() { |
| return; |
| })cpp"; |
| auto Expected = R"cpp( |
| class Foo { |
| void foo(){ |
| return; |
| } |
| }; |
| |
| )cpp"; |
| EXPECT_EQ(apply(Test), Expected); |
| |
| ExtraFiles["a.h"] = R"cpp( |
| class Foo { |
| void foo(); |
| };)cpp"; |
| |
| llvm::StringMap<std::string> EditedFiles; |
| Test = R"cpp( |
| #include "a.h" |
| void Foo::f^oo() { |
| return; |
| })cpp"; |
| Expected = R"cpp( |
| #include "a.h" |
| )cpp"; |
| EXPECT_EQ(apply(Test, &EditedFiles), Expected); |
| |
| Expected = R"cpp( |
| class Foo { |
| void foo(){ |
| return; |
| } |
| };)cpp"; |
| EXPECT_THAT(EditedFiles, |
| ElementsAre(FileWithContents(testPath("a.h"), Expected))); |
| } |
| |
| TEST_F(DefineInlineTest, TransformDependentTypes) { |
| auto Test = R"cpp( |
| namespace a { |
| template <typename T> class Bar {}; |
| } |
| |
| template <typename T> |
| void foo(); |
| |
| using namespace a; |
| template <typename T> |
| void f^oo() { |
| Bar<T> B; |
| Bar<Bar<T>> q; |
| })cpp"; |
| auto Expected = R"cpp( |
| namespace a { |
| template <typename T> class Bar {}; |
| } |
| |
| template <typename T> |
| void foo(){ |
| a::Bar<T> B; |
| a::Bar<a::Bar<T>> q; |
| } |
| |
| using namespace a; |
| )cpp"; |
| |
| EXPECT_EQ(apply(Test), Expected); |
| } |
| |
| TEST_F(DefineInlineTest, TransformFunctionTempls) { |
| // Check we select correct specialization decl. |
| std::pair<llvm::StringRef, llvm::StringRef> Cases[] = { |
| {R"cpp( |
| template <typename T> |
| void foo(T p); |
| |
| template <> |
| void foo<int>(int p); |
| |
| template <> |
| void foo<char>(char p); |
| |
| template <> |
| void fo^o<int>(int p) { |
| return; |
| })cpp", |
| R"cpp( |
| template <typename T> |
| void foo(T p); |
| |
| template <> |
| void foo<int>(int p){ |
| return; |
| } |
| |
| template <> |
| void foo<char>(char p); |
| |
| )cpp"}, |
| {// Make sure we are not selecting the first specialization all the time. |
| R"cpp( |
| template <typename T> |
| void foo(T p); |
| |
| template <> |
| void foo<int>(int p); |
| |
| template <> |
| void foo<char>(char p); |
| |
| template <> |
| void fo^o<char>(char p) { |
| return; |
| })cpp", |
| R"cpp( |
| template <typename T> |
| void foo(T p); |
| |
| template <> |
| void foo<int>(int p); |
| |
| template <> |
| void foo<char>(char p){ |
| return; |
| } |
| |
| )cpp"}, |
| {R"cpp( |
| template <typename T> |
| void foo(T p); |
| |
| template <> |
| void foo<int>(int p); |
| |
| template <typename T> |
| void fo^o(T p) { |
| return; |
| })cpp", |
| R"cpp( |
| template <typename T> |
| void foo(T p){ |
| return; |
| } |
| |
| template <> |
| void foo<int>(int p); |
| |
| )cpp"}, |
| }; |
| for (const auto &Case : Cases) |
| EXPECT_EQ(apply(Case.first), Case.second) << Case.first; |
| } |
| |
| TEST_F(DefineInlineTest, TransformTypeLocs) { |
| auto Test = R"cpp( |
| namespace a { |
| template <typename T> class Bar { |
| public: |
| template <typename Q> class Baz {}; |
| }; |
| class Foo{}; |
| } |
| |
| void foo(); |
| |
| using namespace a; |
| void f^oo() { |
| Bar<int> B; |
| Foo foo; |
| a::Bar<Bar<int>>::Baz<Bar<int>> q; |
| })cpp"; |
| auto Expected = R"cpp( |
| namespace a { |
| template <typename T> class Bar { |
| public: |
| template <typename Q> class Baz {}; |
| }; |
| class Foo{}; |
| } |
| |
| void foo(){ |
| a::Bar<int> B; |
| a::Foo foo; |
| a::Bar<a::Bar<int>>::Baz<a::Bar<int>> q; |
| } |
| |
| using namespace a; |
| )cpp"; |
| EXPECT_EQ(apply(Test), Expected); |
| } |
| |
| TEST_F(DefineInlineTest, TransformDeclRefs) { |
| auto Test = R"cpp( |
| namespace a { |
| template <typename T> class Bar { |
| public: |
| void foo(); |
| static void bar(); |
| int x; |
| static int y; |
| }; |
| void bar(); |
| void test(); |
| } |
| |
| void foo(); |
| using namespace a; |
| void f^oo() { |
| a::Bar<int> B; |
| B.foo(); |
| a::bar(); |
| Bar<Bar<int>>::bar(); |
| a::Bar<int>::bar(); |
| B.x = Bar<int>::y; |
| Bar<int>::y = 3; |
| bar(); |
| a::test(); |
| })cpp"; |
| auto Expected = R"cpp( |
| namespace a { |
| template <typename T> class Bar { |
| public: |
| void foo(); |
| static void bar(); |
| int x; |
| static int y; |
| }; |
| void bar(); |
| void test(); |
| } |
| |
| void foo(){ |
| a::Bar<int> B; |
| B.foo(); |
| a::bar(); |
| a::Bar<a::Bar<int>>::bar(); |
| a::Bar<int>::bar(); |
| B.x = a::Bar<int>::y; |
| a::Bar<int>::y = 3; |
| a::bar(); |
| a::test(); |
| } |
| using namespace a; |
| )cpp"; |
| EXPECT_EQ(apply(Test), Expected); |
| } |
| |
| TEST_F(DefineInlineTest, StaticMembers) { |
| auto Test = R"cpp( |
| namespace ns { class X { static void foo(); void bar(); }; } |
| void ns::X::b^ar() { |
| foo(); |
| })cpp"; |
| auto Expected = R"cpp( |
| namespace ns { class X { static void foo(); void bar(){ |
| foo(); |
| } }; } |
| )cpp"; |
| EXPECT_EQ(apply(Test), Expected); |
| } |
| |
| TEST_F(DefineInlineTest, TransformParamNames) { |
| std::pair<llvm::StringRef, llvm::StringRef> Cases[] = { |
| {R"cpp( |
| void foo(int, bool b, int T\ |
| est); |
| void ^foo(int f, bool x, int z) {})cpp", |
| R"cpp( |
| void foo(int f, bool x, int z){} |
| )cpp"}, |
| {R"cpp( |
| #define PARAM int Z |
| void foo(PARAM); |
| |
| void ^foo(int X) {})cpp", |
| "fail: Cant rename parameter inside macro body."}, |
| {R"cpp( |
| #define TYPE int |
| #define PARAM TYPE Z |
| #define BODY(x) 5 * (x) + 2 |
| template <int P> |
| void foo(PARAM, TYPE Q, TYPE, TYPE W = BODY(P)); |
| template <int x> |
| void ^foo(int Z, int b, int c, int d) {})cpp", |
| R"cpp( |
| #define TYPE int |
| #define PARAM TYPE Z |
| #define BODY(x) 5 * (x) + 2 |
| template <int x> |
| void foo(PARAM, TYPE b, TYPE c, TYPE d = BODY(x)){} |
| )cpp"}, |
| }; |
| for (const auto &Case : Cases) |
| EXPECT_EQ(apply(Case.first), Case.second) << Case.first; |
| } |
| |
| TEST_F(DefineInlineTest, TransformTemplParamNames) { |
| auto Test = R"cpp( |
| struct Foo { |
| struct Bar { |
| template <class, class X, |
| template<typename> class, template<typename> class Y, |
| int, int Z> |
| void foo(X, Y<X>, int W = 5 * Z + 2); |
| }; |
| }; |
| |
| template <class T, class U, |
| template<typename> class V, template<typename> class W, |
| int X, int Y> |
| void Foo::Bar::f^oo(U, W<U>, int Q) {})cpp"; |
| auto Expected = R"cpp( |
| struct Foo { |
| struct Bar { |
| template <class T, class U, |
| template<typename> class V, template<typename> class W, |
| int X, int Y> |
| void foo(U, W<U>, int Q = 5 * Y + 2){} |
| }; |
| }; |
| |
| )cpp"; |
| EXPECT_EQ(apply(Test), Expected); |
| } |
| |
| TEST_F(DefineInlineTest, TransformInlineNamespaces) { |
| auto Test = R"cpp( |
| namespace a { inline namespace b { namespace { struct Foo{}; } } } |
| void foo(); |
| |
| using namespace a; |
| void ^foo() {Foo foo;})cpp"; |
| auto Expected = R"cpp( |
| namespace a { inline namespace b { namespace { struct Foo{}; } } } |
| void foo(){a::Foo foo;} |
| |
| using namespace a; |
| )cpp"; |
| EXPECT_EQ(apply(Test), Expected); |
| } |
| |
| TEST_F(DefineInlineTest, TokensBeforeSemicolon) { |
| std::pair<llvm::StringRef, llvm::StringRef> Cases[] = { |
| {R"cpp( |
| void foo() /*Comment -_-*/ /*Com 2*/ ; |
| void fo^o() { return ; })cpp", |
| R"cpp( |
| void foo() /*Comment -_-*/ /*Com 2*/ { return ; } |
| )cpp"}, |
| |
| {R"cpp( |
| void foo(); |
| void fo^o() { return ; })cpp", |
| R"cpp( |
| void foo(){ return ; } |
| )cpp"}, |
| |
| {R"cpp( |
| #define SEMI ; |
| void foo() SEMI |
| void fo^o() { return ; })cpp", |
| "fail: Couldn't find semicolon for target declaration."}, |
| }; |
| for (const auto &Case : Cases) |
| EXPECT_EQ(apply(Case.first), Case.second) << Case.first; |
| } |
| |
| TEST_F(DefineInlineTest, HandleMacros) { |
| EXPECT_UNAVAILABLE(R"cpp( |
| #define BODY { return; } |
| void foo(); |
| void f^oo()BODY)cpp"); |
| |
| EXPECT_UNAVAILABLE(R"cpp( |
| #define BODY void foo(){ return; } |
| void foo(); |
| [[BODY]])cpp"); |
| |
| std::pair<llvm::StringRef, llvm::StringRef> Cases[] = { |
| // We don't qualify declarations coming from macros. |
| {R"cpp( |
| #define BODY Foo |
| namespace a { class Foo{}; } |
| void foo(); |
| using namespace a; |
| void f^oo(){BODY();})cpp", |
| R"cpp( |
| #define BODY Foo |
| namespace a { class Foo{}; } |
| void foo(){BODY();} |
| using namespace a; |
| )cpp"}, |
| |
| // Macro is not visible at declaration location, but we proceed. |
| {R"cpp( |
| void foo(); |
| #define BODY return; |
| void f^oo(){BODY})cpp", |
| R"cpp( |
| void foo(){BODY} |
| #define BODY return; |
| )cpp"}, |
| |
| {R"cpp( |
| #define TARGET void foo() |
| TARGET; |
| void f^oo(){ return; })cpp", |
| R"cpp( |
| #define TARGET void foo() |
| TARGET{ return; } |
| )cpp"}, |
| |
| {R"cpp( |
| #define TARGET foo |
| void TARGET(); |
| void f^oo(){ return; })cpp", |
| R"cpp( |
| #define TARGET foo |
| void TARGET(){ return; } |
| )cpp"}, |
| }; |
| for (const auto &Case : Cases) |
| EXPECT_EQ(apply(Case.first), Case.second) << Case.first; |
| } |
| |
| TEST_F(DefineInlineTest, DropCommonNameSpecifiers) { |
| struct { |
| llvm::StringRef Test; |
| llvm::StringRef Expected; |
| } Cases[] = { |
| {R"cpp( |
| namespace a { namespace b { void aux(); } } |
| namespace ns1 { |
| void foo(); |
| namespace qq { void test(); } |
| namespace ns2 { |
| void bar(); |
| namespace ns3 { void baz(); } |
| } |
| } |
| |
| using namespace a; |
| using namespace a::b; |
| using namespace ns1::qq; |
| void ns1::ns2::ns3::b^az() { |
| foo(); |
| bar(); |
| baz(); |
| ns1::ns2::ns3::baz(); |
| aux(); |
| test(); |
| })cpp", |
| R"cpp( |
| namespace a { namespace b { void aux(); } } |
| namespace ns1 { |
| void foo(); |
| namespace qq { void test(); } |
| namespace ns2 { |
| void bar(); |
| namespace ns3 { void baz(){ |
| foo(); |
| bar(); |
| baz(); |
| ns1::ns2::ns3::baz(); |
| a::b::aux(); |
| qq::test(); |
| } } |
| } |
| } |
| |
| using namespace a; |
| using namespace a::b; |
| using namespace ns1::qq; |
| )cpp"}, |
| {R"cpp( |
| namespace ns1 { |
| namespace qq { struct Foo { struct Bar {}; }; using B = Foo::Bar; } |
| namespace ns2 { void baz(); } |
| } |
| |
| using namespace ns1::qq; |
| void ns1::ns2::b^az() { Foo f; B b; })cpp", |
| R"cpp( |
| namespace ns1 { |
| namespace qq { struct Foo { struct Bar {}; }; using B = Foo::Bar; } |
| namespace ns2 { void baz(){ qq::Foo f; qq::B b; } } |
| } |
| |
| using namespace ns1::qq; |
| )cpp"}, |
| {R"cpp( |
| namespace ns1 { |
| namespace qq { |
| template<class T> struct Foo { template <class U> struct Bar {}; }; |
| template<class T, class U> |
| using B = typename Foo<T>::template Bar<U>; |
| } |
| namespace ns2 { void baz(); } |
| } |
| |
| using namespace ns1::qq; |
| void ns1::ns2::b^az() { B<int, bool> b; })cpp", |
| R"cpp( |
| namespace ns1 { |
| namespace qq { |
| template<class T> struct Foo { template <class U> struct Bar {}; }; |
| template<class T, class U> |
| using B = typename Foo<T>::template Bar<U>; |
| } |
| namespace ns2 { void baz(){ qq::B<int, bool> b; } } |
| } |
| |
| using namespace ns1::qq; |
| )cpp"}, |
| }; |
| for (const auto &Case : Cases) |
| EXPECT_EQ(apply(Case.Test), Case.Expected) << Case.Test; |
| } |
| |
| TEST_F(DefineInlineTest, QualifyWithUsingDirectives) { |
| llvm::StringRef Test = R"cpp( |
| namespace a { |
| void bar(); |
| namespace b { struct Foo{}; void aux(); } |
| namespace c { void cux(); } |
| } |
| using namespace a; |
| using X = b::Foo; |
| void foo(); |
| |
| using namespace b; |
| using namespace c; |
| void ^foo() { |
| cux(); |
| bar(); |
| X x; |
| aux(); |
| using namespace c; |
| // FIXME: The last reference to cux() in body of foo should not be |
| // qualified, since there is a using directive inside the function body. |
| cux(); |
| })cpp"; |
| llvm::StringRef Expected = R"cpp( |
| namespace a { |
| void bar(); |
| namespace b { struct Foo{}; void aux(); } |
| namespace c { void cux(); } |
| } |
| using namespace a; |
| using X = b::Foo; |
| void foo(){ |
| c::cux(); |
| bar(); |
| X x; |
| b::aux(); |
| using namespace c; |
| // FIXME: The last reference to cux() in body of foo should not be |
| // qualified, since there is a using directive inside the function body. |
| c::cux(); |
| } |
| |
| using namespace b; |
| using namespace c; |
| )cpp"; |
| EXPECT_EQ(apply(Test), Expected) << Test; |
| } |
| |
| TEST_F(DefineInlineTest, AddInline) { |
| llvm::StringMap<std::string> EditedFiles; |
| ExtraFiles["a.h"] = "void foo();"; |
| apply(R"cpp(#include "a.h" |
| void fo^o() {})cpp", |
| &EditedFiles); |
| EXPECT_THAT(EditedFiles, testing::ElementsAre(FileWithContents( |
| testPath("a.h"), "inline void foo(){}"))); |
| |
| // Check we put inline before cv-qualifiers. |
| ExtraFiles["a.h"] = "const int foo();"; |
| apply(R"cpp(#include "a.h" |
| const int fo^o() {})cpp", |
| &EditedFiles); |
| EXPECT_THAT(EditedFiles, testing::ElementsAre(FileWithContents( |
| testPath("a.h"), "inline const int foo(){}"))); |
| |
| // No double inline. |
| ExtraFiles["a.h"] = "inline void foo();"; |
| apply(R"cpp(#include "a.h" |
| inline void fo^o() {})cpp", |
| &EditedFiles); |
| EXPECT_THAT(EditedFiles, testing::ElementsAre(FileWithContents( |
| testPath("a.h"), "inline void foo(){}"))); |
| |
| // Constexprs don't need "inline". |
| ExtraFiles["a.h"] = "constexpr void foo();"; |
| apply(R"cpp(#include "a.h" |
| constexpr void fo^o() {})cpp", |
| &EditedFiles); |
| EXPECT_THAT(EditedFiles, testing::ElementsAre(FileWithContents( |
| testPath("a.h"), "constexpr void foo(){}"))); |
| |
| // Class members don't need "inline". |
| ExtraFiles["a.h"] = "struct Foo { void foo(); };"; |
| apply(R"cpp(#include "a.h" |
| void Foo::fo^o() {})cpp", |
| &EditedFiles); |
| EXPECT_THAT(EditedFiles, |
| testing::ElementsAre(FileWithContents( |
| testPath("a.h"), "struct Foo { void foo(){} };"))); |
| |
| // Function template doesn't need to be "inline"d. |
| ExtraFiles["a.h"] = "template <typename T> void foo();"; |
| apply(R"cpp(#include "a.h" |
| template <typename T> |
| void fo^o() {})cpp", |
| &EditedFiles); |
| EXPECT_THAT(EditedFiles, |
| testing::ElementsAre(FileWithContents( |
| testPath("a.h"), "template <typename T> void foo(){}"))); |
| |
| // Specializations needs to be marked "inline". |
| ExtraFiles["a.h"] = R"cpp( |
| template <typename T> void foo(); |
| template <> void foo<int>();)cpp"; |
| apply(R"cpp(#include "a.h" |
| template <> |
| void fo^o<int>() {})cpp", |
| &EditedFiles); |
| EXPECT_THAT(EditedFiles, |
| testing::ElementsAre(FileWithContents(testPath("a.h"), |
| R"cpp( |
| template <typename T> void foo(); |
| template <> inline void foo<int>(){})cpp"))); |
| } |
| |
| } // namespace |
| } // namespace clangd |
| } // namespace clang |