1 //===-- AddUsingTests.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-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "Config.h" 10 #include "TestTU.h" 11 #include "TweakTesting.h" 12 #include "gmock/gmock-matchers.h" 13 #include "gmock/gmock.h" 14 #include "gtest/gtest.h" 15 16 namespace clang { 17 namespace clangd { 18 namespace { 19 20 TWEAK_TEST(AddUsing); 21 22 TEST_F(AddUsingTest, Prepare) { 23 Config Cfg; 24 Cfg.Style.FullyQualifiedNamespaces.push_back("ban"); 25 WithContextValue WithConfig(Config::Key, std::move(Cfg)); 26 27 const std::string Header = R"cpp( 28 #define NS(name) one::two::name 29 namespace ban { void foo() {} } 30 namespace banana { void foo() {} } 31 namespace one { 32 void oo() {} 33 template<typename TT> class tt {}; 34 namespace two { 35 enum ee {}; 36 void ff() {} 37 class cc { 38 public: 39 struct st {}; 40 static void mm() {} 41 cc operator|(const cc& x) const { return x; } 42 }; 43 } 44 })cpp"; 45 46 EXPECT_AVAILABLE(Header + "void fun() { o^n^e^:^:^t^w^o^:^:^f^f(); }"); 47 EXPECT_AVAILABLE(Header + "void fun() { o^n^e^::^o^o(); }"); 48 EXPECT_AVAILABLE(Header + "void fun() { o^n^e^:^:^t^w^o^:^:^e^e E; }"); 49 EXPECT_AVAILABLE(Header + "void fun() { o^n^e^:^:^t^w^o:^:^c^c C; }"); 50 EXPECT_UNAVAILABLE(Header + 51 "void fun() { o^n^e^:^:^t^w^o^:^:^c^c^:^:^m^m(); }"); 52 EXPECT_UNAVAILABLE(Header + 53 "void fun() { o^n^e^:^:^t^w^o^:^:^c^c^:^:^s^t inst; }"); 54 EXPECT_UNAVAILABLE(Header + 55 "void fun() { o^n^e^:^:^t^w^o^:^:^c^c^:^:^s^t inst; }"); 56 EXPECT_UNAVAILABLE(Header + "void fun() { N^S(c^c) inst; }"); 57 // This used to crash. Ideally we would support this case, but for now we just 58 // test that we don't crash. 59 EXPECT_UNAVAILABLE(Header + 60 "template<typename TT> using foo = one::tt<T^T>;"); 61 // Test that we don't crash or misbehave on unnamed DeclRefExpr. 62 EXPECT_UNAVAILABLE(Header + 63 "void fun() { one::two::cc() ^| one::two::cc(); }"); 64 // Do not offer code action when operating on a banned namespace. 65 EXPECT_UNAVAILABLE(Header + "void fun() { ban::fo^o(); }"); 66 EXPECT_UNAVAILABLE(Header + "void fun() { ::ban::fo^o(); }"); 67 EXPECT_AVAILABLE(Header + "void fun() { banana::fo^o(); }"); 68 69 // Do not offer code action on typo-corrections. 70 EXPECT_UNAVAILABLE(Header + "/*error-ok*/c^c C;"); 71 72 // NestedNameSpecifier, but no namespace. 73 EXPECT_UNAVAILABLE(Header + "class Foo {}; class F^oo foo;"); 74 75 // Check that we do not trigger in header files. 76 FileName = "test.h"; 77 ExtraArgs.push_back("-xc++-header"); // .h file is treated a C by default. 78 EXPECT_UNAVAILABLE(Header + "void fun() { one::two::f^f(); }"); 79 FileName = "test.hpp"; 80 EXPECT_UNAVAILABLE(Header + "void fun() { one::two::f^f(); }"); 81 } 82 83 TEST_F(AddUsingTest, Apply) { 84 FileName = "test.cpp"; 85 struct { 86 llvm::StringRef TestSource; 87 llvm::StringRef ExpectedSource; 88 } Cases[]{{ 89 // Function, no other using, namespace. 90 R"cpp( 91 #include "test.hpp" 92 namespace { 93 void fun() { 94 ^o^n^e^:^:^t^w^o^:^:^f^f(); 95 } 96 })cpp", 97 R"cpp( 98 #include "test.hpp" 99 namespace {using one::two::ff; 100 101 void fun() { 102 ff(); 103 } 104 })cpp", 105 }, 106 // Type, no other using, namespace. 107 { 108 R"cpp( 109 #include "test.hpp" 110 namespace { 111 void fun() { 112 ::on^e::t^wo::c^c inst; 113 } 114 })cpp", 115 R"cpp( 116 #include "test.hpp" 117 namespace {using ::one::two::cc; 118 119 void fun() { 120 cc inst; 121 } 122 })cpp", 123 }, 124 // Type, no other using, no namespace. 125 { 126 R"cpp( 127 #include "test.hpp" 128 129 void fun() { 130 on^e::t^wo::e^e inst; 131 })cpp", 132 R"cpp( 133 #include "test.hpp" 134 135 using one::two::ee; 136 137 void fun() { 138 ee inst; 139 })cpp"}, 140 // Function, other usings. 141 { 142 R"cpp( 143 #include "test.hpp" 144 145 using one::two::cc; 146 using one::two::ee; 147 148 namespace { 149 void fun() { 150 one::two::f^f(); 151 } 152 })cpp", 153 R"cpp( 154 #include "test.hpp" 155 156 using one::two::cc; 157 using one::two::ff;using one::two::ee; 158 159 namespace { 160 void fun() { 161 ff(); 162 } 163 })cpp", 164 }, 165 // Function, other usings inside namespace. 166 { 167 R"cpp( 168 #include "test.hpp" 169 170 using one::two::cc; 171 172 namespace { 173 174 using one::two::ff; 175 176 void fun() { 177 o^ne::o^o(); 178 } 179 })cpp", 180 R"cpp( 181 #include "test.hpp" 182 183 using one::two::cc; 184 185 namespace { 186 187 using one::oo;using one::two::ff; 188 189 void fun() { 190 oo(); 191 } 192 })cpp"}, 193 // Using comes after cursor. 194 { 195 R"cpp( 196 #include "test.hpp" 197 198 namespace { 199 200 void fun() { 201 one::t^wo::ff(); 202 } 203 204 using one::two::cc; 205 206 })cpp", 207 R"cpp( 208 #include "test.hpp" 209 210 namespace {using one::two::ff; 211 212 213 void fun() { 214 ff(); 215 } 216 217 using one::two::cc; 218 219 })cpp"}, 220 // Pointer type. 221 {R"cpp( 222 #include "test.hpp" 223 224 void fun() { 225 one::two::c^c *p; 226 })cpp", 227 R"cpp( 228 #include "test.hpp" 229 230 using one::two::cc; 231 232 void fun() { 233 cc *p; 234 })cpp"}, 235 // Namespace declared via macro. 236 {R"cpp( 237 #include "test.hpp" 238 #define NS_BEGIN(name) namespace name { 239 240 NS_BEGIN(foo) 241 242 void fun() { 243 one::two::f^f(); 244 } 245 })cpp", 246 R"cpp( 247 #include "test.hpp" 248 #define NS_BEGIN(name) namespace name { 249 250 using one::two::ff; 251 252 NS_BEGIN(foo) 253 254 void fun() { 255 ff(); 256 } 257 })cpp"}, 258 // Inside macro argument. 259 {R"cpp( 260 #include "test.hpp" 261 #define CALL(name) name() 262 263 void fun() { 264 CALL(one::t^wo::ff); 265 })cpp", 266 R"cpp( 267 #include "test.hpp" 268 #define CALL(name) name() 269 270 using one::two::ff; 271 272 void fun() { 273 CALL(ff); 274 })cpp"}, 275 // Parent namespace != lexical parent namespace 276 {R"cpp( 277 #include "test.hpp" 278 namespace foo { void fun(); } 279 280 void foo::fun() { 281 one::two::f^f(); 282 })cpp", 283 R"cpp( 284 #include "test.hpp" 285 using one::two::ff; 286 287 namespace foo { void fun(); } 288 289 void foo::fun() { 290 ff(); 291 })cpp"}, 292 // If all other using are fully qualified, add :: 293 {R"cpp( 294 #include "test.hpp" 295 296 using ::one::two::cc; 297 using ::one::two::ee; 298 299 void fun() { 300 one::two::f^f(); 301 })cpp", 302 R"cpp( 303 #include "test.hpp" 304 305 using ::one::two::cc; 306 using ::one::two::ff;using ::one::two::ee; 307 308 void fun() { 309 ff(); 310 })cpp"}, 311 // Make sure we don't add :: if it's already there 312 {R"cpp( 313 #include "test.hpp" 314 315 using ::one::two::cc; 316 using ::one::two::ee; 317 318 void fun() { 319 ::one::two::f^f(); 320 })cpp", 321 R"cpp( 322 #include "test.hpp" 323 324 using ::one::two::cc; 325 using ::one::two::ff;using ::one::two::ee; 326 327 void fun() { 328 ff(); 329 })cpp"}, 330 // If even one using doesn't start with ::, do not add it 331 {R"cpp( 332 #include "test.hpp" 333 334 using ::one::two::cc; 335 using one::two::ee; 336 337 void fun() { 338 one::two::f^f(); 339 })cpp", 340 R"cpp( 341 #include "test.hpp" 342 343 using ::one::two::cc; 344 using one::two::ff;using one::two::ee; 345 346 void fun() { 347 ff(); 348 })cpp"}, 349 // using alias; insert using for the spelled name. 350 {R"cpp( 351 #include "test.hpp" 352 353 void fun() { 354 one::u^u u; 355 })cpp", 356 R"cpp( 357 #include "test.hpp" 358 359 using one::uu; 360 361 void fun() { 362 uu u; 363 })cpp"}, 364 // using namespace. 365 {R"cpp( 366 #include "test.hpp" 367 using namespace one; 368 namespace { 369 two::c^c C; 370 })cpp", 371 R"cpp( 372 #include "test.hpp" 373 using namespace one; 374 namespace {using two::cc; 375 376 cc C; 377 })cpp"}, 378 // Type defined in main file, make sure using is after that. 379 {R"cpp( 380 namespace xx { 381 struct yy {}; 382 } 383 384 x^x::yy X; 385 )cpp", 386 R"cpp( 387 namespace xx { 388 struct yy {}; 389 } 390 391 using xx::yy; 392 393 yy X; 394 )cpp"}, 395 // Type defined in main file via "using", insert after that. 396 {R"cpp( 397 #include "test.hpp" 398 399 namespace xx { 400 using yy = one::two::cc; 401 } 402 403 x^x::yy X; 404 )cpp", 405 R"cpp( 406 #include "test.hpp" 407 408 namespace xx { 409 using yy = one::two::cc; 410 } 411 412 using xx::yy; 413 414 yy X; 415 )cpp"}, 416 // Using must come after function definition. 417 {R"cpp( 418 namespace xx { 419 void yy(); 420 } 421 422 void fun() { 423 x^x::yy(); 424 } 425 )cpp", 426 R"cpp( 427 namespace xx { 428 void yy(); 429 } 430 431 using xx::yy; 432 433 void fun() { 434 yy(); 435 } 436 )cpp"}, 437 // Existing using with non-namespace part. 438 {R"cpp( 439 #include "test.hpp" 440 using one::two::ee::ee_one; 441 one::t^wo::cc c; 442 )cpp", 443 R"cpp( 444 #include "test.hpp" 445 using one::two::cc;using one::two::ee::ee_one; 446 cc c; 447 )cpp"}, 448 // Template (like std::vector). 449 {R"cpp( 450 #include "test.hpp" 451 one::v^ec<int> foo; 452 )cpp", 453 R"cpp( 454 #include "test.hpp" 455 using one::vec; 456 457 vec<int> foo; 458 )cpp"}}; 459 llvm::StringMap<std::string> EditedFiles; 460 for (const auto &Case : Cases) { 461 for (const auto &SubCase : expandCases(Case.TestSource)) { 462 ExtraFiles["test.hpp"] = R"cpp( 463 namespace one { 464 void oo() {} 465 namespace two { 466 enum ee {ee_one}; 467 void ff() {} 468 class cc { 469 public: 470 struct st { struct nested {}; }; 471 static void mm() {} 472 }; 473 } 474 using uu = two::cc; 475 template<typename T> struct vec {}; 476 })cpp"; 477 EXPECT_EQ(apply(SubCase, &EditedFiles), Case.ExpectedSource); 478 } 479 } 480 } 481 482 } // namespace 483 } // namespace clangd 484 } // namespace clang 485