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