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" 31 using ::testing::ElementsAre;
32 using ::testing::Field;
33 using ::testing::IsEmpty;
34 using ::testing::Pair;
35 using ::testing::UnorderedElementsAre;
37 ::testing::Matcher<const Diag &> WithFix(::testing::Matcher<Fix> FixMatcher) {
41 ::testing::Matcher<const Diag &> WithFix(::testing::Matcher<Fix> FixMatcher1,
42 ::testing::Matcher<Fix> FixMatcher2) {
46 ::testing::Matcher<const Diag &>
47 WithNote(::testing::Matcher<Note> NoteMatcher) {
52 "Diag at " + llvm::to_string(
Range) +
" = [" +
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;
68 "LSP diagnostic " + llvm::to_string(LSPDiag)) {
70 *result_listener << llvm::formatv(
"expected:\n{0:2}\ngot\n{1:2}",
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; }
83 if (arg.Message !=
Fix.Message)
85 if (arg.Edits.size() !=
Fix.Edits.size())
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)
96 Position pos(
int line,
int character) {
99 Res.character = character;
103 TEST(DiagnosticsTest, DiagnosticRanges) {
105 Annotations Test(R
"cpp( 108 class T{$explicit[[]]$constructor[[T]](int a);}; 112 foo()$semicolon[[]]//with comments 114 double $type[[bar]] = "foo"; 115 struct Foo { int x; }; Foo a; 117 test::$nomembernamespace[[test]]; 123 TU.build().getDiagnostics(),
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"),
130 Fix(Test.range(
"typo"),
"foo",
"change 'go\\…' to 'foo'")),
132 WithNote(Diag(Test.range(
"decl"),
"'foo' declared here"))),
136 AllOf(Diag(Test.range(
"semicolon"),
"expected ';' after expression"),
137 WithFix(
Fix(Test.range(
"semicolon"),
";",
"insert ';'"))),
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'"),
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 '")))));
154 TEST(DiagnosticsTest, FlagsMatter) {
155 Annotations Test(
"[[void]] main() {}");
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'")))));
164 TU.build().getDiagnostics(),
166 Diag(Test.range(),
"return type of 'main' is not 'int'"),
167 WithFix(
Fix(Test.range(),
"int",
"change return type to 'int'")))));
170 TEST(DiagnosticsTest, DiagnosticPreamble) {
171 Annotations Test(R
"cpp( 172 #include $[["not-found.h"]] 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"))));
182 TEST(DiagnosticsTest, DeduplicatedClangTidyDiagnostics) {
183 Annotations Test(R
"cpp( 184 float foo = [[0.1f]]; 190 "hicpp-uppercase-literal-suffix";
193 TU.build().getDiagnostics(),
194 UnorderedElementsAre(::testing::AllOf(
196 "floating point literal has suffix 'f', which is not uppercase"),
199 Test = Annotations(R
"cpp( 209 TU.Code = Test.code(); 213 TU.build().getDiagnostics(),
214 UnorderedElementsAre(::testing::AllOf(
216 "floating point literal has suffix 'f', which is not uppercase"),
220 TEST(DiagnosticsTest, ClangTidy) {
221 Annotations Test(R
"cpp( 222 #include $deprecated[["assert.h"]] 224 #define $macrodef[[SQUARE]](X) (X)*(X) 225 int $main[[main]]() { 227 return SQUARE($macroarg[[++]]y); 228 return $doubled[[sizeof]](sizeof(int)); 234 "-*, bugprone-sizeof-expression, bugprone-macro-repeated-side-effects, " 235 "modernize-deprecated-headers, modernize-use-trailing-return-type";
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"),
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(...))'"),
249 Diag(Test.range(
"macroarg"),
250 "side effects in the 1st macro argument 'X' are repeated in " 253 DiagName(
"bugprone-macro-repeated-side-effects"),
255 Diag(Test.range(
"macrodef"),
"macro 'SQUARE' defined here"))),
256 Diag(Test.range(
"macroarg"),
257 "multiple unsequenced modifications to 'y'"),
259 Diag(Test.range(
"main"),
260 "use a trailing return type for this function"),
262 DiagName(
"modernize-use-trailing-return-type"),
264 WithFix(FixMessage(
"use a trailing return type for this function")))
268 TEST(DiagnosticTest, ClangTidySuppressionComment) {
269 Annotations Main(R
"cpp( 272 double d = 8 / i; // NOLINT 275 double f = [[8]] / i; 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"),
288 TEST(DiagnosticTest, ClangTidyWarningAsError) {
289 Annotations Main(R
"cpp( 292 double f = [[8]] / i; 297 TU.ClangTidyWarningsAsErrors =
"bugprone-integer-division";
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"),
307 TEST(DiagnosticTest, LongFixMessages) {
309 Annotations Source(R
"cpp( 311 int somereallyreallyreallyreallyreallyreallyreallyreallylongidentifier; 312 [[omereallyreallyreallyreallyreallyreallyreallyreallylongidentifier]]= 10; 317 TU.build().getDiagnostics(), 318 ElementsAre(WithFix(Fix( 320 "somereallyreallyreallyreallyreallyreallyreallyreallylongidentifier",
321 "change 'omereallyreallyreallyreallyreallyreallyreallyreall…' to " 322 "'somereallyreallyreallyreallyreallyreallyreallyreal…'"))));
324 Source = Annotations(R
"cpp( 332 EXPECT_THAT(TU.build().getDiagnostics(), 334 Fix(Source.range(), "ident",
"change 'ide\\…' to 'ident'"))));
337 TEST(DiagnosticTest, ClangTidyWarningAsErrorTrumpsSuppressionComment) {
338 Annotations Main(R
"cpp( 341 double f = [[8]] / i; // NOLINT 346 TU.ClangTidyWarningsAsErrors =
"bugprone-integer-division";
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"),
356 TEST(DiagnosticsTest, Preprocessor) {
362 Annotations Test(R
"cpp( 372 ElementsAre(Diag(Test.range(), "use of undeclared identifier 'b'")));
375 TEST(DiagnosticsTest, InsideMacros) {
376 Annotations Test(R
"cpp( 378 #define RET(x) return x + 10 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'")));
396 TEST(DiagnosticsTest, NoFixItInMacro) {
397 Annotations Test(R
"cpp( 398 #define Define(name) void name() {} 403 EXPECT_THAT(TU.build().getDiagnostics(),
404 ElementsAre(AllOf(Diag(Test.range(),
"'main' must return 'int'"),
408 TEST(DiagnosticsTest, ToLSP) {
411 URIForFile HeaderFile =
415 D.ID = clang::diag::err_enum_class_reference;
416 D.Name =
"enum_class_reference";
418 D.Message =
"something terrible happened";
419 D.Range = {pos(1, 2), pos(3, 4)};
420 D.InsideMainFile =
true;
422 D.File =
"foo/bar/main.cpp";
423 D.AbsFile = MainFile.file();
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();
433 D.Notes.push_back(NoteInMain);
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);
445 F.Message =
"do something";
446 D.Fixes.push_back(F);
449 clangd::Diagnostic MainLSP;
450 MainLSP.range = D.Range;
452 MainLSP.code =
"enum_class_reference";
453 MainLSP.source =
"clang";
455 R
"(Something terrible happened (fix available) 457 main.cpp:6:7: remark: declared somewhere in the main file 459 ../foo/baz/header.h:10:11: 460 note: declared somewhere in the header file)"; 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 467 main.cpp:2:3: error: something terrible happened)"; 469 ClangdDiagnosticOptions Opts; 471 std::vector<std::pair<clangd::Diagnostic, std::vector<clangd::Fix>>> LSPDiags;
473 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix> Fixes) {
476 std::vector<clangd::Fix>(Fixes.begin(), Fixes.end())});
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,
"");
490 Opts.EmitRelatedLocations =
true;
492 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix> Fixes) {
495 std::vector<clangd::Fix>(Fixes.begin(), Fixes.end())});
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)))));
512 struct SymbolWithHeader {
518 std::unique_ptr<SymbolIndex>
519 buildIndexWithSymbol(llvm::ArrayRef<SymbolWithHeader> Syms) {
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);
529 return MemIndex::build(std::move(Slab).build(), RefSlab(), RelationSlab());
532 TEST(IncludeFixerTest, IncompleteType) {
533 Annotations Test(R
"cpp( 534 $insert[[]]namespace ns { 536 $nested[[X::]]Nested n; 538 class Y : $base[[public ns::X]] {}; 545 auto Index = buildIndexWithSymbol(
546 {SymbolWithHeader{
"ns::X",
"unittest:///x.h",
"\"x.h\""}});
547 TU.ExternalIndex = Index.get();
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")))));
565 TEST(IncludeFixerTest, NoSuggestIncludeWhenNoDefinitionInHeader) {
566 Annotations Test(R
"cpp( 567 $insert[[]]namespace ns { 570 class Y : $base[[public ns::X]] {}; 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);
587 TU.ExternalIndex =
Index.get();
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'")));
596 TEST(IncludeFixerTest, Typo) {
597 Annotations Test(R
"cpp( 598 $insert[[]]namespace ns { 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; 607 ns::$qualified1[[X]] x; // ns:: is valid. 608 ns::$qualified2[[X]](); // Error: no member in namespace 610 ::$global[[Global]] glob; 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();
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")))));
640 TEST(IncludeFixerTest, MultipleMatchedSymbols) {
641 Annotations Test(R
"cpp( 642 $insert[[]]namespace na { 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();
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")))));
665 TEST(IncludeFixerTest, NoCrashMemebrAccess) {
666 Annotations Test(R
"cpp( 667 struct X { int xyz; }; 668 void g() { X x; x.$[[xy]] } 671 auto Index = buildIndexWithSymbol(
672 SymbolWithHeader{
"na::X",
"unittest:///a.h",
"\"a.h\""});
673 TU.ExternalIndex = Index.get();
676 TU.build().getDiagnostics(),
677 UnorderedElementsAre(Diag(Test.range(),
"no member named 'xy' in 'X'")));
680 TEST(IncludeFixerTest, UseCachedIndexResults) {
683 Annotations Test(R
"cpp( 684 $insert[[]]void foo() { 707 buildIndexWithSymbol(SymbolWithHeader{
"X",
"unittest:///a.h",
"\"a.h\""});
708 TU.ExternalIndex = Index.get();
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"));
718 TEST(IncludeFixerTest, UnresolvedNameAsSpecifier) {
719 Annotations Test(R
"cpp( 720 $insert[[]]namespace ns { 722 void g() { ns::$[[scope]]::X_Y(); } 725 auto Index = buildIndexWithSymbol(
726 SymbolWithHeader{
"ns::scope::X_Y",
"unittest:///x.h",
"\"x.h\""});
727 TU.ExternalIndex = Index.get();
730 TU.build().getDiagnostics(),
731 UnorderedElementsAre(AllOf(
732 Diag(Test.range(),
"no member named 'scope' in namespace 'ns'"),
733 WithFix(
Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
734 "Add include \"x.h\" for symbol ns::scope::X_Y")))));
737 TEST(IncludeFixerTest, UnresolvedSpecifierWithSemaCorrection) {
738 Annotations Test(R
"cpp( 739 $insert[[]]namespace clang { 741 // "clangd::" will be corrected to "clang::" by Sema. 742 $q1[[clangd]]::$x[[X]] x; 743 $q2[[clangd]]::$ns[[ns]]::Y y; 748 auto Index = buildIndexWithSymbol(
749 {SymbolWithHeader{
"clang::clangd::X",
"unittest:///x.h",
"\"x.h\""},
750 SymbolWithHeader{
"clang::clangd::ns::Y",
"unittest:///y.h",
"\"y.h\""}});
751 TU.ExternalIndex = Index.get();
754 TU.build().getDiagnostics(),
755 UnorderedElementsAre(
757 Diag(Test.range(
"q1"),
"use of undeclared identifier 'clangd'; " 758 "did you mean 'clang'?"),
760 Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
761 "Add include \"x.h\" for symbol clang::clangd::X"))),
763 Diag(Test.range(
"x"),
"no type named 'X' in namespace 'clang'"),
764 WithFix(
Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
765 "Add include \"x.h\" for symbol clang::clangd::X"))),
767 Diag(Test.range(
"q2"),
"use of undeclared identifier 'clangd'; " 768 "did you mean 'clang'?"),
771 Fix(Test.range(
"insert"),
"#include \"y.h\"\n",
772 "Add include \"y.h\" for symbol clang::clangd::ns::Y"))),
773 AllOf(Diag(Test.range(
"ns"),
774 "no member named 'ns' in namespace 'clang'"),
776 Test.range(
"insert"),
"#include \"y.h\"\n",
777 "Add include \"y.h\" for symbol clang::clangd::ns::Y")))));
780 TEST(IncludeFixerTest, SpecifiedScopeIsNamespaceAlias) {
781 Annotations Test(R
"cpp( 782 $insert[[]]namespace a {} 789 auto Index = buildIndexWithSymbol(
790 SymbolWithHeader{
"a::X",
"unittest:///x.h",
"\"x.h\""});
791 TU.ExternalIndex = Index.get();
793 EXPECT_THAT(TU.build().getDiagnostics(),
794 UnorderedElementsAre(AllOf(
795 Diag(Test.range(),
"no type named 'X' in namespace 'a'"),
796 WithFix(
Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
797 "Add include \"x.h\" for symbol a::X")))));
800 TEST(IncludeFixerTest, NoCrashOnTemplateInstantiations) {
801 Annotations Test(R
"cpp( 802 template <typename T> struct Templ { 803 template <typename U> 804 typename U::type operator=(const U &); 809 A() { [[a]]; } // this caused crashes if we compute scopes lazily. 814 auto Index = buildIndexWithSymbol({});
815 TU.ExternalIndex = Index.get();
817 EXPECT_THAT(TU.build().getDiagnostics(),
818 ElementsAre(Diag(Test.range(),
"use of undeclared identifier 'a'")));
821 TEST(DiagsInHeaders, DiagInsideHeader) {
822 Annotations Main(R
"cpp( 825 Annotations Header("[[no_type_spec]];");
828 EXPECT_THAT(TU.build().getDiagnostics(),
829 UnorderedElementsAre(AllOf(
830 Diag(Main.range(),
"in included file: C++ requires a " 831 "type specifier for all declarations"),
832 WithNote(Diag(Header.range(),
"error occurred here")))));
835 TEST(DiagsInHeaders, DiagInTransitiveInclude) {
836 Annotations Main(R
"cpp( 840 TU.AdditionalFiles = {{"a.h",
"#include \"b.h\""}, {
"b.h",
"no_type_spec;"}};
841 EXPECT_THAT(TU.build().getDiagnostics(),
842 UnorderedElementsAre(
843 Diag(Main.range(),
"in included file: C++ requires a " 844 "type specifier for all declarations")));
847 TEST(DiagsInHeaders, DiagInMultipleHeaders) {
848 Annotations Main(R
"cpp( 853 TU.AdditionalFiles = {{"a.h",
"no_type_spec;"}, {
"b.h",
"no_type_spec;"}};
854 EXPECT_THAT(TU.build().getDiagnostics(),
855 UnorderedElementsAre(
856 Diag(Main.range(
"a"),
"in included file: C++ requires a type " 857 "specifier for all declarations"),
858 Diag(Main.range(
"b"),
"in included file: C++ requires a type " 859 "specifier for all declarations")));
862 TEST(DiagsInHeaders, PreferExpansionLocation) {
863 Annotations Main(R
"cpp( 869 {
"b.h",
"#ifndef X\n#define X\nno_type_spec;\n#endif"}};
870 EXPECT_THAT(TU.build().getDiagnostics(),
871 UnorderedElementsAre(Diag(Main.range(),
872 "in included file: C++ requires a type " 873 "specifier for all declarations")));
876 TEST(DiagsInHeaders, PreferExpansionLocationMacros) {
877 Annotations Main(R
"cpp( 885 {
"b.h",
"#include \"c.h\"\n"},
886 {
"c.h",
"#ifndef X\n#define X\nno_type_spec;\n#endif"}};
887 EXPECT_THAT(TU.build().getDiagnostics(),
888 UnorderedElementsAre(
889 Diag(Main.range(),
"in included file: C++ requires a " 890 "type specifier for all declarations")));
893 TEST(DiagsInHeaders, LimitDiagsOutsideMainFile) {
894 Annotations Main(R
"cpp( 900 {
"b.h",
"#include \"c.h\"\n"},
916 EXPECT_THAT(TU.build().getDiagnostics(), 917 UnorderedElementsAre( 918 Diag(Main.range(), "in included file: C++ requires a " 919 "type specifier for all declarations")));
922 TEST(DiagsInHeaders, OnlyErrorOrFatal) {
923 Annotations Main(R
"cpp( 926 Annotations Header(R"cpp( 931 EXPECT_THAT(TU.build().getDiagnostics(),
932 UnorderedElementsAre(AllOf(
933 Diag(Main.range(),
"in included file: C++ requires " 934 "a type specifier for all declarations"),
935 WithNote(Diag(Header.range(),
"error occurred here")))));
939 Annotations Main(R
"cpp( 942 Annotations Header(R"cpp( 944 int b = [[FOO]];)cpp"); 947 TU.ExtraArgs = {
"-DFOO=NOOO"};
948 EXPECT_THAT(TU.build().getDiagnostics(), UnorderedElementsAre());
953 TU.ExtraArgs.push_back(
"--include=a.h");
954 TU.AdditionalFiles = {{
"a.h",
"void main();"}};
957 EXPECT_THAT(TU.build().getDiagnostics(), UnorderedElementsAre());
llvm::Optional< std::string > ClangTidyChecks
llvm::json::Value toJSON(const FuzzyFindRequest &Request)
Symbol cls(llvm::StringRef Name)
constexpr llvm::StringLiteral Message
std::vector< Fix > Fixes
Alternative fixes for this diagnostic, one should be chosen.
static std::unique_ptr< SymbolIndex > build(SymbolSlab Symbols, RefSlab Refs, RelationSlab Relations)
Builds an index from slabs. The index takes ownership of the data.
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.
int getSeverity(DiagnosticsEngine::Level L)
Convert from clang diagnostic level to LSP severity.
TEST(BackgroundQueueTest, Priority)
std::string DeclaringFile
std::string testPath(PathRef File)
static URIForFile canonicalize(llvm::StringRef AbsPath, llvm::StringRef TUPath)
Canonicalizes AbsPath via URI.
static TestTU withCode(llvm::StringRef Code)
CodeCompletionBuilder Builder
std::vector< Note > Notes
Elaborate on the problem, usually pointing to a related piece of code.
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
CharSourceRange Range
SourceRange for the file name.
std::string IncludeHeader
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
llvm::StringMap< std::string > AdditionalFiles
const SymbolIndex * Index