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