473 lines
		
	
	
		
			8.8 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			473 lines
		
	
	
		
			8.8 KiB
		
	
	
	
		
			C++
		
	
	
	
| //===-- AddUsingTests.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 "Config.h"
 | |
| #include "TestTU.h"
 | |
| #include "TweakTesting.h"
 | |
| #include "gmock/gmock-matchers.h"
 | |
| #include "gmock/gmock.h"
 | |
| #include "gtest/gtest.h"
 | |
| 
 | |
| namespace clang {
 | |
| namespace clangd {
 | |
| namespace {
 | |
| 
 | |
| TWEAK_TEST(AddUsing);
 | |
| 
 | |
| TEST_F(AddUsingTest, Prepare) {
 | |
|   Config Cfg;
 | |
|   Cfg.Style.FullyQualifiedNamespaces.push_back("ban");
 | |
|   WithContextValue WithConfig(Config::Key, std::move(Cfg));
 | |
| 
 | |
|   const std::string Header = R"cpp(
 | |
| #define NS(name) one::two::name
 | |
| namespace ban { void foo() {} }
 | |
| namespace banana { void foo() {} }
 | |
| namespace one {
 | |
| void oo() {}
 | |
| template<typename TT> class tt {};
 | |
| namespace two {
 | |
| enum ee {};
 | |
| void ff() {}
 | |
| class cc {
 | |
| public:
 | |
|   struct st {};
 | |
|   static void mm() {}
 | |
|   cc operator|(const cc& x) const { return x; }
 | |
| };
 | |
| }
 | |
| })cpp";
 | |
| 
 | |
|   EXPECT_AVAILABLE(Header + "void fun() { o^n^e^:^:^t^w^o^:^:^f^f(); }");
 | |
|   EXPECT_AVAILABLE(Header + "void fun() { o^n^e^::^o^o(); }");
 | |
|   EXPECT_AVAILABLE(Header + "void fun() { o^n^e^:^:^t^w^o^:^:^e^e E; }");
 | |
|   EXPECT_AVAILABLE(Header + "void fun() { o^n^e^:^:^t^w^o:^:^c^c C; }");
 | |
|   EXPECT_UNAVAILABLE(Header +
 | |
|                      "void fun() { o^n^e^:^:^t^w^o^:^:^c^c^:^:^m^m(); }");
 | |
|   EXPECT_UNAVAILABLE(Header +
 | |
|                      "void fun() { o^n^e^:^:^t^w^o^:^:^c^c^:^:^s^t inst; }");
 | |
|   EXPECT_UNAVAILABLE(Header +
 | |
|                      "void fun() { o^n^e^:^:^t^w^o^:^:^c^c^:^:^s^t inst; }");
 | |
|   EXPECT_UNAVAILABLE(Header + "void fun() { N^S(c^c) inst; }");
 | |
|   // This used to crash. Ideally we would support this case, but for now we just
 | |
|   // test that we don't crash.
 | |
|   EXPECT_UNAVAILABLE(Header +
 | |
|                      "template<typename TT> using foo = one::tt<T^T>;");
 | |
|   // Test that we don't crash or misbehave on unnamed DeclRefExpr.
 | |
|   EXPECT_UNAVAILABLE(Header +
 | |
|                      "void fun() { one::two::cc() ^| one::two::cc(); }");
 | |
|   // Do not offer code action when operating on a banned namespace.
 | |
|   EXPECT_UNAVAILABLE(Header + "void fun() { ban::fo^o(); }");
 | |
|   EXPECT_UNAVAILABLE(Header + "void fun() { ::ban::fo^o(); }");
 | |
|   EXPECT_AVAILABLE(Header + "void fun() { banana::fo^o(); }");
 | |
| 
 | |
|   // Do not offer code action on typo-corrections.
 | |
|   EXPECT_UNAVAILABLE(Header + "/*error-ok*/c^c C;");
 | |
| 
 | |
|   // NestedNameSpecifier, but no namespace.
 | |
|   EXPECT_UNAVAILABLE(Header + "class Foo {}; class F^oo foo;");
 | |
| 
 | |
|   // Check that we do not trigger in header files.
 | |
|   FileName = "test.h";
 | |
|   ExtraArgs.push_back("-xc++-header"); // .h file is treated a C by default.
 | |
|   EXPECT_UNAVAILABLE(Header + "void fun() { one::two::f^f(); }");
 | |
|   FileName = "test.hpp";
 | |
|   EXPECT_UNAVAILABLE(Header + "void fun() { one::two::f^f(); }");
 | |
| }
 | |
| 
 | |
| TEST_F(AddUsingTest, Apply) {
 | |
|   FileName = "test.cpp";
 | |
|   struct {
 | |
|     llvm::StringRef TestSource;
 | |
|     llvm::StringRef ExpectedSource;
 | |
|   } Cases[]{{
 | |
|                 // Function, no other using, namespace.
 | |
|                 R"cpp(
 | |
| #include "test.hpp"
 | |
| namespace {
 | |
| void fun() {
 | |
|   ^o^n^e^:^:^t^w^o^:^:^f^f();
 | |
| }
 | |
| })cpp",
 | |
|                 R"cpp(
 | |
| #include "test.hpp"
 | |
| namespace {using one::two::ff;
 | |
| 
 | |
| void fun() {
 | |
|   ff();
 | |
| }
 | |
| })cpp",
 | |
|             },
 | |
|             // Type, no other using, namespace.
 | |
|             {
 | |
|                 R"cpp(
 | |
| #include "test.hpp"
 | |
| namespace {
 | |
| void fun() {
 | |
|   ::on^e::t^wo::c^c inst;
 | |
| }
 | |
| })cpp",
 | |
|                 R"cpp(
 | |
| #include "test.hpp"
 | |
| namespace {using ::one::two::cc;
 | |
| 
 | |
| void fun() {
 | |
|   cc inst;
 | |
| }
 | |
| })cpp",
 | |
|             },
 | |
|             // Type, no other using, no namespace.
 | |
|             {
 | |
|                 R"cpp(
 | |
| #include "test.hpp"
 | |
| 
 | |
| void fun() {
 | |
|   on^e::t^wo::e^e inst;
 | |
| })cpp",
 | |
|                 R"cpp(
 | |
| #include "test.hpp"
 | |
| 
 | |
| using one::two::ee;
 | |
| 
 | |
| void fun() {
 | |
|   ee inst;
 | |
| })cpp"},
 | |
|             // Function, other usings.
 | |
|             {
 | |
|                 R"cpp(
 | |
| #include "test.hpp"
 | |
| 
 | |
| using one::two::cc;
 | |
| using one::two::ee;
 | |
| 
 | |
| namespace {
 | |
| void fun() {
 | |
|   one::two::f^f();
 | |
| }
 | |
| })cpp",
 | |
|                 R"cpp(
 | |
| #include "test.hpp"
 | |
| 
 | |
| using one::two::cc;
 | |
| using one::two::ff;using one::two::ee;
 | |
| 
 | |
| namespace {
 | |
| void fun() {
 | |
|   ff();
 | |
| }
 | |
| })cpp",
 | |
|             },
 | |
|             // Function, other usings inside namespace.
 | |
|             {
 | |
|                 R"cpp(
 | |
| #include "test.hpp"
 | |
| 
 | |
| using one::two::cc;
 | |
| 
 | |
| namespace {
 | |
| 
 | |
| using one::two::ff;
 | |
| 
 | |
| void fun() {
 | |
|   o^ne::o^o();
 | |
| }
 | |
| })cpp",
 | |
|                 R"cpp(
 | |
| #include "test.hpp"
 | |
| 
 | |
| using one::two::cc;
 | |
| 
 | |
| namespace {
 | |
| 
 | |
| using one::oo;using one::two::ff;
 | |
| 
 | |
| void fun() {
 | |
|   oo();
 | |
| }
 | |
| })cpp"},
 | |
|             // Using comes after cursor.
 | |
|             {
 | |
|                 R"cpp(
 | |
| #include "test.hpp"
 | |
| 
 | |
| namespace {
 | |
| 
 | |
| void fun() {
 | |
|   one::t^wo::ff();
 | |
| }
 | |
| 
 | |
| using one::two::cc;
 | |
| 
 | |
| })cpp",
 | |
|                 R"cpp(
 | |
| #include "test.hpp"
 | |
| 
 | |
| namespace {using one::two::ff;
 | |
| 
 | |
| 
 | |
| void fun() {
 | |
|   ff();
 | |
| }
 | |
| 
 | |
| using one::two::cc;
 | |
| 
 | |
| })cpp"},
 | |
|             // Pointer type.
 | |
|             {R"cpp(
 | |
| #include "test.hpp"
 | |
| 
 | |
| void fun() {
 | |
|   one::two::c^c *p;
 | |
| })cpp",
 | |
|              R"cpp(
 | |
| #include "test.hpp"
 | |
| 
 | |
| using one::two::cc;
 | |
| 
 | |
| void fun() {
 | |
|   cc *p;
 | |
| })cpp"},
 | |
|             // Namespace declared via macro.
 | |
|             {R"cpp(
 | |
| #include "test.hpp"
 | |
| #define NS_BEGIN(name) namespace name {
 | |
| 
 | |
| NS_BEGIN(foo)
 | |
| 
 | |
| void fun() {
 | |
|   one::two::f^f();
 | |
| }
 | |
| })cpp",
 | |
|              R"cpp(
 | |
| #include "test.hpp"
 | |
| #define NS_BEGIN(name) namespace name {
 | |
| 
 | |
| using one::two::ff;
 | |
| 
 | |
| NS_BEGIN(foo)
 | |
| 
 | |
| void fun() {
 | |
|   ff();
 | |
| }
 | |
| })cpp"},
 | |
|             // Inside macro argument.
 | |
|             {R"cpp(
 | |
| #include "test.hpp"
 | |
| #define CALL(name) name()
 | |
| 
 | |
| void fun() {
 | |
|   CALL(one::t^wo::ff);
 | |
| })cpp",
 | |
|              R"cpp(
 | |
| #include "test.hpp"
 | |
| #define CALL(name) name()
 | |
| 
 | |
| using one::two::ff;
 | |
| 
 | |
| void fun() {
 | |
|   CALL(ff);
 | |
| })cpp"},
 | |
|             // Parent namespace != lexical parent namespace
 | |
|             {R"cpp(
 | |
| #include "test.hpp"
 | |
| namespace foo { void fun(); }
 | |
| 
 | |
| void foo::fun() {
 | |
|   one::two::f^f();
 | |
| })cpp",
 | |
|              R"cpp(
 | |
| #include "test.hpp"
 | |
| using one::two::ff;
 | |
| 
 | |
| namespace foo { void fun(); }
 | |
| 
 | |
| void foo::fun() {
 | |
|   ff();
 | |
| })cpp"},
 | |
|             // If all other using are fully qualified, add ::
 | |
|             {R"cpp(
 | |
| #include "test.hpp"
 | |
| 
 | |
| using ::one::two::cc;
 | |
| using ::one::two::ee;
 | |
| 
 | |
| void fun() {
 | |
|   one::two::f^f();
 | |
| })cpp",
 | |
|              R"cpp(
 | |
| #include "test.hpp"
 | |
| 
 | |
| using ::one::two::cc;
 | |
| using ::one::two::ff;using ::one::two::ee;
 | |
| 
 | |
| void fun() {
 | |
|   ff();
 | |
| })cpp"},
 | |
|             // Make sure we don't add :: if it's already there
 | |
|             {R"cpp(
 | |
| #include "test.hpp"
 | |
| 
 | |
| using ::one::two::cc;
 | |
| using ::one::two::ee;
 | |
| 
 | |
| void fun() {
 | |
|   ::one::two::f^f();
 | |
| })cpp",
 | |
|              R"cpp(
 | |
| #include "test.hpp"
 | |
| 
 | |
| using ::one::two::cc;
 | |
| using ::one::two::ff;using ::one::two::ee;
 | |
| 
 | |
| void fun() {
 | |
|   ff();
 | |
| })cpp"},
 | |
|             // If even one using doesn't start with ::, do not add it
 | |
|             {R"cpp(
 | |
| #include "test.hpp"
 | |
| 
 | |
| using ::one::two::cc;
 | |
| using one::two::ee;
 | |
| 
 | |
| void fun() {
 | |
|   one::two::f^f();
 | |
| })cpp",
 | |
|              R"cpp(
 | |
| #include "test.hpp"
 | |
| 
 | |
| using ::one::two::cc;
 | |
| using one::two::ff;using one::two::ee;
 | |
| 
 | |
| void fun() {
 | |
|   ff();
 | |
| })cpp"},
 | |
|             // using alias; insert using for the spelled name.
 | |
|             {R"cpp(
 | |
| #include "test.hpp"
 | |
| 
 | |
| void fun() {
 | |
|   one::u^u u;
 | |
| })cpp",
 | |
|              R"cpp(
 | |
| #include "test.hpp"
 | |
| 
 | |
| using one::uu;
 | |
| 
 | |
| void fun() {
 | |
|   uu u;
 | |
| })cpp"},
 | |
|             // using namespace.
 | |
|             {R"cpp(
 | |
| #include "test.hpp"
 | |
| using namespace one;
 | |
| namespace {
 | |
| two::c^c C;
 | |
| })cpp",
 | |
|              R"cpp(
 | |
| #include "test.hpp"
 | |
| using namespace one;
 | |
| namespace {using two::cc;
 | |
| 
 | |
| cc C;
 | |
| })cpp"},
 | |
|             // Type defined in main file, make sure using is after that.
 | |
|             {R"cpp(
 | |
| namespace xx {
 | |
|   struct yy {};
 | |
| }
 | |
| 
 | |
| x^x::yy X;
 | |
| )cpp",
 | |
|              R"cpp(
 | |
| namespace xx {
 | |
|   struct yy {};
 | |
| }
 | |
| 
 | |
| using xx::yy;
 | |
| 
 | |
| yy X;
 | |
| )cpp"},
 | |
|             // Type defined in main file via "using", insert after that.
 | |
|             {R"cpp(
 | |
| #include "test.hpp"
 | |
| 
 | |
| namespace xx {
 | |
|   using yy = one::two::cc;
 | |
| }
 | |
| 
 | |
| x^x::yy X;
 | |
| )cpp",
 | |
|              R"cpp(
 | |
| #include "test.hpp"
 | |
| 
 | |
| namespace xx {
 | |
|   using yy = one::two::cc;
 | |
| }
 | |
| 
 | |
| using xx::yy;
 | |
| 
 | |
| yy X;
 | |
| )cpp"},
 | |
|             // Using must come after function definition.
 | |
|             {R"cpp(
 | |
| namespace xx {
 | |
|   void yy();
 | |
| }
 | |
| 
 | |
| void fun() {
 | |
|   x^x::yy();
 | |
| }
 | |
| )cpp",
 | |
|              R"cpp(
 | |
| namespace xx {
 | |
|   void yy();
 | |
| }
 | |
| 
 | |
| using xx::yy;
 | |
| 
 | |
| void fun() {
 | |
|   yy();
 | |
| }
 | |
| )cpp"},
 | |
|             // Existing using with non-namespace part.
 | |
|             {R"cpp(
 | |
| #include "test.hpp"
 | |
| using one::two::ee::ee_one;
 | |
| one::t^wo::cc c;
 | |
| )cpp",
 | |
|              R"cpp(
 | |
| #include "test.hpp"
 | |
| using one::two::cc;using one::two::ee::ee_one;
 | |
| cc c;
 | |
| )cpp"}};
 | |
|   llvm::StringMap<std::string> EditedFiles;
 | |
|   for (const auto &Case : Cases) {
 | |
|     for (const auto &SubCase : expandCases(Case.TestSource)) {
 | |
|       ExtraFiles["test.hpp"] = R"cpp(
 | |
| namespace one {
 | |
| void oo() {}
 | |
| namespace two {
 | |
| enum ee {ee_one};
 | |
| void ff() {}
 | |
| class cc {
 | |
| public:
 | |
|   struct st { struct nested {}; };
 | |
|   static void mm() {}
 | |
| };
 | |
| }
 | |
| using uu = two::cc;
 | |
| })cpp";
 | |
|       EXPECT_EQ(apply(SubCase, &EditedFiles), Case.ExpectedSource);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| } // namespace
 | |
| } // namespace clangd
 | |
| } // namespace clang
 |