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