1 //===--- DiagnosticsTests.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 "../clang-tidy/ClangTidyOptions.h" 10 #include "Annotations.h" 11 #include "Config.h" 12 #include "Diagnostics.h" 13 #include "Feature.h" 14 #include "FeatureModule.h" 15 #include "ParsedAST.h" 16 #include "Protocol.h" 17 #include "TestFS.h" 18 #include "TestIndex.h" 19 #include "TestTU.h" 20 #include "TidyProvider.h" 21 #include "index/MemIndex.h" 22 #include "index/Ref.h" 23 #include "index/Relation.h" 24 #include "index/Symbol.h" 25 #include "support/Context.h" 26 #include "support/Path.h" 27 #include "clang/AST/Decl.h" 28 #include "clang/Basic/Diagnostic.h" 29 #include "clang/Basic/DiagnosticSema.h" 30 #include "clang/Basic/LLVM.h" 31 #include "clang/Basic/Specifiers.h" 32 #include "llvm/ADT/ArrayRef.h" 33 #include "llvm/ADT/StringRef.h" 34 #include "llvm/Support/JSON.h" 35 #include "llvm/Support/ScopedPrinter.h" 36 #include "llvm/Support/TargetSelect.h" 37 #include "llvm/Testing/Support/SupportHelpers.h" 38 #include "gmock/gmock.h" 39 #include "gtest/gtest.h" 40 #include <cstddef> 41 #include <memory> 42 #include <optional> 43 #include <string> 44 #include <utility> 45 #include <vector> 46 47 namespace clang { 48 namespace clangd { 49 namespace { 50 51 using ::testing::_; 52 using ::testing::AllOf; 53 using ::testing::Contains; 54 using ::testing::Each; 55 using ::testing::ElementsAre; 56 using ::testing::Field; 57 using ::testing::IsEmpty; 58 using ::testing::Not; 59 using ::testing::Pair; 60 using ::testing::SizeIs; 61 using ::testing::UnorderedElementsAre; 62 63 ::testing::Matcher<const Diag &> withFix(::testing::Matcher<Fix> FixMatcher) { 64 return Field(&Diag::Fixes, ElementsAre(FixMatcher)); 65 } 66 67 ::testing::Matcher<const Diag &> withFix(::testing::Matcher<Fix> FixMatcher1, 68 ::testing::Matcher<Fix> FixMatcher2) { 69 return Field(&Diag::Fixes, UnorderedElementsAre(FixMatcher1, FixMatcher2)); 70 } 71 72 ::testing::Matcher<const Diag &> withID(unsigned ID) { 73 return Field(&Diag::ID, ID); 74 } 75 ::testing::Matcher<const Diag &> 76 withNote(::testing::Matcher<Note> NoteMatcher) { 77 return Field(&Diag::Notes, ElementsAre(NoteMatcher)); 78 } 79 80 ::testing::Matcher<const Diag &> 81 withNote(::testing::Matcher<Note> NoteMatcher1, 82 ::testing::Matcher<Note> NoteMatcher2) { 83 return Field(&Diag::Notes, UnorderedElementsAre(NoteMatcher1, NoteMatcher2)); 84 } 85 86 ::testing::Matcher<const Diag &> 87 withTag(::testing::Matcher<DiagnosticTag> TagMatcher) { 88 return Field(&Diag::Tags, Contains(TagMatcher)); 89 } 90 91 MATCHER_P(hasRange, Range, "") { return arg.Range == Range; } 92 93 MATCHER_P2(Diag, Range, Message, 94 "Diag at " + llvm::to_string(Range) + " = [" + Message + "]") { 95 return arg.Range == Range && arg.Message == Message; 96 } 97 98 MATCHER_P3(Fix, Range, Replacement, Message, 99 "Fix " + llvm::to_string(Range) + " => " + 100 ::testing::PrintToString(Replacement) + " = [" + Message + "]") { 101 return arg.Message == Message && arg.Edits.size() == 1 && 102 arg.Edits[0].range == Range && arg.Edits[0].newText == Replacement; 103 } 104 105 MATCHER_P(fixMessage, Message, "") { return arg.Message == Message; } 106 107 MATCHER_P(equalToLSPDiag, LSPDiag, 108 "LSP diagnostic " + llvm::to_string(LSPDiag)) { 109 if (toJSON(arg) != toJSON(LSPDiag)) { 110 *result_listener << llvm::formatv("expected:\n{0:2}\ngot\n{1:2}", 111 toJSON(LSPDiag), toJSON(arg)) 112 .str(); 113 return false; 114 } 115 return true; 116 } 117 118 MATCHER_P(diagSource, S, "") { return arg.Source == S; } 119 MATCHER_P(diagName, N, "") { return arg.Name == N; } 120 MATCHER_P(diagSeverity, S, "") { return arg.Severity == S; } 121 122 MATCHER_P(equalToFix, Fix, "LSP fix " + llvm::to_string(Fix)) { 123 if (arg.Message != Fix.Message) 124 return false; 125 if (arg.Edits.size() != Fix.Edits.size()) 126 return false; 127 for (std::size_t I = 0; I < arg.Edits.size(); ++I) { 128 if (arg.Edits[I].range != Fix.Edits[I].range || 129 arg.Edits[I].newText != Fix.Edits[I].newText) 130 return false; 131 } 132 return true; 133 } 134 135 // Helper function to make tests shorter. 136 Position pos(int Line, int Character) { 137 Position Res; 138 Res.line = Line; 139 Res.character = Character; 140 return Res; 141 } 142 143 // Normally returns the provided diagnostics matcher. 144 // If clang-tidy checks are not linked in, returns a matcher for no diagnostics! 145 // This is intended for tests where the diagnostics come from clang-tidy checks. 146 // We don't #ifdef each individual test as it's intrusive and we want to ensure 147 // that as much of the test is still compiled an run as possible. 148 ::testing::Matcher<std::vector<clangd::Diag>> 149 ifTidyChecks(::testing::Matcher<std::vector<clangd::Diag>> M) { 150 if (!CLANGD_TIDY_CHECKS) 151 return IsEmpty(); 152 return M; 153 } 154 155 TEST(DiagnosticsTest, DiagnosticRanges) { 156 // Check we report correct ranges, including various edge-cases. 157 Annotations Test(R"cpp( 158 // error-ok 159 #define ID(X) X 160 namespace test{}; 161 void $decl[[foo]](); 162 int main() { 163 struct Container { int* begin(); int* end(); } *container; 164 for (auto i : $insertstar[[]]$range[[container]]) { 165 } 166 167 $typo[[go\ 168 o]](); 169 foo()$semicolon[[]]//with comments 170 $unk[[unknown]](); 171 double $type[[bar]] = "foo"; 172 struct Foo { int x; }; Foo a; 173 a.$nomember[[y]]; 174 test::$nomembernamespace[[test]]; 175 $macro[[ID($macroarg[[fod]])]](); 176 } 177 )cpp"); 178 auto TU = TestTU::withCode(Test.code()); 179 EXPECT_THAT( 180 TU.build().getDiagnostics(), 181 ElementsAre( 182 // Make sure the whole token is highlighted. 183 AllOf(Diag(Test.range("range"), 184 "invalid range expression of type 'struct Container *'; " 185 "did you mean to dereference it with '*'?"), 186 withFix(Fix(Test.range("insertstar"), "*", "insert '*'"))), 187 // This range spans lines. 188 AllOf(Diag(Test.range("typo"), 189 "use of undeclared identifier 'goo'; did you mean 'foo'?"), 190 diagSource(Diag::Clang), diagName("undeclared_var_use_suggest"), 191 withFix( 192 Fix(Test.range("typo"), "foo", "change 'go\\…' to 'foo'")), 193 // This is a pretty normal range. 194 withNote(Diag(Test.range("decl"), "'foo' declared here"))), 195 // This range is zero-width and insertion. Therefore make sure we are 196 // not expanding it into other tokens. Since we are not going to 197 // replace those. 198 AllOf(Diag(Test.range("semicolon"), "expected ';' after expression"), 199 withFix(Fix(Test.range("semicolon"), ";", "insert ';'"))), 200 // This range isn't provided by clang, we expand to the token. 201 Diag(Test.range("unk"), "use of undeclared identifier 'unknown'"), 202 Diag(Test.range("type"), 203 "cannot initialize a variable of type 'double' with an lvalue " 204 "of type 'const char[4]'"), 205 Diag(Test.range("nomember"), "no member named 'y' in 'Foo'"), 206 Diag(Test.range("nomembernamespace"), 207 "no member named 'test' in namespace 'test'"), 208 AllOf(Diag(Test.range("macro"), 209 "use of undeclared identifier 'fod'; did you mean 'foo'?"), 210 withFix(Fix(Test.range("macroarg"), "foo", 211 "change 'fod' to 'foo'"))))); 212 } 213 214 // Verify that the -Wswitch case-not-covered diagnostic range covers the 215 // whole expression. This is important because the "populate-switch" tweak 216 // fires for the full expression range (see tweaks/PopulateSwitchTests.cpp). 217 // The quickfix flow only works end-to-end if the tweak can be triggered on 218 // the diagnostic's range. 219 TEST(DiagnosticsTest, WSwitch) { 220 Annotations Test(R"cpp( 221 enum A { X }; 222 struct B { A a; }; 223 void foo(B b) { 224 switch ([[b.a]]) {} 225 } 226 )cpp"); 227 auto TU = TestTU::withCode(Test.code()); 228 TU.ExtraArgs = {"-Wswitch"}; 229 EXPECT_THAT(TU.build().getDiagnostics(), 230 ElementsAre(Diag(Test.range(), 231 "enumeration value 'X' not handled in switch"))); 232 } 233 234 TEST(DiagnosticsTest, FlagsMatter) { 235 Annotations Test("[[void]] main() {} // error-ok"); 236 auto TU = TestTU::withCode(Test.code()); 237 EXPECT_THAT(TU.build().getDiagnostics(), 238 ElementsAre(AllOf(Diag(Test.range(), "'main' must return 'int'"), 239 withFix(Fix(Test.range(), "int", 240 "change 'void' to 'int'"))))); 241 // Same code built as C gets different diagnostics. 242 TU.Filename = "Plain.c"; 243 EXPECT_THAT( 244 TU.build().getDiagnostics(), 245 ElementsAre(AllOf( 246 Diag(Test.range(), "return type of 'main' is not 'int'"), 247 withFix(Fix(Test.range(), "int", "change return type to 'int'"))))); 248 } 249 250 TEST(DiagnosticsTest, DiagnosticPreamble) { 251 Annotations Test(R"cpp( 252 #include $[["not-found.h"]] // error-ok 253 )cpp"); 254 255 auto TU = TestTU::withCode(Test.code()); 256 EXPECT_THAT(TU.build().getDiagnostics(), 257 ElementsAre(::testing::AllOf( 258 Diag(Test.range(), "'not-found.h' file not found"), 259 diagSource(Diag::Clang), diagName("pp_file_not_found")))); 260 } 261 262 TEST(DiagnosticsTest, DeduplicatedClangTidyDiagnostics) { 263 Annotations Test(R"cpp( 264 float foo = [[0.1f]]; 265 )cpp"); 266 auto TU = TestTU::withCode(Test.code()); 267 // Enable alias clang-tidy checks, these check emits the same diagnostics 268 // (except the check name). 269 TU.ClangTidyProvider = addTidyChecks("readability-uppercase-literal-suffix," 270 "hicpp-uppercase-literal-suffix"); 271 // Verify that we filter out the duplicated diagnostic message. 272 EXPECT_THAT( 273 TU.build().getDiagnostics(), 274 ifTidyChecks(UnorderedElementsAre(::testing::AllOf( 275 Diag(Test.range(), 276 "floating point literal has suffix 'f', which is not uppercase"), 277 diagSource(Diag::ClangTidy))))); 278 279 Test = Annotations(R"cpp( 280 template<typename T> 281 void func(T) { 282 float f = [[0.3f]]; 283 } 284 void k() { 285 func(123); 286 func(2.0); 287 } 288 )cpp"); 289 TU.Code = std::string(Test.code()); 290 // The check doesn't handle template instantiations which ends up emitting 291 // duplicated messages, verify that we deduplicate them. 292 EXPECT_THAT( 293 TU.build().getDiagnostics(), 294 ifTidyChecks(UnorderedElementsAre(::testing::AllOf( 295 Diag(Test.range(), 296 "floating point literal has suffix 'f', which is not uppercase"), 297 diagSource(Diag::ClangTidy))))); 298 } 299 300 TEST(DiagnosticsTest, ClangTidy) { 301 Annotations Test(R"cpp( 302 #include $deprecated[["assert.h"]] 303 304 #define $macrodef[[SQUARE]](X) (X)*(X) 305 int $main[[main]]() { 306 int y = 4; 307 return SQUARE($macroarg[[++]]y); 308 return $doubled[[sizeof(sizeof(int))]]; 309 } 310 311 // misc-no-recursion uses a custom traversal from the TUDecl 312 void foo(); 313 void $bar[[bar]]() { 314 foo(); 315 } 316 void $foo[[foo]]() { 317 bar(); 318 } 319 )cpp"); 320 auto TU = TestTU::withCode(Test.code()); 321 TU.HeaderFilename = "assert.h"; // Suppress "not found" error. 322 TU.ClangTidyProvider = addTidyChecks("bugprone-sizeof-expression," 323 "bugprone-macro-repeated-side-effects," 324 "modernize-deprecated-headers," 325 "modernize-use-trailing-return-type," 326 "misc-no-recursion"); 327 TU.ExtraArgs.push_back("-Wno-unsequenced"); 328 EXPECT_THAT( 329 TU.build().getDiagnostics(), 330 ifTidyChecks(UnorderedElementsAre( 331 AllOf(Diag(Test.range("deprecated"), 332 "inclusion of deprecated C++ header 'assert.h'; consider " 333 "using 'cassert' instead"), 334 diagSource(Diag::ClangTidy), 335 diagName("modernize-deprecated-headers"), 336 withFix(Fix(Test.range("deprecated"), "<cassert>", 337 "change '\"assert.h\"' to '<cassert>'"))), 338 Diag(Test.range("doubled"), 339 "suspicious usage of 'sizeof(sizeof(...))'"), 340 AllOf(Diag(Test.range("macroarg"), 341 "side effects in the 1st macro argument 'X' are " 342 "repeated in " 343 "macro expansion"), 344 diagSource(Diag::ClangTidy), 345 diagName("bugprone-macro-repeated-side-effects"), 346 withNote(Diag(Test.range("macrodef"), 347 "macro 'SQUARE' defined here"))), 348 AllOf(Diag(Test.range("main"), 349 "use a trailing return type for this function"), 350 diagSource(Diag::ClangTidy), 351 diagName("modernize-use-trailing-return-type"), 352 // Verify there's no "[check-name]" suffix in the message. 353 withFix(fixMessage( 354 "use a trailing return type for this function"))), 355 Diag(Test.range("foo"), 356 "function 'foo' is within a recursive call chain"), 357 Diag(Test.range("bar"), 358 "function 'bar' is within a recursive call chain")))); 359 } 360 361 TEST(DiagnosticsTest, ClangTidyEOF) { 362 // clang-format off 363 Annotations Test(R"cpp( 364 [[#]]include <b.h> 365 #include "a.h")cpp"); 366 // clang-format on 367 auto TU = TestTU::withCode(Test.code()); 368 TU.ExtraArgs = {"-isystem."}; 369 TU.AdditionalFiles["a.h"] = TU.AdditionalFiles["b.h"] = ""; 370 TU.ClangTidyProvider = addTidyChecks("llvm-include-order"); 371 EXPECT_THAT( 372 TU.build().getDiagnostics(), 373 ifTidyChecks(Contains( 374 AllOf(Diag(Test.range(), "#includes are not sorted properly"), 375 diagSource(Diag::ClangTidy), diagName("llvm-include-order"))))); 376 } 377 378 TEST(DiagnosticTest, TemplatesInHeaders) { 379 // Diagnostics from templates defined in headers are placed at the expansion. 380 Annotations Main(R"cpp( 381 Derived<int> [[y]]; // error-ok 382 )cpp"); 383 Annotations Header(R"cpp( 384 template <typename T> 385 struct Derived : [[T]] {}; 386 )cpp"); 387 TestTU TU = TestTU::withCode(Main.code()); 388 TU.HeaderCode = Header.code().str(); 389 EXPECT_THAT( 390 TU.build().getDiagnostics(), 391 ElementsAre(AllOf( 392 Diag(Main.range(), "in template: base specifier must name a class"), 393 withNote(Diag(Header.range(), "error occurred here"), 394 Diag(Main.range(), "in instantiation of template class " 395 "'Derived<int>' requested here"))))); 396 } 397 398 TEST(DiagnosticTest, MakeUnique) { 399 // We usually miss diagnostics from header functions as we don't parse them. 400 // std::make_unique is an exception. 401 Annotations Main(R"cpp( 402 struct S { S(char*); }; 403 auto x = std::[[make_unique]]<S>(42); // error-ok 404 )cpp"); 405 TestTU TU = TestTU::withCode(Main.code()); 406 TU.HeaderCode = R"cpp( 407 namespace std { 408 // These mocks aren't quite right - we omit unique_ptr for simplicity. 409 // forward is included to show its body is not needed to get the diagnostic. 410 template <typename T> T&& forward(T& t); 411 template <typename T, typename... A> T* make_unique(A&&... args) { 412 return new T(std::forward<A>(args)...); 413 } 414 } 415 )cpp"; 416 EXPECT_THAT(TU.build().getDiagnostics(), 417 UnorderedElementsAre( 418 Diag(Main.range(), 419 "in template: " 420 "no matching constructor for initialization of 'S'"))); 421 } 422 423 TEST(DiagnosticTest, CoroutineInHeader) { 424 StringRef CoroutineH = R"cpp( 425 namespace std { 426 template <class Ret, typename... T> 427 struct coroutine_traits { using promise_type = typename Ret::promise_type; }; 428 429 template <class Promise = void> 430 struct coroutine_handle { 431 static coroutine_handle from_address(void *) noexcept; 432 static coroutine_handle from_promise(Promise &promise); 433 constexpr void* address() const noexcept; 434 }; 435 template <> 436 struct coroutine_handle<void> { 437 template <class PromiseType> 438 coroutine_handle(coroutine_handle<PromiseType>) noexcept; 439 static coroutine_handle from_address(void *); 440 constexpr void* address() const noexcept; 441 }; 442 443 struct awaitable { 444 bool await_ready() noexcept { return false; } 445 void await_suspend(coroutine_handle<>) noexcept {} 446 void await_resume() noexcept {} 447 }; 448 } // namespace std 449 )cpp"; 450 451 StringRef Header = R"cpp( 452 #include "coroutine.h" 453 template <typename T> struct [[clang::coro_return_type]] Gen { 454 struct promise_type { 455 Gen<T> get_return_object() { 456 return {}; 457 } 458 std::awaitable initial_suspend(); 459 std::awaitable final_suspend() noexcept; 460 void unhandled_exception(); 461 void return_value(T t); 462 }; 463 }; 464 465 Gen<int> foo_coro(int b) { co_return b; } 466 )cpp"; 467 Annotations Main(R"cpp( 468 // error-ok 469 #include "header.hpp" 470 Gen<int> $[[bar_coro]](int b) { return foo_coro(b); } 471 )cpp"); 472 TestTU TU = TestTU::withCode(Main.code()); 473 TU.AdditionalFiles["coroutine.h"] = std::string(CoroutineH); 474 TU.AdditionalFiles["header.hpp"] = std::string(Header); 475 TU.ExtraArgs.push_back("--std=c++20"); 476 EXPECT_THAT(TU.build().getDiagnostics(), ElementsAre(hasRange(Main.range()))); 477 } 478 479 TEST(DiagnosticTest, MakeShared) { 480 // We usually miss diagnostics from header functions as we don't parse them. 481 // std::make_shared is only parsed when --parse-forwarding-functions is set 482 Annotations Main(R"cpp( 483 struct S { S(char*); }; 484 auto x = std::[[make_shared]]<S>(42); // error-ok 485 )cpp"); 486 TestTU TU = TestTU::withCode(Main.code()); 487 TU.HeaderCode = R"cpp( 488 namespace std { 489 // These mocks aren't quite right - we omit shared_ptr for simplicity. 490 // forward is included to show its body is not needed to get the diagnostic. 491 template <typename T> T&& forward(T& t); 492 template <typename T, typename... A> T* make_shared(A&&... args) { 493 return new T(std::forward<A>(args)...); 494 } 495 } 496 )cpp"; 497 TU.ParseOpts.PreambleParseForwardingFunctions = true; 498 EXPECT_THAT(TU.build().getDiagnostics(), 499 UnorderedElementsAre( 500 Diag(Main.range(), 501 "in template: " 502 "no matching constructor for initialization of 'S'"))); 503 } 504 505 TEST(DiagnosticTest, NoMultipleDiagnosticInFlight) { 506 Annotations Main(R"cpp( 507 template <typename T> struct Foo { 508 T *begin(); 509 T *end(); 510 }; 511 struct LabelInfo { 512 int a; 513 bool b; 514 }; 515 516 void f() { 517 Foo<LabelInfo> label_info_map; 518 [[for]] (auto it = label_info_map.begin(); it != label_info_map.end(); ++it) { 519 auto S = *it; 520 } 521 } 522 )cpp"); 523 TestTU TU = TestTU::withCode(Main.code()); 524 TU.ClangTidyProvider = addTidyChecks("modernize-loop-convert"); 525 EXPECT_THAT( 526 TU.build().getDiagnostics(), 527 ifTidyChecks(UnorderedElementsAre(::testing::AllOf( 528 Diag(Main.range(), "use range-based for loop instead"), 529 diagSource(Diag::ClangTidy), diagName("modernize-loop-convert"))))); 530 } 531 532 TEST(DiagnosticTest, RespectsDiagnosticConfig) { 533 Annotations Main(R"cpp( 534 // error-ok 535 void x() { 536 [[unknown]](); 537 $ret[[return]] 42; 538 } 539 )cpp"); 540 auto TU = TestTU::withCode(Main.code()); 541 EXPECT_THAT( 542 TU.build().getDiagnostics(), 543 ElementsAre(Diag(Main.range(), "use of undeclared identifier 'unknown'"), 544 Diag(Main.range("ret"), 545 "void function 'x' should not return a value"))); 546 Config Cfg; 547 Cfg.Diagnostics.Suppress.insert("return-mismatch"); 548 WithContextValue WithCfg(Config::Key, std::move(Cfg)); 549 EXPECT_THAT(TU.build().getDiagnostics(), 550 ElementsAre(Diag(Main.range(), 551 "use of undeclared identifier 'unknown'"))); 552 } 553 554 TEST(DiagnosticTest, RespectsDiagnosticConfigInHeader) { 555 Annotations Header(R"cpp( 556 int x = "42"; // error-ok 557 )cpp"); 558 Annotations Main(R"cpp( 559 #include "header.hpp" 560 )cpp"); 561 auto TU = TestTU::withCode(Main.code()); 562 TU.AdditionalFiles["header.hpp"] = std::string(Header.code()); 563 Config Cfg; 564 Cfg.Diagnostics.Suppress.insert("init_conversion_failed"); 565 WithContextValue WithCfg(Config::Key, std::move(Cfg)); 566 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty()); 567 } 568 569 TEST(DiagnosticTest, ClangTidySuppressionComment) { 570 Annotations Main(R"cpp( 571 int main() { 572 int i = 3; 573 double d = 8 / i; // NOLINT 574 // NOLINTNEXTLINE 575 double e = 8 / i; 576 #define BAD 8 / i 577 double f = BAD; // NOLINT 578 double g = [[8]] / i; 579 #define BAD2 BAD 580 double h = BAD2; // NOLINT 581 // NOLINTBEGIN 582 double x = BAD2; 583 double y = BAD2; 584 // NOLINTEND 585 586 // verify no crashes on unmatched nolints. 587 // NOLINTBEGIN 588 } 589 )cpp"); 590 TestTU TU = TestTU::withCode(Main.code()); 591 TU.ClangTidyProvider = addTidyChecks("bugprone-integer-division"); 592 EXPECT_THAT( 593 TU.build().getDiagnostics(), 594 ifTidyChecks(UnorderedElementsAre(::testing::AllOf( 595 Diag(Main.range(), "result of integer division used in a floating " 596 "point context; possible loss of precision"), 597 diagSource(Diag::ClangTidy), 598 diagName("bugprone-integer-division"))))); 599 } 600 601 TEST(DiagnosticTest, ClangTidySystemMacro) { 602 Annotations Main(R"cpp( 603 #include "user.h" 604 #include "system.h" 605 int i = 3; 606 double x = $inline[[8]] / i; 607 double y = $user[[DIVIDE_USER]](i); 608 double z = DIVIDE_SYS(i); 609 )cpp"); 610 611 auto TU = TestTU::withCode(Main.code()); 612 TU.AdditionalFiles["user.h"] = R"cpp( 613 #define DIVIDE_USER(Y) 8/Y 614 )cpp"; 615 TU.AdditionalFiles["system.h"] = R"cpp( 616 #pragma clang system_header 617 #define DIVIDE_SYS(Y) 8/Y 618 )cpp"; 619 620 TU.ClangTidyProvider = addTidyChecks("bugprone-integer-division"); 621 std::string BadDivision = "result of integer division used in a floating " 622 "point context; possible loss of precision"; 623 624 // Expect to see warning from user macros, but not system macros. 625 // This matches clang-tidy --system-headers=0 (the default). 626 EXPECT_THAT(TU.build().getDiagnostics(), 627 ifTidyChecks( 628 UnorderedElementsAre(Diag(Main.range("inline"), BadDivision), 629 Diag(Main.range("user"), BadDivision)))); 630 } 631 632 TEST(DiagnosticTest, ClangTidyWarningAsError) { 633 Annotations Main(R"cpp( 634 int main() { 635 int i = 3; 636 double f = [[8]] / i; // error-ok 637 } 638 )cpp"); 639 TestTU TU = TestTU::withCode(Main.code()); 640 TU.ClangTidyProvider = 641 addTidyChecks("bugprone-integer-division", "bugprone-integer-division"); 642 EXPECT_THAT( 643 TU.build().getDiagnostics(), 644 ifTidyChecks(UnorderedElementsAre(::testing::AllOf( 645 Diag(Main.range(), "result of integer division used in a floating " 646 "point context; possible loss of precision"), 647 diagSource(Diag::ClangTidy), diagName("bugprone-integer-division"), 648 diagSeverity(DiagnosticsEngine::Error))))); 649 } 650 651 TidyProvider addClangArgs(std::vector<llvm::StringRef> ExtraArgs, 652 llvm::StringRef Checks) { 653 return [ExtraArgs = std::move(ExtraArgs), Checks = Checks.str()]( 654 tidy::ClangTidyOptions &Opts, llvm::StringRef) { 655 if (!Opts.ExtraArgs) 656 Opts.ExtraArgs.emplace(); 657 for (llvm::StringRef Arg : ExtraArgs) 658 Opts.ExtraArgs->emplace_back(Arg); 659 if (!Checks.empty()) 660 Opts.Checks = Checks; 661 }; 662 } 663 664 TEST(DiagnosticTest, ClangTidyEnablesClangWarning) { 665 Annotations Main(R"cpp( // error-ok 666 static void [[foo]]() {} 667 )cpp"); 668 TestTU TU = TestTU::withCode(Main.code()); 669 // This is always emitted as a clang warning, not a clang-tidy diagnostic. 670 auto UnusedFooWarning = 671 AllOf(Diag(Main.range(), "unused function 'foo'"), 672 diagName("-Wunused-function"), diagSource(Diag::Clang), 673 diagSeverity(DiagnosticsEngine::Warning)); 674 675 // Check the -Wunused warning isn't initially on. 676 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty()); 677 678 // We enable warnings based on clang-tidy extra args, if the matching 679 // clang-diagnostic- is there. 680 TU.ClangTidyProvider = 681 addClangArgs({"-Wunused"}, "clang-diagnostic-unused-function"); 682 EXPECT_THAT(TU.build().getDiagnostics(), ElementsAre(UnusedFooWarning)); 683 684 // clang-diagnostic-* is acceptable 685 TU.ClangTidyProvider = addClangArgs({"-Wunused"}, "clang-diagnostic-*"); 686 EXPECT_THAT(TU.build().getDiagnostics(), ElementsAre(UnusedFooWarning)); 687 // And plain * (may turn on other checks too). 688 TU.ClangTidyProvider = addClangArgs({"-Wunused"}, "*"); 689 EXPECT_THAT(TU.build().getDiagnostics(), Contains(UnusedFooWarning)); 690 // And we can explicitly exclude a category too. 691 TU.ClangTidyProvider = addClangArgs( 692 {"-Wunused"}, "clang-diagnostic-*,-clang-diagnostic-unused-function"); 693 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty()); 694 695 // Without the exact check specified, the warnings are not enabled. 696 TU.ClangTidyProvider = addClangArgs({"-Wunused"}, "clang-diagnostic-unused"); 697 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty()); 698 699 // We don't respect other args. 700 TU.ClangTidyProvider = addClangArgs({"-Wunused", "-Dfoo=bar"}, 701 "clang-diagnostic-unused-function"); 702 EXPECT_THAT(TU.build().getDiagnostics(), ElementsAre(UnusedFooWarning)) 703 << "Not unused function 'bar'!"; 704 705 // -Werror doesn't apply to warnings enabled by clang-tidy extra args. 706 TU.ExtraArgs = {"-Werror"}; 707 TU.ClangTidyProvider = 708 addClangArgs({"-Wunused"}, "clang-diagnostic-unused-function"); 709 EXPECT_THAT(TU.build().getDiagnostics(), 710 ElementsAre(diagSeverity(DiagnosticsEngine::Warning))); 711 712 // But clang-tidy extra args won't *downgrade* errors to warnings either. 713 TU.ExtraArgs = {"-Wunused", "-Werror"}; 714 TU.ClangTidyProvider = 715 addClangArgs({"-Wunused"}, "clang-diagnostic-unused-function"); 716 EXPECT_THAT(TU.build().getDiagnostics(), 717 ElementsAre(diagSeverity(DiagnosticsEngine::Error))); 718 719 // FIXME: we're erroneously downgrading the whole group, this should be Error. 720 TU.ExtraArgs = {"-Wunused-function", "-Werror"}; 721 TU.ClangTidyProvider = 722 addClangArgs({"-Wunused"}, "clang-diagnostic-unused-label"); 723 EXPECT_THAT(TU.build().getDiagnostics(), 724 ElementsAre(diagSeverity(DiagnosticsEngine::Warning))); 725 726 // This looks silly, but it's the typical result if a warning is enabled by a 727 // high-level .clang-tidy file and disabled by a low-level one. 728 TU.ExtraArgs = {}; 729 TU.ClangTidyProvider = addClangArgs({"-Wunused", "-Wno-unused"}, 730 "clang-diagnostic-unused-function"); 731 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty()); 732 733 // Overriding only works in the proper order. 734 TU.ClangTidyProvider = 735 addClangArgs({"-Wunused"}, {"clang-diagnostic-unused-function"}); 736 EXPECT_THAT(TU.build().getDiagnostics(), SizeIs(1)); 737 738 // More specific vs less-specific: match clang behavior 739 TU.ClangTidyProvider = addClangArgs({"-Wunused", "-Wno-unused-function"}, 740 {"clang-diagnostic-unused-function"}); 741 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty()); 742 TU.ClangTidyProvider = addClangArgs({"-Wunused-function", "-Wno-unused"}, 743 {"clang-diagnostic-unused-function"}); 744 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty()); 745 746 // We do allow clang-tidy config to disable warnings from the compile 747 // command. It's unclear this is ideal, but it's hard to avoid. 748 TU.ExtraArgs = {"-Wunused"}; 749 TU.ClangTidyProvider = addClangArgs({"-Wno-unused"}, {}); 750 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty()); 751 752 TU.ExtraArgs = {"-Wno-unused"}; 753 TU.ClangTidyProvider = addClangArgs({"-Wunused"}, {"-*, clang-diagnostic-*"}); 754 EXPECT_THAT(TU.build().getDiagnostics(), SizeIs(1)); 755 } 756 757 TEST(DiagnosticTest, LongFixMessages) { 758 // We limit the size of printed code. 759 Annotations Source(R"cpp( 760 int main() { 761 // error-ok 762 int somereallyreallyreallyreallyreallyreallyreallyreallylongidentifier; 763 [[omereallyreallyreallyreallyreallyreallyreallyreallylongidentifier]]= 10; 764 } 765 )cpp"); 766 TestTU TU = TestTU::withCode(Source.code()); 767 EXPECT_THAT( 768 TU.build().getDiagnostics(), 769 ElementsAre(withFix(Fix( 770 Source.range(), 771 "somereallyreallyreallyreallyreallyreallyreallyreallylongidentifier", 772 "change 'omereallyreallyreallyreallyreallyreallyreallyreall…' to " 773 "'somereallyreallyreallyreallyreallyreallyreallyreal…'")))); 774 // Only show changes up to a first newline. 775 Source = Annotations(R"cpp( 776 // error-ok 777 int main() { 778 int ident; 779 [[ide\ 780 n]] = 10; // error-ok 781 } 782 )cpp"); 783 TU.Code = std::string(Source.code()); 784 EXPECT_THAT(TU.build().getDiagnostics(), 785 ElementsAre(withFix( 786 Fix(Source.range(), "ident", "change 'ide\\…' to 'ident'")))); 787 } 788 789 TEST(DiagnosticTest, NewLineFixMessage) { 790 Annotations Source("int a;[[]]"); 791 TestTU TU = TestTU::withCode(Source.code()); 792 TU.ExtraArgs = {"-Wnewline-eof"}; 793 EXPECT_THAT( 794 TU.build().getDiagnostics(), 795 ElementsAre(withFix((Fix(Source.range(), "\n", "insert '\\n'"))))); 796 } 797 798 TEST(DiagnosticTest, ClangTidySuppressionCommentTrumpsWarningAsError) { 799 Annotations Main(R"cpp( 800 int main() { 801 int i = 3; 802 double f = [[8]] / i; // NOLINT 803 } 804 )cpp"); 805 TestTU TU = TestTU::withCode(Main.code()); 806 TU.ClangTidyProvider = 807 addTidyChecks("bugprone-integer-division", "bugprone-integer-division"); 808 EXPECT_THAT(TU.build().getDiagnostics(), UnorderedElementsAre()); 809 } 810 811 TEST(DiagnosticTest, ClangTidyNoLiteralDataInMacroToken) { 812 Annotations Main(R"cpp( 813 #define SIGTERM 15 814 using pthread_t = int; 815 int pthread_kill(pthread_t thread, int sig); 816 int func() { 817 pthread_t thread; 818 return pthread_kill(thread, 0); 819 } 820 )cpp"); 821 TestTU TU = TestTU::withCode(Main.code()); 822 TU.ClangTidyProvider = addTidyChecks("bugprone-bad-signal-to-kill-thread"); 823 EXPECT_THAT(TU.build().getDiagnostics(), UnorderedElementsAre()); // no-crash 824 } 825 826 TEST(DiagnosticTest, ElseAfterReturnRange) { 827 Annotations Main(R"cpp( 828 int foo(int cond) { 829 if (cond == 1) { 830 return 42; 831 } [[else]] if (cond == 2) { 832 return 43; 833 } 834 return 44; 835 } 836 )cpp"); 837 TestTU TU = TestTU::withCode(Main.code()); 838 TU.ClangTidyProvider = addTidyChecks("llvm-else-after-return"); 839 EXPECT_THAT(TU.build().getDiagnostics(), 840 ifTidyChecks(ElementsAre( 841 Diag(Main.range(), "do not use 'else' after 'return'")))); 842 } 843 844 TEST(DiagnosticTest, ClangTidySelfContainedDiags) { 845 Annotations Main(R"cpp($MathHeader[[]] 846 struct Foo{ 847 int A, B; 848 Foo()$Fix[[]] { 849 $A[[A = 1;]] 850 $B[[B = 1;]] 851 } 852 }; 853 void InitVariables() { 854 float $C[[C]]$CFix[[]]; 855 double $D[[D]]$DFix[[]]; 856 } 857 )cpp"); 858 TestTU TU = TestTU::withCode(Main.code()); 859 TU.ClangTidyProvider = 860 addTidyChecks("cppcoreguidelines-prefer-member-initializer," 861 "cppcoreguidelines-init-variables"); 862 clangd::Fix ExpectedAFix; 863 ExpectedAFix.Message = 864 "'A' should be initialized in a member initializer of the constructor"; 865 ExpectedAFix.Edits.push_back(TextEdit{Main.range("Fix"), " : A(1)"}); 866 ExpectedAFix.Edits.push_back(TextEdit{Main.range("A"), ""}); 867 868 // When invoking clang-tidy normally, this code would produce `, B(1)` as the 869 // fix the `B` member, as it would think its already included the ` : ` from 870 // the previous `A` fix. 871 clangd::Fix ExpectedBFix; 872 ExpectedBFix.Message = 873 "'B' should be initialized in a member initializer of the constructor"; 874 ExpectedBFix.Edits.push_back(TextEdit{Main.range("Fix"), " : B(1)"}); 875 ExpectedBFix.Edits.push_back(TextEdit{Main.range("B"), ""}); 876 877 clangd::Fix ExpectedCFix; 878 ExpectedCFix.Message = "variable 'C' is not initialized"; 879 ExpectedCFix.Edits.push_back(TextEdit{Main.range("CFix"), " = NAN"}); 880 ExpectedCFix.Edits.push_back( 881 TextEdit{Main.range("MathHeader"), "#include <math.h>\n\n"}); 882 883 // Again in clang-tidy only the include directive would be emitted for the 884 // first warning. However we need the include attaching for both warnings. 885 clangd::Fix ExpectedDFix; 886 ExpectedDFix.Message = "variable 'D' is not initialized"; 887 ExpectedDFix.Edits.push_back(TextEdit{Main.range("DFix"), " = NAN"}); 888 ExpectedDFix.Edits.push_back( 889 TextEdit{Main.range("MathHeader"), "#include <math.h>\n\n"}); 890 EXPECT_THAT( 891 TU.build().getDiagnostics(), 892 ifTidyChecks(UnorderedElementsAre( 893 AllOf(Diag(Main.range("A"), "'A' should be initialized in a member " 894 "initializer of the constructor"), 895 withFix(equalToFix(ExpectedAFix))), 896 AllOf(Diag(Main.range("B"), "'B' should be initialized in a member " 897 "initializer of the constructor"), 898 withFix(equalToFix(ExpectedBFix))), 899 AllOf(Diag(Main.range("C"), "variable 'C' is not initialized"), 900 withFix(equalToFix(ExpectedCFix))), 901 AllOf(Diag(Main.range("D"), "variable 'D' is not initialized"), 902 withFix(equalToFix(ExpectedDFix)))))); 903 } 904 905 TEST(DiagnosticTest, ClangTidySelfContainedDiagsFormatting) { 906 Annotations Main(R"cpp( 907 class Interface { 908 public: 909 virtual void Reset1() = 0; 910 virtual void Reset2() = 0; 911 }; 912 class A : public Interface { 913 // This will be marked by clangd to use override instead of virtual 914 $virtual1[[virtual ]]void $Reset1[[Reset1]]()$override1[[]]; 915 $virtual2[[virtual ]]/**/void $Reset2[[Reset2]]()$override2[[]]; 916 }; 917 )cpp"); 918 TestTU TU = TestTU::withCode(Main.code()); 919 TU.ClangTidyProvider = 920 addTidyChecks("cppcoreguidelines-explicit-virtual-functions,"); 921 clangd::Fix const ExpectedFix1{ 922 "prefer using 'override' or (rarely) 'final' " 923 "instead of 'virtual'", 924 {TextEdit{Main.range("override1"), " override"}, 925 TextEdit{Main.range("virtual1"), ""}}, 926 {}}; 927 clangd::Fix const ExpectedFix2{ 928 "prefer using 'override' or (rarely) 'final' " 929 "instead of 'virtual'", 930 {TextEdit{Main.range("override2"), " override"}, 931 TextEdit{Main.range("virtual2"), ""}}, 932 {}}; 933 // Note that in the Fix we expect the "virtual" keyword and the following 934 // whitespace to be deleted 935 EXPECT_THAT(TU.build().getDiagnostics(), 936 ifTidyChecks(UnorderedElementsAre( 937 AllOf(Diag(Main.range("Reset1"), 938 "prefer using 'override' or (rarely) 'final' " 939 "instead of 'virtual'"), 940 withFix(equalToFix(ExpectedFix1))), 941 AllOf(Diag(Main.range("Reset2"), 942 "prefer using 'override' or (rarely) 'final' " 943 "instead of 'virtual'"), 944 withFix(equalToFix(ExpectedFix2)))))); 945 } 946 947 TEST(DiagnosticsTest, ClangTidyCallingIntoPreprocessor) { 948 std::string Main = R"cpp( 949 extern "C" { 950 #include "b.h" 951 } 952 )cpp"; 953 std::string Header = R"cpp( 954 #define EXTERN extern 955 EXTERN int waldo(); 956 )cpp"; 957 auto TU = TestTU::withCode(Main); 958 TU.AdditionalFiles["b.h"] = Header; 959 TU.ClangTidyProvider = addTidyChecks("modernize-use-trailing-return-type"); 960 // Check that no assertion failures occur during the build 961 TU.build(); 962 } 963 964 TEST(DiagnosticsTest, Preprocessor) { 965 // This looks like a preamble, but there's an #else in the middle! 966 // Check that: 967 // - the #else doesn't generate diagnostics (we had this bug) 968 // - we get diagnostics from the taken branch 969 // - we get no diagnostics from the not taken branch 970 Annotations Test(R"cpp( 971 #ifndef FOO 972 #define FOO 973 int a = [[b]]; // error-ok 974 #else 975 int x = y; 976 #endif 977 )cpp"); 978 EXPECT_THAT( 979 TestTU::withCode(Test.code()).build().getDiagnostics(), 980 ElementsAre(Diag(Test.range(), "use of undeclared identifier 'b'"))); 981 } 982 983 TEST(DiagnosticsTest, IgnoreVerify) { 984 auto TU = TestTU::withCode(R"cpp( 985 int a; // expected-error {{}} 986 )cpp"); 987 TU.ExtraArgs.push_back("-Xclang"); 988 TU.ExtraArgs.push_back("-verify"); 989 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty()); 990 } 991 992 TEST(DiagnosticTest, IgnoreBEFilelistOptions) { 993 auto TU = TestTU::withCode(""); 994 TU.ExtraArgs.push_back("-Xclang"); 995 for (const auto *DisableOption : 996 {"-fsanitize-ignorelist=null", "-fprofile-list=null", 997 "-fxray-always-instrument=null", "-fxray-never-instrument=null", 998 "-fxray-attr-list=null"}) { 999 TU.ExtraArgs.push_back(DisableOption); 1000 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty()); 1001 TU.ExtraArgs.pop_back(); 1002 } 1003 } 1004 1005 // Recursive main-file include is diagnosed, and doesn't crash. 1006 TEST(DiagnosticsTest, RecursivePreamble) { 1007 auto TU = TestTU::withCode(R"cpp( 1008 #include "foo.h" // error-ok 1009 int symbol; 1010 )cpp"); 1011 TU.Filename = "foo.h"; 1012 EXPECT_THAT(TU.build().getDiagnostics(), 1013 ElementsAre(diagName("pp_including_mainfile_in_preamble"))); 1014 EXPECT_THAT(TU.build().getLocalTopLevelDecls(), SizeIs(1)); 1015 } 1016 1017 // Recursive main-file include with #pragma once guard is OK. 1018 TEST(DiagnosticsTest, RecursivePreamblePragmaOnce) { 1019 auto TU = TestTU::withCode(R"cpp( 1020 #pragma once 1021 #include "foo.h" 1022 int symbol; 1023 )cpp"); 1024 TU.Filename = "foo.h"; 1025 EXPECT_THAT(TU.build().getDiagnostics(), 1026 Not(Contains(diagName("pp_including_mainfile_in_preamble")))); 1027 EXPECT_THAT(TU.build().getLocalTopLevelDecls(), SizeIs(1)); 1028 } 1029 1030 // Recursive main-file include with #ifndef guard should be OK. 1031 // However, it's not yet recognized (incomplete at end of preamble). 1032 TEST(DiagnosticsTest, RecursivePreambleIfndefGuard) { 1033 auto TU = TestTU::withCode(R"cpp( 1034 #ifndef FOO 1035 #define FOO 1036 #include "foo.h" // error-ok 1037 int symbol; 1038 #endif 1039 )cpp"); 1040 TU.Filename = "foo.h"; 1041 // FIXME: should be no errors here. 1042 EXPECT_THAT(TU.build().getDiagnostics(), 1043 ElementsAre(diagName("pp_including_mainfile_in_preamble"))); 1044 EXPECT_THAT(TU.build().getLocalTopLevelDecls(), SizeIs(1)); 1045 } 1046 1047 TEST(DiagnosticsTest, PreambleWithPragmaAssumeNonnull) { 1048 auto TU = TestTU::withCode(R"cpp( 1049 #pragma clang assume_nonnull begin 1050 void foo(int *x); 1051 #pragma clang assume_nonnull end 1052 )cpp"); 1053 auto AST = TU.build(); 1054 EXPECT_THAT(AST.getDiagnostics(), IsEmpty()); 1055 const auto *X = cast<FunctionDecl>(findDecl(AST, "foo")).getParamDecl(0); 1056 ASSERT_TRUE(X->getOriginalType()->getNullability() == 1057 NullabilityKind::NonNull); 1058 } 1059 1060 TEST(DiagnosticsTest, PreambleHeaderWithBadPragmaAssumeNonnull) { 1061 Annotations Header(R"cpp( 1062 #pragma clang assume_nonnull begin // error-ok 1063 void foo(int *X); 1064 )cpp"); 1065 auto TU = TestTU::withCode(R"cpp( 1066 #include "foo.h" // unterminated assume_nonnull should not affect bar. 1067 void bar(int *Y); 1068 )cpp"); 1069 TU.AdditionalFiles = {{"foo.h", std::string(Header.code())}}; 1070 auto AST = TU.build(); 1071 EXPECT_THAT(AST.getDiagnostics(), 1072 ElementsAre(diagName("pp_eof_in_assume_nonnull"))); 1073 const auto *X = cast<FunctionDecl>(findDecl(AST, "foo")).getParamDecl(0); 1074 ASSERT_TRUE(X->getOriginalType()->getNullability() == 1075 NullabilityKind::NonNull); 1076 const auto *Y = cast<FunctionDecl>(findDecl(AST, "bar")).getParamDecl(0); 1077 ASSERT_FALSE(Y->getOriginalType()->getNullability()); 1078 } 1079 1080 TEST(DiagnosticsTest, InsideMacros) { 1081 Annotations Test(R"cpp( 1082 #define TEN 10 1083 #define RET(x) return x + 10 1084 1085 int* foo() { 1086 RET($foo[[0]]); // error-ok 1087 } 1088 int* bar() { 1089 return $bar[[TEN]]; 1090 } 1091 )cpp"); 1092 EXPECT_THAT(TestTU::withCode(Test.code()).build().getDiagnostics(), 1093 ElementsAre(Diag(Test.range("foo"), 1094 "cannot initialize return object of type " 1095 "'int *' with an rvalue of type 'int'"), 1096 Diag(Test.range("bar"), 1097 "cannot initialize return object of type " 1098 "'int *' with an rvalue of type 'int'"))); 1099 } 1100 1101 TEST(DiagnosticsTest, NoFixItInMacro) { 1102 Annotations Test(R"cpp( 1103 #define Define(name) void name() {} 1104 1105 [[Define]](main) // error-ok 1106 )cpp"); 1107 auto TU = TestTU::withCode(Test.code()); 1108 EXPECT_THAT(TU.build().getDiagnostics(), 1109 ElementsAre(AllOf(Diag(Test.range(), "'main' must return 'int'"), 1110 Not(withFix(_))))); 1111 } 1112 1113 TEST(DiagnosticsTest, PragmaSystemHeader) { 1114 Annotations Test("#pragma clang [[system_header]]\n"); 1115 auto TU = TestTU::withCode(Test.code()); 1116 EXPECT_THAT( 1117 TU.build().getDiagnostics(), 1118 ElementsAre(AllOf( 1119 Diag(Test.range(), "#pragma system_header ignored in main file")))); 1120 TU.Filename = "TestTU.h"; 1121 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty()); 1122 } 1123 1124 TEST(ClangdTest, MSAsm) { 1125 // Parsing MS assembly tries to use the target MCAsmInfo, which we don't link. 1126 // We used to crash here. Now clang emits a diagnostic, which we filter out. 1127 llvm::InitializeAllTargetInfos(); // As in ClangdMain 1128 auto TU = TestTU::withCode("void fn() { __asm { cmp cl,64 } }"); 1129 TU.ExtraArgs = {"-fms-extensions"}; 1130 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty()); 1131 } 1132 1133 TEST(DiagnosticsTest, ToLSP) { 1134 URIForFile MainFile = 1135 URIForFile::canonicalize(testPath("foo/bar/main.cpp"), ""); 1136 URIForFile HeaderFile = 1137 URIForFile::canonicalize(testPath("foo/bar/header.h"), ""); 1138 1139 clangd::Diag D; 1140 D.ID = clang::diag::err_undeclared_var_use; 1141 D.Tags = {DiagnosticTag::Unnecessary}; 1142 D.Name = "undeclared_var_use"; 1143 D.Source = clangd::Diag::Clang; 1144 D.Message = "something terrible happened"; 1145 D.Range = {pos(1, 2), pos(3, 4)}; 1146 D.InsideMainFile = true; 1147 D.Severity = DiagnosticsEngine::Error; 1148 D.File = "foo/bar/main.cpp"; 1149 D.AbsFile = std::string(MainFile.file()); 1150 D.OpaqueData["test"] = "bar"; 1151 1152 clangd::Note NoteInMain; 1153 NoteInMain.Message = "declared somewhere in the main file"; 1154 NoteInMain.Range = {pos(5, 6), pos(7, 8)}; 1155 NoteInMain.Severity = DiagnosticsEngine::Remark; 1156 NoteInMain.File = "../foo/bar/main.cpp"; 1157 NoteInMain.InsideMainFile = true; 1158 NoteInMain.AbsFile = std::string(MainFile.file()); 1159 1160 D.Notes.push_back(NoteInMain); 1161 1162 clangd::Note NoteInHeader; 1163 NoteInHeader.Message = "declared somewhere in the header file"; 1164 NoteInHeader.Range = {pos(9, 10), pos(11, 12)}; 1165 NoteInHeader.Severity = DiagnosticsEngine::Note; 1166 NoteInHeader.File = "../foo/baz/header.h"; 1167 NoteInHeader.InsideMainFile = false; 1168 NoteInHeader.AbsFile = std::string(HeaderFile.file()); 1169 D.Notes.push_back(NoteInHeader); 1170 1171 clangd::Fix F; 1172 F.Message = "do something"; 1173 D.Fixes.push_back(F); 1174 1175 // Diagnostics should turn into these: 1176 clangd::Diagnostic MainLSP; 1177 MainLSP.range = D.Range; 1178 MainLSP.severity = getSeverity(DiagnosticsEngine::Error); 1179 MainLSP.code = "undeclared_var_use"; 1180 MainLSP.source = "clang"; 1181 MainLSP.message = 1182 R"(Something terrible happened (fix available) 1183 1184 main.cpp:6:7: remark: declared somewhere in the main file 1185 1186 ../foo/baz/header.h:10:11: 1187 note: declared somewhere in the header file)"; 1188 MainLSP.tags = {DiagnosticTag::Unnecessary}; 1189 MainLSP.data = D.OpaqueData; 1190 1191 clangd::Diagnostic NoteInMainLSP; 1192 NoteInMainLSP.range = NoteInMain.Range; 1193 NoteInMainLSP.severity = getSeverity(DiagnosticsEngine::Remark); 1194 NoteInMainLSP.message = R"(Declared somewhere in the main file 1195 1196 main.cpp:2:3: error: something terrible happened)"; 1197 1198 ClangdDiagnosticOptions Opts; 1199 // Transform diagnostics and check the results. 1200 std::vector<std::pair<clangd::Diagnostic, std::vector<clangd::Fix>>> LSPDiags; 1201 toLSPDiags(D, MainFile, Opts, 1202 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix> Fixes) { 1203 LSPDiags.push_back( 1204 {std::move(LSPDiag), 1205 std::vector<clangd::Fix>(Fixes.begin(), Fixes.end())}); 1206 }); 1207 1208 EXPECT_THAT( 1209 LSPDiags, 1210 ElementsAre(Pair(equalToLSPDiag(MainLSP), ElementsAre(equalToFix(F))), 1211 Pair(equalToLSPDiag(NoteInMainLSP), IsEmpty()))); 1212 EXPECT_EQ(LSPDiags[0].first.code, "undeclared_var_use"); 1213 EXPECT_EQ(LSPDiags[0].first.source, "clang"); 1214 EXPECT_EQ(LSPDiags[1].first.code, ""); 1215 EXPECT_EQ(LSPDiags[1].first.source, ""); 1216 1217 // Same thing, but don't flatten notes into the main list. 1218 LSPDiags.clear(); 1219 Opts.EmitRelatedLocations = true; 1220 toLSPDiags(D, MainFile, Opts, 1221 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix> Fixes) { 1222 LSPDiags.push_back( 1223 {std::move(LSPDiag), 1224 std::vector<clangd::Fix>(Fixes.begin(), Fixes.end())}); 1225 }); 1226 MainLSP.message = "Something terrible happened (fix available)"; 1227 DiagnosticRelatedInformation NoteInMainDRI; 1228 NoteInMainDRI.message = "Declared somewhere in the main file"; 1229 NoteInMainDRI.location.range = NoteInMain.Range; 1230 NoteInMainDRI.location.uri = MainFile; 1231 MainLSP.relatedInformation = {NoteInMainDRI}; 1232 DiagnosticRelatedInformation NoteInHeaderDRI; 1233 NoteInHeaderDRI.message = "Declared somewhere in the header file"; 1234 NoteInHeaderDRI.location.range = NoteInHeader.Range; 1235 NoteInHeaderDRI.location.uri = HeaderFile; 1236 MainLSP.relatedInformation = {NoteInMainDRI, NoteInHeaderDRI}; 1237 EXPECT_THAT(LSPDiags, ElementsAre(Pair(equalToLSPDiag(MainLSP), 1238 ElementsAre(equalToFix(F))))); 1239 } 1240 1241 struct SymbolWithHeader { 1242 std::string QName; 1243 std::string DeclaringFile; 1244 std::string IncludeHeader; 1245 }; 1246 1247 std::unique_ptr<SymbolIndex> 1248 buildIndexWithSymbol(llvm::ArrayRef<SymbolWithHeader> Syms) { 1249 SymbolSlab::Builder Slab; 1250 for (const auto &S : Syms) { 1251 Symbol Sym = cls(S.QName); 1252 Sym.Flags |= Symbol::IndexedForCodeCompletion; 1253 Sym.CanonicalDeclaration.FileURI = S.DeclaringFile.c_str(); 1254 Sym.Definition.FileURI = S.DeclaringFile.c_str(); 1255 Sym.IncludeHeaders.emplace_back(S.IncludeHeader, 1, Symbol::Include); 1256 Slab.insert(Sym); 1257 } 1258 return MemIndex::build(std::move(Slab).build(), RefSlab(), RelationSlab()); 1259 } 1260 1261 TEST(IncludeFixerTest, IncompleteType) { 1262 auto TU = TestTU::withHeaderCode("namespace ns { class X; } ns::X *x;"); 1263 TU.ExtraArgs.push_back("-std=c++20"); 1264 auto Index = buildIndexWithSymbol( 1265 {SymbolWithHeader{"ns::X", "unittest:///x.h", "\"x.h\""}}); 1266 TU.ExternalIndex = Index.get(); 1267 1268 std::vector<std::pair<llvm::StringRef, llvm::StringRef>> Tests{ 1269 {"incomplete_nested_name_spec", "[[ns::X::]]Nested n;"}, 1270 {"incomplete_base_class", "class Y : [[ns::X]] {};"}, 1271 {"incomplete_member_access", "auto i = x[[->]]f();"}, 1272 {"incomplete_type", "auto& [[[]]m] = *x;"}, 1273 {"init_incomplete_type", 1274 "struct C { static int f(ns::X&); }; int i = C::f([[{]]});"}, 1275 {"bad_cast_incomplete", "auto a = [[static_cast]]<ns::X>(0);"}, 1276 {"template_nontype_parm_incomplete", "template <ns::X [[foo]]> int a;"}, 1277 {"typecheck_decl_incomplete_type", "ns::X [[var]];"}, 1278 {"typecheck_incomplete_tag", "auto i = [[(*x)]]->f();"}, 1279 {"typecheck_nonviable_condition_incomplete", 1280 "struct A { operator ns::X(); } a; const ns::X &[[b]] = a;"}, 1281 {"invalid_incomplete_type_use", "auto var = [[ns::X()]];"}, 1282 {"sizeof_alignof_incomplete_or_sizeless_type", 1283 "auto s = [[sizeof]](ns::X);"}, 1284 {"for_range_incomplete_type", "void foo() { for (auto i : [[*]]x ) {} }"}, 1285 {"func_def_incomplete_result", "ns::X [[func]] () {}"}, 1286 {"field_incomplete_or_sizeless", "class M { ns::X [[member]]; };"}, 1287 {"array_incomplete_or_sizeless_type", "auto s = [[(ns::X[]){}]];"}, 1288 {"call_incomplete_return", "ns::X f(); auto fp = &f; auto z = [[fp()]];"}, 1289 {"call_function_incomplete_return", "ns::X foo(); auto a = [[foo()]];"}, 1290 {"call_incomplete_argument", "int m(ns::X); int i = m([[*x]]);"}, 1291 {"switch_incomplete_class_type", "void a() { [[switch]](*x) {} }"}, 1292 {"delete_incomplete_class_type", "void f() { [[delete]] *x; }"}, 1293 {"-Wdelete-incomplete", "void f() { [[delete]] x; }"}, 1294 {"dereference_incomplete_type", 1295 R"cpp(void f() { asm("" : "=r"([[*]]x)::); })cpp"}, 1296 }; 1297 for (auto Case : Tests) { 1298 Annotations Main(Case.second); 1299 TU.Code = Main.code().str() + "\n // error-ok"; 1300 EXPECT_THAT( 1301 TU.build().getDiagnostics(), 1302 ElementsAre(AllOf(diagName(Case.first), hasRange(Main.range()), 1303 withFix(Fix(Range{}, "#include \"x.h\"\n", 1304 "Include \"x.h\" for symbol ns::X"))))) 1305 << Case.second; 1306 } 1307 } 1308 1309 TEST(IncludeFixerTest, IncompleteEnum) { 1310 Symbol Sym = enm("X"); 1311 Sym.Flags |= Symbol::IndexedForCodeCompletion; 1312 Sym.CanonicalDeclaration.FileURI = Sym.Definition.FileURI = "unittest:///x.h"; 1313 Sym.IncludeHeaders.emplace_back("\"x.h\"", 1, Symbol::Include); 1314 SymbolSlab::Builder Slab; 1315 Slab.insert(Sym); 1316 auto Index = 1317 MemIndex::build(std::move(Slab).build(), RefSlab(), RelationSlab()); 1318 1319 TestTU TU; 1320 TU.ExternalIndex = Index.get(); 1321 TU.ExtraArgs.push_back("-std=c++20"); 1322 TU.ExtraArgs.push_back("-fno-ms-compatibility"); // else incomplete enum is OK 1323 1324 std::vector<std::pair<llvm::StringRef, llvm::StringRef>> Tests{ 1325 {"incomplete_enum", "enum class X : int; using enum [[X]];"}, 1326 {"underlying_type_of_incomplete_enum", 1327 "[[__underlying_type]](enum X) i;"}, 1328 }; 1329 for (auto Case : Tests) { 1330 Annotations Main(Case.second); 1331 TU.Code = Main.code().str() + "\n // error-ok"; 1332 EXPECT_THAT(TU.build().getDiagnostics(), 1333 Contains(AllOf(diagName(Case.first), hasRange(Main.range()), 1334 withFix(Fix(Range{}, "#include \"x.h\"\n", 1335 "Include \"x.h\" for symbol X"))))) 1336 << Case.second; 1337 } 1338 } 1339 1340 TEST(IncludeFixerTest, NoSuggestIncludeWhenNoDefinitionInHeader) { 1341 Annotations Test(R"cpp(// error-ok 1342 $insert[[]]namespace ns { 1343 class X; 1344 } 1345 class Y : $base[[public ns::X]] {}; 1346 int main() { 1347 ns::X *x; 1348 x$access[[->]]f(); 1349 } 1350 )cpp"); 1351 auto TU = TestTU::withCode(Test.code()); 1352 Symbol Sym = cls("ns::X"); 1353 Sym.Flags |= Symbol::IndexedForCodeCompletion; 1354 Sym.CanonicalDeclaration.FileURI = "unittest:///x.h"; 1355 Sym.Definition.FileURI = "unittest:///x.cc"; 1356 Sym.IncludeHeaders.emplace_back("\"x.h\"", 1, Symbol::Include); 1357 1358 SymbolSlab::Builder Slab; 1359 Slab.insert(Sym); 1360 auto Index = 1361 MemIndex::build(std::move(Slab).build(), RefSlab(), RelationSlab()); 1362 TU.ExternalIndex = Index.get(); 1363 1364 EXPECT_THAT(TU.build().getDiagnostics(), 1365 UnorderedElementsAre( 1366 Diag(Test.range("base"), "base class has incomplete type"), 1367 Diag(Test.range("access"), 1368 "member access into incomplete type 'ns::X'"))); 1369 } 1370 1371 TEST(IncludeFixerTest, Typo) { 1372 Annotations Test(R"cpp(// error-ok 1373 $insert[[]]namespace ns { 1374 void foo() { 1375 $unqualified1[[X]] x; 1376 // No fix if the unresolved type is used as specifier. (ns::)X::Nested will be 1377 // considered the unresolved type. 1378 $unqualified2[[X]]::Nested n; 1379 } 1380 struct S : $base[[X]] {}; 1381 } 1382 void bar() { 1383 ns::$qualified1[[X]] x; // ns:: is valid. 1384 ns::$qualified2[[X]](); // Error: no member in namespace 1385 1386 ::$global[[Global]] glob; 1387 } 1388 using Type = ns::$template[[Foo]]<int>; 1389 )cpp"); 1390 auto TU = TestTU::withCode(Test.code()); 1391 auto Index = buildIndexWithSymbol( 1392 {SymbolWithHeader{"ns::X", "unittest:///x.h", "\"x.h\""}, 1393 SymbolWithHeader{"Global", "unittest:///global.h", "\"global.h\""}, 1394 SymbolWithHeader{"ns::Foo", "unittest:///foo.h", "\"foo.h\""}}); 1395 TU.ExternalIndex = Index.get(); 1396 1397 EXPECT_THAT( 1398 TU.build().getDiagnostics(), 1399 UnorderedElementsAre( 1400 AllOf(Diag(Test.range("unqualified1"), "unknown type name 'X'"), 1401 diagName("unknown_typename"), 1402 withFix(Fix(Test.range("insert"), "#include \"x.h\"\n", 1403 "Include \"x.h\" for symbol ns::X"))), 1404 Diag(Test.range("unqualified2"), "use of undeclared identifier 'X'"), 1405 AllOf(Diag(Test.range("qualified1"), 1406 "no type named 'X' in namespace 'ns'"), 1407 diagName("typename_nested_not_found"), 1408 withFix(Fix(Test.range("insert"), "#include \"x.h\"\n", 1409 "Include \"x.h\" for symbol ns::X"))), 1410 AllOf(Diag(Test.range("qualified2"), 1411 "no member named 'X' in namespace 'ns'"), 1412 diagName("no_member"), 1413 withFix(Fix(Test.range("insert"), "#include \"x.h\"\n", 1414 "Include \"x.h\" for symbol ns::X"))), 1415 AllOf(Diag(Test.range("global"), 1416 "no type named 'Global' in the global namespace"), 1417 diagName("typename_nested_not_found"), 1418 withFix(Fix(Test.range("insert"), "#include \"global.h\"\n", 1419 "Include \"global.h\" for symbol Global"))), 1420 AllOf(Diag(Test.range("template"), 1421 "no template named 'Foo' in namespace 'ns'"), 1422 diagName("no_member_template"), 1423 withFix(Fix(Test.range("insert"), "#include \"foo.h\"\n", 1424 "Include \"foo.h\" for symbol ns::Foo"))), 1425 AllOf(Diag(Test.range("base"), "expected class name"), 1426 diagName("expected_class_name"), 1427 withFix(Fix(Test.range("insert"), "#include \"x.h\"\n", 1428 "Include \"x.h\" for symbol ns::X"))))); 1429 } 1430 1431 TEST(IncludeFixerTest, TypoInMacro) { 1432 auto TU = TestTU::withCode(R"cpp(// error-ok 1433 #define ID(T) T 1434 X a1; 1435 ID(X a2); 1436 ns::X a3; 1437 ID(ns::X a4); 1438 namespace ns{}; 1439 ns::X a5; 1440 ID(ns::X a6); 1441 )cpp"); 1442 auto Index = buildIndexWithSymbol( 1443 {SymbolWithHeader{"X", "unittest:///x.h", "\"x.h\""}, 1444 SymbolWithHeader{"ns::X", "unittest:///ns.h", "\"x.h\""}}); 1445 TU.ExternalIndex = Index.get(); 1446 // FIXME: -fms-compatibility (which is default on windows) breaks the 1447 // ns::X cases when the namespace is undeclared. Find out why! 1448 TU.ExtraArgs = {"-fno-ms-compatibility"}; 1449 EXPECT_THAT(TU.build().getDiagnostics(), Each(withFix(_))); 1450 } 1451 1452 TEST(IncludeFixerTest, MultipleMatchedSymbols) { 1453 Annotations Test(R"cpp(// error-ok 1454 $insert[[]]namespace na { 1455 namespace nb { 1456 void foo() { 1457 $unqualified[[X]] x; 1458 } 1459 } 1460 } 1461 )cpp"); 1462 auto TU = TestTU::withCode(Test.code()); 1463 auto Index = buildIndexWithSymbol( 1464 {SymbolWithHeader{"na::X", "unittest:///a.h", "\"a.h\""}, 1465 SymbolWithHeader{"na::nb::X", "unittest:///b.h", "\"b.h\""}}); 1466 TU.ExternalIndex = Index.get(); 1467 1468 EXPECT_THAT(TU.build().getDiagnostics(), 1469 UnorderedElementsAre(AllOf( 1470 Diag(Test.range("unqualified"), "unknown type name 'X'"), 1471 diagName("unknown_typename"), 1472 withFix(Fix(Test.range("insert"), "#include \"a.h\"\n", 1473 "Include \"a.h\" for symbol na::X"), 1474 Fix(Test.range("insert"), "#include \"b.h\"\n", 1475 "Include \"b.h\" for symbol na::nb::X"))))); 1476 } 1477 1478 TEST(IncludeFixerTest, NoCrashMemberAccess) { 1479 Annotations Test(R"cpp(// error-ok 1480 struct X { int xyz; }; 1481 void g() { X x; x.$[[xy]]; } 1482 )cpp"); 1483 auto TU = TestTU::withCode(Test.code()); 1484 auto Index = buildIndexWithSymbol( 1485 SymbolWithHeader{"na::X", "unittest:///a.h", "\"a.h\""}); 1486 TU.ExternalIndex = Index.get(); 1487 1488 EXPECT_THAT( 1489 TU.build().getDiagnostics(), 1490 UnorderedElementsAre(Diag(Test.range(), "no member named 'xy' in 'X'"))); 1491 } 1492 1493 TEST(IncludeFixerTest, UseCachedIndexResults) { 1494 // As index results for the identical request are cached, more than 5 fixes 1495 // are generated. 1496 Annotations Test(R"cpp(// error-ok 1497 $insert[[]]void foo() { 1498 $x1[[X]] x; 1499 $x2[[X]] x; 1500 $x3[[X]] x; 1501 $x4[[X]] x; 1502 $x5[[X]] x; 1503 $x6[[X]] x; 1504 $x7[[X]] x; 1505 } 1506 1507 class X; 1508 void bar(X *x) { 1509 x$a1[[->]]f(); 1510 x$a2[[->]]f(); 1511 x$a3[[->]]f(); 1512 x$a4[[->]]f(); 1513 x$a5[[->]]f(); 1514 x$a6[[->]]f(); 1515 x$a7[[->]]f(); 1516 } 1517 )cpp"); 1518 auto TU = TestTU::withCode(Test.code()); 1519 auto Index = 1520 buildIndexWithSymbol(SymbolWithHeader{"X", "unittest:///a.h", "\"a.h\""}); 1521 TU.ExternalIndex = Index.get(); 1522 1523 auto Parsed = TU.build(); 1524 for (const auto &D : Parsed.getDiagnostics()) { 1525 if (D.Fixes.size() != 1) { 1526 ADD_FAILURE() << "D.Fixes.size() != 1"; 1527 continue; 1528 } 1529 EXPECT_EQ(D.Fixes[0].Message, std::string("Include \"a.h\" for symbol X")); 1530 } 1531 } 1532 1533 TEST(IncludeFixerTest, UnresolvedNameAsSpecifier) { 1534 Annotations Test(R"cpp(// error-ok 1535 $insert[[]]namespace ns { 1536 } 1537 void g() { ns::$[[scope]]::X_Y(); } 1538 )cpp"); 1539 TestTU TU; 1540 TU.Code = std::string(Test.code()); 1541 // FIXME: Figure out why this is needed and remove it, PR43662. 1542 TU.ExtraArgs.push_back("-fno-ms-compatibility"); 1543 auto Index = buildIndexWithSymbol( 1544 SymbolWithHeader{"ns::scope::X_Y", "unittest:///x.h", "\"x.h\""}); 1545 TU.ExternalIndex = Index.get(); 1546 1547 EXPECT_THAT( 1548 TU.build().getDiagnostics(), 1549 UnorderedElementsAre( 1550 AllOf(Diag(Test.range(), "no member named 'scope' in namespace 'ns'"), 1551 diagName("no_member"), 1552 withFix(Fix(Test.range("insert"), "#include \"x.h\"\n", 1553 "Include \"x.h\" for symbol ns::scope::X_Y"))))); 1554 } 1555 1556 TEST(IncludeFixerTest, UnresolvedSpecifierWithSemaCorrection) { 1557 Annotations Test(R"cpp(// error-ok 1558 $insert[[]]namespace clang { 1559 void f() { 1560 // "clangd::" will be corrected to "clang::" by Sema. 1561 $q1[[clangd]]::$x[[X]] x; 1562 $q2[[clangd]]::$ns[[ns]]::Y y; 1563 } 1564 } 1565 )cpp"); 1566 TestTU TU; 1567 TU.Code = std::string(Test.code()); 1568 // FIXME: Figure out why this is needed and remove it, PR43662. 1569 TU.ExtraArgs.push_back("-fno-ms-compatibility"); 1570 auto Index = buildIndexWithSymbol( 1571 {SymbolWithHeader{"clang::clangd::X", "unittest:///x.h", "\"x.h\""}, 1572 SymbolWithHeader{"clang::clangd::ns::Y", "unittest:///y.h", "\"y.h\""}}); 1573 TU.ExternalIndex = Index.get(); 1574 1575 EXPECT_THAT( 1576 TU.build().getDiagnostics(), 1577 UnorderedElementsAre( 1578 AllOf(Diag(Test.range("q1"), "use of undeclared identifier 'clangd'; " 1579 "did you mean 'clang'?"), 1580 diagName("undeclared_var_use_suggest"), 1581 withFix(_, // change clangd to clang 1582 Fix(Test.range("insert"), "#include \"x.h\"\n", 1583 "Include \"x.h\" for symbol clang::clangd::X"))), 1584 AllOf(Diag(Test.range("x"), "no type named 'X' in namespace 'clang'"), 1585 diagName("typename_nested_not_found"), 1586 withFix(Fix(Test.range("insert"), "#include \"x.h\"\n", 1587 "Include \"x.h\" for symbol clang::clangd::X"))), 1588 AllOf( 1589 Diag(Test.range("q2"), "use of undeclared identifier 'clangd'; " 1590 "did you mean 'clang'?"), 1591 diagName("undeclared_var_use_suggest"), 1592 withFix(_, // change clangd to clang 1593 Fix(Test.range("insert"), "#include \"y.h\"\n", 1594 "Include \"y.h\" for symbol clang::clangd::ns::Y"))), 1595 AllOf(Diag(Test.range("ns"), 1596 "no member named 'ns' in namespace 'clang'"), 1597 diagName("no_member"), 1598 withFix( 1599 Fix(Test.range("insert"), "#include \"y.h\"\n", 1600 "Include \"y.h\" for symbol clang::clangd::ns::Y"))))); 1601 } 1602 1603 TEST(IncludeFixerTest, SpecifiedScopeIsNamespaceAlias) { 1604 Annotations Test(R"cpp(// error-ok 1605 $insert[[]]namespace a {} 1606 namespace b = a; 1607 namespace c { 1608 b::$[[X]] x; 1609 } 1610 )cpp"); 1611 auto TU = TestTU::withCode(Test.code()); 1612 auto Index = buildIndexWithSymbol( 1613 SymbolWithHeader{"a::X", "unittest:///x.h", "\"x.h\""}); 1614 TU.ExternalIndex = Index.get(); 1615 1616 EXPECT_THAT(TU.build().getDiagnostics(), 1617 UnorderedElementsAre(AllOf( 1618 Diag(Test.range(), "no type named 'X' in namespace 'a'"), 1619 diagName("typename_nested_not_found"), 1620 withFix(Fix(Test.range("insert"), "#include \"x.h\"\n", 1621 "Include \"x.h\" for symbol a::X"))))); 1622 } 1623 1624 TEST(IncludeFixerTest, NoCrashOnTemplateInstantiations) { 1625 Annotations Test(R"cpp( 1626 template <typename T> struct Templ { 1627 template <typename U> 1628 typename U::type operator=(const U &); 1629 }; 1630 1631 struct A { 1632 Templ<char> s; 1633 A() { [[a]]; /*error-ok*/ } // crash if we compute scopes lazily. 1634 }; 1635 )cpp"); 1636 1637 auto TU = TestTU::withCode(Test.code()); 1638 auto Index = buildIndexWithSymbol({}); 1639 TU.ExternalIndex = Index.get(); 1640 1641 EXPECT_THAT( 1642 TU.build().getDiagnostics(), 1643 ElementsAre(Diag(Test.range(), "use of undeclared identifier 'a'"))); 1644 } 1645 1646 TEST(IncludeFixerTest, HeaderNamedInDiag) { 1647 Annotations Test(R"cpp( 1648 $insert[[]]int main() { 1649 [[printf]](""); 1650 } 1651 )cpp"); 1652 auto TU = TestTU::withCode(Test.code()); 1653 TU.ExtraArgs = {"-xc", "-std=c99", 1654 "-Wno-error=implicit-function-declaration"}; 1655 auto Index = buildIndexWithSymbol({}); 1656 TU.ExternalIndex = Index.get(); 1657 1658 EXPECT_THAT( 1659 TU.build().getDiagnostics(), 1660 ElementsAre(AllOf( 1661 Diag(Test.range(), "call to undeclared library function 'printf' " 1662 "with type 'int (const char *, ...)'; ISO C99 " 1663 "and later do not support implicit function " 1664 "declarations"), 1665 withFix(Fix(Test.range("insert"), "#include <stdio.h>\n", 1666 "Include <stdio.h> for symbol printf"))))); 1667 1668 TU.ExtraArgs = {"-xc", "-std=c89"}; 1669 EXPECT_THAT( 1670 TU.build().getDiagnostics(), 1671 ElementsAre(AllOf( 1672 Diag(Test.range(), "implicitly declaring library function 'printf' " 1673 "with type 'int (const char *, ...)'"), 1674 withFix(Fix(Test.range("insert"), "#include <stdio.h>\n", 1675 "Include <stdio.h> for symbol printf"))))); 1676 } 1677 1678 TEST(IncludeFixerTest, CImplicitFunctionDecl) { 1679 Annotations Test("void x() { [[foo]](); }"); 1680 auto TU = TestTU::withCode(Test.code()); 1681 TU.Filename = "test.c"; 1682 TU.ExtraArgs = {"-std=c99", "-Wno-error=implicit-function-declaration"}; 1683 1684 Symbol Sym = func("foo"); 1685 Sym.Flags |= Symbol::IndexedForCodeCompletion; 1686 Sym.CanonicalDeclaration.FileURI = "unittest:///foo.h"; 1687 Sym.IncludeHeaders.emplace_back("\"foo.h\"", 1, Symbol::Include); 1688 1689 SymbolSlab::Builder Slab; 1690 Slab.insert(Sym); 1691 auto Index = 1692 MemIndex::build(std::move(Slab).build(), RefSlab(), RelationSlab()); 1693 TU.ExternalIndex = Index.get(); 1694 1695 EXPECT_THAT( 1696 TU.build().getDiagnostics(), 1697 ElementsAre(AllOf( 1698 Diag(Test.range(), 1699 "call to undeclared function 'foo'; ISO C99 and later do not " 1700 "support implicit function declarations"), 1701 withFix(Fix(Range{}, "#include \"foo.h\"\n", 1702 "Include \"foo.h\" for symbol foo"))))); 1703 1704 TU.ExtraArgs = {"-std=c89", "-Wall"}; 1705 EXPECT_THAT(TU.build().getDiagnostics(), 1706 ElementsAre(AllOf( 1707 Diag(Test.range(), "implicit declaration of function 'foo'"), 1708 withFix(Fix(Range{}, "#include \"foo.h\"\n", 1709 "Include \"foo.h\" for symbol foo"))))); 1710 } 1711 1712 TEST(DiagsInHeaders, DiagInsideHeader) { 1713 Annotations Main(R"cpp( 1714 #include [["a.h"]] 1715 void foo() {})cpp"); 1716 Annotations Header("[[no_type_spec]]; // error-ok"); 1717 TestTU TU = TestTU::withCode(Main.code()); 1718 TU.AdditionalFiles = {{"a.h", std::string(Header.code())}}; 1719 EXPECT_THAT(TU.build().getDiagnostics(), 1720 UnorderedElementsAre(AllOf( 1721 Diag(Main.range(), "in included file: a type specifier is " 1722 "required for all declarations"), 1723 withNote(Diag(Header.range(), "error occurred here"))))); 1724 } 1725 1726 TEST(DiagsInHeaders, DiagInTransitiveInclude) { 1727 Annotations Main(R"cpp( 1728 #include [["a.h"]] 1729 void foo() {})cpp"); 1730 TestTU TU = TestTU::withCode(Main.code()); 1731 TU.AdditionalFiles = {{"a.h", "#include \"b.h\""}, 1732 {"b.h", "no_type_spec; // error-ok"}}; 1733 EXPECT_THAT(TU.build().getDiagnostics(), 1734 UnorderedElementsAre(Diag(Main.range(), 1735 "in included file: a type specifier is " 1736 "required for all declarations"))); 1737 } 1738 1739 TEST(DiagsInHeaders, DiagInMultipleHeaders) { 1740 Annotations Main(R"cpp( 1741 #include $a[["a.h"]] 1742 #include $b[["b.h"]] 1743 void foo() {})cpp"); 1744 TestTU TU = TestTU::withCode(Main.code()); 1745 TU.AdditionalFiles = {{"a.h", "no_type_spec; // error-ok"}, 1746 {"b.h", "no_type_spec; // error-ok"}}; 1747 EXPECT_THAT(TU.build().getDiagnostics(), 1748 UnorderedElementsAre( 1749 Diag(Main.range("a"), "in included file: a type specifier is " 1750 "required for all declarations"), 1751 Diag(Main.range("b"), "in included file: a type specifier is " 1752 "required for all declarations"))); 1753 } 1754 1755 TEST(DiagsInHeaders, PreferExpansionLocation) { 1756 Annotations Main(R"cpp( 1757 #include [["a.h"]] 1758 #include "b.h" 1759 void foo() {})cpp"); 1760 TestTU TU = TestTU::withCode(Main.code()); 1761 TU.AdditionalFiles = { 1762 {"a.h", "#include \"b.h\"\n"}, 1763 {"b.h", "#ifndef X\n#define X\nno_type_spec; // error-ok\n#endif"}}; 1764 EXPECT_THAT(TU.build().getDiagnostics(), 1765 Contains(Diag(Main.range(), "in included file: a type specifier " 1766 "is required for all declarations"))); 1767 } 1768 1769 TEST(DiagsInHeaders, PreferExpansionLocationMacros) { 1770 Annotations Main(R"cpp( 1771 #define X 1772 #include "a.h" 1773 #undef X 1774 #include [["b.h"]] 1775 void foo() {})cpp"); 1776 TestTU TU = TestTU::withCode(Main.code()); 1777 TU.AdditionalFiles = { 1778 {"a.h", "#include \"c.h\"\n"}, 1779 {"b.h", "#include \"c.h\"\n"}, 1780 {"c.h", "#ifndef X\n#define X\nno_type_spec; // error-ok\n#endif"}}; 1781 EXPECT_THAT(TU.build().getDiagnostics(), 1782 UnorderedElementsAre(Diag(Main.range(), 1783 "in included file: a type specifier is " 1784 "required for all declarations"))); 1785 } 1786 1787 TEST(DiagsInHeaders, LimitDiagsOutsideMainFile) { 1788 Annotations Main(R"cpp( 1789 #include [["a.h"]] 1790 #include "b.h" 1791 void foo() {})cpp"); 1792 TestTU TU = TestTU::withCode(Main.code()); 1793 TU.AdditionalFiles = {{"a.h", "#include \"c.h\"\n"}, 1794 {"b.h", "#include \"c.h\"\n"}, 1795 {"c.h", R"cpp( 1796 #ifndef X 1797 #define X 1798 no_type_spec_0; // error-ok 1799 no_type_spec_1; 1800 no_type_spec_2; 1801 no_type_spec_3; 1802 no_type_spec_4; 1803 no_type_spec_5; 1804 no_type_spec_6; 1805 no_type_spec_7; 1806 no_type_spec_8; 1807 no_type_spec_9; 1808 no_type_spec_10; 1809 #endif)cpp"}}; 1810 EXPECT_THAT(TU.build().getDiagnostics(), 1811 UnorderedElementsAre(Diag(Main.range(), 1812 "in included file: a type specifier is " 1813 "required for all declarations"))); 1814 } 1815 1816 TEST(DiagsInHeaders, OnlyErrorOrFatal) { 1817 Annotations Main(R"cpp( 1818 #include [["a.h"]] 1819 void foo() {})cpp"); 1820 Annotations Header(R"cpp( 1821 [[no_type_spec]]; // error-ok 1822 int x = 5/0;)cpp"); 1823 TestTU TU = TestTU::withCode(Main.code()); 1824 TU.AdditionalFiles = {{"a.h", std::string(Header.code())}}; 1825 EXPECT_THAT(TU.build().getDiagnostics(), 1826 UnorderedElementsAre(AllOf( 1827 Diag(Main.range(), "in included file: a type specifier is " 1828 "required for all declarations"), 1829 withNote(Diag(Header.range(), "error occurred here"))))); 1830 } 1831 1832 TEST(DiagsInHeaders, OnlyDefaultErrorOrFatal) { 1833 Annotations Main(R"cpp( 1834 #include [["a.h"]] // get unused "foo" warning when building preamble. 1835 )cpp"); 1836 Annotations Header(R"cpp( 1837 namespace { void foo() {} } 1838 void func() {foo();} ;)cpp"); 1839 TestTU TU = TestTU::withCode(Main.code()); 1840 TU.AdditionalFiles = {{"a.h", std::string(Header.code())}}; 1841 // promote warnings to errors. 1842 TU.ExtraArgs = {"-Werror", "-Wunused"}; 1843 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty()); 1844 } 1845 1846 TEST(DiagsInHeaders, FromNonWrittenSources) { 1847 Annotations Main(R"cpp( 1848 #include [["a.h"]] 1849 void foo() {})cpp"); 1850 Annotations Header(R"cpp( 1851 int x = 5/0; 1852 int b = [[FOO]]; // error-ok)cpp"); 1853 TestTU TU = TestTU::withCode(Main.code()); 1854 TU.AdditionalFiles = {{"a.h", std::string(Header.code())}}; 1855 TU.ExtraArgs = {"-DFOO=NOOO"}; 1856 EXPECT_THAT(TU.build().getDiagnostics(), 1857 UnorderedElementsAre(AllOf( 1858 Diag(Main.range(), 1859 "in included file: use of undeclared identifier 'NOOO'"), 1860 withNote(Diag(Header.range(), "error occurred here"))))); 1861 } 1862 1863 TEST(DiagsInHeaders, ErrorFromMacroExpansion) { 1864 Annotations Main(R"cpp( 1865 void bar() { 1866 int fo; // error-ok 1867 #include [["a.h"]] 1868 })cpp"); 1869 Annotations Header(R"cpp( 1870 #define X foo 1871 X;)cpp"); 1872 TestTU TU = TestTU::withCode(Main.code()); 1873 TU.AdditionalFiles = {{"a.h", std::string(Header.code())}}; 1874 EXPECT_THAT(TU.build().getDiagnostics(), 1875 UnorderedElementsAre( 1876 Diag(Main.range(), "in included file: use of undeclared " 1877 "identifier 'foo'; did you mean 'fo'?"))); 1878 } 1879 1880 TEST(DiagsInHeaders, ErrorFromMacroArgument) { 1881 Annotations Main(R"cpp( 1882 void bar() { 1883 int fo; // error-ok 1884 #include [["a.h"]] 1885 })cpp"); 1886 Annotations Header(R"cpp( 1887 #define X(arg) arg 1888 X(foo);)cpp"); 1889 TestTU TU = TestTU::withCode(Main.code()); 1890 TU.AdditionalFiles = {{"a.h", std::string(Header.code())}}; 1891 EXPECT_THAT(TU.build().getDiagnostics(), 1892 UnorderedElementsAre( 1893 Diag(Main.range(), "in included file: use of undeclared " 1894 "identifier 'foo'; did you mean 'fo'?"))); 1895 } 1896 1897 TEST(IgnoreDiags, FromNonWrittenInclude) { 1898 TestTU TU; 1899 TU.ExtraArgs.push_back("--include=a.h"); 1900 TU.AdditionalFiles = {{"a.h", "void main();"}}; 1901 // The diagnostic "main must return int" is from the header, we don't attempt 1902 // to render it in the main file as there is no written location there. 1903 EXPECT_THAT(TU.build().getDiagnostics(), UnorderedElementsAre()); 1904 } 1905 1906 TEST(ToLSPDiag, RangeIsInMain) { 1907 ClangdDiagnosticOptions Opts; 1908 clangd::Diag D; 1909 D.Range = {pos(1, 2), pos(3, 4)}; 1910 D.Notes.emplace_back(); 1911 Note &N = D.Notes.back(); 1912 N.Range = {pos(2, 3), pos(3, 4)}; 1913 1914 D.InsideMainFile = true; 1915 N.InsideMainFile = false; 1916 toLSPDiags(D, {}, Opts, 1917 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix>) { 1918 EXPECT_EQ(LSPDiag.range, D.Range); 1919 }); 1920 1921 D.InsideMainFile = false; 1922 N.InsideMainFile = true; 1923 toLSPDiags(D, {}, Opts, 1924 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix>) { 1925 EXPECT_EQ(LSPDiag.range, N.Range); 1926 }); 1927 } 1928 1929 TEST(ParsedASTTest, ModuleSawDiag) { 1930 TestTU TU; 1931 1932 auto AST = TU.build(); 1933 #if 0 1934 EXPECT_THAT(AST.getDiagnostics(), 1935 testing::Contains(Diag(Code.range(), KDiagMsg.str()))); 1936 #endif 1937 } 1938 1939 TEST(Preamble, EndsOnNonEmptyLine) { 1940 TestTU TU; 1941 TU.ExtraArgs = {"-Wnewline-eof"}; 1942 1943 { 1944 TU.Code = "#define FOO\n void bar();\n"; 1945 auto AST = TU.build(); 1946 EXPECT_THAT(AST.getDiagnostics(), IsEmpty()); 1947 } 1948 { 1949 Annotations Code("#define FOO[[]]"); 1950 TU.Code = Code.code().str(); 1951 auto AST = TU.build(); 1952 EXPECT_THAT( 1953 AST.getDiagnostics(), 1954 testing::Contains(Diag(Code.range(), "no newline at end of file"))); 1955 } 1956 } 1957 1958 TEST(Diagnostics, Tags) { 1959 TestTU TU; 1960 TU.ExtraArgs = {"-Wunused", "-Wdeprecated"}; 1961 Annotations Test(R"cpp( 1962 void bar() __attribute__((deprecated)); 1963 void foo() { 1964 int $unused[[x]]; 1965 $deprecated[[bar]](); 1966 })cpp"); 1967 TU.Code = Test.code().str(); 1968 EXPECT_THAT(TU.build().getDiagnostics(), 1969 UnorderedElementsAre( 1970 AllOf(Diag(Test.range("unused"), "unused variable 'x'"), 1971 withTag(DiagnosticTag::Unnecessary)), 1972 AllOf(Diag(Test.range("deprecated"), "'bar' is deprecated"), 1973 withTag(DiagnosticTag::Deprecated)))); 1974 1975 Test = Annotations(R"cpp( 1976 $typedef[[typedef int INT]]; 1977 )cpp"); 1978 TU.Code = Test.code(); 1979 TU.ClangTidyProvider = addTidyChecks("modernize-use-using"); 1980 EXPECT_THAT( 1981 TU.build().getDiagnostics(), 1982 ifTidyChecks(UnorderedElementsAre( 1983 AllOf(Diag(Test.range("typedef"), "use 'using' instead of 'typedef'"), 1984 withTag(DiagnosticTag::Deprecated))))); 1985 } 1986 1987 TEST(Diagnostics, TidyDiagsArentAffectedFromWerror) { 1988 TestTU TU; 1989 TU.ExtraArgs = {"-Werror"}; 1990 Annotations Test(R"cpp($typedef[[typedef int INT]]; // error-ok)cpp"); 1991 TU.Code = Test.code().str(); 1992 TU.ClangTidyProvider = addTidyChecks("modernize-use-using"); 1993 EXPECT_THAT( 1994 TU.build().getDiagnostics(), 1995 ifTidyChecks(UnorderedElementsAre( 1996 AllOf(Diag(Test.range("typedef"), "use 'using' instead of 'typedef'"), 1997 // Make sure severity for clang-tidy finding isn't bumped to 1998 // error due to Werror in compile flags. 1999 diagSeverity(DiagnosticsEngine::Warning))))); 2000 2001 TU.ClangTidyProvider = 2002 addTidyChecks("modernize-use-using", /*WarningsAsErrors=*/"modernize-*"); 2003 EXPECT_THAT( 2004 TU.build().getDiagnostics(), 2005 ifTidyChecks(UnorderedElementsAre( 2006 AllOf(Diag(Test.range("typedef"), "use 'using' instead of 'typedef'"), 2007 // Unless bumped explicitly with WarnAsError. 2008 diagSeverity(DiagnosticsEngine::Error))))); 2009 } 2010 2011 TEST(Diagnostics, DeprecatedDiagsAreHints) { 2012 ClangdDiagnosticOptions Opts; 2013 std::optional<clangd::Diagnostic> Diag; 2014 clangd::Diag D; 2015 D.Range = {pos(1, 2), pos(3, 4)}; 2016 D.InsideMainFile = true; 2017 2018 // Downgrade warnings with deprecated tags to remark. 2019 D.Tags = {Deprecated}; 2020 D.Severity = DiagnosticsEngine::Warning; 2021 toLSPDiags(D, {}, Opts, 2022 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix>) { 2023 Diag = std::move(LSPDiag); 2024 }); 2025 EXPECT_EQ(Diag->severity, getSeverity(DiagnosticsEngine::Remark)); 2026 Diag.reset(); 2027 2028 // Preserve errors. 2029 D.Severity = DiagnosticsEngine::Error; 2030 toLSPDiags(D, {}, Opts, 2031 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix>) { 2032 Diag = std::move(LSPDiag); 2033 }); 2034 EXPECT_EQ(Diag->severity, getSeverity(DiagnosticsEngine::Error)); 2035 Diag.reset(); 2036 2037 // No-op without tag. 2038 D.Tags = {}; 2039 D.Severity = DiagnosticsEngine::Warning; 2040 toLSPDiags(D, {}, Opts, 2041 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix>) { 2042 Diag = std::move(LSPDiag); 2043 }); 2044 EXPECT_EQ(Diag->severity, getSeverity(DiagnosticsEngine::Warning)); 2045 } 2046 2047 TEST(DiagnosticsTest, IncludeCleaner) { 2048 Annotations Test(R"cpp( 2049 $fix[[ $diag[[#include "unused.h"]] 2050 ]] 2051 #include "used.h" 2052 2053 #include "ignore.h" 2054 2055 #include <system_header.h> 2056 2057 void foo() { 2058 used(); 2059 } 2060 )cpp"); 2061 TestTU TU; 2062 TU.Code = Test.code().str(); 2063 TU.AdditionalFiles["unused.h"] = R"cpp( 2064 #pragma once 2065 void unused() {} 2066 )cpp"; 2067 TU.AdditionalFiles["used.h"] = R"cpp( 2068 #pragma once 2069 void used() {} 2070 )cpp"; 2071 TU.AdditionalFiles["ignore.h"] = R"cpp( 2072 #pragma once 2073 void ignore() {} 2074 )cpp"; 2075 TU.AdditionalFiles["system/system_header.h"] = ""; 2076 TU.ExtraArgs = {"-isystem" + testPath("system")}; 2077 Config Cfg; 2078 Cfg.Diagnostics.UnusedIncludes = Config::IncludesPolicy::Strict; 2079 // Set filtering. 2080 Cfg.Diagnostics.Includes.IgnoreHeader.emplace_back( 2081 [](llvm::StringRef Header) { return Header.ends_with("ignore.h"); }); 2082 WithContextValue WithCfg(Config::Key, std::move(Cfg)); 2083 auto AST = TU.build(); 2084 EXPECT_THAT( 2085 AST.getDiagnostics(), 2086 Contains(AllOf( 2087 Diag(Test.range("diag"), 2088 "included header unused.h is not used directly"), 2089 withTag(DiagnosticTag::Unnecessary), diagSource(Diag::Clangd), 2090 withFix(Fix(Test.range("fix"), "", "remove #include directive"))))); 2091 auto &Diag = AST.getDiagnostics().front(); 2092 EXPECT_THAT(getDiagnosticDocURI(Diag.Source, Diag.ID, Diag.Name), 2093 llvm::ValueIs(Not(IsEmpty()))); 2094 Cfg.Diagnostics.SuppressAll = true; 2095 WithContextValue SuppressAllWithCfg(Config::Key, std::move(Cfg)); 2096 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty()); 2097 Cfg.Diagnostics.SuppressAll = false; 2098 Cfg.Diagnostics.Suppress = {"unused-includes"}; 2099 WithContextValue SuppressFilterWithCfg(Config::Key, std::move(Cfg)); 2100 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty()); 2101 } 2102 2103 TEST(DiagnosticsTest, FixItFromHeader) { 2104 llvm::StringLiteral Header(R"cpp( 2105 void foo(int *); 2106 void foo(int *, int);)cpp"); 2107 Annotations Source(R"cpp( 2108 /*error-ok*/ 2109 void bar() { 2110 int x; 2111 $diag[[foo]]($fix[[]]x, 1); 2112 })cpp"); 2113 TestTU TU; 2114 TU.Code = Source.code().str(); 2115 TU.HeaderCode = Header.str(); 2116 EXPECT_THAT( 2117 TU.build().getDiagnostics(), 2118 UnorderedElementsAre(AllOf( 2119 Diag(Source.range("diag"), "no matching function for call to 'foo'"), 2120 withFix(Fix(Source.range("fix"), "&", 2121 "candidate function not viable: no known conversion from " 2122 "'int' to 'int *' for 1st argument; take the address of " 2123 "the argument with &"))))); 2124 } 2125 2126 TEST(DiagnosticsTest, UnusedInHeader) { 2127 // Clang diagnoses unused static inline functions outside headers. 2128 auto TU = TestTU::withCode("static inline void foo(void) {}"); 2129 TU.ExtraArgs.push_back("-Wunused-function"); 2130 TU.Filename = "test.c"; 2131 EXPECT_THAT(TU.build().getDiagnostics(), 2132 ElementsAre(withID(diag::warn_unused_function))); 2133 // Sema should recognize a *.h file open in clangd as a header. 2134 // https://github.com/clangd/vscode-clangd/issues/360 2135 TU.Filename = "test.h"; 2136 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty()); 2137 } 2138 2139 } // namespace 2140 } // namespace clangd 2141 } // namespace clang 2142