9#include "../clang-tidy/ClangTidyOptions.h"
27#include "clang/AST/Decl.h"
28#include "clang/Basic/Diagnostic.h"
29#include "clang/Basic/DiagnosticSema.h"
30#include "clang/Basic/LLVM.h"
31#include "clang/Basic/Specifiers.h"
32#include "llvm/ADT/ArrayRef.h"
33#include "llvm/ADT/StringRef.h"
34#include "llvm/Support/JSON.h"
35#include "llvm/Support/ScopedPrinter.h"
36#include "llvm/Support/TargetSelect.h"
37#include "llvm/Testing/Support/SupportHelpers.h"
38#include "gmock/gmock.h"
39#include "gtest/gtest.h"
52using ::testing::AllOf;
53using ::testing::Contains;
55using ::testing::ElementsAre;
56using ::testing::Field;
57using ::testing::IsEmpty;
60using ::testing::SizeIs;
61using ::testing::UnorderedElementsAre;
63::testing::Matcher<const Diag &> withFix(::testing::Matcher<Fix> FixMatcher) {
67::testing::Matcher<const Diag &> withFix(::testing::Matcher<Fix> FixMatcher1,
68 ::testing::Matcher<Fix> FixMatcher2) {
72::testing::Matcher<const Diag &> withID(
unsigned ID) {
75::testing::Matcher<const Diag &>
76withNote(::testing::Matcher<Note> NoteMatcher) {
80::testing::Matcher<const Diag &>
81withNote(::testing::Matcher<Note> NoteMatcher1,
82 ::testing::Matcher<Note> NoteMatcher2) {
83 return Field(&
Diag::Notes, UnorderedElementsAre(NoteMatcher1, NoteMatcher2));
86::testing::Matcher<const Diag &>
87withTag(::testing::Matcher<DiagnosticTag> TagMatcher) {
94 "Diag at " + llvm::to_string(Range) +
" = [" + Message +
"]") {
98MATCHER_P3(Fix, Range, Replacement, Message,
99 "Fix " + llvm::to_string(Range) +
" => " +
100 ::testing::PrintToString(Replacement) +
" = [" + Message +
"]") {
101 return arg.Message ==
Message && arg.Edits.size() == 1 &&
102 arg.Edits[0].range ==
Range && arg.Edits[0].newText == Replacement;
108 "LSP diagnostic " + llvm::to_string(LSPDiag)) {
110 *result_listener << llvm::formatv(
"expected:\n{0:2}\ngot\n{1:2}",
118MATCHER_P(diagSource, S,
"") {
return arg.Source == S; }
119MATCHER_P(diagName, N,
"") {
return arg.Name == N; }
120MATCHER_P(diagSeverity, S,
"") {
return arg.Severity == S; }
122MATCHER_P(equalToFix, Fix,
"LSP fix " + llvm::to_string(Fix)) {
123 if (arg.Message != Fix.Message)
125 if (arg.Edits.size() != Fix.Edits.size())
127 for (std::size_t I = 0; I < arg.Edits.size(); ++I) {
128 if (arg.Edits[I].range != Fix.Edits[I].range ||
129 arg.Edits[I].newText != Fix.Edits[I].newText)
136Position pos(
int Line,
int Character) {
148::testing::Matcher<std::vector<clangd::Diag>>
149ifTidyChecks(::testing::Matcher<std::vector<clangd::Diag>>
M) {
150 if (!CLANGD_TIDY_CHECKS)
155TEST(DiagnosticsTest, DiagnosticRanges) {
157 Annotations Test(R
"cpp(
163 struct Container { int* begin(); int* end(); } *container;
164 for (auto i : $insertstar[[]]$range[[container]]) {
169 foo()$semicolon[[]]//with comments
171 double $type[[bar]] = "foo";
172 struct Foo { int x; }; Foo a;
174 test::$nomembernamespace[[test]];
175 $macro[[ID($macroarg[[fod]])]]();
180 TU.build().getDiagnostics(),
183 AllOf(Diag(Test.range(
"range"),
184 "invalid range expression of type 'struct Container *'; "
185 "did you mean to dereference it with '*'?"),
186 withFix(Fix(Test.range(
"insertstar"),
"*",
"insert '*'"))),
188 AllOf(Diag(Test.range(
"typo"),
189 "use of undeclared identifier 'goo'; did you mean 'foo'?"),
190 diagSource(
Diag::Clang), diagName(
"undeclared_var_use_suggest"),
192 Fix(Test.range(
"typo"),
"foo",
"change 'go\\…' to 'foo'")),
194 withNote(Diag(Test.range(
"decl"),
"'foo' declared here"))),
198 AllOf(Diag(Test.range(
"semicolon"),
"expected ';' after expression"),
199 withFix(Fix(Test.range(
"semicolon"),
";",
"insert ';'"))),
201 Diag(Test.range(
"unk"),
"use of undeclared identifier 'unknown'"),
202 Diag(Test.range(
"type"),
203 "cannot initialize a variable of type 'double' with an lvalue "
204 "of type 'const char[4]'"),
205 Diag(Test.range(
"nomember"),
"no member named 'y' in 'Foo'"),
206 Diag(Test.range(
"nomembernamespace"),
207 "no member named 'test' in namespace 'test'"),
208 AllOf(Diag(Test.range(
"macro"),
209 "use of undeclared identifier 'fod'; did you mean 'foo'?"),
210 withFix(Fix(Test.range(
"macroarg"),
"foo",
211 "change 'fod' to 'foo'")))));
219TEST(DiagnosticsTest, WSwitch) {
220 Annotations Test(R
"cpp(
228 TU.ExtraArgs = {
"-Wswitch"};
229 EXPECT_THAT(TU.build().getDiagnostics(),
230 ElementsAre(Diag(Test.range(),
231 "enumeration value 'X' not handled in switch")));
234TEST(DiagnosticsTest, FlagsMatter) {
235 Annotations Test(
"[[void]] main() {} // error-ok");
237 EXPECT_THAT(TU.build().getDiagnostics(),
238 ElementsAre(AllOf(Diag(Test.range(),
"'main' must return 'int'"),
239 withFix(Fix(Test.range(),
"int",
240 "change 'void' to 'int'")))));
242 TU.Filename =
"Plain.c";
244 TU.build().getDiagnostics(),
246 Diag(Test.range(),
"return type of 'main' is not 'int'"),
247 withFix(Fix(Test.range(),
"int",
"change return type to 'int'")))));
250TEST(DiagnosticsTest, DiagnosticPreamble) {
251 Annotations Test(R
"cpp(
252 #include $[["not-found.h"]] // error-ok
256 EXPECT_THAT(TU.build().getDiagnostics(),
257 ElementsAre(::testing::AllOf(
258 Diag(Test.range(),
"'not-found.h' file not found"),
259 diagSource(
Diag::Clang), diagName(
"pp_file_not_found"))));
262TEST(DiagnosticsTest, DeduplicatedClangTidyDiagnostics) {
263 Annotations Test(R
"cpp(
264 float foo = [[0.1f]];
269 TU.ClangTidyProvider =
addTidyChecks(
"readability-uppercase-literal-suffix,"
270 "hicpp-uppercase-literal-suffix");
273 TU.build().getDiagnostics(),
274 ifTidyChecks(UnorderedElementsAre(::testing::AllOf(
276 "floating point literal has suffix 'f', which is not uppercase"),
279 Test = Annotations(R
"cpp(
289 TU.Code = std::string(Test.code());
293 TU.build().getDiagnostics(),
294 ifTidyChecks(UnorderedElementsAre(::testing::AllOf(
296 "floating point literal has suffix 'f', which is not uppercase"),
300TEST(DiagnosticsTest, ClangTidy) {
301 Annotations Test(R
"cpp(
302 #include $deprecated[["assert.h"]]
304 #define $macrodef[[SQUARE]](X) (X)*(X)
305 int $main[[main]]() {
307 return SQUARE($macroarg[[++]]y);
308 return $doubled[[sizeof(sizeof(int))]];
311 // misc-no-recursion uses a custom traversal from the TUDecl
321 TU.HeaderFilename =
"assert.h";
322 TU.ClangTidyProvider =
addTidyChecks(
"bugprone-sizeof-expression,"
323 "bugprone-macro-repeated-side-effects,"
324 "modernize-deprecated-headers,"
325 "modernize-use-trailing-return-type,"
326 "misc-no-recursion");
327 TU.ExtraArgs.push_back(
"-Wno-unsequenced");
329 TU.build().getDiagnostics(),
330 ifTidyChecks(UnorderedElementsAre(
331 AllOf(Diag(Test.range(
"deprecated"),
332 "inclusion of deprecated C++ header 'assert.h'; consider "
333 "using 'cassert' instead"),
335 diagName(
"modernize-deprecated-headers"),
336 withFix(Fix(Test.range(
"deprecated"),
"<cassert>",
337 "change '\"assert.h\"' to '<cassert>'"))),
338 Diag(Test.range(
"doubled"),
339 "suspicious usage of 'sizeof(sizeof(...))'"),
340 AllOf(Diag(Test.range(
"macroarg"),
341 "side effects in the 1st macro argument 'X' are "
345 diagName(
"bugprone-macro-repeated-side-effects"),
346 withNote(Diag(Test.range(
"macrodef"),
347 "macro 'SQUARE' defined here"))),
348 AllOf(Diag(Test.range(
"main"),
349 "use a trailing return type for this function"),
351 diagName(
"modernize-use-trailing-return-type"),
354 "use a trailing return type for this function"))),
355 Diag(Test.range(
"foo"),
356 "function 'foo' is within a recursive call chain"),
357 Diag(Test.range(
"bar"),
358 "function 'bar' is within a recursive call chain"))));
361TEST(DiagnosticsTest, ClangTidyEOF) {
363 Annotations Test(R
"cpp(
365 #include "a.h")cpp");
368 TU.ExtraArgs = {
"-isystem."};
369 TU.AdditionalFiles[
"a.h"] = TU.AdditionalFiles[
"b.h"] =
"";
372 TU.build().getDiagnostics(),
373 ifTidyChecks(Contains(
374 AllOf(Diag(Test.range(),
"#includes are not sorted properly"),
378TEST(DiagnosticTest, TemplatesInHeaders) {
380 Annotations Main(R
"cpp(
381 Derived<int> [[y]]; // error-ok
383 Annotations Header(R"cpp(
384 template <typename T>
385 struct Derived : [[T]] {};
388 TU.HeaderCode = Header.code().str();
390 TU.build().getDiagnostics(),
392 Diag(Main.range(), "in template: base specifier must name a class"),
393 withNote(Diag(Header.range(),
"error occurred here"),
394 Diag(Main.range(),
"in instantiation of template class "
395 "'Derived<int>' requested here")))));
398TEST(DiagnosticTest, MakeUnique) {
401 Annotations Main(R
"cpp(
402 struct S { S(char*); };
403 auto x = std::[[make_unique]]<S>(42); // error-ok
406 TU.HeaderCode = R"cpp(
408 // These mocks aren't quite right - we omit unique_ptr for simplicity.
409 // forward is included to show its body is not needed to get the diagnostic.
410 template <typename T> T&& forward(T& t);
411 template <typename T, typename... A> T* make_unique(A&&... args) {
412 return new T(std::forward<A>(args)...);
416 EXPECT_THAT(TU.build().getDiagnostics(),
417 UnorderedElementsAre(
420 "no matching constructor for initialization of 'S'")));
423TEST(DiagnosticTest, CoroutineInHeader) {
424 StringRef CoroutineH = R
"cpp(
426template <class Ret, typename... T>
427struct coroutine_traits { using promise_type = typename Ret::promise_type; };
429template <class Promise = void>
430struct coroutine_handle {
431 static coroutine_handle from_address(void *) noexcept;
432 static coroutine_handle from_promise(Promise &promise);
433 constexpr void* address() const noexcept;
436struct coroutine_handle<void> {
437 template <class PromiseType>
438 coroutine_handle(coroutine_handle<PromiseType>) noexcept;
439 static coroutine_handle from_address(void *);
440 constexpr void* address() const noexcept;
444 bool await_ready() noexcept { return false; }
445 void await_suspend(coroutine_handle<>) noexcept {}
446 void await_resume() noexcept {}
451 StringRef Header = R"cpp(
452#include "coroutine.h"
453template <typename T> struct [[clang::coro_return_type]] Gen {
454 struct promise_type {
455 Gen<T> get_return_object() {
458 std::awaitable initial_suspend();
459 std::awaitable final_suspend() noexcept;
460 void unhandled_exception();
461 void return_value(T t);
465Gen<int> foo_coro(int b) { co_return b; }
467 Annotations Main(R"cpp(
470Gen<int> $[[bar_coro]](int b) { return foo_coro(b); }
473 TU.AdditionalFiles["coroutine.h"] = std::string(CoroutineH);
474 TU.AdditionalFiles[
"header.hpp"] = std::string(Header);
475 TU.ExtraArgs.push_back(
"--std=c++20");
476 EXPECT_THAT(TU.build().getDiagnostics(), ElementsAre(hasRange(Main.range())));
479TEST(DiagnosticTest, MakeShared) {
482 Annotations Main(R
"cpp(
483 struct S { S(char*); };
484 auto x = std::[[make_shared]]<S>(42); // error-ok
487 TU.HeaderCode = R"cpp(
489 // These mocks aren't quite right - we omit shared_ptr for simplicity.
490 // forward is included to show its body is not needed to get the diagnostic.
491 template <typename T> T&& forward(T& t);
492 template <typename T, typename... A> T* make_shared(A&&... args) {
493 return new T(std::forward<A>(args)...);
497 TU.ParseOpts.PreambleParseForwardingFunctions = true;
498 EXPECT_THAT(TU.build().getDiagnostics(),
499 UnorderedElementsAre(
502 "no matching constructor for initialization of 'S'")));
505TEST(DiagnosticTest, NoMultipleDiagnosticInFlight) {
506 Annotations Main(R
"cpp(
507 template <typename T> struct Foo {
517 Foo<LabelInfo> label_info_map;
518 [[for]] (auto it = label_info_map.begin(); it != label_info_map.end(); ++it) {
524 TU.ClangTidyProvider = addTidyChecks("modernize-loop-convert");
526 TU.build().getDiagnostics(),
527 ifTidyChecks(UnorderedElementsAre(::testing::AllOf(
528 Diag(Main.range(),
"use range-based for loop instead"),
532TEST(DiagnosticTest, RespectsDiagnosticConfig) {
533 Annotations Main(R
"cpp(
542 TU.build().getDiagnostics(),
543 ElementsAre(Diag(Main.range(),
"use of undeclared identifier 'unknown'"),
544 Diag(Main.range(
"ret"),
545 "void function 'x' should not return a value")));
548 WithContextValue WithCfg(
Config::Key, std::move(Cfg));
549 EXPECT_THAT(TU.build().getDiagnostics(),
550 ElementsAre(Diag(Main.range(),
551 "use of undeclared identifier 'unknown'")));
554TEST(DiagnosticTest, RespectsDiagnosticConfigInHeader) {
555 Annotations Header(R
"cpp(
556 int x = "42"; // error-ok
558 Annotations Main(R"cpp(
559 #include "header.hpp"
562 TU.AdditionalFiles[
"header.hpp"] = std::string(Header.code());
565 WithContextValue WithCfg(
Config::Key, std::move(Cfg));
566 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
569TEST(DiagnosticTest, ClangTidySuppressionComment) {
570 Annotations Main(R
"cpp(
573 double d = 8 / i; // NOLINT
577 double f = BAD; // NOLINT
578 double g = [[8]] / i;
580 double h = BAD2; // NOLINT
586 // verify no crashes on unmatched nolints.
591 TU.ClangTidyProvider = addTidyChecks("bugprone-integer-division");
593 TU.build().getDiagnostics(),
594 ifTidyChecks(UnorderedElementsAre(::testing::AllOf(
595 Diag(Main.range(),
"result of integer division used in a floating "
596 "point context; possible loss of precision"),
598 diagName(
"bugprone-integer-division")))));
601TEST(DiagnosticTest, ClangTidySystemMacro) {
602 Annotations Main(R
"cpp(
606 double x = $inline[[8]] / i;
607 double y = $user[[DIVIDE_USER]](i);
608 double z = DIVIDE_SYS(i);
612 TU.AdditionalFiles[
"user.h"] = R
"cpp(
613 #define DIVIDE_USER(Y) 8/Y
615 TU.AdditionalFiles["system.h"] = R
"cpp(
616 #pragma clang system_header
617 #define DIVIDE_SYS(Y) 8/Y
620 TU.ClangTidyProvider = addTidyChecks("bugprone-integer-division");
621 std::string BadDivision =
"result of integer division used in a floating "
622 "point context; possible loss of precision";
626 EXPECT_THAT(TU.build().getDiagnostics(),
628 UnorderedElementsAre(Diag(Main.range(
"inline"), BadDivision),
629 Diag(Main.range(
"user"), BadDivision))));
632TEST(DiagnosticTest, ClangTidyWarningAsError) {
633 Annotations Main(R
"cpp(
636 double f = [[8]] / i; // error-ok
640 TU.ClangTidyProvider =
641 addTidyChecks("bugprone-integer-division",
"bugprone-integer-division");
643 TU.build().getDiagnostics(),
644 ifTidyChecks(UnorderedElementsAre(::testing::AllOf(
645 Diag(Main.range(),
"result of integer division used in a floating "
646 "point context; possible loss of precision"),
648 diagSeverity(DiagnosticsEngine::Error)))));
651TidyProvider addClangArgs(std::vector<llvm::StringRef> ExtraArgs,
652 llvm::StringRef Checks) {
653 return [ExtraArgs = std::move(ExtraArgs), Checks = Checks.str()](
654 tidy::ClangTidyOptions &Opts, llvm::StringRef) {
656 Opts.ExtraArgs.emplace();
657 for (llvm::StringRef Arg : ExtraArgs)
658 Opts.ExtraArgs->emplace_back(Arg);
660 Opts.Checks = Checks;
664TEST(DiagnosticTest, ClangTidyEnablesClangWarning) {
665 Annotations Main(R
"cpp( // error-ok
666 static void [[foo]]() {}
670 auto UnusedFooWarning =
671 AllOf(Diag(Main.range(),
"unused function 'foo'"),
672 diagName(
"-Wunused-function"), diagSource(
Diag::Clang),
673 diagSeverity(DiagnosticsEngine::Warning));
676 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
680 TU.ClangTidyProvider =
681 addClangArgs({
"-Wunused"},
"clang-diagnostic-unused-function");
682 EXPECT_THAT(TU.build().getDiagnostics(), ElementsAre(UnusedFooWarning));
685 TU.ClangTidyProvider = addClangArgs({
"-Wunused"},
"clang-diagnostic-*");
686 EXPECT_THAT(TU.build().getDiagnostics(), ElementsAre(UnusedFooWarning));
688 TU.ClangTidyProvider = addClangArgs({
"-Wunused"},
"*");
689 EXPECT_THAT(TU.build().getDiagnostics(), Contains(UnusedFooWarning));
691 TU.ClangTidyProvider = addClangArgs(
692 {
"-Wunused"},
"clang-diagnostic-*,-clang-diagnostic-unused-function");
693 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
696 TU.ClangTidyProvider = addClangArgs({
"-Wunused"},
"clang-diagnostic-unused");
697 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
700 TU.ClangTidyProvider = addClangArgs({
"-Wunused",
"-Dfoo=bar"},
701 "clang-diagnostic-unused-function");
702 EXPECT_THAT(TU.build().getDiagnostics(), ElementsAre(UnusedFooWarning))
703 <<
"Not unused function 'bar'!";
706 TU.ExtraArgs = {
"-Werror"};
707 TU.ClangTidyProvider =
708 addClangArgs({
"-Wunused"},
"clang-diagnostic-unused-function");
709 EXPECT_THAT(TU.build().getDiagnostics(),
710 ElementsAre(diagSeverity(DiagnosticsEngine::Warning)));
713 TU.ExtraArgs = {
"-Wunused",
"-Werror"};
714 TU.ClangTidyProvider =
715 addClangArgs({
"-Wunused"},
"clang-diagnostic-unused-function");
716 EXPECT_THAT(TU.build().getDiagnostics(),
717 ElementsAre(diagSeverity(DiagnosticsEngine::Error)));
720 TU.ExtraArgs = {
"-Wunused-function",
"-Werror"};
721 TU.ClangTidyProvider =
722 addClangArgs({
"-Wunused"},
"clang-diagnostic-unused-label");
723 EXPECT_THAT(TU.build().getDiagnostics(),
724 ElementsAre(diagSeverity(DiagnosticsEngine::Warning)));
729 TU.ClangTidyProvider = addClangArgs({
"-Wunused",
"-Wno-unused"},
730 "clang-diagnostic-unused-function");
731 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
734 TU.ClangTidyProvider =
735 addClangArgs({
"-Wunused"}, {
"clang-diagnostic-unused-function"});
736 EXPECT_THAT(TU.build().getDiagnostics(), SizeIs(1));
739 TU.ClangTidyProvider = addClangArgs({
"-Wunused",
"-Wno-unused-function"},
740 {
"clang-diagnostic-unused-function"});
741 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
742 TU.ClangTidyProvider = addClangArgs({
"-Wunused-function",
"-Wno-unused"},
743 {
"clang-diagnostic-unused-function"});
744 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
748 TU.ExtraArgs = {
"-Wunused"};
749 TU.ClangTidyProvider = addClangArgs({
"-Wno-unused"}, {});
750 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
753TEST(DiagnosticTest, LongFixMessages) {
755 Annotations Source(R
"cpp(
758 int somereallyreallyreallyreallyreallyreallyreallyreallylongidentifier;
759 [[omereallyreallyreallyreallyreallyreallyreallyreallylongidentifier]]= 10;
764 TU.build().getDiagnostics(),
765 ElementsAre(withFix(Fix(
767 "somereallyreallyreallyreallyreallyreallyreallyreallylongidentifier",
768 "change 'omereallyreallyreallyreallyreallyreallyreallyreall…' to "
769 "'somereallyreallyreallyreallyreallyreallyreallyreal…'"))));
771 Source = Annotations(R
"cpp(
779 TU.Code = std::string(Source.code());
780 EXPECT_THAT(TU.build().getDiagnostics(),
782 Fix(Source.range(), "ident",
"change 'ide\\…' to 'ident'"))));
785TEST(DiagnosticTest, NewLineFixMessage) {
786 Annotations Source(
"int a;[[]]");
788 TU.ExtraArgs = {
"-Wnewline-eof"};
790 TU.build().getDiagnostics(),
791 ElementsAre(withFix((Fix(Source.range(),
"\n",
"insert '\\n'")))));
794TEST(DiagnosticTest, ClangTidySuppressionCommentTrumpsWarningAsError) {
795 Annotations Main(R
"cpp(
798 double f = [[8]] / i; // NOLINT
802 TU.ClangTidyProvider =
803 addTidyChecks("bugprone-integer-division",
"bugprone-integer-division");
804 EXPECT_THAT(TU.build().getDiagnostics(), UnorderedElementsAre());
807TEST(DiagnosticTest, ClangTidyNoLiteralDataInMacroToken) {
808 Annotations Main(R
"cpp(
810 using pthread_t = int;
811 int pthread_kill(pthread_t thread, int sig);
814 return pthread_kill(thread, 0);
818 TU.ClangTidyProvider = addTidyChecks("bugprone-bad-signal-to-kill-thread");
819 EXPECT_THAT(TU.build().getDiagnostics(), UnorderedElementsAre());
822TEST(DiagnosticTest, ElseAfterReturnRange) {
823 Annotations Main(R
"cpp(
827 } [[else]] if (cond == 2) {
834 TU.ClangTidyProvider = addTidyChecks("llvm-else-after-return");
835 EXPECT_THAT(TU.build().getDiagnostics(),
836 ifTidyChecks(ElementsAre(
837 Diag(Main.range(),
"do not use 'else' after 'return'"))));
840TEST(DiagnosticTest, ClangTidySelfContainedDiags) {
841 Annotations Main(R
"cpp($MathHeader[[]]
849 void InitVariables() {
850 float $C[[C]]$CFix[[]];
851 double $D[[D]]$DFix[[]];
855 TU.ClangTidyProvider =
857 "cppcoreguidelines-init-variables");
858 clangd::Fix ExpectedAFix;
859 ExpectedAFix.Message =
860 "'A' should be initialized in a member initializer of the constructor";
861 ExpectedAFix.Edits.push_back(TextEdit{Main.range(
"Fix"),
" : A(1)"});
862 ExpectedAFix.Edits.push_back(TextEdit{Main.range(
"A"),
""});
867 clangd::Fix ExpectedBFix;
868 ExpectedBFix.Message =
869 "'B' should be initialized in a member initializer of the constructor";
870 ExpectedBFix.Edits.push_back(TextEdit{Main.range(
"Fix"),
" : B(1)"});
871 ExpectedBFix.Edits.push_back(TextEdit{Main.range(
"B"),
""});
873 clangd::Fix ExpectedCFix;
874 ExpectedCFix.Message =
"variable 'C' is not initialized";
875 ExpectedCFix.Edits.push_back(TextEdit{Main.range(
"CFix"),
" = NAN"});
876 ExpectedCFix.Edits.push_back(
877 TextEdit{Main.range(
"MathHeader"),
"#include <math.h>\n\n"});
881 clangd::Fix ExpectedDFix;
882 ExpectedDFix.Message =
"variable 'D' is not initialized";
883 ExpectedDFix.Edits.push_back(TextEdit{Main.range(
"DFix"),
" = NAN"});
884 ExpectedDFix.Edits.push_back(
885 TextEdit{Main.range(
"MathHeader"),
"#include <math.h>\n\n"});
887 TU.build().getDiagnostics(),
888 ifTidyChecks(UnorderedElementsAre(
889 AllOf(Diag(Main.range(
"A"),
"'A' should be initialized in a member "
890 "initializer of the constructor"),
891 withFix(equalToFix(ExpectedAFix))),
892 AllOf(Diag(Main.range(
"B"),
"'B' should be initialized in a member "
893 "initializer of the constructor"),
894 withFix(equalToFix(ExpectedBFix))),
895 AllOf(Diag(Main.range(
"C"),
"variable 'C' is not initialized"),
896 withFix(equalToFix(ExpectedCFix))),
897 AllOf(Diag(Main.range(
"D"),
"variable 'D' is not initialized"),
898 withFix(equalToFix(ExpectedDFix))))));
901TEST(DiagnosticTest, ClangTidySelfContainedDiagsFormatting) {
902 Annotations Main(R
"cpp(
905 virtual void Reset1() = 0;
906 virtual void Reset2() = 0;
908 class A : public Interface {
909 // This will be marked by clangd to use override instead of virtual
910 $virtual1[[virtual ]]void $Reset1[[Reset1]]()$override1[[]];
911 $virtual2[[virtual ]]/**/void $Reset2[[Reset2]]()$override2[[]];
915 TU.ClangTidyProvider =
916 addTidyChecks("cppcoreguidelines-explicit-virtual-functions,");
917 clangd::Fix
const ExpectedFix1{
918 "prefer using 'override' or (rarely) 'final' "
919 "instead of 'virtual'",
920 {TextEdit{Main.range(
"override1"),
" override"},
921 TextEdit{Main.range(
"virtual1"),
""}},
923 clangd::Fix
const ExpectedFix2{
924 "prefer using 'override' or (rarely) 'final' "
925 "instead of 'virtual'",
926 {TextEdit{Main.range(
"override2"),
" override"},
927 TextEdit{Main.range(
"virtual2"),
""}},
931 EXPECT_THAT(TU.build().getDiagnostics(),
932 ifTidyChecks(UnorderedElementsAre(
933 AllOf(Diag(Main.range(
"Reset1"),
934 "prefer using 'override' or (rarely) 'final' "
935 "instead of 'virtual'"),
936 withFix(equalToFix(ExpectedFix1))),
937 AllOf(Diag(Main.range(
"Reset2"),
938 "prefer using 'override' or (rarely) 'final' "
939 "instead of 'virtual'"),
940 withFix(equalToFix(ExpectedFix2))))));
943TEST(DiagnosticsTest, Preprocessor) {
949 Annotations Test(R
"cpp(
952 int a = [[b]]; // error-ok
959 ElementsAre(Diag(Test.range(), "use of undeclared identifier 'b'")));
962TEST(DiagnosticsTest, IgnoreVerify) {
964 int a; // expected-error {{}}
966 TU.ExtraArgs.push_back("-Xclang");
967 TU.ExtraArgs.push_back(
"-verify");
968 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
971TEST(DiagnosticTest, IgnoreBEFilelistOptions) {
973 TU.ExtraArgs.push_back(
"-Xclang");
974 for (
const auto *DisableOption :
975 {
"-fsanitize-ignorelist=null",
"-fprofile-list=null",
976 "-fxray-always-instrument=null",
"-fxray-never-instrument=null",
977 "-fxray-attr-list=null"}) {
978 TU.ExtraArgs.push_back(DisableOption);
979 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
980 TU.ExtraArgs.pop_back();
985TEST(DiagnosticsTest, RecursivePreamble) {
987 #include "foo.h" // error-ok
990 TU.Filename = "foo.h";
991 EXPECT_THAT(TU.build().getDiagnostics(),
992 ElementsAre(diagName(
"pp_including_mainfile_in_preamble")));
993 EXPECT_THAT(TU.build().getLocalTopLevelDecls(), SizeIs(1));
997TEST(DiagnosticsTest, RecursivePreamblePragmaOnce) {
1003 TU.Filename = "foo.h";
1004 EXPECT_THAT(TU.build().getDiagnostics(),
1005 Not(Contains(diagName(
"pp_including_mainfile_in_preamble"))));
1006 EXPECT_THAT(TU.build().getLocalTopLevelDecls(), SizeIs(1));
1011TEST(DiagnosticsTest, RecursivePreambleIfndefGuard) {
1015 #include "foo.h" // error-ok
1019 TU.Filename = "foo.h";
1021 EXPECT_THAT(TU.build().getDiagnostics(),
1022 ElementsAre(diagName(
"pp_including_mainfile_in_preamble")));
1023 EXPECT_THAT(TU.build().getLocalTopLevelDecls(), SizeIs(1));
1026TEST(DiagnosticsTest, PreambleWithPragmaAssumeNonnull) {
1028#pragma clang assume_nonnull begin
1030#pragma clang assume_nonnull end
1032 auto AST = TU.build();
1033 EXPECT_THAT(
AST.getDiagnostics(), IsEmpty());
1034 const auto *
X = cast<FunctionDecl>(
findDecl(
AST,
"foo")).getParamDecl(0);
1035 ASSERT_TRUE(
X->getOriginalType()->getNullability() ==
1036 NullabilityKind::NonNull);
1039TEST(DiagnosticsTest, PreambleHeaderWithBadPragmaAssumeNonnull) {
1040 Annotations Header(R
"cpp(
1041#pragma clang assume_nonnull begin // error-ok
1045#include "foo.h" // unterminated assume_nonnull should not affect bar.
1048 TU.AdditionalFiles = {{"foo.h", std::string(Header.code())}};
1049 auto AST = TU.build();
1050 EXPECT_THAT(
AST.getDiagnostics(),
1051 ElementsAre(diagName(
"pp_eof_in_assume_nonnull")));
1052 const auto *
X = cast<FunctionDecl>(
findDecl(
AST,
"foo")).getParamDecl(0);
1053 ASSERT_TRUE(
X->getOriginalType()->getNullability() ==
1054 NullabilityKind::NonNull);
1055 const auto *Y = cast<FunctionDecl>(
findDecl(
AST,
"bar")).getParamDecl(0);
1056 ASSERT_FALSE(Y->getOriginalType()->getNullability());
1059TEST(DiagnosticsTest, InsideMacros) {
1060 Annotations Test(R
"cpp(
1062 #define RET(x) return x + 10
1065 RET($foo[[0]]); // error-ok
1072 ElementsAre(Diag(Test.range("foo"),
1073 "cannot initialize return object of type "
1074 "'int *' with an rvalue of type 'int'"),
1075 Diag(Test.range(
"bar"),
1076 "cannot initialize return object of type "
1077 "'int *' with an rvalue of type 'int'")));
1080TEST(DiagnosticsTest, NoFixItInMacro) {
1081 Annotations Test(R
"cpp(
1082 #define Define(name) void name() {}
1084 [[Define]](main) // error-ok
1087 EXPECT_THAT(TU.build().getDiagnostics(),
1088 ElementsAre(AllOf(Diag(Test.range(),
"'main' must return 'int'"),
1092TEST(DiagnosticsTest, PragmaSystemHeader) {
1093 Annotations Test(
"#pragma clang [[system_header]]\n");
1096 TU.build().getDiagnostics(),
1098 Diag(Test.range(),
"#pragma system_header ignored in main file"))));
1099 TU.Filename =
"TestTU.h";
1100 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
1103TEST(ClangdTest, MSAsm) {
1106 llvm::InitializeAllTargetInfos();
1108 TU.ExtraArgs = {
"-fms-extensions"};
1109 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
1112TEST(DiagnosticsTest, ToLSP) {
1115 URIForFile HeaderFile =
1119 D.ID = clang::diag::err_undeclared_var_use;
1121 D.Name =
"undeclared_var_use";
1123 D.Message =
"something terrible happened";
1124 D.Range = {pos(1, 2), pos(3, 4)};
1125 D.InsideMainFile =
true;
1126 D.Severity = DiagnosticsEngine::Error;
1127 D.File =
"foo/bar/main.cpp";
1128 D.AbsFile = std::string(
MainFile.file());
1129 D.OpaqueData[
"test"] =
"bar";
1131 clangd::Note NoteInMain;
1132 NoteInMain.Message =
"declared somewhere in the main file";
1133 NoteInMain.Range = {pos(5, 6), pos(7, 8)};
1134 NoteInMain.Severity = DiagnosticsEngine::Remark;
1135 NoteInMain.File =
"../foo/bar/main.cpp";
1136 NoteInMain.InsideMainFile =
true;
1137 NoteInMain.AbsFile = std::string(
MainFile.file());
1139 D.Notes.push_back(NoteInMain);
1141 clangd::Note NoteInHeader;
1142 NoteInHeader.Message =
"declared somewhere in the header file";
1143 NoteInHeader.Range = {pos(9, 10), pos(11, 12)};
1144 NoteInHeader.Severity = DiagnosticsEngine::Note;
1145 NoteInHeader.File =
"../foo/baz/header.h";
1146 NoteInHeader.InsideMainFile =
false;
1147 NoteInHeader.AbsFile = std::string(HeaderFile.file());
1148 D.Notes.push_back(NoteInHeader);
1151 F.Message =
"do something";
1152 D.Fixes.push_back(F);
1155 clangd::Diagnostic MainLSP;
1156 MainLSP.range = D.Range;
1157 MainLSP.severity =
getSeverity(DiagnosticsEngine::Error);
1158 MainLSP.code =
"undeclared_var_use";
1159 MainLSP.source =
"clang";
1161 R
"(Something terrible happened (fix available)
1163main.cpp:6:7: remark: declared somewhere in the main file
1165../foo/baz/header.h:10:11:
1166note: declared somewhere in the header file)";
1168 MainLSP.data = D.OpaqueData;
1170 clangd::Diagnostic NoteInMainLSP;
1171 NoteInMainLSP.range = NoteInMain.Range;
1172 NoteInMainLSP.severity = getSeverity(DiagnosticsEngine::Remark);
1173 NoteInMainLSP.message = R"(Declared somewhere in the main file
1175main.cpp:2:3: error: something terrible happened)";
1177 ClangdDiagnosticOptions Opts;
1179 std::vector<std::pair<clangd::Diagnostic, std::vector<clangd::Fix>>> LSPDiags;
1181 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix> Fixes) {
1183 {std::move(LSPDiag),
1184 std::vector<clangd::Fix>(Fixes.begin(), Fixes.end())});
1189 ElementsAre(Pair(equalToLSPDiag(MainLSP), ElementsAre(equalToFix(F))),
1190 Pair(equalToLSPDiag(NoteInMainLSP), IsEmpty())));
1191 EXPECT_EQ(LSPDiags[0].first.code,
"undeclared_var_use");
1192 EXPECT_EQ(LSPDiags[0].first.source,
"clang");
1193 EXPECT_EQ(LSPDiags[1].first.code,
"");
1194 EXPECT_EQ(LSPDiags[1].first.source,
"");
1198 Opts.EmitRelatedLocations =
true;
1200 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix> Fixes) {
1202 {std::move(LSPDiag),
1203 std::vector<clangd::Fix>(Fixes.begin(), Fixes.end())});
1205 MainLSP.message =
"Something terrible happened (fix available)";
1206 DiagnosticRelatedInformation NoteInMainDRI;
1207 NoteInMainDRI.message =
"Declared somewhere in the main file";
1208 NoteInMainDRI.location.range = NoteInMain.Range;
1209 NoteInMainDRI.location.uri =
MainFile;
1210 MainLSP.relatedInformation = {NoteInMainDRI};
1211 DiagnosticRelatedInformation NoteInHeaderDRI;
1212 NoteInHeaderDRI.message =
"Declared somewhere in the header file";
1213 NoteInHeaderDRI.location.range = NoteInHeader.Range;
1214 NoteInHeaderDRI.location.uri = HeaderFile;
1215 MainLSP.relatedInformation = {NoteInMainDRI, NoteInHeaderDRI};
1216 EXPECT_THAT(LSPDiags, ElementsAre(Pair(equalToLSPDiag(MainLSP),
1217 ElementsAre(equalToFix(F)))));
1220struct SymbolWithHeader {
1222 std::string DeclaringFile;
1223 std::string IncludeHeader;
1226std::unique_ptr<SymbolIndex>
1227buildIndexWithSymbol(llvm::ArrayRef<SymbolWithHeader> Syms) {
1228 SymbolSlab::Builder Slab;
1229 for (
const auto &S : Syms) {
1230 Symbol Sym =
cls(S.QName);
1232 Sym.CanonicalDeclaration.FileURI = S.DeclaringFile.c_str();
1233 Sym.Definition.FileURI = S.DeclaringFile.c_str();
1237 return MemIndex::build(std::move(Slab).build(), RefSlab(), RelationSlab());
1240TEST(IncludeFixerTest, IncompleteType) {
1242 TU.ExtraArgs.push_back(
"-std=c++20");
1243 auto Index = buildIndexWithSymbol(
1244 {SymbolWithHeader{
"ns::X",
"unittest:///x.h",
"\"x.h\""}});
1245 TU.ExternalIndex = Index.get();
1247 std::vector<std::pair<llvm::StringRef, llvm::StringRef>> Tests{
1248 {
"incomplete_nested_name_spec",
"[[ns::X::]]Nested n;"},
1249 {
"incomplete_base_class",
"class Y : [[ns::X]] {};"},
1250 {
"incomplete_member_access",
"auto i = x[[->]]f();"},
1251 {
"incomplete_type",
"auto& [[[]]m] = *x;"},
1252 {
"init_incomplete_type",
1253 "struct C { static int f(ns::X&); }; int i = C::f([[{]]});"},
1254 {
"bad_cast_incomplete",
"auto a = [[static_cast]]<ns::X>(0);"},
1255 {
"template_nontype_parm_incomplete",
"template <ns::X [[foo]]> int a;"},
1256 {
"typecheck_decl_incomplete_type",
"ns::X [[var]];"},
1257 {
"typecheck_incomplete_tag",
"auto i = [[(*x)]]->f();"},
1258 {
"typecheck_nonviable_condition_incomplete",
1259 "struct A { operator ns::X(); } a; const ns::X &[[b]] = a;"},
1260 {
"invalid_incomplete_type_use",
"auto var = [[ns::X()]];"},
1261 {
"sizeof_alignof_incomplete_or_sizeless_type",
1262 "auto s = [[sizeof]](ns::X);"},
1263 {
"for_range_incomplete_type",
"void foo() { for (auto i : [[*]]x ) {} }"},
1264 {
"func_def_incomplete_result",
"ns::X [[func]] () {}"},
1265 {
"field_incomplete_or_sizeless",
"class M { ns::X [[member]]; };"},
1266 {
"array_incomplete_or_sizeless_type",
"auto s = [[(ns::X[]){}]];"},
1267 {
"call_incomplete_return",
"ns::X f(); auto fp = &f; auto z = [[fp()]];"},
1268 {
"call_function_incomplete_return",
"ns::X foo(); auto a = [[foo()]];"},
1269 {
"call_incomplete_argument",
"int m(ns::X); int i = m([[*x]]);"},
1270 {
"switch_incomplete_class_type",
"void a() { [[switch]](*x) {} }"},
1271 {
"delete_incomplete_class_type",
"void f() { [[delete]] *x; }"},
1272 {
"-Wdelete-incomplete",
"void f() { [[delete]] x; }"},
1273 {
"dereference_incomplete_type",
1274 R
"cpp(void f() { asm("" : "=r"([[*]]x)::); })cpp"},
1276 for (
auto Case : Tests) {
1277 Annotations Main(Case.second);
1278 TU.Code = Main.code().str() +
"\n // error-ok";
1280 TU.build().getDiagnostics(),
1281 ElementsAre(AllOf(diagName(Case.first), hasRange(Main.range()),
1282 withFix(Fix(Range{},
"#include \"x.h\"\n",
1283 "Include \"x.h\" for symbol ns::X")))))
1288TEST(IncludeFixerTest, IncompleteEnum) {
1289 Symbol Sym =
enm(
"X");
1291 Sym.CanonicalDeclaration.FileURI = Sym.Definition.FileURI =
"unittest:///x.h";
1293 SymbolSlab::Builder Slab;
1299 TU.ExternalIndex = Index.get();
1300 TU.ExtraArgs.push_back(
"-std=c++20");
1301 TU.ExtraArgs.push_back(
"-fno-ms-compatibility");
1303 std::vector<std::pair<llvm::StringRef, llvm::StringRef>> Tests{
1304 {
"incomplete_enum",
"enum class X : int; using enum [[X]];"},
1305 {
"underlying_type_of_incomplete_enum",
1306 "[[__underlying_type]](enum X) i;"},
1308 for (
auto Case : Tests) {
1309 Annotations Main(Case.second);
1310 TU.Code = Main.code().str() +
"\n // error-ok";
1311 EXPECT_THAT(TU.build().getDiagnostics(),
1312 Contains(AllOf(diagName(Case.first), hasRange(Main.range()),
1313 withFix(Fix(Range{},
"#include \"x.h\"\n",
1314 "Include \"x.h\" for symbol X")))))
1319TEST(IncludeFixerTest, NoSuggestIncludeWhenNoDefinitionInHeader) {
1320 Annotations Test(R
"cpp(// error-ok
1321$insert[[]]namespace ns {
1324class Y : $base[[public ns::X]] {};
1331 Symbol Sym =
cls(
"ns::X");
1333 Sym.CanonicalDeclaration.FileURI =
"unittest:///x.h";
1334 Sym.Definition.FileURI =
"unittest:///x.cc";
1337 SymbolSlab::Builder Slab;
1341 TU.ExternalIndex = Index.get();
1343 EXPECT_THAT(TU.build().getDiagnostics(),
1344 UnorderedElementsAre(
1345 Diag(Test.range(
"base"),
"base class has incomplete type"),
1346 Diag(Test.range(
"access"),
1347 "member access into incomplete type 'ns::X'")));
1350TEST(IncludeFixerTest, Typo) {
1351 Annotations Test(R
"cpp(// error-ok
1352$insert[[]]namespace ns {
1354 $unqualified1[[X]] x;
1355 // No fix if the unresolved type is used as specifier. (ns::)X::Nested will be
1356 // considered the unresolved type.
1357 $unqualified2[[X]]::Nested n;
1359struct S : $base[[X]] {};
1362 ns::$qualified1[[X]] x; // ns:: is valid.
1363 ns::$qualified2[[X]](); // Error: no member in namespace
1365 ::$global[[Global]] glob;
1367using Type = ns::$template[[Foo]]<int>;
1370 auto Index = buildIndexWithSymbol(
1371 {SymbolWithHeader{
"ns::X",
"unittest:///x.h",
"\"x.h\""},
1372 SymbolWithHeader{
"Global",
"unittest:///global.h",
"\"global.h\""},
1373 SymbolWithHeader{
"ns::Foo",
"unittest:///foo.h",
"\"foo.h\""}});
1374 TU.ExternalIndex = Index.get();
1377 TU.build().getDiagnostics(),
1378 UnorderedElementsAre(
1379 AllOf(Diag(Test.range(
"unqualified1"),
"unknown type name 'X'"),
1380 diagName(
"unknown_typename"),
1381 withFix(Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
1382 "Include \"x.h\" for symbol ns::X"))),
1383 Diag(Test.range(
"unqualified2"),
"use of undeclared identifier 'X'"),
1384 AllOf(Diag(Test.range(
"qualified1"),
1385 "no type named 'X' in namespace 'ns'"),
1386 diagName(
"typename_nested_not_found"),
1387 withFix(Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
1388 "Include \"x.h\" for symbol ns::X"))),
1389 AllOf(Diag(Test.range(
"qualified2"),
1390 "no member named 'X' in namespace 'ns'"),
1391 diagName(
"no_member"),
1392 withFix(Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
1393 "Include \"x.h\" for symbol ns::X"))),
1394 AllOf(Diag(Test.range(
"global"),
1395 "no type named 'Global' in the global namespace"),
1396 diagName(
"typename_nested_not_found"),
1397 withFix(Fix(Test.range(
"insert"),
"#include \"global.h\"\n",
1398 "Include \"global.h\" for symbol Global"))),
1399 AllOf(Diag(Test.range(
"template"),
1400 "no template named 'Foo' in namespace 'ns'"),
1401 diagName(
"no_member_template"),
1402 withFix(Fix(Test.range(
"insert"),
"#include \"foo.h\"\n",
1403 "Include \"foo.h\" for symbol ns::Foo"))),
1404 AllOf(Diag(Test.range(
"base"),
"expected class name"),
1405 diagName(
"expected_class_name"),
1406 withFix(Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
1407 "Include \"x.h\" for symbol ns::X")))));
1410TEST(IncludeFixerTest, TypoInMacro) {
1421 auto Index = buildIndexWithSymbol(
1422 {SymbolWithHeader{
"X",
"unittest:///x.h",
"\"x.h\""},
1423 SymbolWithHeader{
"ns::X",
"unittest:///ns.h",
"\"x.h\""}});
1424 TU.ExternalIndex = Index.get();
1427 TU.ExtraArgs = {
"-fno-ms-compatibility"};
1428 EXPECT_THAT(TU.build().getDiagnostics(), Each(withFix(_)));
1431TEST(IncludeFixerTest, MultipleMatchedSymbols) {
1432 Annotations Test(R
"cpp(// error-ok
1433$insert[[]]namespace na {
1436 $unqualified[[X]] x;
1442 auto Index = buildIndexWithSymbol(
1443 {SymbolWithHeader{
"na::X",
"unittest:///a.h",
"\"a.h\""},
1444 SymbolWithHeader{
"na::nb::X",
"unittest:///b.h",
"\"b.h\""}});
1445 TU.ExternalIndex = Index.get();
1447 EXPECT_THAT(TU.build().getDiagnostics(),
1448 UnorderedElementsAre(AllOf(
1449 Diag(Test.range(
"unqualified"),
"unknown type name 'X'"),
1450 diagName(
"unknown_typename"),
1451 withFix(Fix(Test.range(
"insert"),
"#include \"a.h\"\n",
1452 "Include \"a.h\" for symbol na::X"),
1453 Fix(Test.range(
"insert"),
"#include \"b.h\"\n",
1454 "Include \"b.h\" for symbol na::nb::X")))));
1457TEST(IncludeFixerTest, NoCrashMemberAccess) {
1458 Annotations Test(R
"cpp(// error-ok
1459 struct X { int xyz; };
1460 void g() { X x; x.$[[xy]]; }
1463 auto Index = buildIndexWithSymbol(
1464 SymbolWithHeader{
"na::X",
"unittest:///a.h",
"\"a.h\""});
1465 TU.ExternalIndex = Index.get();
1468 TU.build().getDiagnostics(),
1469 UnorderedElementsAre(Diag(Test.range(),
"no member named 'xy' in 'X'")));
1472TEST(IncludeFixerTest, UseCachedIndexResults) {
1475 Annotations Test(R
"cpp(// error-ok
1476$insert[[]]void foo() {
1499 buildIndexWithSymbol(SymbolWithHeader{
"X",
"unittest:///a.h",
"\"a.h\""});
1500 TU.ExternalIndex = Index.get();
1502 auto Parsed = TU.build();
1503 for (
const auto &D : Parsed.getDiagnostics()) {
1504 if (D.Fixes.size() != 1) {
1505 ADD_FAILURE() <<
"D.Fixes.size() != 1";
1508 EXPECT_EQ(D.Fixes[0].Message, std::string(
"Include \"a.h\" for symbol X"));
1512TEST(IncludeFixerTest, UnresolvedNameAsSpecifier) {
1513 Annotations Test(R
"cpp(// error-ok
1514$insert[[]]namespace ns {
1516void g() { ns::$[[scope]]::X_Y(); }
1519 TU.Code = std::string(Test.code());
1521 TU.ExtraArgs.push_back(
"-fno-ms-compatibility");
1522 auto Index = buildIndexWithSymbol(
1523 SymbolWithHeader{
"ns::scope::X_Y",
"unittest:///x.h",
"\"x.h\""});
1524 TU.ExternalIndex = Index.get();
1527 TU.build().getDiagnostics(),
1528 UnorderedElementsAre(
1529 AllOf(Diag(Test.range(),
"no member named 'scope' in namespace 'ns'"),
1530 diagName(
"no_member"),
1531 withFix(Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
1532 "Include \"x.h\" for symbol ns::scope::X_Y")))));
1535TEST(IncludeFixerTest, UnresolvedSpecifierWithSemaCorrection) {
1536 Annotations Test(R
"cpp(// error-ok
1537$insert[[]]namespace clang {
1539 // "clangd::" will be corrected to "clang::" by Sema.
1540 $q1[[clangd]]::$x[[X]] x;
1541 $q2[[clangd]]::$ns[[ns]]::Y y;
1546 TU.Code = std::string(Test.code());
1548 TU.ExtraArgs.push_back(
"-fno-ms-compatibility");
1549 auto Index = buildIndexWithSymbol(
1550 {SymbolWithHeader{
"clang::clangd::X",
"unittest:///x.h",
"\"x.h\""},
1551 SymbolWithHeader{
"clang::clangd::ns::Y",
"unittest:///y.h",
"\"y.h\""}});
1552 TU.ExternalIndex = Index.get();
1555 TU.build().getDiagnostics(),
1556 UnorderedElementsAre(
1557 AllOf(Diag(Test.range(
"q1"),
"use of undeclared identifier 'clangd'; "
1558 "did you mean 'clang'?"),
1559 diagName(
"undeclared_var_use_suggest"),
1561 Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
1562 "Include \"x.h\" for symbol clang::clangd::X"))),
1563 AllOf(Diag(Test.range(
"x"),
"no type named 'X' in namespace 'clang'"),
1564 diagName(
"typename_nested_not_found"),
1565 withFix(Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
1566 "Include \"x.h\" for symbol clang::clangd::X"))),
1568 Diag(Test.range(
"q2"),
"use of undeclared identifier 'clangd'; "
1569 "did you mean 'clang'?"),
1570 diagName(
"undeclared_var_use_suggest"),
1572 Fix(Test.range(
"insert"),
"#include \"y.h\"\n",
1573 "Include \"y.h\" for symbol clang::clangd::ns::Y"))),
1574 AllOf(Diag(Test.range(
"ns"),
1575 "no member named 'ns' in namespace 'clang'"),
1576 diagName(
"no_member"),
1578 Fix(Test.range(
"insert"),
"#include \"y.h\"\n",
1579 "Include \"y.h\" for symbol clang::clangd::ns::Y")))));
1582TEST(IncludeFixerTest, SpecifiedScopeIsNamespaceAlias) {
1583 Annotations Test(R
"cpp(// error-ok
1584$insert[[]]namespace a {}
1591 auto Index = buildIndexWithSymbol(
1592 SymbolWithHeader{
"a::X",
"unittest:///x.h",
"\"x.h\""});
1593 TU.ExternalIndex = Index.get();
1595 EXPECT_THAT(TU.build().getDiagnostics(),
1596 UnorderedElementsAre(AllOf(
1597 Diag(Test.range(),
"no type named 'X' in namespace 'a'"),
1598 diagName(
"typename_nested_not_found"),
1599 withFix(Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
1600 "Include \"x.h\" for symbol a::X")))));
1603TEST(IncludeFixerTest, NoCrashOnTemplateInstantiations) {
1604 Annotations Test(R
"cpp(
1605 template <typename T> struct Templ {
1606 template <typename U>
1607 typename U::type operator=(const U &);
1612 A() { [[a]]; /*error-ok*/ } // crash if we compute scopes lazily.
1617 auto Index = buildIndexWithSymbol({});
1618 TU.ExternalIndex = Index.get();
1621 TU.build().getDiagnostics(),
1622 ElementsAre(Diag(Test.range(),
"use of undeclared identifier 'a'")));
1625TEST(IncludeFixerTest, HeaderNamedInDiag) {
1626 Annotations Test(R
"cpp(
1627 $insert[[]]int main() {
1632 TU.ExtraArgs = {
"-xc",
"-std=c99",
1633 "-Wno-error=implicit-function-declaration"};
1634 auto Index = buildIndexWithSymbol({});
1635 TU.ExternalIndex = Index.get();
1638 TU.build().getDiagnostics(),
1640 Diag(Test.range(),
"call to undeclared library function 'printf' "
1641 "with type 'int (const char *, ...)'; ISO C99 "
1642 "and later do not support implicit function "
1644 withFix(Fix(Test.range(
"insert"),
"#include <stdio.h>\n",
1645 "Include <stdio.h> for symbol printf")))));
1647 TU.ExtraArgs = {
"-xc",
"-std=c89"};
1649 TU.build().getDiagnostics(),
1651 Diag(Test.range(),
"implicitly declaring library function 'printf' "
1652 "with type 'int (const char *, ...)'"),
1653 withFix(Fix(Test.range(
"insert"),
"#include <stdio.h>\n",
1654 "Include <stdio.h> for symbol printf")))));
1657TEST(IncludeFixerTest, CImplicitFunctionDecl) {
1658 Annotations Test(
"void x() { [[foo]](); }");
1660 TU.Filename =
"test.c";
1661 TU.ExtraArgs = {
"-std=c99",
"-Wno-error=implicit-function-declaration"};
1663 Symbol Sym =
func(
"foo");
1665 Sym.CanonicalDeclaration.FileURI =
"unittest:///foo.h";
1668 SymbolSlab::Builder Slab;
1672 TU.ExternalIndex = Index.get();
1675 TU.build().getDiagnostics(),
1678 "call to undeclared function 'foo'; ISO C99 and later do not "
1679 "support implicit function declarations"),
1680 withFix(Fix(Range{},
"#include \"foo.h\"\n",
1681 "Include \"foo.h\" for symbol foo")))));
1683 TU.ExtraArgs = {
"-std=c89",
"-Wall"};
1684 EXPECT_THAT(TU.build().getDiagnostics(),
1686 Diag(Test.range(),
"implicit declaration of function 'foo'"),
1687 withFix(Fix(Range{},
"#include \"foo.h\"\n",
1688 "Include \"foo.h\" for symbol foo")))));
1691TEST(DiagsInHeaders, DiagInsideHeader) {
1692 Annotations Main(R
"cpp(
1694 void foo() {})cpp");
1695 Annotations Header("[[no_type_spec]]; // error-ok");
1697 TU.AdditionalFiles = {{
"a.h", std::string(Header.code())}};
1698 EXPECT_THAT(TU.build().getDiagnostics(),
1699 UnorderedElementsAre(AllOf(
1700 Diag(Main.range(),
"in included file: a type specifier is "
1701 "required for all declarations"),
1702 withNote(Diag(Header.range(),
"error occurred here")))));
1705TEST(DiagsInHeaders, DiagInTransitiveInclude) {
1706 Annotations Main(R
"cpp(
1708 void foo() {})cpp");
1710 TU.AdditionalFiles = {{"a.h",
"#include \"b.h\""},
1711 {
"b.h",
"no_type_spec; // error-ok"}};
1712 EXPECT_THAT(TU.build().getDiagnostics(),
1713 UnorderedElementsAre(Diag(Main.range(),
1714 "in included file: a type specifier is "
1715 "required for all declarations")));
1718TEST(DiagsInHeaders, DiagInMultipleHeaders) {
1719 Annotations Main(R
"cpp(
1720 #include $a[["a.h"]]
1721 #include $b[["b.h"]]
1722 void foo() {})cpp");
1724 TU.AdditionalFiles = {{"a.h",
"no_type_spec; // error-ok"},
1725 {
"b.h",
"no_type_spec; // error-ok"}};
1726 EXPECT_THAT(TU.build().getDiagnostics(),
1727 UnorderedElementsAre(
1728 Diag(Main.range(
"a"),
"in included file: a type specifier is "
1729 "required for all declarations"),
1730 Diag(Main.range(
"b"),
"in included file: a type specifier is "
1731 "required for all declarations")));
1734TEST(DiagsInHeaders, PreferExpansionLocation) {
1735 Annotations Main(R
"cpp(
1738 void foo() {})cpp");
1740 TU.AdditionalFiles = {
1741 {"a.h",
"#include \"b.h\"\n"},
1742 {
"b.h",
"#ifndef X\n#define X\nno_type_spec; // error-ok\n#endif"}};
1743 EXPECT_THAT(TU.build().getDiagnostics(),
1744 Contains(Diag(Main.range(),
"in included file: a type specifier "
1745 "is required for all declarations")));
1748TEST(DiagsInHeaders, PreferExpansionLocationMacros) {
1749 Annotations Main(R
"cpp(
1754 void foo() {})cpp");
1756 TU.AdditionalFiles = {
1757 {"a.h",
"#include \"c.h\"\n"},
1758 {
"b.h",
"#include \"c.h\"\n"},
1759 {
"c.h",
"#ifndef X\n#define X\nno_type_spec; // error-ok\n#endif"}};
1760 EXPECT_THAT(TU.build().getDiagnostics(),
1761 UnorderedElementsAre(Diag(Main.range(),
1762 "in included file: a type specifier is "
1763 "required for all declarations")));
1766TEST(DiagsInHeaders, LimitDiagsOutsideMainFile) {
1767 Annotations Main(R
"cpp(
1770 void foo() {})cpp");
1772 TU.AdditionalFiles = {{"a.h",
"#include \"c.h\"\n"},
1773 {
"b.h",
"#include \"c.h\"\n"},
1777 no_type_spec_0; // error-ok
1789 EXPECT_THAT(TU.build().getDiagnostics(),
1790 UnorderedElementsAre(Diag(Main.range(),
1791 "in included file: a type specifier is "
1792 "required for all declarations")));
1795TEST(DiagsInHeaders, OnlyErrorOrFatal) {
1796 Annotations Main(R
"cpp(
1798 void foo() {})cpp");
1799 Annotations Header(R"cpp(
1800 [[no_type_spec]]; // error-ok
1803 TU.AdditionalFiles = {{"a.h", std::string(Header.code())}};
1804 EXPECT_THAT(TU.build().getDiagnostics(),
1805 UnorderedElementsAre(AllOf(
1806 Diag(Main.range(),
"in included file: a type specifier is "
1807 "required for all declarations"),
1808 withNote(Diag(Header.range(),
"error occurred here")))));
1811TEST(DiagsInHeaders, OnlyDefaultErrorOrFatal) {
1812 Annotations Main(R
"cpp(
1813 #include [["a.h"]] // get unused "foo" warning when building preamble.
1815 Annotations Header(R"cpp(
1816 namespace { void foo() {} }
1817 void func() {foo();} ;)cpp");
1819 TU.AdditionalFiles = {{"a.h", std::string(Header.code())}};
1821 TU.ExtraArgs = {
"-Werror",
"-Wunused"};
1822 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
1825TEST(DiagsInHeaders, FromNonWrittenSources) {
1826 Annotations Main(R
"cpp(
1828 void foo() {})cpp");
1829 Annotations Header(R"cpp(
1831 int b = [[FOO]]; // error-ok)cpp");
1833 TU.AdditionalFiles = {{"a.h", std::string(Header.code())}};
1834 TU.ExtraArgs = {
"-DFOO=NOOO"};
1835 EXPECT_THAT(TU.build().getDiagnostics(),
1836 UnorderedElementsAre(AllOf(
1838 "in included file: use of undeclared identifier 'NOOO'"),
1839 withNote(Diag(Header.range(),
"error occurred here")))));
1842TEST(DiagsInHeaders, ErrorFromMacroExpansion) {
1843 Annotations Main(R
"cpp(
1848 Annotations Header(R"cpp(
1852 TU.AdditionalFiles = {{"a.h", std::string(Header.code())}};
1853 EXPECT_THAT(TU.build().getDiagnostics(),
1854 UnorderedElementsAre(
1855 Diag(Main.range(),
"in included file: use of undeclared "
1856 "identifier 'foo'; did you mean 'fo'?")));
1859TEST(DiagsInHeaders, ErrorFromMacroArgument) {
1860 Annotations Main(R
"cpp(
1865 Annotations Header(R"cpp(
1869 TU.AdditionalFiles = {{"a.h", std::string(Header.code())}};
1870 EXPECT_THAT(TU.build().getDiagnostics(),
1871 UnorderedElementsAre(
1872 Diag(Main.range(),
"in included file: use of undeclared "
1873 "identifier 'foo'; did you mean 'fo'?")));
1878 TU.ExtraArgs.push_back(
"--include=a.h");
1879 TU.AdditionalFiles = {{
"a.h",
"void main();"}};
1882 EXPECT_THAT(TU.build().getDiagnostics(), UnorderedElementsAre());
1885TEST(ToLSPDiag, RangeIsInMain) {
1886 ClangdDiagnosticOptions Opts;
1888 D.Range = {pos(1, 2), pos(3, 4)};
1889 D.Notes.emplace_back();
1890 Note &N = D.Notes.back();
1891 N.Range = {pos(2, 3), pos(3, 4)};
1893 D.InsideMainFile =
true;
1894 N.InsideMainFile =
false;
1896 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix>) {
1897 EXPECT_EQ(LSPDiag.range, D.Range);
1900 D.InsideMainFile =
false;
1901 N.InsideMainFile =
true;
1903 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix>) {
1904 EXPECT_EQ(LSPDiag.range, N.Range);
1908TEST(ParsedASTTest, ModuleSawDiag) {
1911 auto AST = TU.build();
1913 EXPECT_THAT(
AST.getDiagnostics(),
1914 testing::Contains(Diag(
Code.range(), KDiagMsg.str())));
1920 TU.ExtraArgs = {
"-Wnewline-eof"};
1923 TU.Code =
"#define FOO\n void bar();\n";
1924 auto AST = TU.build();
1925 EXPECT_THAT(
AST.getDiagnostics(), IsEmpty());
1928 Annotations
Code(
"#define FOO[[]]");
1929 TU.Code =
Code.code().str();
1930 auto AST = TU.build();
1932 AST.getDiagnostics(),
1933 testing::Contains(Diag(
Code.range(),
"no newline at end of file")));
1939 TU.ExtraArgs = {
"-Wunused",
"-Wdeprecated"};
1940 Annotations Test(R
"cpp(
1941 void bar() __attribute__((deprecated));
1944 $deprecated[[bar]]();
1946 TU.Code = Test.code().str();
1947 EXPECT_THAT(TU.build().getDiagnostics(),
1948 UnorderedElementsAre(
1949 AllOf(Diag(Test.range("unused"),
"unused variable 'x'"),
1951 AllOf(Diag(Test.range(
"deprecated"),
"'bar' is deprecated"),
1954 Test = Annotations(R
"cpp(
1955 $typedef[[typedef int INT]];
1957 TU.Code = Test.code();
1958 TU.ClangTidyProvider = addTidyChecks("modernize-use-using");
1960 TU.build().getDiagnostics(),
1961 ifTidyChecks(UnorderedElementsAre(
1962 AllOf(Diag(Test.range(
"typedef"),
"use 'using' instead of 'typedef'"),
1967 ClangdDiagnosticOptions Opts;
1968 std::optional<clangd::Diagnostic> Diag;
1970 D.Range = {pos(1, 2), pos(3, 4)};
1971 D.InsideMainFile =
true;
1975 D.Severity = DiagnosticsEngine::Warning;
1977 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix>) {
1978 Diag = std::move(LSPDiag);
1980 EXPECT_EQ(Diag->severity,
getSeverity(DiagnosticsEngine::Remark));
1984 D.Severity = DiagnosticsEngine::Error;
1986 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix>) {
1987 Diag = std::move(LSPDiag);
1989 EXPECT_EQ(Diag->severity,
getSeverity(DiagnosticsEngine::Error));
1994 D.Severity = DiagnosticsEngine::Warning;
1996 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix>) {
1997 Diag = std::move(LSPDiag);
1999 EXPECT_EQ(Diag->severity,
getSeverity(DiagnosticsEngine::Warning));
2002TEST(DiagnosticsTest, IncludeCleaner) {
2003 Annotations Test(R
"cpp(
2004$fix[[ $diag[[#include "unused.h"]]
2010 #include <system_header.h>
2017 TU.Code = Test.code().str();
2018 TU.AdditionalFiles["unused.h"] = R
"cpp(
2022 TU.AdditionalFiles["used.h"] = R
"cpp(
2026 TU.AdditionalFiles["ignore.h"] = R
"cpp(
2030 TU.AdditionalFiles["system/system_header.h"] =
"";
2031 TU.ExtraArgs = {
"-isystem" +
testPath(
"system")};
2036 [](llvm::StringRef Header) {
return Header.ends_with(
"ignore.h"); });
2037 WithContextValue WithCfg(
Config::Key, std::move(Cfg));
2038 auto AST = TU.build();
2040 AST.getDiagnostics(),
2042 Diag(Test.range(
"diag"),
2043 "included header unused.h is not used directly"),
2045 withFix(Fix(Test.range(
"fix"),
"",
"remove #include directive")))));
2046 auto &Diag =
AST.getDiagnostics().front();
2048 llvm::ValueIs(Not(IsEmpty())));
2050 WithContextValue SuppressAllWithCfg(
Config::Key, std::move(Cfg));
2051 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
2054 WithContextValue SuppressFilterWithCfg(
Config::Key, std::move(Cfg));
2055 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
2058TEST(DiagnosticsTest, FixItFromHeader) {
2059 llvm::StringLiteral Header(R
"cpp(
2061 void foo(int *, int);)cpp");
2062 Annotations Source(R"cpp(
2066 $diag[[foo]]($fix[[]]x, 1);
2069 TU.Code = Source.code().str();
2070 TU.HeaderCode = Header.str();
2072 TU.build().getDiagnostics(),
2073 UnorderedElementsAre(AllOf(
2074 Diag(Source.range("diag"),
"no matching function for call to 'foo'"),
2075 withFix(Fix(Source.range(
"fix"),
"&",
2076 "candidate function not viable: no known conversion from "
2077 "'int' to 'int *' for 1st argument; take the address of "
2078 "the argument with &")))));
2081TEST(DiagnosticsTest, UnusedInHeader) {
2084 TU.ExtraArgs.push_back(
"-Wunused-function");
2085 TU.Filename =
"test.c";
2086 EXPECT_THAT(TU.build().getDiagnostics(),
2087 ElementsAre(withID(diag::warn_unused_function)));
2090 TU.Filename =
"test.h";
2091 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
CharSourceRange Range
SourceRange for the file name.
const google::protobuf::Message & M
WantDiagnostics Diagnostics
static std::unique_ptr< SymbolIndex > build(SymbolSlab Symbols, RefSlab Refs, RelationSlab Relations)
Builds an index from slabs. The index takes ownership of the data.
llvm::ArrayRef< Diag > getDiagnostics() const
Symbol func(llvm::StringRef Name)
const NamedDecl & findDecl(ParsedAST &AST, llvm::StringRef QName)
Symbol cls(llvm::StringRef Name)
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.
MATCHER_P2(hasFlag, Flag, Path, "")
std::string testPath(PathRef File, llvm::sys::path::Style Style)
llvm::json::Value toJSON(const FuzzyFindRequest &Request)
TidyProvider addTidyChecks(llvm::StringRef Checks, llvm::StringRef WarningsAsErrors)
Provider the enables a specific set of checks and warnings as errors.
TEST(BackgroundQueueTest, Priority)
Symbol enm(llvm::StringRef Name)
int getSeverity(DiagnosticsEngine::Level L)
Convert from clang diagnostic level to LSP severity.
@ Deprecated
Deprecated or obsolete code.
@ Unnecessary
Unused or unnecessary code.
std::optional< std::string > getDiagnosticDocURI(Diag::DiagSource Source, unsigned ID, llvm::StringRef Name)
Returns a URI providing more information about a particular diagnostic.
llvm::unique_function< void(tidy::ClangTidyOptions &, llvm::StringRef) const > TidyProvider
A factory to modify a tidy::ClangTidyOptions.
constexpr llvm::StringLiteral Message
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
static clangd::Key< Config > Key
Context key which can be used to set the current Config.
struct clang::clangd::Config::@4::@13 Includes
@ Strict
Diagnose missing and unused includes.
std::vector< std::function< bool(llvm::StringRef)> > IgnoreHeader
IncludeCleaner will not diagnose usages of these headers matched by these regexes.
IncludesPolicy UnusedIncludes
struct clang::clangd::Config::@4 Diagnostics
Controls warnings and errors when parsing code.
std::vector< Fix > Fixes
Alternative fixes for this diagnostic, one should be chosen.
llvm::SmallVector< DiagnosticTag, 1 > Tags
std::vector< Note > Notes
Elaborate on the problem, usually pointing to a related piece of code.
@ IndexedForCodeCompletion
Whether or not this symbol is meant to be used for the code completion.
@ Include
#include "header.h"
static TestTU withHeaderCode(llvm::StringRef HeaderCode)
static TestTU withCode(llvm::StringRef Code)
static URIForFile canonicalize(llvm::StringRef AbsPath, llvm::StringRef TUPath)
Canonicalizes AbsPath via URI.