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