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