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