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