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::SizeIs;
37 using ::testing::UnorderedElementsAre;
39 ::testing::Matcher<const Diag &> WithFix(::testing::Matcher<Fix> FixMatcher) {
43 ::testing::Matcher<const Diag &> WithFix(::testing::Matcher<Fix> FixMatcher1,
44 ::testing::Matcher<Fix> FixMatcher2) {
48 ::testing::Matcher<const Diag &>
49 WithNote(::testing::Matcher<Note> NoteMatcher) {
53 ::testing::Matcher<const Diag &>
54 WithNote(::testing::Matcher<Note> NoteMatcher1,
55 ::testing::Matcher<Note> NoteMatcher2) {
56 return Field(&
Diag::Notes, UnorderedElementsAre(NoteMatcher1, NoteMatcher2));
60 "Diag at " + llvm::to_string(
Range) +
" = [" +
Message +
"]") {
65 "Fix " + llvm::to_string(
Range) +
" => " +
66 ::testing::PrintToString(Replacement) +
" = [" +
Message +
"]") {
67 return arg.Message ==
Message && arg.Edits.size() == 1 &&
68 arg.Edits[0].range ==
Range && arg.Edits[0].newText == Replacement;
74 "LSP diagnostic " + llvm::to_string(LSPDiag)) {
76 *result_listener << llvm::formatv(
"expected:\n{0:2}\ngot\n{1:2}",
84 MATCHER_P(DiagSource, S,
"") {
return arg.Source == S; }
85 MATCHER_P(DiagName, N,
"") {
return arg.Name == N; }
86 MATCHER_P(DiagSeverity, S,
"") {
return arg.Severity == S; }
89 if (arg.Message !=
Fix.Message)
91 if (arg.Edits.size() !=
Fix.Edits.size())
93 for (std::size_t I = 0; I < arg.Edits.size(); ++I) {
94 if (arg.Edits[I].range !=
Fix.Edits[I].range ||
95 arg.Edits[I].newText !=
Fix.Edits[I].newText)
102 Position pos(
int line,
int character) {
105 Res.character = character;
109 TEST(DiagnosticsTest, DiagnosticRanges) {
111 Annotations Test(R
"cpp(
116 class T{$explicit[[]]$constructor[[T]](int a);};
120 foo()$semicolon[[]]//with comments
122 double $type[[bar]] = "foo";
123 struct Foo { int x; }; Foo a;
125 test::$nomembernamespace[[test]];
126 $macro[[ID($macroarg[[fod]])]]();
132 TU.build().getDiagnostics(),
135 AllOf(Diag(Test.range(
"typo"),
136 "use of undeclared identifier 'goo'; did you mean 'foo'?"),
137 DiagSource(
Diag::Clang), DiagName(
"undeclared_var_use_suggest"),
139 Fix(Test.range(
"typo"),
"foo",
"change 'go\\…' to 'foo'")),
141 WithNote(Diag(Test.range(
"decl"),
"'foo' declared here"))),
145 AllOf(Diag(Test.range(
"semicolon"),
"expected ';' after expression"),
146 WithFix(
Fix(Test.range(
"semicolon"),
";",
"insert ';'"))),
148 Diag(Test.range(
"unk"),
"use of undeclared identifier 'unknown'"),
149 Diag(Test.range(
"type"),
150 "cannot initialize a variable of type 'double' with an lvalue "
151 "of type 'const char [4]'"),
152 Diag(Test.range(
"nomember"),
"no member named 'y' in 'Foo'"),
153 Diag(Test.range(
"nomembernamespace"),
154 "no member named 'test' in namespace 'test'"),
155 AllOf(Diag(Test.range(
"macro"),
156 "use of undeclared identifier 'fod'; did you mean 'foo'?"),
157 WithFix(
Fix(Test.range(
"macroarg"),
"foo",
158 "change 'fod' to 'foo'"))),
160 AllOf(Diag(Test.range(
"constructor"),
161 "single-argument constructors must be marked explicit to "
162 "avoid unintentional implicit conversions"),
163 WithFix(
Fix(Test.range(
"explicit"),
"explicit ",
164 "insert 'explicit '")))));
167 TEST(DiagnosticsTest, FlagsMatter) {
168 Annotations Test(
"[[void]] main() {} // error-ok");
170 EXPECT_THAT(TU.build().getDiagnostics(),
171 ElementsAre(AllOf(Diag(Test.range(),
"'main' must return 'int'"),
172 WithFix(
Fix(Test.range(),
"int",
173 "change 'void' to 'int'")))));
177 TU.build().getDiagnostics(),
179 Diag(Test.range(),
"return type of 'main' is not 'int'"),
180 WithFix(
Fix(Test.range(),
"int",
"change return type to 'int'")))));
183 TEST(DiagnosticsTest, DiagnosticPreamble) {
184 Annotations Test(R
"cpp(
185 #include $[["not-found.h"]] // error-ok
189 EXPECT_THAT(TU.build().getDiagnostics(),
190 ElementsAre(::testing::AllOf(
191 Diag(Test.range(),
"'not-found.h' file not found"),
192 DiagSource(
Diag::Clang), DiagName(
"pp_file_not_found"))));
195 TEST(DiagnosticsTest, DeduplicatedClangTidyDiagnostics) {
196 Annotations Test(R
"cpp(
197 float foo = [[0.1f]];
203 "hicpp-uppercase-literal-suffix";
206 TU.build().getDiagnostics(),
207 UnorderedElementsAre(::testing::AllOf(
209 "floating point literal has suffix 'f', which is not uppercase"),
212 Test = Annotations(R
"cpp(
222 TU.Code = std::string(Test.code());
226 TU.build().getDiagnostics(),
227 UnorderedElementsAre(::testing::AllOf(
229 "floating point literal has suffix 'f', which is not uppercase"),
233 TEST(DiagnosticsTest, ClangTidy) {
234 Annotations Test(R
"cpp(
235 #include $deprecated[["assert.h"]]
237 #define $macrodef[[SQUARE]](X) (X)*(X)
238 int $main[[main]]() {
240 return SQUARE($macroarg[[++]]y);
241 return $doubled[[sizeof]](sizeof(int));
247 "-*, bugprone-sizeof-expression, bugprone-macro-repeated-side-effects, "
248 "modernize-deprecated-headers, modernize-use-trailing-return-type";
250 TU.build().getDiagnostics(),
251 UnorderedElementsAre(
252 AllOf(Diag(Test.range(
"deprecated"),
253 "inclusion of deprecated C++ header 'assert.h'; consider "
254 "using 'cassert' instead"),
256 DiagName(
"modernize-deprecated-headers"),
257 WithFix(
Fix(Test.range(
"deprecated"),
"<cassert>",
258 "change '\"assert.h\"' to '<cassert>'"))),
259 Diag(Test.range(
"doubled"),
260 "suspicious usage of 'sizeof(sizeof(...))'"),
262 Diag(Test.range(
"macroarg"),
263 "side effects in the 1st macro argument 'X' are repeated in "
266 DiagName(
"bugprone-macro-repeated-side-effects"),
268 Diag(Test.range(
"macrodef"),
"macro 'SQUARE' defined here"))),
269 Diag(Test.range(
"macroarg"),
270 "multiple unsequenced modifications to 'y'"),
272 Diag(Test.range(
"main"),
273 "use a trailing return type for this function"),
275 DiagName(
"modernize-use-trailing-return-type"),
278 "use a trailing return type for this function")))));
281 TEST(DiagnosticTest, TemplatesInHeaders) {
283 Annotations Main(R
"cpp(
284 Derived<int> [[y]]; // error-ok
286 Annotations Header(R"cpp(
287 template <typename T>
288 struct Derived : [[T]] {};
293 TU.build().getDiagnostics(),
295 Diag(Main.range(), "in template: base specifier must name a class"),
296 WithNote(Diag(Header.range(),
"error occurred here"),
297 Diag(Main.range(),
"in instantiation of template class "
298 "'Derived<int>' requested here")))));
301 TEST(DiagnosticTest, MakeUnique) {
304 Annotations Main(R
"cpp(
305 struct S { S(char*); };
306 auto x = std::[[make_unique]]<S>(42); // error-ok
311 // These mocks aren't quite right - we omit unique_ptr for simplicity.
312 // forward is included to show its body is not needed to get the diagnostic.
313 template <typename T> T&& forward(T& t) { return static_cast<T&&>(t); }
314 template <typename T, typename... A> T* make_unique(A&&... args) {
315 return new T(std::forward<A>(args)...);
319 EXPECT_THAT(TU.build().getDiagnostics(),
320 UnorderedElementsAre(
323 "no matching constructor for initialization of 'S'")));
326 TEST(DiagnosticTest, NoMultipleDiagnosticInFlight) {
327 Annotations Main(R
"cpp(
328 template <typename T> struct Foo {
338 Foo<LabelInfo> label_info_map;
339 [[for]] (auto it = label_info_map.begin(); it != label_info_map.end(); ++it) {
347 TU.build().getDiagnostics(),
348 UnorderedElementsAre(::testing::AllOf(
349 Diag(Main.range(),
"use range-based for loop instead"),
353 TEST(DiagnosticTest, ClangTidySuppressionComment) {
354 Annotations Main(R
"cpp(
357 double d = 8 / i; // NOLINT
361 double f = BAD; // NOLINT
362 double g = [[8]] / i;
364 double h = BAD2; // NOLINT
370 TU.build().getDiagnostics(),
371 UnorderedElementsAre(::testing::AllOf(
372 Diag(Main.range(),
"result of integer division used in a floating "
373 "point context; possible loss of precision"),
377 TEST(DiagnosticTest, ClangTidyWarningAsError) {
378 Annotations Main(R
"cpp(
381 double f = [[8]] / i; // error-ok
386 TU.ClangTidyWarningsAsErrors =
"bugprone-integer-division";
388 TU.build().getDiagnostics(),
389 UnorderedElementsAre(::testing::AllOf(
390 Diag(Main.range(),
"result of integer division used in a floating "
391 "point context; possible loss of precision"),
396 TEST(DiagnosticTest, LongFixMessages) {
398 Annotations Source(R
"cpp(
401 int somereallyreallyreallyreallyreallyreallyreallyreallylongidentifier;
402 [[omereallyreallyreallyreallyreallyreallyreallyreallylongidentifier]]= 10;
407 TU.build().getDiagnostics(),
408 ElementsAre(WithFix(Fix(
410 "somereallyreallyreallyreallyreallyreallyreallyreallylongidentifier",
411 "change 'omereallyreallyreallyreallyreallyreallyreallyreall…' to "
412 "'somereallyreallyreallyreallyreallyreallyreallyreal…'"))));
414 Source = Annotations(R
"cpp(
419 n]] = 10; // error-ok
422 TU.Code = std::string(Source.code());
423 EXPECT_THAT(TU.build().getDiagnostics(),
425 Fix(Source.range(), "ident",
"change 'ide\\…' to 'ident'"))));
428 TEST(DiagnosticTest, ClangTidySuppressionCommentTrumpsWarningAsError) {
429 Annotations Main(R
"cpp(
432 double f = [[8]] / i; // NOLINT
437 TU.ClangTidyWarningsAsErrors =
"bugprone-integer-division";
438 EXPECT_THAT(TU.build().getDiagnostics(), UnorderedElementsAre());
441 TEST(DiagnosticsTest, Preprocessor) {
447 Annotations Test(R
"cpp(
450 int a = [[b]]; // error-ok
457 ElementsAre(Diag(Test.range(), "use of undeclared identifier 'b'")));
461 TEST(DiagnosticsTest, RecursivePreamble) {
463 #include "foo.h" // error-ok
466 TU.Filename = "foo.h";
467 EXPECT_THAT(TU.build().getDiagnostics(),
468 ElementsAre(DiagName(
"pp_including_mainfile_in_preamble")));
469 EXPECT_THAT(TU.build().getLocalTopLevelDecls(), SizeIs(1));
473 TEST(DiagnosticsTest, RecursivePreamblePragmaOnce) {
479 TU.Filename = "foo.h";
480 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
481 EXPECT_THAT(TU.build().getLocalTopLevelDecls(), SizeIs(1));
486 TEST(DiagnosticsTest, RecursivePreambleIfndefGuard) {
490 #include "foo.h" // error-ok
494 TU.Filename = "foo.h";
496 EXPECT_THAT(TU.build().getDiagnostics(),
497 ElementsAre(DiagName(
"pp_including_mainfile_in_preamble")));
498 EXPECT_THAT(TU.build().getLocalTopLevelDecls(), SizeIs(1));
501 TEST(DiagnosticsTest, InsideMacros) {
502 Annotations Test(R
"cpp(
504 #define RET(x) return x + 10
507 RET($foo[[0]]); // error-ok
514 ElementsAre(Diag(Test.range("foo"),
515 "cannot initialize return object of type "
516 "'int *' with an rvalue of type 'int'"),
517 Diag(Test.range(
"bar"),
518 "cannot initialize return object of type "
519 "'int *' with an rvalue of type 'int'")));
522 TEST(DiagnosticsTest, NoFixItInMacro) {
523 Annotations Test(R
"cpp(
524 #define Define(name) void name() {}
526 [[Define]](main) // error-ok
529 EXPECT_THAT(TU.build().getDiagnostics(),
530 ElementsAre(AllOf(Diag(Test.range(),
"'main' must return 'int'"),
534 TEST(ClangdTest, MSAsm) {
537 llvm::InitializeAllTargetInfos();
539 TU.ExtraArgs = {
"-fms-extensions"};
540 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
543 TEST(DiagnosticsTest, ToLSP) {
546 URIForFile HeaderFile =
550 D.ID = clang::diag::err_undeclared_var_use;
551 D.Name =
"undeclared_var_use";
553 D.Message =
"something terrible happened";
554 D.Range = {pos(1, 2), pos(3, 4)};
555 D.InsideMainFile =
true;
557 D.File =
"foo/bar/main.cpp";
558 D.AbsFile = std::string(
MainFile.file());
560 clangd::Note NoteInMain;
561 NoteInMain.Message =
"declared somewhere in the main file";
562 NoteInMain.Range = {pos(5, 6), pos(7, 8)};
563 NoteInMain.Severity = DiagnosticsEngine::Remark;
564 NoteInMain.File =
"../foo/bar/main.cpp";
565 NoteInMain.InsideMainFile =
true;
566 NoteInMain.AbsFile = std::string(
MainFile.file());
568 D.Notes.push_back(NoteInMain);
570 clangd::Note NoteInHeader;
571 NoteInHeader.Message =
"declared somewhere in the header file";
572 NoteInHeader.Range = {pos(9, 10), pos(11, 12)};
573 NoteInHeader.Severity = DiagnosticsEngine::Note;
574 NoteInHeader.File =
"../foo/baz/header.h";
575 NoteInHeader.InsideMainFile =
false;
576 NoteInHeader.AbsFile = std::string(HeaderFile.file());
577 D.Notes.push_back(NoteInHeader);
580 F.Message =
"do something";
581 D.Fixes.push_back(F);
585 MainLSP.range = D.Range;
587 MainLSP.code =
"undeclared_var_use";
588 MainLSP.source =
"clang";
590 R
"(Something terrible happened (fix available)
592 main.cpp:6:7: remark: declared somewhere in the main file
594 ../foo/baz/header.h:10:11:
595 note: declared somewhere in the header file)";
598 NoteInMainLSP.range = NoteInMain.Range;
599 NoteInMainLSP.severity = getSeverity(DiagnosticsEngine::Remark);
600 NoteInMainLSP.message = R"(Declared somewhere in the main file
602 main.cpp:2:3: error: something terrible happened)";
604 ClangdDiagnosticOptions Opts;
606 std::vector<std::pair<clangd::Diagnostic, std::vector<clangd::Fix>>> LSPDiags;
611 std::vector<clangd::Fix>(Fixes.begin(), Fixes.end())});
616 ElementsAre(Pair(EqualToLSPDiag(MainLSP), ElementsAre(EqualToFix(F))),
617 Pair(EqualToLSPDiag(NoteInMainLSP), IsEmpty())));
618 EXPECT_EQ(LSPDiags[0].first.code,
"undeclared_var_use");
619 EXPECT_EQ(LSPDiags[0].first.source,
"clang");
620 EXPECT_EQ(LSPDiags[1].first.code,
"");
621 EXPECT_EQ(LSPDiags[1].first.source,
"");
625 Opts.EmitRelatedLocations =
true;
630 std::vector<clangd::Fix>(Fixes.begin(), Fixes.end())});
632 MainLSP.message =
"Something terrible happened (fix available)";
633 DiagnosticRelatedInformation NoteInMainDRI;
634 NoteInMainDRI.message =
"Declared somewhere in the main file";
635 NoteInMainDRI.location.range = NoteInMain.Range;
636 NoteInMainDRI.location.uri =
MainFile;
637 MainLSP.relatedInformation = {NoteInMainDRI};
638 DiagnosticRelatedInformation NoteInHeaderDRI;
639 NoteInHeaderDRI.message =
"Declared somewhere in the header file";
640 NoteInHeaderDRI.location.range = NoteInHeader.Range;
641 NoteInHeaderDRI.location.uri = HeaderFile;
642 MainLSP.relatedInformation = {NoteInMainDRI, NoteInHeaderDRI};
643 EXPECT_THAT(LSPDiags, ElementsAre(Pair(EqualToLSPDiag(MainLSP),
644 ElementsAre(EqualToFix(F)))));
647 struct SymbolWithHeader {
649 std::string DeclaringFile;
650 std::string IncludeHeader;
653 std::unique_ptr<SymbolIndex>
654 buildIndexWithSymbol(llvm::ArrayRef<SymbolWithHeader> Syms) {
656 for (
const auto &S : Syms) {
657 Symbol Sym =
cls(S.QName);
659 Sym.CanonicalDeclaration.FileURI = S.DeclaringFile.c_str();
660 Sym.Definition.FileURI = S.DeclaringFile.c_str();
661 Sym.IncludeHeaders.emplace_back(S.IncludeHeader, 1);
664 return MemIndex::build(std::move(Slab).build(), RefSlab(), RelationSlab());
667 TEST(IncludeFixerTest, IncompleteType) {
668 Annotations Test(R
"cpp(// error-ok
669 $insert[[]]namespace ns {
671 $nested[[X::]]Nested n;
673 class Y : $base[[public ns::X]] {};
680 auto Index = buildIndexWithSymbol(
681 {SymbolWithHeader{
"ns::X",
"unittest:///x.h",
"\"x.h\""}});
682 TU.ExternalIndex =
Index.get();
685 TU.build().getDiagnostics(),
686 UnorderedElementsAre(
687 AllOf(Diag(Test.range(
"nested"),
688 "incomplete type 'ns::X' named in nested name specifier"),
689 WithFix(
Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
690 "Add include \"x.h\" for symbol ns::X"))),
691 AllOf(Diag(Test.range(
"base"),
"base class has incomplete type"),
692 WithFix(
Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
693 "Add include \"x.h\" for symbol ns::X"))),
694 AllOf(Diag(Test.range(
"access"),
695 "member access into incomplete type 'ns::X'"),
696 WithFix(
Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
697 "Add include \"x.h\" for symbol ns::X")))));
700 TEST(IncludeFixerTest, NoSuggestIncludeWhenNoDefinitionInHeader) {
701 Annotations Test(R
"cpp(// error-ok
702 $insert[[]]namespace ns {
705 class Y : $base[[public ns::X]] {};
712 Symbol Sym =
cls(
"ns::X");
714 Sym.CanonicalDeclaration.FileURI =
"unittest:///x.h";
715 Sym.Definition.FileURI =
"unittest:///x.cc";
716 Sym.IncludeHeaders.emplace_back(
"\"x.h\"", 1);
722 TU.ExternalIndex =
Index.get();
724 EXPECT_THAT(TU.build().getDiagnostics(),
725 UnorderedElementsAre(
726 Diag(Test.range(
"base"),
"base class has incomplete type"),
727 Diag(Test.range(
"access"),
728 "member access into incomplete type 'ns::X'")));
731 TEST(IncludeFixerTest, Typo) {
732 Annotations Test(R
"cpp(// error-ok
733 $insert[[]]namespace ns {
735 $unqualified1[[X]] x;
736 // No fix if the unresolved type is used as specifier. (ns::)X::Nested will be
737 // considered the unresolved type.
738 $unqualified2[[X]]::Nested n;
742 ns::$qualified1[[X]] x; // ns:: is valid.
743 ns::$qualified2[[X]](); // Error: no member in namespace
745 ::$global[[Global]] glob;
749 auto Index = buildIndexWithSymbol(
750 {SymbolWithHeader{
"ns::X",
"unittest:///x.h",
"\"x.h\""},
751 SymbolWithHeader{
"Global",
"unittest:///global.h",
"\"global.h\""}});
752 TU.ExternalIndex =
Index.get();
755 TU.build().getDiagnostics(),
756 UnorderedElementsAre(
757 AllOf(Diag(Test.range(
"unqualified1"),
"unknown type name 'X'"),
758 WithFix(
Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
759 "Add include \"x.h\" for symbol ns::X"))),
760 Diag(Test.range(
"unqualified2"),
"use of undeclared identifier 'X'"),
761 AllOf(Diag(Test.range(
"qualified1"),
762 "no type named 'X' in namespace 'ns'"),
763 WithFix(
Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
764 "Add include \"x.h\" for symbol ns::X"))),
765 AllOf(Diag(Test.range(
"qualified2"),
766 "no member named 'X' in namespace 'ns'"),
767 WithFix(
Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
768 "Add include \"x.h\" for symbol ns::X"))),
769 AllOf(Diag(Test.range(
"global"),
770 "no type named 'Global' in the global namespace"),
771 WithFix(
Fix(Test.range(
"insert"),
"#include \"global.h\"\n",
772 "Add include \"global.h\" for symbol Global")))));
775 TEST(IncludeFixerTest, MultipleMatchedSymbols) {
776 Annotations Test(R
"cpp(// error-ok
777 $insert[[]]namespace na {
786 auto Index = buildIndexWithSymbol(
787 {SymbolWithHeader{
"na::X",
"unittest:///a.h",
"\"a.h\""},
788 SymbolWithHeader{
"na::nb::X",
"unittest:///b.h",
"\"b.h\""}});
789 TU.ExternalIndex =
Index.get();
791 EXPECT_THAT(TU.build().getDiagnostics(),
792 UnorderedElementsAre(AllOf(
793 Diag(Test.range(
"unqualified"),
"unknown type name 'X'"),
794 WithFix(
Fix(Test.range(
"insert"),
"#include \"a.h\"\n",
795 "Add include \"a.h\" for symbol na::X"),
796 Fix(Test.range(
"insert"),
"#include \"b.h\"\n",
797 "Add include \"b.h\" for symbol na::nb::X")))));
800 TEST(IncludeFixerTest, NoCrashMemebrAccess) {
801 Annotations Test(R
"cpp(// error-ok
802 struct X { int xyz; };
803 void g() { X x; x.$[[xy]]; }
806 auto Index = buildIndexWithSymbol(
807 SymbolWithHeader{
"na::X",
"unittest:///a.h",
"\"a.h\""});
808 TU.ExternalIndex =
Index.get();
811 TU.build().getDiagnostics(),
812 UnorderedElementsAre(Diag(Test.range(),
"no member named 'xy' in 'X'")));
815 TEST(IncludeFixerTest, UseCachedIndexResults) {
818 Annotations Test(R
"cpp(// error-ok
819 $insert[[]]void foo() {
842 buildIndexWithSymbol(SymbolWithHeader{
"X",
"unittest:///a.h",
"\"a.h\""});
843 TU.ExternalIndex =
Index.get();
845 auto Parsed = TU.build();
846 for (
const auto &D : Parsed.getDiagnostics()) {
847 if (D.Fixes.size() != 1) {
848 ADD_FAILURE() <<
"D.Fixes.size() != 1";
851 EXPECT_EQ(D.Fixes[0].Message,
852 std::string(
"Add include \"a.h\" for symbol X"));
856 TEST(IncludeFixerTest, UnresolvedNameAsSpecifier) {
857 Annotations Test(R
"cpp(// error-ok
858 $insert[[]]namespace ns {
860 void g() { ns::$[[scope]]::X_Y(); }
863 TU.Code = std::string(Test.code());
865 TU.ExtraArgs.push_back(
"-fno-ms-compatibility");
866 auto Index = buildIndexWithSymbol(
867 SymbolWithHeader{
"ns::scope::X_Y",
"unittest:///x.h",
"\"x.h\""});
868 TU.ExternalIndex =
Index.get();
871 TU.build().getDiagnostics(),
872 UnorderedElementsAre(AllOf(
873 Diag(Test.range(),
"no member named 'scope' in namespace 'ns'"),
874 WithFix(
Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
875 "Add include \"x.h\" for symbol ns::scope::X_Y")))));
878 TEST(IncludeFixerTest, UnresolvedSpecifierWithSemaCorrection) {
879 Annotations Test(R
"cpp(// error-ok
880 $insert[[]]namespace clang {
882 // "clangd::" will be corrected to "clang::" by Sema.
883 $q1[[clangd]]::$x[[X]] x;
884 $q2[[clangd]]::$ns[[ns]]::Y y;
889 TU.Code = std::string(Test.code());
891 TU.ExtraArgs.push_back(
"-fno-ms-compatibility");
892 auto Index = buildIndexWithSymbol(
893 {SymbolWithHeader{
"clang::clangd::X",
"unittest:///x.h",
"\"x.h\""},
894 SymbolWithHeader{
"clang::clangd::ns::Y",
"unittest:///y.h",
"\"y.h\""}});
895 TU.ExternalIndex =
Index.get();
898 TU.build().getDiagnostics(),
899 UnorderedElementsAre(
901 Diag(Test.range(
"q1"),
"use of undeclared identifier 'clangd'; "
902 "did you mean 'clang'?"),
904 Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
905 "Add include \"x.h\" for symbol clang::clangd::X"))),
907 Diag(Test.range(
"x"),
"no type named 'X' in namespace 'clang'"),
908 WithFix(
Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
909 "Add include \"x.h\" for symbol clang::clangd::X"))),
911 Diag(Test.range(
"q2"),
"use of undeclared identifier 'clangd'; "
912 "did you mean 'clang'?"),
915 Fix(Test.range(
"insert"),
"#include \"y.h\"\n",
916 "Add include \"y.h\" for symbol clang::clangd::ns::Y"))),
917 AllOf(Diag(Test.range(
"ns"),
918 "no member named 'ns' in namespace 'clang'"),
920 Test.range(
"insert"),
"#include \"y.h\"\n",
921 "Add include \"y.h\" for symbol clang::clangd::ns::Y")))));
924 TEST(IncludeFixerTest, SpecifiedScopeIsNamespaceAlias) {
925 Annotations Test(R
"cpp(// error-ok
926 $insert[[]]namespace a {}
933 auto Index = buildIndexWithSymbol(
934 SymbolWithHeader{
"a::X",
"unittest:///x.h",
"\"x.h\""});
935 TU.ExternalIndex =
Index.get();
937 EXPECT_THAT(TU.build().getDiagnostics(),
938 UnorderedElementsAre(AllOf(
939 Diag(Test.range(),
"no type named 'X' in namespace 'a'"),
940 WithFix(
Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
941 "Add include \"x.h\" for symbol a::X")))));
944 TEST(IncludeFixerTest, NoCrashOnTemplateInstantiations) {
945 Annotations Test(R
"cpp(
946 template <typename T> struct Templ {
947 template <typename U>
948 typename U::type operator=(const U &);
953 A() { [[a]]; /*error-ok*/ } // crash if we compute scopes lazily.
958 auto Index = buildIndexWithSymbol({});
959 TU.ExternalIndex =
Index.get();
962 TU.build().getDiagnostics(),
963 ElementsAre(Diag(Test.range(),
"use of undeclared identifier 'a'")));
966 TEST(DiagsInHeaders, DiagInsideHeader) {
967 Annotations Main(R
"cpp(
970 Annotations Header("[[no_type_spec]]; // error-ok");
973 EXPECT_THAT(TU.build().getDiagnostics(),
974 UnorderedElementsAre(AllOf(
975 Diag(Main.range(),
"in included file: C++ requires a "
976 "type specifier for all declarations"),
977 WithNote(Diag(Header.range(),
"error occurred here")))));
980 TEST(DiagsInHeaders, DiagInTransitiveInclude) {
981 Annotations Main(R
"cpp(
986 {
"b.h",
"no_type_spec; // error-ok"}};
987 EXPECT_THAT(TU.build().getDiagnostics(),
988 UnorderedElementsAre(
989 Diag(Main.range(),
"in included file: C++ requires a "
990 "type specifier for all declarations")));
993 TEST(DiagsInHeaders, DiagInMultipleHeaders) {
994 Annotations Main(R
"cpp(
1000 {
"b.h",
"no_type_spec; // error-ok"}};
1001 EXPECT_THAT(TU.build().getDiagnostics(),
1002 UnorderedElementsAre(
1003 Diag(Main.range(
"a"),
"in included file: C++ requires a type "
1004 "specifier for all declarations"),
1005 Diag(Main.range(
"b"),
"in included file: C++ requires a type "
1006 "specifier for all declarations")));
1009 TEST(DiagsInHeaders, PreferExpansionLocation) {
1010 Annotations Main(R
"cpp(
1013 void foo() {})cpp");
1016 {"a.h",
"#include \"b.h\"\n"},
1017 {
"b.h",
"#ifndef X\n#define X\nno_type_spec; // error-ok\n#endif"}};
1018 EXPECT_THAT(TU.build().getDiagnostics(),
1019 UnorderedElementsAre(Diag(Main.range(),
1020 "in included file: C++ requires a type "
1021 "specifier for all declarations")));
1024 TEST(DiagsInHeaders, PreferExpansionLocationMacros) {
1025 Annotations Main(R
"cpp(
1030 void foo() {})cpp");
1033 {"a.h",
"#include \"c.h\"\n"},
1034 {
"b.h",
"#include \"c.h\"\n"},
1035 {
"c.h",
"#ifndef X\n#define X\nno_type_spec; // error-ok\n#endif"}};
1036 EXPECT_THAT(TU.build().getDiagnostics(),
1037 UnorderedElementsAre(
1038 Diag(Main.range(),
"in included file: C++ requires a "
1039 "type specifier for all declarations")));
1042 TEST(DiagsInHeaders, LimitDiagsOutsideMainFile) {
1043 Annotations Main(R
"cpp(
1046 void foo() {})cpp");
1049 {
"b.h",
"#include \"c.h\"\n"},
1053 no_type_spec_0; // error-ok
1065 EXPECT_THAT(TU.build().getDiagnostics(),
1066 UnorderedElementsAre(
1067 Diag(Main.range(), "in included file: C++ requires a "
1068 "type specifier for all declarations")));
1071 TEST(DiagsInHeaders, OnlyErrorOrFatal) {
1072 Annotations Main(R
"cpp(
1074 void foo() {})cpp");
1075 Annotations Header(R"cpp(
1076 [[no_type_spec]]; // error-ok
1080 EXPECT_THAT(TU.build().getDiagnostics(),
1081 UnorderedElementsAre(AllOf(
1082 Diag(Main.range(),
"in included file: C++ requires "
1083 "a type specifier for all declarations"),
1084 WithNote(Diag(Header.range(),
"error occurred here")))));
1087 TEST(DiagsInHeaders, OnlyDefaultErrorOrFatal) {
1088 Annotations Main(R
"cpp(
1089 #include [["a.h"]] // get unused "foo" warning when building preamble.
1091 Annotations Header(R"cpp(
1092 namespace { void foo() {} }
1093 void func() {foo();} ;)cpp");
1097 TU.ExtraArgs = {
"-Werror",
"-Wunused"};
1098 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
1101 TEST(DiagsInHeaders, FromNonWrittenSources) {
1102 Annotations Main(R
"cpp(
1104 void foo() {})cpp");
1105 Annotations Header(R"cpp(
1107 int b = [[FOO]]; // error-ok)cpp");
1110 TU.ExtraArgs = {
"-DFOO=NOOO"};
1111 EXPECT_THAT(TU.build().getDiagnostics(),
1112 UnorderedElementsAre(AllOf(
1114 "in included file: use of undeclared identifier 'NOOO'"),
1115 WithNote(Diag(Header.range(),
"error occurred here")))));
1118 TEST(DiagsInHeaders, ErrorFromMacroExpansion) {
1119 Annotations Main(R
"cpp(
1124 Annotations Header(R"cpp(
1129 EXPECT_THAT(TU.build().getDiagnostics(),
1130 UnorderedElementsAre(
1131 Diag(Main.range(),
"in included file: use of undeclared "
1132 "identifier 'foo'; did you mean 'fo'?")));
1135 TEST(DiagsInHeaders, ErrorFromMacroArgument) {
1136 Annotations Main(R
"cpp(
1141 Annotations Header(R"cpp(
1146 EXPECT_THAT(TU.build().getDiagnostics(),
1147 UnorderedElementsAre(
1148 Diag(Main.range(),
"in included file: use of undeclared "
1149 "identifier 'foo'; did you mean 'fo'?")));
1154 TU.ExtraArgs.push_back(
"--include=a.h");
1155 TU.AdditionalFiles = {{
"a.h",
"void main();"}};
1158 EXPECT_THAT(TU.build().getDiagnostics(), UnorderedElementsAre());
1161 TEST(ToLSPDiag, RangeIsInMain) {
1162 ClangdDiagnosticOptions Opts;
1164 D.Range = {pos(1, 2), pos(3, 4)};
1165 D.Notes.emplace_back();
1166 Note &N = D.Notes.back();
1167 N.Range = {pos(2, 3), pos(3, 4)};
1169 D.InsideMainFile =
true;
1170 N.InsideMainFile =
false;
1173 EXPECT_EQ(LSPDiag.range, D.Range);
1176 D.InsideMainFile =
false;
1177 N.InsideMainFile =
true;
1180 EXPECT_EQ(LSPDiag.range, N.Range);