19 #include "clang/Basic/Diagnostic.h" 20 #include "clang/Basic/DiagnosticSema.h" 21 #include "llvm/Support/ScopedPrinter.h" 22 #include "llvm/Support/TargetSelect.h" 23 #include "gmock/gmock.h" 24 #include "gtest/gtest.h" 32 using ::testing::ElementsAre;
33 using ::testing::Field;
34 using ::testing::IsEmpty;
35 using ::testing::Pair;
36 using ::testing::UnorderedElementsAre;
38 ::testing::Matcher<const Diag &> WithFix(::testing::Matcher<Fix> FixMatcher) {
42 ::testing::Matcher<const Diag &> WithFix(::testing::Matcher<Fix> FixMatcher1,
43 ::testing::Matcher<Fix> FixMatcher2) {
47 ::testing::Matcher<const Diag &>
48 WithNote(::testing::Matcher<Note> NoteMatcher) {
53 "Diag at " + llvm::to_string(
Range) +
" = [" +
Message +
"]") {
58 "Fix " + llvm::to_string(
Range) +
" => " +
59 ::testing::PrintToString(Replacement) +
" = [" +
Message +
"]") {
60 return arg.Message ==
Message && arg.Edits.size() == 1 &&
61 arg.Edits[0].range ==
Range && arg.Edits[0].newText == Replacement;
69 "LSP diagnostic " + llvm::to_string(LSPDiag)) {
71 *result_listener << llvm::formatv(
"expected:\n{0:2}\ngot\n{1:2}",
79 MATCHER_P(DiagSource, S,
"") {
return arg.Source == S; }
80 MATCHER_P(DiagName, N,
"") {
return arg.Name == N; }
81 MATCHER_P(DiagSeverity, S,
"") {
return arg.Severity == S; }
84 if (arg.Message !=
Fix.Message)
86 if (arg.Edits.size() !=
Fix.Edits.size())
88 for (std::size_t I = 0; I < arg.Edits.size(); ++I) {
89 if (arg.Edits[I].range !=
Fix.Edits[I].range ||
90 arg.Edits[I].newText !=
Fix.Edits[I].newText)
97 Position pos(
int line,
int character) {
100 Res.character = character;
104 TEST(DiagnosticsTest, DiagnosticRanges) {
106 Annotations Test(R
"cpp( 109 class T{$explicit[[]]$constructor[[T]](int a);}; 113 foo()$semicolon[[]]//with comments 115 double $type[[bar]] = "foo"; 116 struct Foo { int x; }; Foo a; 118 test::$nomembernamespace[[test]]; 124 TU.build().getDiagnostics(),
127 AllOf(Diag(Test.range(
"typo"),
128 "use of undeclared identifier 'goo'; did you mean 'foo'?"),
129 DiagSource(
Diag::Clang), DiagName(
"undeclared_var_use_suggest"),
131 Fix(Test.range(
"typo"),
"foo",
"change 'go\\…' to 'foo'")),
133 WithNote(Diag(Test.range(
"decl"),
"'foo' declared here"))),
137 AllOf(Diag(Test.range(
"semicolon"),
"expected ';' after expression"),
138 WithFix(
Fix(Test.range(
"semicolon"),
";",
"insert ';'"))),
140 Diag(Test.range(
"unk"),
"use of undeclared identifier 'unknown'"),
141 Diag(Test.range(
"type"),
142 "cannot initialize a variable of type 'double' with an lvalue " 143 "of type 'const char [4]'"),
144 Diag(Test.range(
"nomember"),
"no member named 'y' in 'Foo'"),
145 Diag(Test.range(
"nomembernamespace"),
146 "no member named 'test' in namespace 'test'"),
148 AllOf(Diag(Test.range(
"constructor"),
149 "single-argument constructors must be marked explicit to " 150 "avoid unintentional implicit conversions"),
151 WithFix(
Fix(Test.range(
"explicit"),
"explicit ",
152 "insert 'explicit '")))));
155 TEST(DiagnosticsTest, FlagsMatter) {
156 Annotations Test(
"[[void]] main() {}");
158 EXPECT_THAT(TU.build().getDiagnostics(),
159 ElementsAre(AllOf(Diag(Test.range(),
"'main' must return 'int'"),
160 WithFix(
Fix(Test.range(),
"int",
161 "change 'void' to 'int'")))));
165 TU.build().getDiagnostics(),
167 Diag(Test.range(),
"return type of 'main' is not 'int'"),
168 WithFix(
Fix(Test.range(),
"int",
"change return type to 'int'")))));
171 TEST(DiagnosticsTest, DiagnosticPreamble) {
172 Annotations Test(R
"cpp( 173 #include $[["not-found.h"]] 177 EXPECT_THAT(TU.build().getDiagnostics(),
178 ElementsAre(::testing::AllOf(
179 Diag(Test.range(),
"'not-found.h' file not found"),
180 DiagSource(
Diag::Clang), DiagName(
"pp_file_not_found"))));
183 TEST(DiagnosticsTest, DeduplicatedClangTidyDiagnostics) {
184 Annotations Test(R
"cpp( 185 float foo = [[0.1f]]; 191 "hicpp-uppercase-literal-suffix";
194 TU.build().getDiagnostics(),
195 UnorderedElementsAre(::testing::AllOf(
197 "floating point literal has suffix 'f', which is not uppercase"),
200 Test = Annotations(R
"cpp( 210 TU.Code = Test.code(); 214 TU.build().getDiagnostics(),
215 UnorderedElementsAre(::testing::AllOf(
217 "floating point literal has suffix 'f', which is not uppercase"),
221 TEST(DiagnosticsTest, ClangTidy) {
222 Annotations Test(R
"cpp( 223 #include $deprecated[["assert.h"]] 225 #define $macrodef[[SQUARE]](X) (X)*(X) 226 int $main[[main]]() { 228 return SQUARE($macroarg[[++]]y); 229 return $doubled[[sizeof]](sizeof(int)); 235 "-*, bugprone-sizeof-expression, bugprone-macro-repeated-side-effects, " 236 "modernize-deprecated-headers, modernize-use-trailing-return-type";
238 TU.build().getDiagnostics(),
239 UnorderedElementsAre(
240 AllOf(Diag(Test.range(
"deprecated"),
241 "inclusion of deprecated C++ header 'assert.h'; consider " 242 "using 'cassert' instead"),
244 DiagName(
"modernize-deprecated-headers"),
245 WithFix(
Fix(Test.range(
"deprecated"),
"<cassert>",
246 "change '\"assert.h\"' to '<cassert>'"))),
247 Diag(Test.range(
"doubled"),
248 "suspicious usage of 'sizeof(sizeof(...))'"),
250 Diag(Test.range(
"macroarg"),
251 "side effects in the 1st macro argument 'X' are repeated in " 254 DiagName(
"bugprone-macro-repeated-side-effects"),
256 Diag(Test.range(
"macrodef"),
"macro 'SQUARE' defined here"))),
257 Diag(Test.range(
"macroarg"),
258 "multiple unsequenced modifications to 'y'"),
260 Diag(Test.range(
"main"),
261 "use a trailing return type for this function"),
263 DiagName(
"modernize-use-trailing-return-type"),
265 WithFix(FixMessage(
"use a trailing return type for this function")))
269 TEST(DiagnosticTest, ClangTidySuppressionComment) {
270 Annotations Main(R
"cpp( 273 double d = 8 / i; // NOLINT 276 double f = [[8]] / i; 282 TU.build().getDiagnostics(),
283 UnorderedElementsAre(::testing::AllOf(
284 Diag(Main.range(),
"result of integer division used in a floating " 285 "point context; possible loss of precision"),
289 TEST(DiagnosticTest, ClangTidyWarningAsError) {
290 Annotations Main(R
"cpp( 293 double f = [[8]] / i; 298 TU.ClangTidyWarningsAsErrors =
"bugprone-integer-division";
300 TU.build().getDiagnostics(),
301 UnorderedElementsAre(::testing::AllOf(
302 Diag(Main.range(),
"result of integer division used in a floating " 303 "point context; possible loss of precision"),
308 TEST(DiagnosticTest, LongFixMessages) {
310 Annotations Source(R
"cpp( 312 int somereallyreallyreallyreallyreallyreallyreallyreallylongidentifier; 313 [[omereallyreallyreallyreallyreallyreallyreallyreallylongidentifier]]= 10; 318 TU.build().getDiagnostics(), 319 ElementsAre(WithFix(Fix( 321 "somereallyreallyreallyreallyreallyreallyreallyreallylongidentifier",
322 "change 'omereallyreallyreallyreallyreallyreallyreallyreall…' to " 323 "'somereallyreallyreallyreallyreallyreallyreallyreal…'"))));
325 Source = Annotations(R
"cpp( 333 EXPECT_THAT(TU.build().getDiagnostics(), 335 Fix(Source.range(), "ident",
"change 'ide\\…' to 'ident'"))));
338 TEST(DiagnosticTest, ClangTidyWarningAsErrorTrumpsSuppressionComment) {
339 Annotations Main(R
"cpp( 342 double f = [[8]] / i; // NOLINT 347 TU.ClangTidyWarningsAsErrors =
"bugprone-integer-division";
349 TU.build().getDiagnostics(),
350 UnorderedElementsAre(::testing::AllOf(
351 Diag(Main.range(),
"result of integer division used in a floating " 352 "point context; possible loss of precision"),
357 TEST(DiagnosticsTest, Preprocessor) {
363 Annotations Test(R
"cpp( 373 ElementsAre(Diag(Test.range(), "use of undeclared identifier 'b'")));
376 TEST(DiagnosticsTest, InsideMacros) {
377 Annotations Test(R
"cpp( 379 #define RET(x) return x + 10 389 ElementsAre(Diag(Test.range("foo"),
390 "cannot initialize return object of type " 391 "'int *' with an rvalue of type 'int'"),
392 Diag(Test.range(
"bar"),
393 "cannot initialize return object of type " 394 "'int *' with an rvalue of type 'int'")));
397 TEST(DiagnosticsTest, NoFixItInMacro) {
398 Annotations Test(R
"cpp( 399 #define Define(name) void name() {} 404 EXPECT_THAT(TU.build().getDiagnostics(),
405 ElementsAre(AllOf(Diag(Test.range(),
"'main' must return 'int'"),
409 TEST(ClangdTest, MSAsm) {
412 llvm::InitializeAllTargetInfos();
414 TU.ExtraArgs = {
"-fms-extensions"};
415 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
418 TEST(DiagnosticsTest, ToLSP) {
421 URIForFile HeaderFile =
425 D.ID = clang::diag::err_enum_class_reference;
426 D.Name =
"enum_class_reference";
428 D.Message =
"something terrible happened";
429 D.Range = {pos(1, 2), pos(3, 4)};
430 D.InsideMainFile =
true;
432 D.File =
"foo/bar/main.cpp";
433 D.AbsFile = MainFile.file();
435 clangd::Note NoteInMain;
436 NoteInMain.Message =
"declared somewhere in the main file";
437 NoteInMain.Range = {pos(5, 6), pos(7, 8)};
438 NoteInMain.Severity = DiagnosticsEngine::Remark;
439 NoteInMain.File =
"../foo/bar/main.cpp";
440 NoteInMain.InsideMainFile =
true;
441 NoteInMain.AbsFile = MainFile.file();
443 D.Notes.push_back(NoteInMain);
445 clangd::Note NoteInHeader;
446 NoteInHeader.Message =
"declared somewhere in the header file";
447 NoteInHeader.Range = {pos(9, 10), pos(11, 12)};
448 NoteInHeader.Severity = DiagnosticsEngine::Note;
449 NoteInHeader.File =
"../foo/baz/header.h";
450 NoteInHeader.InsideMainFile =
false;
451 NoteInHeader.AbsFile = HeaderFile.file();
452 D.Notes.push_back(NoteInHeader);
455 F.Message =
"do something";
456 D.Fixes.push_back(F);
459 clangd::Diagnostic MainLSP;
460 MainLSP.range = D.Range;
462 MainLSP.code =
"enum_class_reference";
463 MainLSP.source =
"clang";
465 R
"(Something terrible happened (fix available) 467 main.cpp:6:7: remark: declared somewhere in the main file 469 ../foo/baz/header.h:10:11: 470 note: declared somewhere in the header file)"; 472 clangd::Diagnostic NoteInMainLSP; 473 NoteInMainLSP.range = NoteInMain.Range; 474 NoteInMainLSP.severity = getSeverity(DiagnosticsEngine::Remark); 475 NoteInMainLSP.message = R"(Declared somewhere in the main file 477 main.cpp:2:3: error: something terrible happened)"; 479 ClangdDiagnosticOptions Opts; 481 std::vector<std::pair<clangd::Diagnostic, std::vector<clangd::Fix>>> LSPDiags;
483 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix> Fixes) {
486 std::vector<clangd::Fix>(Fixes.begin(), Fixes.end())});
491 ElementsAre(Pair(EqualToLSPDiag(MainLSP), ElementsAre(EqualToFix(F))),
492 Pair(EqualToLSPDiag(NoteInMainLSP), IsEmpty())));
493 EXPECT_EQ(LSPDiags[0].first.code,
"enum_class_reference");
494 EXPECT_EQ(LSPDiags[0].first.source,
"clang");
495 EXPECT_EQ(LSPDiags[1].first.code,
"");
496 EXPECT_EQ(LSPDiags[1].first.source,
"");
500 Opts.EmitRelatedLocations =
true;
502 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix> Fixes) {
505 std::vector<clangd::Fix>(Fixes.begin(), Fixes.end())});
507 MainLSP.message =
"Something terrible happened (fix available)";
508 DiagnosticRelatedInformation NoteInMainDRI;
509 NoteInMainDRI.message =
"Declared somewhere in the main file";
510 NoteInMainDRI.location.range = NoteInMain.Range;
511 NoteInMainDRI.location.uri =
MainFile;
512 MainLSP.relatedInformation = {NoteInMainDRI};
513 DiagnosticRelatedInformation NoteInHeaderDRI;
514 NoteInHeaderDRI.message =
"Declared somewhere in the header file";
515 NoteInHeaderDRI.location.range = NoteInHeader.Range;
516 NoteInHeaderDRI.location.uri = HeaderFile;
517 MainLSP.relatedInformation = {NoteInMainDRI, NoteInHeaderDRI};
518 EXPECT_THAT(LSPDiags, ElementsAre(Pair(EqualToLSPDiag(MainLSP),
519 ElementsAre(EqualToFix(F)))));
522 struct SymbolWithHeader {
528 std::unique_ptr<SymbolIndex>
529 buildIndexWithSymbol(llvm::ArrayRef<SymbolWithHeader> Syms) {
531 for (
const auto &S : Syms) {
532 Symbol Sym =
cls(S.QName);
534 Sym.CanonicalDeclaration.FileURI = S.DeclaringFile.c_str();
535 Sym.Definition.FileURI = S.DeclaringFile.c_str();
536 Sym.IncludeHeaders.emplace_back(S.IncludeHeader, 1);
539 return MemIndex::build(std::move(Slab).build(), RefSlab(), RelationSlab());
542 TEST(IncludeFixerTest, IncompleteType) {
543 Annotations Test(R
"cpp( 544 $insert[[]]namespace ns { 546 $nested[[X::]]Nested n; 548 class Y : $base[[public ns::X]] {}; 555 auto Index = buildIndexWithSymbol(
556 {SymbolWithHeader{
"ns::X",
"unittest:///x.h",
"\"x.h\""}});
557 TU.ExternalIndex = Index.get();
560 TU.build().getDiagnostics(),
561 UnorderedElementsAre(
562 AllOf(Diag(Test.range(
"nested"),
563 "incomplete type 'ns::X' named in nested name specifier"),
564 WithFix(
Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
565 "Add include \"x.h\" for symbol ns::X"))),
566 AllOf(Diag(Test.range(
"base"),
"base class has incomplete type"),
567 WithFix(
Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
568 "Add include \"x.h\" for symbol ns::X"))),
569 AllOf(Diag(Test.range(
"access"),
570 "member access into incomplete type 'ns::X'"),
571 WithFix(
Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
572 "Add include \"x.h\" for symbol ns::X")))));
575 TEST(IncludeFixerTest, NoSuggestIncludeWhenNoDefinitionInHeader) {
576 Annotations Test(R
"cpp( 577 $insert[[]]namespace ns { 580 class Y : $base[[public ns::X]] {}; 587 Symbol Sym =
cls(
"ns::X");
589 Sym.CanonicalDeclaration.FileURI =
"unittest:///x.h";
590 Sym.Definition.FileURI =
"unittest:///x.cc";
591 Sym.IncludeHeaders.emplace_back(
"\"x.h\"", 1);
597 TU.ExternalIndex =
Index.get();
599 EXPECT_THAT(TU.build().getDiagnostics(),
600 UnorderedElementsAre(
601 Diag(Test.range(
"base"),
"base class has incomplete type"),
602 Diag(Test.range(
"access"),
603 "member access into incomplete type 'ns::X'")));
606 TEST(IncludeFixerTest, Typo) {
607 Annotations Test(R
"cpp( 608 $insert[[]]namespace ns { 610 $unqualified1[[X]] x; 611 // No fix if the unresolved type is used as specifier. (ns::)X::Nested will be 612 // considered the unresolved type. 613 $unqualified2[[X]]::Nested n; 617 ns::$qualified1[[X]] x; // ns:: is valid. 618 ns::$qualified2[[X]](); // Error: no member in namespace 620 ::$global[[Global]] glob; 624 auto Index = buildIndexWithSymbol(
625 {SymbolWithHeader{
"ns::X",
"unittest:///x.h",
"\"x.h\""},
626 SymbolWithHeader{
"Global",
"unittest:///global.h",
"\"global.h\""}});
627 TU.ExternalIndex = Index.get();
630 TU.build().getDiagnostics(),
631 UnorderedElementsAre(
632 AllOf(Diag(Test.range(
"unqualified1"),
"unknown type name 'X'"),
633 WithFix(
Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
634 "Add include \"x.h\" for symbol ns::X"))),
635 Diag(Test.range(
"unqualified2"),
"use of undeclared identifier 'X'"),
636 AllOf(Diag(Test.range(
"qualified1"),
637 "no type named 'X' in namespace 'ns'"),
638 WithFix(
Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
639 "Add include \"x.h\" for symbol ns::X"))),
640 AllOf(Diag(Test.range(
"qualified2"),
641 "no member named 'X' in namespace 'ns'"),
642 WithFix(
Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
643 "Add include \"x.h\" for symbol ns::X"))),
644 AllOf(Diag(Test.range(
"global"),
645 "no type named 'Global' in the global namespace"),
646 WithFix(
Fix(Test.range(
"insert"),
"#include \"global.h\"\n",
647 "Add include \"global.h\" for symbol Global")))));
650 TEST(IncludeFixerTest, MultipleMatchedSymbols) {
651 Annotations Test(R
"cpp( 652 $insert[[]]namespace na { 661 auto Index = buildIndexWithSymbol(
662 {SymbolWithHeader{
"na::X",
"unittest:///a.h",
"\"a.h\""},
663 SymbolWithHeader{
"na::nb::X",
"unittest:///b.h",
"\"b.h\""}});
664 TU.ExternalIndex = Index.get();
666 EXPECT_THAT(TU.build().getDiagnostics(),
667 UnorderedElementsAre(AllOf(
668 Diag(Test.range(
"unqualified"),
"unknown type name 'X'"),
669 WithFix(
Fix(Test.range(
"insert"),
"#include \"a.h\"\n",
670 "Add include \"a.h\" for symbol na::X"),
671 Fix(Test.range(
"insert"),
"#include \"b.h\"\n",
672 "Add include \"b.h\" for symbol na::nb::X")))));
675 TEST(IncludeFixerTest, NoCrashMemebrAccess) {
676 Annotations Test(R
"cpp( 677 struct X { int xyz; }; 678 void g() { X x; x.$[[xy]] } 681 auto Index = buildIndexWithSymbol(
682 SymbolWithHeader{
"na::X",
"unittest:///a.h",
"\"a.h\""});
683 TU.ExternalIndex = Index.get();
686 TU.build().getDiagnostics(),
687 UnorderedElementsAre(Diag(Test.range(),
"no member named 'xy' in 'X'")));
690 TEST(IncludeFixerTest, UseCachedIndexResults) {
693 Annotations Test(R
"cpp( 694 $insert[[]]void foo() { 717 buildIndexWithSymbol(SymbolWithHeader{
"X",
"unittest:///a.h",
"\"a.h\""});
718 TU.ExternalIndex = Index.get();
720 auto Parsed = TU.build();
721 for (
const auto &D : Parsed.getDiagnostics()) {
722 if (D.Fixes.size() != 1) {
723 ADD_FAILURE() <<
"D.Fixes.size() != 1";
726 EXPECT_EQ(D.Fixes[0].Message,
727 std::string(
"Add include \"a.h\" for symbol X"));
731 TEST(IncludeFixerTest, UnresolvedNameAsSpecifier) {
732 Annotations Test(R
"cpp( 733 $insert[[]]namespace ns { 735 void g() { ns::$[[scope]]::X_Y(); } 738 TU.Code = Test.code(); 740 TU.ExtraArgs.push_back(
"-fno-ms-compatibility");
741 auto Index = buildIndexWithSymbol(
742 SymbolWithHeader{
"ns::scope::X_Y",
"unittest:///x.h",
"\"x.h\""});
743 TU.ExternalIndex =
Index.get();
746 TU.build().getDiagnostics(),
747 UnorderedElementsAre(AllOf(
748 Diag(Test.range(),
"no member named 'scope' in namespace 'ns'"),
749 WithFix(
Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
750 "Add include \"x.h\" for symbol ns::scope::X_Y")))));
753 TEST(IncludeFixerTest, UnresolvedSpecifierWithSemaCorrection) {
754 Annotations Test(R
"cpp( 755 $insert[[]]namespace clang { 757 // "clangd::" will be corrected to "clang::" by Sema. 758 $q1[[clangd]]::$x[[X]] x; 759 $q2[[clangd]]::$ns[[ns]]::Y y; 764 TU.Code = Test.code(); 766 TU.ExtraArgs.push_back(
"-fno-ms-compatibility");
767 auto Index = buildIndexWithSymbol(
768 {SymbolWithHeader{
"clang::clangd::X",
"unittest:///x.h",
"\"x.h\""},
769 SymbolWithHeader{
"clang::clangd::ns::Y",
"unittest:///y.h",
"\"y.h\""}});
770 TU.ExternalIndex =
Index.get();
773 TU.build().getDiagnostics(),
774 UnorderedElementsAre(
776 Diag(Test.range(
"q1"),
"use of undeclared identifier 'clangd'; " 777 "did you mean 'clang'?"),
779 Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
780 "Add include \"x.h\" for symbol clang::clangd::X"))),
782 Diag(Test.range(
"x"),
"no type named 'X' in namespace 'clang'"),
783 WithFix(
Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
784 "Add include \"x.h\" for symbol clang::clangd::X"))),
786 Diag(Test.range(
"q2"),
"use of undeclared identifier 'clangd'; " 787 "did you mean 'clang'?"),
790 Fix(Test.range(
"insert"),
"#include \"y.h\"\n",
791 "Add include \"y.h\" for symbol clang::clangd::ns::Y"))),
792 AllOf(Diag(Test.range(
"ns"),
793 "no member named 'ns' in namespace 'clang'"),
795 Test.range(
"insert"),
"#include \"y.h\"\n",
796 "Add include \"y.h\" for symbol clang::clangd::ns::Y")))));
799 TEST(IncludeFixerTest, SpecifiedScopeIsNamespaceAlias) {
800 Annotations Test(R
"cpp( 801 $insert[[]]namespace a {} 808 auto Index = buildIndexWithSymbol(
809 SymbolWithHeader{
"a::X",
"unittest:///x.h",
"\"x.h\""});
810 TU.ExternalIndex = Index.get();
812 EXPECT_THAT(TU.build().getDiagnostics(),
813 UnorderedElementsAre(AllOf(
814 Diag(Test.range(),
"no type named 'X' in namespace 'a'"),
815 WithFix(
Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
816 "Add include \"x.h\" for symbol a::X")))));
819 TEST(IncludeFixerTest, NoCrashOnTemplateInstantiations) {
820 Annotations Test(R
"cpp( 821 template <typename T> struct Templ { 822 template <typename U> 823 typename U::type operator=(const U &); 828 A() { [[a]]; } // this caused crashes if we compute scopes lazily. 833 auto Index = buildIndexWithSymbol({});
834 TU.ExternalIndex = Index.get();
836 EXPECT_THAT(TU.build().getDiagnostics(),
837 ElementsAre(Diag(Test.range(),
"use of undeclared identifier 'a'")));
840 TEST(DiagsInHeaders, DiagInsideHeader) {
841 Annotations Main(R
"cpp( 844 Annotations Header("[[no_type_spec]];");
847 EXPECT_THAT(TU.build().getDiagnostics(),
848 UnorderedElementsAre(AllOf(
849 Diag(Main.range(),
"in included file: C++ requires a " 850 "type specifier for all declarations"),
851 WithNote(Diag(Header.range(),
"error occurred here")))));
854 TEST(DiagsInHeaders, DiagInTransitiveInclude) {
855 Annotations Main(R
"cpp( 859 TU.AdditionalFiles = {{"a.h",
"#include \"b.h\""}, {
"b.h",
"no_type_spec;"}};
860 EXPECT_THAT(TU.build().getDiagnostics(),
861 UnorderedElementsAre(
862 Diag(Main.range(),
"in included file: C++ requires a " 863 "type specifier for all declarations")));
866 TEST(DiagsInHeaders, DiagInMultipleHeaders) {
867 Annotations Main(R
"cpp( 872 TU.AdditionalFiles = {{"a.h",
"no_type_spec;"}, {
"b.h",
"no_type_spec;"}};
873 EXPECT_THAT(TU.build().getDiagnostics(),
874 UnorderedElementsAre(
875 Diag(Main.range(
"a"),
"in included file: C++ requires a type " 876 "specifier for all declarations"),
877 Diag(Main.range(
"b"),
"in included file: C++ requires a type " 878 "specifier for all declarations")));
881 TEST(DiagsInHeaders, PreferExpansionLocation) {
882 Annotations Main(R
"cpp( 888 {
"b.h",
"#ifndef X\n#define X\nno_type_spec;\n#endif"}};
889 EXPECT_THAT(TU.build().getDiagnostics(),
890 UnorderedElementsAre(Diag(Main.range(),
891 "in included file: C++ requires a type " 892 "specifier for all declarations")));
895 TEST(DiagsInHeaders, PreferExpansionLocationMacros) {
896 Annotations Main(R
"cpp( 904 {
"b.h",
"#include \"c.h\"\n"},
905 {
"c.h",
"#ifndef X\n#define X\nno_type_spec;\n#endif"}};
906 EXPECT_THAT(TU.build().getDiagnostics(),
907 UnorderedElementsAre(
908 Diag(Main.range(),
"in included file: C++ requires a " 909 "type specifier for all declarations")));
912 TEST(DiagsInHeaders, LimitDiagsOutsideMainFile) {
913 Annotations Main(R
"cpp( 919 {
"b.h",
"#include \"c.h\"\n"},
935 EXPECT_THAT(TU.build().getDiagnostics(), 936 UnorderedElementsAre( 937 Diag(Main.range(), "in included file: C++ requires a " 938 "type specifier for all declarations")));
941 TEST(DiagsInHeaders, OnlyErrorOrFatal) {
942 Annotations Main(R
"cpp( 945 Annotations Header(R"cpp( 950 EXPECT_THAT(TU.build().getDiagnostics(),
951 UnorderedElementsAre(AllOf(
952 Diag(Main.range(),
"in included file: C++ requires " 953 "a type specifier for all declarations"),
954 WithNote(Diag(Header.range(),
"error occurred here")))));
957 TEST(DiagsInHeaders, FromNonWrittenSources) {
958 Annotations Main(R
"cpp( 961 Annotations Header(R"cpp( 963 int b = [[FOO]];)cpp"); 966 TU.ExtraArgs = {
"-DFOO=NOOO"};
967 EXPECT_THAT(TU.build().getDiagnostics(),
968 UnorderedElementsAre(AllOf(
970 "in included file: use of undeclared identifier 'NOOO'"),
971 WithNote(Diag(Header.range(),
"error occurred here")))));
974 TEST(DiagsInHeaders, ErrorFromMacroExpansion) {
975 Annotations Main(R
"cpp( 980 Annotations Header(R"cpp( 985 EXPECT_THAT(TU.build().getDiagnostics(),
986 UnorderedElementsAre(
987 Diag(Main.range(),
"in included file: use of undeclared " 988 "identifier 'foo'; did you mean 'fo'?")));
991 TEST(DiagsInHeaders, ErrorFromMacroArgument) {
992 Annotations Main(R
"cpp( 997 Annotations Header(R"cpp( 1002 EXPECT_THAT(TU.build().getDiagnostics(),
1003 UnorderedElementsAre(
1004 Diag(Main.range(),
"in included file: use of undeclared " 1005 "identifier 'foo'; did you mean 'fo'?")));
1010 TU.ExtraArgs.push_back(
"--include=a.h");
1011 TU.AdditionalFiles = {{
"a.h",
"void main();"}};
1014 EXPECT_THAT(TU.build().getDiagnostics(), UnorderedElementsAre());
1017 TEST(ToLSPDiag, RangeIsInMain) {
1018 ClangdDiagnosticOptions Opts;
1020 D.Range = {pos(1, 2), pos(3, 4)};
1021 D.Notes.emplace_back();
1022 Note &N = D.Notes.back();
1023 N.Range = {pos(2, 3), pos(3, 4)};
1025 D.InsideMainFile =
true;
1026 N.InsideMainFile =
false;
1028 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix>) {
1029 EXPECT_EQ(LSPDiag.range, D.Range);
1032 D.InsideMainFile =
false;
1033 N.InsideMainFile =
true;
1035 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix>) {
1036 EXPECT_EQ(LSPDiag.range, N.Range);
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