[TargetVersion] Only enable on RISC-V and AArch64 (#115991)
[llvm-project.git] / clang-tools-extra / clangd / unittests / DiagnosticsTests.cpp
blob7a47d6ebebf3b253a6690954a0203fbceb59ce37
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 //===----------------------------------------------------------------------===//
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>
47 namespace clang {
48 namespace clangd {
49 namespace {
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;
63 ::testing::Matcher<const Diag &> withFix(::testing::Matcher<Fix> FixMatcher) {
64 return Field(&Diag::Fixes, ElementsAre(FixMatcher));
67 ::testing::Matcher<const Diag &> withFix(::testing::Matcher<Fix> FixMatcher1,
68 ::testing::Matcher<Fix> FixMatcher2) {
69 return Field(&Diag::Fixes, UnorderedElementsAre(FixMatcher1, FixMatcher2));
72 ::testing::Matcher<const Diag &> withID(unsigned ID) {
73 return Field(&Diag::ID, ID);
75 ::testing::Matcher<const Diag &>
76 withNote(::testing::Matcher<Note> NoteMatcher) {
77 return Field(&Diag::Notes, ElementsAre(NoteMatcher));
80 ::testing::Matcher<const Diag &>
81 withNote(::testing::Matcher<Note> NoteMatcher1,
82 ::testing::Matcher<Note> NoteMatcher2) {
83 return Field(&Diag::Notes, UnorderedElementsAre(NoteMatcher1, NoteMatcher2));
86 ::testing::Matcher<const Diag &>
87 withTag(::testing::Matcher<DiagnosticTag> TagMatcher) {
88 return Field(&Diag::Tags, Contains(TagMatcher));
91 MATCHER_P(hasRange, Range, "") { return arg.Range == Range; }
93 MATCHER_P2(Diag, Range, Message,
94 "Diag at " + llvm::to_string(Range) + " = [" + Message + "]") {
95 return arg.Range == Range && arg.Message == Message;
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;
105 MATCHER_P(fixMessage, Message, "") { return arg.Message == Message; }
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;
115 return true;
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; }
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;
132 return true;
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;
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;
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]]) {
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]])]]();
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'")))));
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]]) {}
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")));
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'")))));
250 TEST(DiagnosticsTest, DiagnosticPreamble) {
251 Annotations Test(R"cpp(
252 #include $[["not-found.h"]] // error-ok
253 )cpp");
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"))));
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)))));
279 Test = Annotations(R"cpp(
280 template<typename T>
281 void func(T) {
282 float f = [[0.3f]];
284 void k() {
285 func(123);
286 func(2.0);
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)))));
300 TEST(DiagnosticsTest, ClangTidy) {
301 Annotations Test(R"cpp(
302 #include $deprecated[["assert.h"]]
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))]];
311 // misc-no-recursion uses a custom traversal from the TUDecl
312 void foo();
313 void $bar[[bar]]() {
314 foo();
316 void $foo[[foo]]() {
317 bar();
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"))));
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")))));
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")))));
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)...);
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'")));
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; };
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;
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;
443 struct awaitable {
444 bool await_ready() noexcept { return false; }
445 void await_suspend(coroutine_handle<>) noexcept {}
446 void await_resume() noexcept {}
448 } // namespace std
449 )cpp";
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 {};
458 std::awaitable initial_suspend();
459 std::awaitable final_suspend() noexcept;
460 void unhandled_exception();
461 void return_value(T t);
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())));
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)...);
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'")));
505 TEST(DiagnosticTest, NoMultipleDiagnosticInFlight) {
506 Annotations Main(R"cpp(
507 template <typename T> struct Foo {
508 T *begin();
509 T *end();
511 struct LabelInfo {
512 int a;
513 bool b;
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;
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")))));
532 TEST(DiagnosticTest, RespectsDiagnosticConfig) {
533 Annotations Main(R"cpp(
534 // error-ok
535 void x() {
536 [[unknown]]();
537 $ret[[return]] 42;
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'")));
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());
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
586 // verify no crashes on unmatched nolints.
587 // NOLINTBEGIN
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")))));
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");
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";
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";
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))));
632 TEST(DiagnosticTest, ClangTidyWarningAsError) {
633 Annotations Main(R"cpp(
634 int main() {
635 int i = 3;
636 double f = [[8]] / i; // error-ok
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)))));
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;
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));
675 // Check the -Wunused warning isn't initially on.
676 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
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));
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());
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());
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'!";
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)));
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)));
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)));
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());
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));
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());
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());
752 TU.ExtraArgs = {"-Wno-unused"};
753 TU.ClangTidyProvider = addClangArgs({"-Wunused"}, {"-*, clang-diagnostic-*"});
754 EXPECT_THAT(TU.build().getDiagnostics(), SizeIs(1));
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;
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
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'"))));
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'")))));
798 TEST(DiagnosticTest, ClangTidySuppressionCommentTrumpsWarningAsError) {
799 Annotations Main(R"cpp(
800 int main() {
801 int i = 3;
802 double f = [[8]] / i; // NOLINT
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());
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);
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
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;
834 return 44;
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'"))));
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;]]
853 void InitVariables() {
854 float $C[[C]]$CFix[[]];
855 double $D[[D]]$DFix[[]];
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"), ""});
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"), ""});
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"});
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))))));
905 TEST(DiagnosticTest, ClangTidySelfContainedDiagsFormatting) {
906 Annotations Main(R"cpp(
907 class Interface {
908 public:
909 virtual void Reset1() = 0;
910 virtual void Reset2() = 0;
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[[]];
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))))));
947 TEST(DiagnosticsTest, ClangTidyCallingIntoPreprocessor) {
948 std::string Main = R"cpp(
949 extern "C" {
950 #include "b.h"
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();
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'")));
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());
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();
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));
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));
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));
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);
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());
1080 TEST(DiagnosticsTest, InsideMacros) {
1081 Annotations Test(R"cpp(
1082 #define TEN 10
1083 #define RET(x) return x + 10
1085 int* foo() {
1086 RET($foo[[0]]); // error-ok
1088 int* bar() {
1089 return $bar[[TEN]];
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'")));
1101 TEST(DiagnosticsTest, NoFixItInMacro) {
1102 Annotations Test(R"cpp(
1103 #define Define(name) void name() {}
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(_)))));
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());
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());
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"), "");
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";
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());
1160 D.Notes.push_back(NoteInMain);
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);
1171 clangd::Fix F;
1172 F.Message = "do something";
1173 D.Fixes.push_back(F);
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)
1184 main.cpp:6:7: remark: declared somewhere in the main file
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;
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
1196 main.cpp:2:3: error: something terrible happened)";
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())});
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, "");
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())});
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)))));
1241 struct SymbolWithHeader {
1242 std::string QName;
1243 std::string DeclaringFile;
1244 std::string IncludeHeader;
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);
1258 return MemIndex::build(std::move(Slab).build(), RefSlab(), RelationSlab());
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();
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"},
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;
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());
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
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;"},
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;
1340 TEST(IncludeFixerTest, NoSuggestIncludeWhenNoDefinitionInHeader) {
1341 Annotations Test(R"cpp(// error-ok
1342 $insert[[]]namespace ns {
1343 class X;
1345 class Y : $base[[public ns::X]] {};
1346 int main() {
1347 ns::X *x;
1348 x$access[[->]]f();
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);
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();
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'")));
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;
1380 struct S : $base[[X]] {};
1382 void bar() {
1383 ns::$qualified1[[X]] x; // ns:: is valid.
1384 ns::$qualified2[[X]](); // Error: no member in namespace
1386 ::$global[[Global]] glob;
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();
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")))));
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(_)));
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;
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();
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")))));
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();
1488 EXPECT_THAT(
1489 TU.build().getDiagnostics(),
1490 UnorderedElementsAre(Diag(Test.range(), "no member named 'xy' in 'X'")));
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;
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();
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();
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;
1529 EXPECT_EQ(D.Fixes[0].Message, std::string("Include \"a.h\" for symbol X"));
1533 TEST(IncludeFixerTest, UnresolvedNameAsSpecifier) {
1534 Annotations Test(R"cpp(// error-ok
1535 $insert[[]]namespace ns {
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();
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")))));
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;
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();
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")))));
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;
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();
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")))));
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 &);
1631 struct A {
1632 Templ<char> s;
1633 A() { [[a]]; /*error-ok*/ } // crash if we compute scopes lazily.
1635 )cpp");
1637 auto TU = TestTU::withCode(Test.code());
1638 auto Index = buildIndexWithSymbol({});
1639 TU.ExternalIndex = Index.get();
1641 EXPECT_THAT(
1642 TU.build().getDiagnostics(),
1643 ElementsAre(Diag(Test.range(), "use of undeclared identifier 'a'")));
1646 TEST(IncludeFixerTest, HeaderNamedInDiag) {
1647 Annotations Test(R"cpp(
1648 $insert[[]]int main() {
1649 [[printf]]("");
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();
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")))));
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")))));
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"};
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);
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();
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")))));
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")))));
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")))));
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")));
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")));
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")));
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")));
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")));
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")))));
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());
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")))));
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'?")));
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'?")));
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());
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)};
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);
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);
1929 TEST(ParsedASTTest, ModuleSawDiag) {
1930 TestTU TU;
1932 auto AST = TU.build();
1933 #if 0
1934 EXPECT_THAT(AST.getDiagnostics(),
1935 testing::Contains(Diag(Code.range(), KDiagMsg.str())));
1936 #endif
1939 TEST(Preamble, EndsOnNonEmptyLine) {
1940 TestTU TU;
1941 TU.ExtraArgs = {"-Wnewline-eof"};
1944 TU.Code = "#define FOO\n void bar();\n";
1945 auto AST = TU.build();
1946 EXPECT_THAT(AST.getDiagnostics(), IsEmpty());
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")));
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))));
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)))));
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)))));
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)))));
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;
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);
2025 EXPECT_EQ(Diag->severity, getSeverity(DiagnosticsEngine::Remark));
2026 Diag.reset();
2028 // Preserve errors.
2029 D.Severity = DiagnosticsEngine::Error;
2030 toLSPDiags(D, {}, Opts,
2031 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix>) {
2032 Diag = std::move(LSPDiag);
2034 EXPECT_EQ(Diag->severity, getSeverity(DiagnosticsEngine::Error));
2035 Diag.reset();
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);
2044 EXPECT_EQ(Diag->severity, getSeverity(DiagnosticsEngine::Warning));
2047 TEST(DiagnosticsTest, IncludeCleaner) {
2048 Annotations Test(R"cpp(
2049 $fix[[ $diag[[#include "unused.h"]]
2051 #include "used.h"
2053 #include "ignore.h"
2055 #include <system_header.h>
2057 void foo() {
2058 used();
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());
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 &")))));
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());
2139 } // namespace
2140 } // namespace clangd
2141 } // namespace clang