1 //===--- DiagnosticsTests.cpp ------------------------------------*- C++-*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "Annotations.h"
10 #include "Config.h"
11 #include "Diagnostics.h"
12 #include "ParsedAST.h"
13 #include "Protocol.h"
14 #include "SourceCode.h"
15 #include "TestFS.h"
16 #include "TestIndex.h"
17 #include "TestTU.h"
18 #include "TidyProvider.h"
19 #include "index/MemIndex.h"
20 #include "support/Context.h"
21 #include "support/Path.h"
22 #include "clang/Basic/Diagnostic.h"
23 #include "clang/Basic/DiagnosticSema.h"
24 #include "llvm/Support/ScopedPrinter.h"
25 #include "llvm/Support/TargetSelect.h"
26 #include "gmock/gmock.h"
27 #include "gtest/gtest.h"
28 #include <algorithm>
29
30 namespace clang {
31 namespace clangd {
32 namespace {
33
34 using ::testing::_;
35 using ::testing::AllOf;
36 using ::testing::Contains;
37 using ::testing::ElementsAre;
38 using ::testing::Field;
39 using ::testing::IsEmpty;
40 using ::testing::Pair;
41 using testing::SizeIs;
42 using ::testing::UnorderedElementsAre;
43
WithFix(::testing::Matcher<Fix> FixMatcher)44 ::testing::Matcher<const Diag &> WithFix(::testing::Matcher<Fix> FixMatcher) {
45 return Field(&Diag::Fixes, ElementsAre(FixMatcher));
46 }
47
WithFix(::testing::Matcher<Fix> FixMatcher1,::testing::Matcher<Fix> FixMatcher2)48 ::testing::Matcher<const Diag &> WithFix(::testing::Matcher<Fix> FixMatcher1,
49 ::testing::Matcher<Fix> FixMatcher2) {
50 return Field(&Diag::Fixes, UnorderedElementsAre(FixMatcher1, FixMatcher2));
51 }
52
53 ::testing::Matcher<const Diag &>
WithNote(::testing::Matcher<Note> NoteMatcher)54 WithNote(::testing::Matcher<Note> NoteMatcher) {
55 return Field(&Diag::Notes, ElementsAre(NoteMatcher));
56 }
57
58 ::testing::Matcher<const Diag &>
WithNote(::testing::Matcher<Note> NoteMatcher1,::testing::Matcher<Note> NoteMatcher2)59 WithNote(::testing::Matcher<Note> NoteMatcher1,
60 ::testing::Matcher<Note> NoteMatcher2) {
61 return Field(&Diag::Notes, UnorderedElementsAre(NoteMatcher1, NoteMatcher2));
62 }
63
64 MATCHER_P2(Diag, Range, Message,
65 "Diag at " + llvm::to_string(Range) + " = [" + Message + "]") {
66 return arg.Range == Range && arg.Message == Message;
67 }
68
69 MATCHER_P3(Fix, Range, Replacement, Message,
70 "Fix " + llvm::to_string(Range) + " => " +
71 ::testing::PrintToString(Replacement) + " = [" + Message + "]") {
72 return arg.Message == Message && arg.Edits.size() == 1 &&
73 arg.Edits[0].range == Range && arg.Edits[0].newText == Replacement;
74 }
75
76 MATCHER_P(FixMessage, Message, "") { return arg.Message == Message; }
77
78 MATCHER_P(EqualToLSPDiag, LSPDiag,
79 "LSP diagnostic " + llvm::to_string(LSPDiag)) {
80 if (toJSON(arg) != toJSON(LSPDiag)) {
81 *result_listener << llvm::formatv("expected:\n{0:2}\ngot\n{1:2}",
82 toJSON(LSPDiag), toJSON(arg))
83 .str();
84 return false;
85 }
86 return true;
87 }
88
89 MATCHER_P(DiagSource, S, "") { return arg.Source == S; }
90 MATCHER_P(DiagName, N, "") { return arg.Name == N; }
91 MATCHER_P(DiagSeverity, S, "") { return arg.Severity == S; }
92
93 MATCHER_P(EqualToFix, Fix, "LSP fix " + llvm::to_string(Fix)) {
94 if (arg.Message != Fix.Message)
95 return false;
96 if (arg.Edits.size() != Fix.Edits.size())
97 return false;
98 for (std::size_t I = 0; I < arg.Edits.size(); ++I) {
99 if (arg.Edits[I].range != Fix.Edits[I].range ||
100 arg.Edits[I].newText != Fix.Edits[I].newText)
101 return false;
102 }
103 return true;
104 }
105
106 // Helper function to make tests shorter.
pos(int line,int character)107 Position pos(int line, int character) {
108 Position Res;
109 Res.line = line;
110 Res.character = character;
111 return Res;
112 }
113
TEST(DiagnosticsTest,DiagnosticRanges)114 TEST(DiagnosticsTest, DiagnosticRanges) {
115 // Check we report correct ranges, including various edge-cases.
116 Annotations Test(R"cpp(
117 // error-ok
118 #define ID(X) X
119 namespace test{};
120 void $decl[[foo]]();
121 class T{$explicit[[]]$constructor[[T]](int a);};
122 int main() {
123 $typo[[go\
124 o]]();
125 foo()$semicolon[[]]//with comments
126 $unk[[unknown]]();
127 double $type[[bar]] = "foo";
128 struct Foo { int x; }; Foo a;
129 a.$nomember[[y]];
130 test::$nomembernamespace[[test]];
131 $macro[[ID($macroarg[[fod]])]]();
132 }
133 )cpp");
134 auto TU = TestTU::withCode(Test.code());
135 TU.ClangTidyProvider = addTidyChecks("google-explicit-constructor");
136 EXPECT_THAT(
137 TU.build().getDiagnostics(),
138 ElementsAre(
139 // This range spans lines.
140 AllOf(Diag(Test.range("typo"),
141 "use of undeclared identifier 'goo'; did you mean 'foo'?"),
142 DiagSource(Diag::Clang), DiagName("undeclared_var_use_suggest"),
143 WithFix(
144 Fix(Test.range("typo"), "foo", "change 'go\\…' to 'foo'")),
145 // This is a pretty normal range.
146 WithNote(Diag(Test.range("decl"), "'foo' declared here"))),
147 // This range is zero-width and insertion. Therefore make sure we are
148 // not expanding it into other tokens. Since we are not going to
149 // replace those.
150 AllOf(Diag(Test.range("semicolon"), "expected ';' after expression"),
151 WithFix(Fix(Test.range("semicolon"), ";", "insert ';'"))),
152 // This range isn't provided by clang, we expand to the token.
153 Diag(Test.range("unk"), "use of undeclared identifier 'unknown'"),
154 Diag(Test.range("type"),
155 "cannot initialize a variable of type 'double' with an lvalue "
156 "of type 'const char [4]'"),
157 Diag(Test.range("nomember"), "no member named 'y' in 'Foo'"),
158 Diag(Test.range("nomembernamespace"),
159 "no member named 'test' in namespace 'test'"),
160 AllOf(Diag(Test.range("macro"),
161 "use of undeclared identifier 'fod'; did you mean 'foo'?"),
162 WithFix(Fix(Test.range("macroarg"), "foo",
163 "change 'fod' to 'foo'"))),
164 // We make sure here that the entire token is highlighted
165 AllOf(Diag(Test.range("constructor"),
166 "single-argument constructors must be marked explicit to "
167 "avoid unintentional implicit conversions"),
168 WithFix(Fix(Test.range("explicit"), "explicit ",
169 "insert 'explicit '")))));
170 }
171
TEST(DiagnosticsTest,FlagsMatter)172 TEST(DiagnosticsTest, FlagsMatter) {
173 Annotations Test("[[void]] main() {} // error-ok");
174 auto TU = TestTU::withCode(Test.code());
175 EXPECT_THAT(TU.build().getDiagnostics(),
176 ElementsAre(AllOf(Diag(Test.range(), "'main' must return 'int'"),
177 WithFix(Fix(Test.range(), "int",
178 "change 'void' to 'int'")))));
179 // Same code built as C gets different diagnostics.
180 TU.Filename = "Plain.c";
181 EXPECT_THAT(
182 TU.build().getDiagnostics(),
183 ElementsAre(AllOf(
184 Diag(Test.range(), "return type of 'main' is not 'int'"),
185 WithFix(Fix(Test.range(), "int", "change return type to 'int'")))));
186 }
187
TEST(DiagnosticsTest,DiagnosticPreamble)188 TEST(DiagnosticsTest, DiagnosticPreamble) {
189 Annotations Test(R"cpp(
190 #include $[["not-found.h"]] // error-ok
191 )cpp");
192
193 auto TU = TestTU::withCode(Test.code());
194 EXPECT_THAT(TU.build().getDiagnostics(),
195 ElementsAre(::testing::AllOf(
196 Diag(Test.range(), "'not-found.h' file not found"),
197 DiagSource(Diag::Clang), DiagName("pp_file_not_found"))));
198 }
199
TEST(DiagnosticsTest,DeduplicatedClangTidyDiagnostics)200 TEST(DiagnosticsTest, DeduplicatedClangTidyDiagnostics) {
201 Annotations Test(R"cpp(
202 float foo = [[0.1f]];
203 )cpp");
204 auto TU = TestTU::withCode(Test.code());
205 // Enable alias clang-tidy checks, these check emits the same diagnostics
206 // (except the check name).
207 TU.ClangTidyProvider = addTidyChecks("readability-uppercase-literal-suffix,"
208 "hicpp-uppercase-literal-suffix");
209 // Verify that we filter out the duplicated diagnostic message.
210 EXPECT_THAT(
211 TU.build().getDiagnostics(),
212 UnorderedElementsAre(::testing::AllOf(
213 Diag(Test.range(),
214 "floating point literal has suffix 'f', which is not uppercase"),
215 DiagSource(Diag::ClangTidy))));
216
217 Test = Annotations(R"cpp(
218 template<typename T>
219 void func(T) {
220 float f = [[0.3f]];
221 }
222 void k() {
223 func(123);
224 func(2.0);
225 }
226 )cpp");
227 TU.Code = std::string(Test.code());
228 // The check doesn't handle template instantiations which ends up emitting
229 // duplicated messages, verify that we deduplicate them.
230 EXPECT_THAT(
231 TU.build().getDiagnostics(),
232 UnorderedElementsAre(::testing::AllOf(
233 Diag(Test.range(),
234 "floating point literal has suffix 'f', which is not uppercase"),
235 DiagSource(Diag::ClangTidy))));
236 }
237
TEST(DiagnosticsTest,ClangTidy)238 TEST(DiagnosticsTest, ClangTidy) {
239 Annotations Test(R"cpp(
240 #include $deprecated[["assert.h"]]
241
242 #define $macrodef[[SQUARE]](X) (X)*(X)
243 int $main[[main]]() {
244 int y = 4;
245 return SQUARE($macroarg[[++]]y);
246 return $doubled[[sizeof]](sizeof(int));
247 }
248 )cpp");
249 auto TU = TestTU::withCode(Test.code());
250 TU.HeaderFilename = "assert.h"; // Suppress "not found" error.
251 TU.ClangTidyProvider = addTidyChecks("bugprone-sizeof-expression,"
252 "bugprone-macro-repeated-side-effects,"
253 "modernize-deprecated-headers,"
254 "modernize-use-trailing-return-type");
255 EXPECT_THAT(
256 TU.build().getDiagnostics(),
257 UnorderedElementsAre(
258 AllOf(Diag(Test.range("deprecated"),
259 "inclusion of deprecated C++ header 'assert.h'; consider "
260 "using 'cassert' instead"),
261 DiagSource(Diag::ClangTidy),
262 DiagName("modernize-deprecated-headers"),
263 WithFix(Fix(Test.range("deprecated"), "<cassert>",
264 "change '\"assert.h\"' to '<cassert>'"))),
265 Diag(Test.range("doubled"),
266 "suspicious usage of 'sizeof(sizeof(...))'"),
267 AllOf(
268 Diag(Test.range("macroarg"),
269 "side effects in the 1st macro argument 'X' are repeated in "
270 "macro expansion"),
271 DiagSource(Diag::ClangTidy),
272 DiagName("bugprone-macro-repeated-side-effects"),
273 WithNote(
274 Diag(Test.range("macrodef"), "macro 'SQUARE' defined here"))),
275 Diag(Test.range("macroarg"),
276 "multiple unsequenced modifications to 'y'"),
277 AllOf(
278 Diag(Test.range("main"),
279 "use a trailing return type for this function"),
280 DiagSource(Diag::ClangTidy),
281 DiagName("modernize-use-trailing-return-type"),
282 // Verify that we don't have "[check-name]" suffix in the message.
283 WithFix(FixMessage(
284 "use a trailing return type for this function")))));
285 }
286
TEST(DiagnosticsTest,ClangTidyEOF)287 TEST(DiagnosticsTest, ClangTidyEOF) {
288 // clang-format off
289 Annotations Test(R"cpp(
290 [[#]]include <b.h>
291 #include "a.h")cpp");
292 // clang-format on
293 auto TU = TestTU::withCode(Test.code());
294 TU.ExtraArgs = {"-isystem."};
295 TU.AdditionalFiles["a.h"] = TU.AdditionalFiles["b.h"] = "";
296 TU.ClangTidyProvider = addTidyChecks("llvm-include-order");
297 EXPECT_THAT(
298 TU.build().getDiagnostics(),
299 Contains(AllOf(Diag(Test.range(), "#includes are not sorted properly"),
300 DiagSource(Diag::ClangTidy),
301 DiagName("llvm-include-order"))));
302 }
303
TEST(DiagnosticTest,TemplatesInHeaders)304 TEST(DiagnosticTest, TemplatesInHeaders) {
305 // Diagnostics from templates defined in headers are placed at the expansion.
306 Annotations Main(R"cpp(
307 Derived<int> [[y]]; // error-ok
308 )cpp");
309 Annotations Header(R"cpp(
310 template <typename T>
311 struct Derived : [[T]] {};
312 )cpp");
313 TestTU TU = TestTU::withCode(Main.code());
314 TU.HeaderCode = Header.code().str();
315 EXPECT_THAT(
316 TU.build().getDiagnostics(),
317 ElementsAre(AllOf(
318 Diag(Main.range(), "in template: base specifier must name a class"),
319 WithNote(Diag(Header.range(), "error occurred here"),
320 Diag(Main.range(), "in instantiation of template class "
321 "'Derived<int>' requested here")))));
322 }
323
TEST(DiagnosticTest,MakeUnique)324 TEST(DiagnosticTest, MakeUnique) {
325 // We usually miss diagnostics from header functions as we don't parse them.
326 // std::make_unique is an exception.
327 Annotations Main(R"cpp(
328 struct S { S(char*); };
329 auto x = std::[[make_unique]]<S>(42); // error-ok
330 )cpp");
331 TestTU TU = TestTU::withCode(Main.code());
332 TU.HeaderCode = R"cpp(
333 namespace std {
334 // These mocks aren't quite right - we omit unique_ptr for simplicity.
335 // forward is included to show its body is not needed to get the diagnostic.
336 template <typename T> T&& forward(T& t) { return static_cast<T&&>(t); }
337 template <typename T, typename... A> T* make_unique(A&&... args) {
338 return new T(std::forward<A>(args)...);
339 }
340 }
341 )cpp";
342 EXPECT_THAT(TU.build().getDiagnostics(),
343 UnorderedElementsAre(
344 Diag(Main.range(),
345 "in template: "
346 "no matching constructor for initialization of 'S'")));
347 }
348
TEST(DiagnosticTest,NoMultipleDiagnosticInFlight)349 TEST(DiagnosticTest, NoMultipleDiagnosticInFlight) {
350 Annotations Main(R"cpp(
351 template <typename T> struct Foo {
352 T *begin();
353 T *end();
354 };
355 struct LabelInfo {
356 int a;
357 bool b;
358 };
359
360 void f() {
361 Foo<LabelInfo> label_info_map;
362 [[for]] (auto it = label_info_map.begin(); it != label_info_map.end(); ++it) {
363 auto S = *it;
364 }
365 }
366 )cpp");
367 TestTU TU = TestTU::withCode(Main.code());
368 TU.ClangTidyProvider = addTidyChecks("modernize-loop-convert");
369 EXPECT_THAT(
370 TU.build().getDiagnostics(),
371 UnorderedElementsAre(::testing::AllOf(
372 Diag(Main.range(), "use range-based for loop instead"),
373 DiagSource(Diag::ClangTidy), DiagName("modernize-loop-convert"))));
374 }
375
TEST(DiagnosticTest,RespectsDiagnosticConfig)376 TEST(DiagnosticTest, RespectsDiagnosticConfig) {
377 Annotations Main(R"cpp(
378 // error-ok
379 void x() {
380 [[unknown]]();
381 $ret[[return]] 42;
382 }
383 )cpp");
384 auto TU = TestTU::withCode(Main.code());
385 EXPECT_THAT(
386 TU.build().getDiagnostics(),
387 ElementsAre(Diag(Main.range(), "use of undeclared identifier 'unknown'"),
388 Diag(Main.range("ret"),
389 "void function 'x' should not return a value")));
390 Config Cfg;
391 Cfg.Diagnostics.Suppress.insert("return-type");
392 WithContextValue WithCfg(Config::Key, std::move(Cfg));
393 EXPECT_THAT(TU.build().getDiagnostics(),
394 ElementsAre(Diag(Main.range(),
395 "use of undeclared identifier 'unknown'")));
396 }
397
TEST(DiagnosticTest,ClangTidySuppressionComment)398 TEST(DiagnosticTest, ClangTidySuppressionComment) {
399 Annotations Main(R"cpp(
400 int main() {
401 int i = 3;
402 double d = 8 / i; // NOLINT
403 // NOLINTNEXTLINE
404 double e = 8 / i;
405 #define BAD 8 / i
406 double f = BAD; // NOLINT
407 double g = [[8]] / i;
408 #define BAD2 BAD
409 double h = BAD2; // NOLINT
410 }
411 )cpp");
412 TestTU TU = TestTU::withCode(Main.code());
413 TU.ClangTidyProvider = addTidyChecks("bugprone-integer-division");
414 EXPECT_THAT(
415 TU.build().getDiagnostics(),
416 UnorderedElementsAre(::testing::AllOf(
417 Diag(Main.range(), "result of integer division used in a floating "
418 "point context; possible loss of precision"),
419 DiagSource(Diag::ClangTidy), DiagName("bugprone-integer-division"))));
420 }
421
TEST(DiagnosticTest,ClangTidyWarningAsError)422 TEST(DiagnosticTest, ClangTidyWarningAsError) {
423 Annotations Main(R"cpp(
424 int main() {
425 int i = 3;
426 double f = [[8]] / i; // error-ok
427 }
428 )cpp");
429 TestTU TU = TestTU::withCode(Main.code());
430 TU.ClangTidyProvider =
431 addTidyChecks("bugprone-integer-division", "bugprone-integer-division");
432 EXPECT_THAT(
433 TU.build().getDiagnostics(),
434 UnorderedElementsAre(::testing::AllOf(
435 Diag(Main.range(), "result of integer division used in a floating "
436 "point context; possible loss of precision"),
437 DiagSource(Diag::ClangTidy), DiagName("bugprone-integer-division"),
438 DiagSeverity(DiagnosticsEngine::Error))));
439 }
440
TEST(DiagnosticTest,LongFixMessages)441 TEST(DiagnosticTest, LongFixMessages) {
442 // We limit the size of printed code.
443 Annotations Source(R"cpp(
444 int main() {
445 // error-ok
446 int somereallyreallyreallyreallyreallyreallyreallyreallylongidentifier;
447 [[omereallyreallyreallyreallyreallyreallyreallyreallylongidentifier]]= 10;
448 }
449 )cpp");
450 TestTU TU = TestTU::withCode(Source.code());
451 EXPECT_THAT(
452 TU.build().getDiagnostics(),
453 ElementsAre(WithFix(Fix(
454 Source.range(),
455 "somereallyreallyreallyreallyreallyreallyreallyreallylongidentifier",
456 "change 'omereallyreallyreallyreallyreallyreallyreallyreall…' to "
457 "'somereallyreallyreallyreallyreallyreallyreallyreal…'"))));
458 // Only show changes up to a first newline.
459 Source = Annotations(R"cpp(
460 // error-ok
461 int main() {
462 int ident;
463 [[ide\
464 n]] = 10; // error-ok
465 }
466 )cpp");
467 TU.Code = std::string(Source.code());
468 EXPECT_THAT(TU.build().getDiagnostics(),
469 ElementsAre(WithFix(
470 Fix(Source.range(), "ident", "change 'ide\\…' to 'ident'"))));
471 }
472
TEST(DiagnosticTest,ClangTidySuppressionCommentTrumpsWarningAsError)473 TEST(DiagnosticTest, ClangTidySuppressionCommentTrumpsWarningAsError) {
474 Annotations Main(R"cpp(
475 int main() {
476 int i = 3;
477 double f = [[8]] / i; // NOLINT
478 }
479 )cpp");
480 TestTU TU = TestTU::withCode(Main.code());
481 TU.ClangTidyProvider =
482 addTidyChecks("bugprone-integer-division", "bugprone-integer-division");
483 EXPECT_THAT(TU.build().getDiagnostics(), UnorderedElementsAre());
484 }
485
TEST(DiagnosticTest,ClangTidyNoLiteralDataInMacroToken)486 TEST(DiagnosticTest, ClangTidyNoLiteralDataInMacroToken) {
487 Annotations Main(R"cpp(
488 #define SIGTERM 15
489 using pthread_t = int;
490 int pthread_kill(pthread_t thread, int sig);
491 int func() {
492 pthread_t thread;
493 return pthread_kill(thread, 0);
494 }
495 )cpp");
496 TestTU TU = TestTU::withCode(Main.code());
497 TU.ClangTidyProvider = addTidyChecks("bugprone-bad-signal-to-kill-thread");
498 EXPECT_THAT(TU.build().getDiagnostics(), UnorderedElementsAre()); // no-crash
499 }
500
TEST(DiagnosticTest,ElseAfterReturnRange)501 TEST(DiagnosticTest, ElseAfterReturnRange) {
502 Annotations Main(R"cpp(
503 int foo(int cond) {
504 if (cond == 1) {
505 return 42;
506 } [[else]] if (cond == 2) {
507 return 43;
508 }
509 return 44;
510 }
511 )cpp");
512 TestTU TU = TestTU::withCode(Main.code());
513 TU.ClangTidyProvider = addTidyChecks("llvm-else-after-return");
514 EXPECT_THAT(
515 TU.build().getDiagnostics(),
516 ElementsAre(Diag(Main.range(), "do not use 'else' after 'return'")));
517 }
518
TEST(DiagnosticsTest,Preprocessor)519 TEST(DiagnosticsTest, Preprocessor) {
520 // This looks like a preamble, but there's an #else in the middle!
521 // Check that:
522 // - the #else doesn't generate diagnostics (we had this bug)
523 // - we get diagnostics from the taken branch
524 // - we get no diagnostics from the not taken branch
525 Annotations Test(R"cpp(
526 #ifndef FOO
527 #define FOO
528 int a = [[b]]; // error-ok
529 #else
530 int x = y;
531 #endif
532 )cpp");
533 EXPECT_THAT(
534 TestTU::withCode(Test.code()).build().getDiagnostics(),
535 ElementsAre(Diag(Test.range(), "use of undeclared identifier 'b'")));
536 }
537
TEST(DiagnosticsTest,IgnoreVerify)538 TEST(DiagnosticsTest, IgnoreVerify) {
539 auto TU = TestTU::withCode(R"cpp(
540 int a; // expected-error {{}}
541 )cpp");
542 TU.ExtraArgs.push_back("-Xclang");
543 TU.ExtraArgs.push_back("-verify");
544 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
545 }
546
547 // Recursive main-file include is diagnosed, and doesn't crash.
TEST(DiagnosticsTest,RecursivePreamble)548 TEST(DiagnosticsTest, RecursivePreamble) {
549 auto TU = TestTU::withCode(R"cpp(
550 #include "foo.h" // error-ok
551 int symbol;
552 )cpp");
553 TU.Filename = "foo.h";
554 EXPECT_THAT(TU.build().getDiagnostics(),
555 ElementsAre(DiagName("pp_including_mainfile_in_preamble")));
556 EXPECT_THAT(TU.build().getLocalTopLevelDecls(), SizeIs(1));
557 }
558
559 // Recursive main-file include with #pragma once guard is OK.
TEST(DiagnosticsTest,RecursivePreamblePragmaOnce)560 TEST(DiagnosticsTest, RecursivePreamblePragmaOnce) {
561 auto TU = TestTU::withCode(R"cpp(
562 #pragma once
563 #include "foo.h"
564 int symbol;
565 )cpp");
566 TU.Filename = "foo.h";
567 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
568 EXPECT_THAT(TU.build().getLocalTopLevelDecls(), SizeIs(1));
569 }
570
571 // Recursive main-file include with #ifndef guard should be OK.
572 // However, it's not yet recognized (incomplete at end of preamble).
TEST(DiagnosticsTest,RecursivePreambleIfndefGuard)573 TEST(DiagnosticsTest, RecursivePreambleIfndefGuard) {
574 auto TU = TestTU::withCode(R"cpp(
575 #ifndef FOO
576 #define FOO
577 #include "foo.h" // error-ok
578 int symbol;
579 #endif
580 )cpp");
581 TU.Filename = "foo.h";
582 // FIXME: should be no errors here.
583 EXPECT_THAT(TU.build().getDiagnostics(),
584 ElementsAre(DiagName("pp_including_mainfile_in_preamble")));
585 EXPECT_THAT(TU.build().getLocalTopLevelDecls(), SizeIs(1));
586 }
587
TEST(DiagnosticsTest,InsideMacros)588 TEST(DiagnosticsTest, InsideMacros) {
589 Annotations Test(R"cpp(
590 #define TEN 10
591 #define RET(x) return x + 10
592
593 int* foo() {
594 RET($foo[[0]]); // error-ok
595 }
596 int* bar() {
597 return $bar[[TEN]];
598 }
599 )cpp");
600 EXPECT_THAT(TestTU::withCode(Test.code()).build().getDiagnostics(),
601 ElementsAre(Diag(Test.range("foo"),
602 "cannot initialize return object of type "
603 "'int *' with an rvalue of type 'int'"),
604 Diag(Test.range("bar"),
605 "cannot initialize return object of type "
606 "'int *' with an rvalue of type 'int'")));
607 }
608
TEST(DiagnosticsTest,NoFixItInMacro)609 TEST(DiagnosticsTest, NoFixItInMacro) {
610 Annotations Test(R"cpp(
611 #define Define(name) void name() {}
612
613 [[Define]](main) // error-ok
614 )cpp");
615 auto TU = TestTU::withCode(Test.code());
616 EXPECT_THAT(TU.build().getDiagnostics(),
617 ElementsAre(AllOf(Diag(Test.range(), "'main' must return 'int'"),
618 Not(WithFix(_)))));
619 }
620
TEST(ClangdTest,MSAsm)621 TEST(ClangdTest, MSAsm) {
622 // Parsing MS assembly tries to use the target MCAsmInfo, which we don't link.
623 // We used to crash here. Now clang emits a diagnostic, which we filter out.
624 llvm::InitializeAllTargetInfos(); // As in ClangdMain
625 auto TU = TestTU::withCode("void fn() { __asm { cmp cl,64 } }");
626 TU.ExtraArgs = {"-fms-extensions"};
627 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
628 }
629
TEST(DiagnosticsTest,ToLSP)630 TEST(DiagnosticsTest, ToLSP) {
631 URIForFile MainFile =
632 URIForFile::canonicalize(testPath("foo/bar/main.cpp"), "");
633 URIForFile HeaderFile =
634 URIForFile::canonicalize(testPath("foo/bar/header.h"), "");
635
636 clangd::Diag D;
637 D.ID = clang::diag::err_undeclared_var_use;
638 D.Name = "undeclared_var_use";
639 D.Source = clangd::Diag::Clang;
640 D.Message = "something terrible happened";
641 D.Range = {pos(1, 2), pos(3, 4)};
642 D.InsideMainFile = true;
643 D.Severity = DiagnosticsEngine::Error;
644 D.File = "foo/bar/main.cpp";
645 D.AbsFile = std::string(MainFile.file());
646
647 clangd::Note NoteInMain;
648 NoteInMain.Message = "declared somewhere in the main file";
649 NoteInMain.Range = {pos(5, 6), pos(7, 8)};
650 NoteInMain.Severity = DiagnosticsEngine::Remark;
651 NoteInMain.File = "../foo/bar/main.cpp";
652 NoteInMain.InsideMainFile = true;
653 NoteInMain.AbsFile = std::string(MainFile.file());
654
655 D.Notes.push_back(NoteInMain);
656
657 clangd::Note NoteInHeader;
658 NoteInHeader.Message = "declared somewhere in the header file";
659 NoteInHeader.Range = {pos(9, 10), pos(11, 12)};
660 NoteInHeader.Severity = DiagnosticsEngine::Note;
661 NoteInHeader.File = "../foo/baz/header.h";
662 NoteInHeader.InsideMainFile = false;
663 NoteInHeader.AbsFile = std::string(HeaderFile.file());
664 D.Notes.push_back(NoteInHeader);
665
666 clangd::Fix F;
667 F.Message = "do something";
668 D.Fixes.push_back(F);
669
670 // Diagnostics should turn into these:
671 clangd::Diagnostic MainLSP;
672 MainLSP.range = D.Range;
673 MainLSP.severity = getSeverity(DiagnosticsEngine::Error);
674 MainLSP.code = "undeclared_var_use";
675 MainLSP.source = "clang";
676 MainLSP.message =
677 R"(Something terrible happened (fix available)
678
679 main.cpp:6:7: remark: declared somewhere in the main file
680
681 ../foo/baz/header.h:10:11:
682 note: declared somewhere in the header file)";
683
684 clangd::Diagnostic NoteInMainLSP;
685 NoteInMainLSP.range = NoteInMain.Range;
686 NoteInMainLSP.severity = getSeverity(DiagnosticsEngine::Remark);
687 NoteInMainLSP.message = R"(Declared somewhere in the main file
688
689 main.cpp:2:3: error: something terrible happened)";
690
691 ClangdDiagnosticOptions Opts;
692 // Transform diagnostics and check the results.
693 std::vector<std::pair<clangd::Diagnostic, std::vector<clangd::Fix>>> LSPDiags;
694 toLSPDiags(D, MainFile, Opts,
695 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix> Fixes) {
696 LSPDiags.push_back(
697 {std::move(LSPDiag),
698 std::vector<clangd::Fix>(Fixes.begin(), Fixes.end())});
699 });
700
701 EXPECT_THAT(
702 LSPDiags,
703 ElementsAre(Pair(EqualToLSPDiag(MainLSP), ElementsAre(EqualToFix(F))),
704 Pair(EqualToLSPDiag(NoteInMainLSP), IsEmpty())));
705 EXPECT_EQ(LSPDiags[0].first.code, "undeclared_var_use");
706 EXPECT_EQ(LSPDiags[0].first.source, "clang");
707 EXPECT_EQ(LSPDiags[1].first.code, "");
708 EXPECT_EQ(LSPDiags[1].first.source, "");
709
710 // Same thing, but don't flatten notes into the main list.
711 LSPDiags.clear();
712 Opts.EmitRelatedLocations = true;
713 toLSPDiags(D, MainFile, Opts,
714 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix> Fixes) {
715 LSPDiags.push_back(
716 {std::move(LSPDiag),
717 std::vector<clangd::Fix>(Fixes.begin(), Fixes.end())});
718 });
719 MainLSP.message = "Something terrible happened (fix available)";
720 DiagnosticRelatedInformation NoteInMainDRI;
721 NoteInMainDRI.message = "Declared somewhere in the main file";
722 NoteInMainDRI.location.range = NoteInMain.Range;
723 NoteInMainDRI.location.uri = MainFile;
724 MainLSP.relatedInformation = {NoteInMainDRI};
725 DiagnosticRelatedInformation NoteInHeaderDRI;
726 NoteInHeaderDRI.message = "Declared somewhere in the header file";
727 NoteInHeaderDRI.location.range = NoteInHeader.Range;
728 NoteInHeaderDRI.location.uri = HeaderFile;
729 MainLSP.relatedInformation = {NoteInMainDRI, NoteInHeaderDRI};
730 EXPECT_THAT(LSPDiags, ElementsAre(Pair(EqualToLSPDiag(MainLSP),
731 ElementsAre(EqualToFix(F)))));
732 }
733
734 struct SymbolWithHeader {
735 std::string QName;
736 std::string DeclaringFile;
737 std::string IncludeHeader;
738 };
739
740 std::unique_ptr<SymbolIndex>
buildIndexWithSymbol(llvm::ArrayRef<SymbolWithHeader> Syms)741 buildIndexWithSymbol(llvm::ArrayRef<SymbolWithHeader> Syms) {
742 SymbolSlab::Builder Slab;
743 for (const auto &S : Syms) {
744 Symbol Sym = cls(S.QName);
745 Sym.Flags |= Symbol::IndexedForCodeCompletion;
746 Sym.CanonicalDeclaration.FileURI = S.DeclaringFile.c_str();
747 Sym.Definition.FileURI = S.DeclaringFile.c_str();
748 Sym.IncludeHeaders.emplace_back(S.IncludeHeader, 1);
749 Slab.insert(Sym);
750 }
751 return MemIndex::build(std::move(Slab).build(), RefSlab(), RelationSlab());
752 }
753
TEST(IncludeFixerTest,IncompleteType)754 TEST(IncludeFixerTest, IncompleteType) {
755 Annotations Test(R"cpp(// error-ok
756 $insert[[]]namespace ns {
757 class X;
758 $nested[[X::]]Nested n;
759 }
760 class Y : $base[[public ns::X]] {};
761 void test(ns::X *x, ns::X& ref_x) {
762 x$access[[->]]f();
763 auto& $type[[[]]a] = *x;
764
765 ns::X $incomplete[[var]];
766 $tag[[ref_x]]->f();
767 $use[[ns::X()]];
768 $sizeof[[sizeof]](ns::X);
769 for (auto it : $for[[ref_x]]);
770 }
771
772 ns::X $return[[func]]() {}
773 )cpp");
774 auto TU = TestTU::withCode(Test.code());
775 TU.ExtraArgs.push_back("-std=c++17");
776 auto Index = buildIndexWithSymbol(
777 {SymbolWithHeader{"ns::X", "unittest:///x.h", "\"x.h\""}});
778 TU.ExternalIndex = Index.get();
779
780 EXPECT_THAT(
781 TU.build().getDiagnostics(),
782 UnorderedElementsAre(
783 AllOf(Diag(Test.range("nested"),
784 "incomplete type 'ns::X' named in nested name specifier"),
785 DiagName("incomplete_nested_name_spec"),
786 WithFix(Fix(Test.range("insert"), "#include \"x.h\"\n",
787 "Add include \"x.h\" for symbol ns::X"))),
788 AllOf(Diag(Test.range("base"), "base class has incomplete type"),
789 DiagName("incomplete_base_class"),
790 WithFix(Fix(Test.range("insert"), "#include \"x.h\"\n",
791 "Add include \"x.h\" for symbol ns::X"))),
792 AllOf(Diag(Test.range("access"),
793 "member access into incomplete type 'ns::X'"),
794 DiagName("incomplete_member_access"),
795 WithFix(Fix(Test.range("insert"), "#include \"x.h\"\n",
796 "Add include \"x.h\" for symbol ns::X"))),
797 AllOf(
798 Diag(Test.range("type"),
799 "incomplete type 'ns::X' where a complete type is required"),
800 DiagName("incomplete_type"),
801 WithFix(Fix(Test.range("insert"), "#include \"x.h\"\n",
802 "Add include \"x.h\" for symbol ns::X"))),
803 AllOf(Diag(Test.range("incomplete"),
804 "variable has incomplete type 'ns::X'"),
805 DiagName("typecheck_decl_incomplete_type"),
806 WithFix(Fix(Test.range("insert"), "#include \"x.h\"\n",
807 "Add include \"x.h\" for symbol ns::X"))),
808 AllOf(
809 Diag(Test.range("tag"), "incomplete definition of type 'ns::X'"),
810 DiagName("typecheck_incomplete_tag"),
811 WithFix(Fix(Test.range("insert"), "#include \"x.h\"\n",
812 "Add include \"x.h\" for symbol ns::X"))),
813 AllOf(
814 Diag(Test.range("use"), "invalid use of incomplete type 'ns::X'"),
815 DiagName("invalid_incomplete_type_use"),
816 WithFix(Fix(Test.range("insert"), "#include \"x.h\"\n",
817 "Add include \"x.h\" for symbol ns::X"))),
818 AllOf(Diag(Test.range("sizeof"), "invalid application of 'sizeof' to "
819 "an incomplete type 'ns::X'"),
820 DiagName("sizeof_alignof_incomplete_or_sizeless_type"),
821 WithFix(Fix(Test.range("insert"), "#include \"x.h\"\n",
822 "Add include \"x.h\" for symbol ns::X"))),
823 AllOf(Diag(Test.range("for"),
824 "cannot use incomplete type 'ns::X' as a range"),
825 DiagName("for_range_incomplete_type"),
826 WithFix(Fix(Test.range("insert"), "#include \"x.h\"\n",
827 "Add include \"x.h\" for symbol ns::X"))),
828 AllOf(Diag(Test.range("return"),
829 "incomplete result type 'ns::X' in function definition"),
830 DiagName("func_def_incomplete_result"),
831 WithFix(Fix(Test.range("insert"), "#include \"x.h\"\n",
832 "Add include \"x.h\" for symbol ns::X")))));
833 }
834
TEST(IncludeFixerTest,NoSuggestIncludeWhenNoDefinitionInHeader)835 TEST(IncludeFixerTest, NoSuggestIncludeWhenNoDefinitionInHeader) {
836 Annotations Test(R"cpp(// error-ok
837 $insert[[]]namespace ns {
838 class X;
839 }
840 class Y : $base[[public ns::X]] {};
841 int main() {
842 ns::X *x;
843 x$access[[->]]f();
844 }
845 )cpp");
846 auto TU = TestTU::withCode(Test.code());
847 Symbol Sym = cls("ns::X");
848 Sym.Flags |= Symbol::IndexedForCodeCompletion;
849 Sym.CanonicalDeclaration.FileURI = "unittest:///x.h";
850 Sym.Definition.FileURI = "unittest:///x.cc";
851 Sym.IncludeHeaders.emplace_back("\"x.h\"", 1);
852
853 SymbolSlab::Builder Slab;
854 Slab.insert(Sym);
855 auto Index =
856 MemIndex::build(std::move(Slab).build(), RefSlab(), RelationSlab());
857 TU.ExternalIndex = Index.get();
858
859 EXPECT_THAT(TU.build().getDiagnostics(),
860 UnorderedElementsAre(
861 Diag(Test.range("base"), "base class has incomplete type"),
862 Diag(Test.range("access"),
863 "member access into incomplete type 'ns::X'")));
864 }
865
TEST(IncludeFixerTest,Typo)866 TEST(IncludeFixerTest, Typo) {
867 Annotations Test(R"cpp(// error-ok
868 $insert[[]]namespace ns {
869 void foo() {
870 $unqualified1[[X]] x;
871 // No fix if the unresolved type is used as specifier. (ns::)X::Nested will be
872 // considered the unresolved type.
873 $unqualified2[[X]]::Nested n;
874 }
875 }
876 void bar() {
877 ns::$qualified1[[X]] x; // ns:: is valid.
878 ns::$qualified2[[X]](); // Error: no member in namespace
879
880 ::$global[[Global]] glob;
881 }
882 using Type = ns::$template[[Foo]]<int>;
883 )cpp");
884 auto TU = TestTU::withCode(Test.code());
885 auto Index = buildIndexWithSymbol(
886 {SymbolWithHeader{"ns::X", "unittest:///x.h", "\"x.h\""},
887 SymbolWithHeader{"Global", "unittest:///global.h", "\"global.h\""},
888 SymbolWithHeader{"ns::Foo", "unittest:///foo.h", "\"foo.h\""}});
889 TU.ExternalIndex = Index.get();
890
891 EXPECT_THAT(
892 TU.build().getDiagnostics(),
893 UnorderedElementsAre(
894 AllOf(Diag(Test.range("unqualified1"), "unknown type name 'X'"),
895 DiagName("unknown_typename"),
896 WithFix(Fix(Test.range("insert"), "#include \"x.h\"\n",
897 "Add include \"x.h\" for symbol ns::X"))),
898 Diag(Test.range("unqualified2"), "use of undeclared identifier 'X'"),
899 AllOf(Diag(Test.range("qualified1"),
900 "no type named 'X' in namespace 'ns'"),
901 DiagName("typename_nested_not_found"),
902 WithFix(Fix(Test.range("insert"), "#include \"x.h\"\n",
903 "Add include \"x.h\" for symbol ns::X"))),
904 AllOf(Diag(Test.range("qualified2"),
905 "no member named 'X' in namespace 'ns'"),
906 DiagName("no_member"),
907 WithFix(Fix(Test.range("insert"), "#include \"x.h\"\n",
908 "Add include \"x.h\" for symbol ns::X"))),
909 AllOf(Diag(Test.range("global"),
910 "no type named 'Global' in the global namespace"),
911 DiagName("typename_nested_not_found"),
912 WithFix(Fix(Test.range("insert"), "#include \"global.h\"\n",
913 "Add include \"global.h\" for symbol Global"))),
914 AllOf(Diag(Test.range("template"),
915 "no template named 'Foo' in namespace 'ns'"),
916 DiagName("no_member_template"),
917 WithFix(Fix(Test.range("insert"), "#include \"foo.h\"\n",
918 "Add include \"foo.h\" for symbol ns::Foo")))));
919 }
920
TEST(IncludeFixerTest,MultipleMatchedSymbols)921 TEST(IncludeFixerTest, MultipleMatchedSymbols) {
922 Annotations Test(R"cpp(// error-ok
923 $insert[[]]namespace na {
924 namespace nb {
925 void foo() {
926 $unqualified[[X]] x;
927 }
928 }
929 }
930 )cpp");
931 auto TU = TestTU::withCode(Test.code());
932 auto Index = buildIndexWithSymbol(
933 {SymbolWithHeader{"na::X", "unittest:///a.h", "\"a.h\""},
934 SymbolWithHeader{"na::nb::X", "unittest:///b.h", "\"b.h\""}});
935 TU.ExternalIndex = Index.get();
936
937 EXPECT_THAT(TU.build().getDiagnostics(),
938 UnorderedElementsAre(AllOf(
939 Diag(Test.range("unqualified"), "unknown type name 'X'"),
940 DiagName("unknown_typename"),
941 WithFix(Fix(Test.range("insert"), "#include \"a.h\"\n",
942 "Add include \"a.h\" for symbol na::X"),
943 Fix(Test.range("insert"), "#include \"b.h\"\n",
944 "Add include \"b.h\" for symbol na::nb::X")))));
945 }
946
TEST(IncludeFixerTest,NoCrashMemebrAccess)947 TEST(IncludeFixerTest, NoCrashMemebrAccess) {
948 Annotations Test(R"cpp(// error-ok
949 struct X { int xyz; };
950 void g() { X x; x.$[[xy]]; }
951 )cpp");
952 auto TU = TestTU::withCode(Test.code());
953 auto Index = buildIndexWithSymbol(
954 SymbolWithHeader{"na::X", "unittest:///a.h", "\"a.h\""});
955 TU.ExternalIndex = Index.get();
956
957 EXPECT_THAT(
958 TU.build().getDiagnostics(),
959 UnorderedElementsAre(Diag(Test.range(), "no member named 'xy' in 'X'")));
960 }
961
TEST(IncludeFixerTest,UseCachedIndexResults)962 TEST(IncludeFixerTest, UseCachedIndexResults) {
963 // As index results for the identical request are cached, more than 5 fixes
964 // are generated.
965 Annotations Test(R"cpp(// error-ok
966 $insert[[]]void foo() {
967 $x1[[X]] x;
968 $x2[[X]] x;
969 $x3[[X]] x;
970 $x4[[X]] x;
971 $x5[[X]] x;
972 $x6[[X]] x;
973 $x7[[X]] x;
974 }
975
976 class X;
977 void bar(X *x) {
978 x$a1[[->]]f();
979 x$a2[[->]]f();
980 x$a3[[->]]f();
981 x$a4[[->]]f();
982 x$a5[[->]]f();
983 x$a6[[->]]f();
984 x$a7[[->]]f();
985 }
986 )cpp");
987 auto TU = TestTU::withCode(Test.code());
988 auto Index =
989 buildIndexWithSymbol(SymbolWithHeader{"X", "unittest:///a.h", "\"a.h\""});
990 TU.ExternalIndex = Index.get();
991
992 auto Parsed = TU.build();
993 for (const auto &D : Parsed.getDiagnostics()) {
994 if (D.Fixes.size() != 1) {
995 ADD_FAILURE() << "D.Fixes.size() != 1";
996 continue;
997 }
998 EXPECT_EQ(D.Fixes[0].Message,
999 std::string("Add include \"a.h\" for symbol X"));
1000 }
1001 }
1002
TEST(IncludeFixerTest,UnresolvedNameAsSpecifier)1003 TEST(IncludeFixerTest, UnresolvedNameAsSpecifier) {
1004 Annotations Test(R"cpp(// error-ok
1005 $insert[[]]namespace ns {
1006 }
1007 void g() { ns::$[[scope]]::X_Y(); }
1008 )cpp");
1009 TestTU TU;
1010 TU.Code = std::string(Test.code());
1011 // FIXME: Figure out why this is needed and remove it, PR43662.
1012 TU.ExtraArgs.push_back("-fno-ms-compatibility");
1013 auto Index = buildIndexWithSymbol(
1014 SymbolWithHeader{"ns::scope::X_Y", "unittest:///x.h", "\"x.h\""});
1015 TU.ExternalIndex = Index.get();
1016
1017 EXPECT_THAT(
1018 TU.build().getDiagnostics(),
1019 UnorderedElementsAre(AllOf(
1020 Diag(Test.range(), "no member named 'scope' in namespace 'ns'"),
1021 DiagName("no_member"),
1022 WithFix(Fix(Test.range("insert"), "#include \"x.h\"\n",
1023 "Add include \"x.h\" for symbol ns::scope::X_Y")))));
1024 }
1025
TEST(IncludeFixerTest,UnresolvedSpecifierWithSemaCorrection)1026 TEST(IncludeFixerTest, UnresolvedSpecifierWithSemaCorrection) {
1027 Annotations Test(R"cpp(// error-ok
1028 $insert[[]]namespace clang {
1029 void f() {
1030 // "clangd::" will be corrected to "clang::" by Sema.
1031 $q1[[clangd]]::$x[[X]] x;
1032 $q2[[clangd]]::$ns[[ns]]::Y y;
1033 }
1034 }
1035 )cpp");
1036 TestTU TU;
1037 TU.Code = std::string(Test.code());
1038 // FIXME: Figure out why this is needed and remove it, PR43662.
1039 TU.ExtraArgs.push_back("-fno-ms-compatibility");
1040 auto Index = buildIndexWithSymbol(
1041 {SymbolWithHeader{"clang::clangd::X", "unittest:///x.h", "\"x.h\""},
1042 SymbolWithHeader{"clang::clangd::ns::Y", "unittest:///y.h", "\"y.h\""}});
1043 TU.ExternalIndex = Index.get();
1044
1045 EXPECT_THAT(
1046 TU.build().getDiagnostics(),
1047 UnorderedElementsAre(
1048 AllOf(
1049 Diag(Test.range("q1"), "use of undeclared identifier 'clangd'; "
1050 "did you mean 'clang'?"),
1051 DiagName("undeclared_var_use_suggest"),
1052 WithFix(_, // change clangd to clang
1053 Fix(Test.range("insert"), "#include \"x.h\"\n",
1054 "Add include \"x.h\" for symbol clang::clangd::X"))),
1055 AllOf(
1056 Diag(Test.range("x"), "no type named 'X' in namespace 'clang'"),
1057 DiagName("typename_nested_not_found"),
1058 WithFix(Fix(Test.range("insert"), "#include \"x.h\"\n",
1059 "Add include \"x.h\" for symbol clang::clangd::X"))),
1060 AllOf(
1061 Diag(Test.range("q2"), "use of undeclared identifier 'clangd'; "
1062 "did you mean 'clang'?"),
1063 DiagName("undeclared_var_use_suggest"),
1064 WithFix(
1065 _, // change clangd to clang
1066 Fix(Test.range("insert"), "#include \"y.h\"\n",
1067 "Add include \"y.h\" for symbol clang::clangd::ns::Y"))),
1068 AllOf(Diag(Test.range("ns"),
1069 "no member named 'ns' in namespace 'clang'"),
1070 DiagName("no_member"),
1071 WithFix(Fix(
1072 Test.range("insert"), "#include \"y.h\"\n",
1073 "Add include \"y.h\" for symbol clang::clangd::ns::Y")))));
1074 }
1075
TEST(IncludeFixerTest,SpecifiedScopeIsNamespaceAlias)1076 TEST(IncludeFixerTest, SpecifiedScopeIsNamespaceAlias) {
1077 Annotations Test(R"cpp(// error-ok
1078 $insert[[]]namespace a {}
1079 namespace b = a;
1080 namespace c {
1081 b::$[[X]] x;
1082 }
1083 )cpp");
1084 auto TU = TestTU::withCode(Test.code());
1085 auto Index = buildIndexWithSymbol(
1086 SymbolWithHeader{"a::X", "unittest:///x.h", "\"x.h\""});
1087 TU.ExternalIndex = Index.get();
1088
1089 EXPECT_THAT(TU.build().getDiagnostics(),
1090 UnorderedElementsAre(AllOf(
1091 Diag(Test.range(), "no type named 'X' in namespace 'a'"),
1092 DiagName("typename_nested_not_found"),
1093 WithFix(Fix(Test.range("insert"), "#include \"x.h\"\n",
1094 "Add include \"x.h\" for symbol a::X")))));
1095 }
1096
TEST(IncludeFixerTest,NoCrashOnTemplateInstantiations)1097 TEST(IncludeFixerTest, NoCrashOnTemplateInstantiations) {
1098 Annotations Test(R"cpp(
1099 template <typename T> struct Templ {
1100 template <typename U>
1101 typename U::type operator=(const U &);
1102 };
1103
1104 struct A {
1105 Templ<char> s;
1106 A() { [[a]]; /*error-ok*/ } // crash if we compute scopes lazily.
1107 };
1108 )cpp");
1109
1110 auto TU = TestTU::withCode(Test.code());
1111 auto Index = buildIndexWithSymbol({});
1112 TU.ExternalIndex = Index.get();
1113
1114 EXPECT_THAT(
1115 TU.build().getDiagnostics(),
1116 ElementsAre(Diag(Test.range(), "use of undeclared identifier 'a'")));
1117 }
1118
TEST(DiagsInHeaders,DiagInsideHeader)1119 TEST(DiagsInHeaders, DiagInsideHeader) {
1120 Annotations Main(R"cpp(
1121 #include [["a.h"]]
1122 void foo() {})cpp");
1123 Annotations Header("[[no_type_spec]]; // error-ok");
1124 TestTU TU = TestTU::withCode(Main.code());
1125 TU.AdditionalFiles = {{"a.h", std::string(Header.code())}};
1126 EXPECT_THAT(TU.build().getDiagnostics(),
1127 UnorderedElementsAre(AllOf(
1128 Diag(Main.range(), "in included file: C++ requires a "
1129 "type specifier for all declarations"),
1130 WithNote(Diag(Header.range(), "error occurred here")))));
1131 }
1132
TEST(DiagsInHeaders,DiagInTransitiveInclude)1133 TEST(DiagsInHeaders, DiagInTransitiveInclude) {
1134 Annotations Main(R"cpp(
1135 #include [["a.h"]]
1136 void foo() {})cpp");
1137 TestTU TU = TestTU::withCode(Main.code());
1138 TU.AdditionalFiles = {{"a.h", "#include \"b.h\""},
1139 {"b.h", "no_type_spec; // error-ok"}};
1140 EXPECT_THAT(TU.build().getDiagnostics(),
1141 UnorderedElementsAre(
1142 Diag(Main.range(), "in included file: C++ requires a "
1143 "type specifier for all declarations")));
1144 }
1145
TEST(DiagsInHeaders,DiagInMultipleHeaders)1146 TEST(DiagsInHeaders, DiagInMultipleHeaders) {
1147 Annotations Main(R"cpp(
1148 #include $a[["a.h"]]
1149 #include $b[["b.h"]]
1150 void foo() {})cpp");
1151 TestTU TU = TestTU::withCode(Main.code());
1152 TU.AdditionalFiles = {{"a.h", "no_type_spec; // error-ok"},
1153 {"b.h", "no_type_spec; // error-ok"}};
1154 EXPECT_THAT(TU.build().getDiagnostics(),
1155 UnorderedElementsAre(
1156 Diag(Main.range("a"), "in included file: C++ requires a type "
1157 "specifier for all declarations"),
1158 Diag(Main.range("b"), "in included file: C++ requires a type "
1159 "specifier for all declarations")));
1160 }
1161
TEST(DiagsInHeaders,PreferExpansionLocation)1162 TEST(DiagsInHeaders, PreferExpansionLocation) {
1163 Annotations Main(R"cpp(
1164 #include [["a.h"]]
1165 #include "b.h"
1166 void foo() {})cpp");
1167 TestTU TU = TestTU::withCode(Main.code());
1168 TU.AdditionalFiles = {
1169 {"a.h", "#include \"b.h\"\n"},
1170 {"b.h", "#ifndef X\n#define X\nno_type_spec; // error-ok\n#endif"}};
1171 EXPECT_THAT(TU.build().getDiagnostics(),
1172 UnorderedElementsAre(Diag(Main.range(),
1173 "in included file: C++ requires a type "
1174 "specifier for all declarations")));
1175 }
1176
TEST(DiagsInHeaders,PreferExpansionLocationMacros)1177 TEST(DiagsInHeaders, PreferExpansionLocationMacros) {
1178 Annotations Main(R"cpp(
1179 #define X
1180 #include "a.h"
1181 #undef X
1182 #include [["b.h"]]
1183 void foo() {})cpp");
1184 TestTU TU = TestTU::withCode(Main.code());
1185 TU.AdditionalFiles = {
1186 {"a.h", "#include \"c.h\"\n"},
1187 {"b.h", "#include \"c.h\"\n"},
1188 {"c.h", "#ifndef X\n#define X\nno_type_spec; // error-ok\n#endif"}};
1189 EXPECT_THAT(TU.build().getDiagnostics(),
1190 UnorderedElementsAre(
1191 Diag(Main.range(), "in included file: C++ requires a "
1192 "type specifier for all declarations")));
1193 }
1194
TEST(DiagsInHeaders,LimitDiagsOutsideMainFile)1195 TEST(DiagsInHeaders, LimitDiagsOutsideMainFile) {
1196 Annotations Main(R"cpp(
1197 #include [["a.h"]]
1198 #include "b.h"
1199 void foo() {})cpp");
1200 TestTU TU = TestTU::withCode(Main.code());
1201 TU.AdditionalFiles = {{"a.h", "#include \"c.h\"\n"},
1202 {"b.h", "#include \"c.h\"\n"},
1203 {"c.h", R"cpp(
1204 #ifndef X
1205 #define X
1206 no_type_spec_0; // error-ok
1207 no_type_spec_1;
1208 no_type_spec_2;
1209 no_type_spec_3;
1210 no_type_spec_4;
1211 no_type_spec_5;
1212 no_type_spec_6;
1213 no_type_spec_7;
1214 no_type_spec_8;
1215 no_type_spec_9;
1216 no_type_spec_10;
1217 #endif)cpp"}};
1218 EXPECT_THAT(TU.build().getDiagnostics(),
1219 UnorderedElementsAre(
1220 Diag(Main.range(), "in included file: C++ requires a "
1221 "type specifier for all declarations")));
1222 }
1223
TEST(DiagsInHeaders,OnlyErrorOrFatal)1224 TEST(DiagsInHeaders, OnlyErrorOrFatal) {
1225 Annotations Main(R"cpp(
1226 #include [["a.h"]]
1227 void foo() {})cpp");
1228 Annotations Header(R"cpp(
1229 [[no_type_spec]]; // error-ok
1230 int x = 5/0;)cpp");
1231 TestTU TU = TestTU::withCode(Main.code());
1232 TU.AdditionalFiles = {{"a.h", std::string(Header.code())}};
1233 EXPECT_THAT(TU.build().getDiagnostics(),
1234 UnorderedElementsAre(AllOf(
1235 Diag(Main.range(), "in included file: C++ requires "
1236 "a type specifier for all declarations"),
1237 WithNote(Diag(Header.range(), "error occurred here")))));
1238 }
1239
TEST(DiagsInHeaders,OnlyDefaultErrorOrFatal)1240 TEST(DiagsInHeaders, OnlyDefaultErrorOrFatal) {
1241 Annotations Main(R"cpp(
1242 #include [["a.h"]] // get unused "foo" warning when building preamble.
1243 )cpp");
1244 Annotations Header(R"cpp(
1245 namespace { void foo() {} }
1246 void func() {foo();} ;)cpp");
1247 TestTU TU = TestTU::withCode(Main.code());
1248 TU.AdditionalFiles = {{"a.h", std::string(Header.code())}};
1249 // promote warnings to errors.
1250 TU.ExtraArgs = {"-Werror", "-Wunused"};
1251 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
1252 }
1253
TEST(DiagsInHeaders,FromNonWrittenSources)1254 TEST(DiagsInHeaders, FromNonWrittenSources) {
1255 Annotations Main(R"cpp(
1256 #include [["a.h"]]
1257 void foo() {})cpp");
1258 Annotations Header(R"cpp(
1259 int x = 5/0;
1260 int b = [[FOO]]; // error-ok)cpp");
1261 TestTU TU = TestTU::withCode(Main.code());
1262 TU.AdditionalFiles = {{"a.h", std::string(Header.code())}};
1263 TU.ExtraArgs = {"-DFOO=NOOO"};
1264 EXPECT_THAT(TU.build().getDiagnostics(),
1265 UnorderedElementsAre(AllOf(
1266 Diag(Main.range(),
1267 "in included file: use of undeclared identifier 'NOOO'"),
1268 WithNote(Diag(Header.range(), "error occurred here")))));
1269 }
1270
TEST(DiagsInHeaders,ErrorFromMacroExpansion)1271 TEST(DiagsInHeaders, ErrorFromMacroExpansion) {
1272 Annotations Main(R"cpp(
1273 void bar() {
1274 int fo; // error-ok
1275 #include [["a.h"]]
1276 })cpp");
1277 Annotations Header(R"cpp(
1278 #define X foo
1279 X;)cpp");
1280 TestTU TU = TestTU::withCode(Main.code());
1281 TU.AdditionalFiles = {{"a.h", std::string(Header.code())}};
1282 EXPECT_THAT(TU.build().getDiagnostics(),
1283 UnorderedElementsAre(
1284 Diag(Main.range(), "in included file: use of undeclared "
1285 "identifier 'foo'; did you mean 'fo'?")));
1286 }
1287
TEST(DiagsInHeaders,ErrorFromMacroArgument)1288 TEST(DiagsInHeaders, ErrorFromMacroArgument) {
1289 Annotations Main(R"cpp(
1290 void bar() {
1291 int fo; // error-ok
1292 #include [["a.h"]]
1293 })cpp");
1294 Annotations Header(R"cpp(
1295 #define X(arg) arg
1296 X(foo);)cpp");
1297 TestTU TU = TestTU::withCode(Main.code());
1298 TU.AdditionalFiles = {{"a.h", std::string(Header.code())}};
1299 EXPECT_THAT(TU.build().getDiagnostics(),
1300 UnorderedElementsAre(
1301 Diag(Main.range(), "in included file: use of undeclared "
1302 "identifier 'foo'; did you mean 'fo'?")));
1303 }
1304
TEST(IgnoreDiags,FromNonWrittenInclude)1305 TEST(IgnoreDiags, FromNonWrittenInclude) {
1306 TestTU TU;
1307 TU.ExtraArgs.push_back("--include=a.h");
1308 TU.AdditionalFiles = {{"a.h", "void main();"}};
1309 // The diagnostic "main must return int" is from the header, we don't attempt
1310 // to render it in the main file as there is no written location there.
1311 EXPECT_THAT(TU.build().getDiagnostics(), UnorderedElementsAre());
1312 }
1313
TEST(ToLSPDiag,RangeIsInMain)1314 TEST(ToLSPDiag, RangeIsInMain) {
1315 ClangdDiagnosticOptions Opts;
1316 clangd::Diag D;
1317 D.Range = {pos(1, 2), pos(3, 4)};
1318 D.Notes.emplace_back();
1319 Note &N = D.Notes.back();
1320 N.Range = {pos(2, 3), pos(3, 4)};
1321
1322 D.InsideMainFile = true;
1323 N.InsideMainFile = false;
1324 toLSPDiags(D, {}, Opts,
1325 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix>) {
1326 EXPECT_EQ(LSPDiag.range, D.Range);
1327 });
1328
1329 D.InsideMainFile = false;
1330 N.InsideMainFile = true;
1331 toLSPDiags(D, {}, Opts,
1332 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix>) {
1333 EXPECT_EQ(LSPDiag.range, N.Range);
1334 });
1335 }
1336
1337 } // namespace
1338 } // namespace clangd
1339 } // namespace clang
1340