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