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());
752 TU.ExtraArgs = {
"-Wno-unused"};
753 TU.ClangTidyProvider = addClangArgs({
"-Wunused"}, {
"-*, clang-diagnostic-*"});
754 EXPECT_THAT(TU.build().getDiagnostics(), SizeIs(1));
757TEST(DiagnosticTest, LongFixMessages) {
759 Annotations Source(R
"cpp(
762 int somereallyreallyreallyreallyreallyreallyreallyreallylongidentifier;
763 [[omereallyreallyreallyreallyreallyreallyreallyreallylongidentifier]]= 10;
768 TU.build().getDiagnostics(),
769 ElementsAre(withFix(Fix(
771 "somereallyreallyreallyreallyreallyreallyreallyreallylongidentifier",
772 "change 'omereallyreallyreallyreallyreallyreallyreallyreall…' to "
773 "'somereallyreallyreallyreallyreallyreallyreallyreal…'"))));
775 Source = Annotations(R
"cpp(
783 TU.Code = std::string(Source.code());
784 EXPECT_THAT(TU.build().getDiagnostics(),
786 Fix(Source.range(), "ident",
"change 'ide\\…' to 'ident'"))));
789TEST(DiagnosticTest, NewLineFixMessage) {
790 Annotations Source(
"int a;[[]]");
792 TU.ExtraArgs = {
"-Wnewline-eof"};
794 TU.build().getDiagnostics(),
795 ElementsAre(withFix((Fix(Source.range(),
"\n",
"insert '\\n'")))));
798TEST(DiagnosticTest, ClangTidySuppressionCommentTrumpsWarningAsError) {
799 Annotations Main(R
"cpp(
802 double f = [[8]] / i; // NOLINT
806 TU.ClangTidyProvider =
807 addTidyChecks("bugprone-integer-division",
"bugprone-integer-division");
808 EXPECT_THAT(TU.build().getDiagnostics(), UnorderedElementsAre());
811TEST(DiagnosticTest, ClangTidyNoLiteralDataInMacroToken) {
812 Annotations Main(R
"cpp(
814 using pthread_t = int;
815 int pthread_kill(pthread_t thread, int sig);
818 return pthread_kill(thread, 0);
822 TU.ClangTidyProvider = addTidyChecks("bugprone-bad-signal-to-kill-thread");
823 EXPECT_THAT(TU.build().getDiagnostics(), UnorderedElementsAre());
826TEST(DiagnosticTest, ElseAfterReturnRange) {
827 Annotations Main(R
"cpp(
831 } [[else]] if (cond == 2) {
838 TU.ClangTidyProvider = addTidyChecks("llvm-else-after-return");
839 EXPECT_THAT(TU.build().getDiagnostics(),
840 ifTidyChecks(ElementsAre(
841 Diag(Main.range(),
"do not use 'else' after 'return'"))));
844TEST(DiagnosticTest, ClangTidySelfContainedDiags) {
845 Annotations Main(R
"cpp($MathHeader[[]]
853 void InitVariables() {
854 float $C[[C]]$CFix[[]];
855 double $D[[D]]$DFix[[]];
859 TU.ClangTidyProvider =
861 "cppcoreguidelines-init-variables");
862 clangd::Fix ExpectedAFix;
863 ExpectedAFix.Message =
864 "'A' should be initialized in a member initializer of the constructor";
865 ExpectedAFix.Edits.push_back(TextEdit{Main.range(
"Fix"),
" : A(1)"});
866 ExpectedAFix.Edits.push_back(TextEdit{Main.range(
"A"),
""});
871 clangd::Fix ExpectedBFix;
872 ExpectedBFix.Message =
873 "'B' should be initialized in a member initializer of the constructor";
874 ExpectedBFix.Edits.push_back(TextEdit{Main.range(
"Fix"),
" : B(1)"});
875 ExpectedBFix.Edits.push_back(TextEdit{Main.range(
"B"),
""});
877 clangd::Fix ExpectedCFix;
878 ExpectedCFix.Message =
"variable 'C' is not initialized";
879 ExpectedCFix.Edits.push_back(TextEdit{Main.range(
"CFix"),
" = NAN"});
880 ExpectedCFix.Edits.push_back(
881 TextEdit{Main.range(
"MathHeader"),
"#include <math.h>\n\n"});
885 clangd::Fix ExpectedDFix;
886 ExpectedDFix.Message =
"variable 'D' is not initialized";
887 ExpectedDFix.Edits.push_back(TextEdit{Main.range(
"DFix"),
" = NAN"});
888 ExpectedDFix.Edits.push_back(
889 TextEdit{Main.range(
"MathHeader"),
"#include <math.h>\n\n"});
891 TU.build().getDiagnostics(),
892 ifTidyChecks(UnorderedElementsAre(
893 AllOf(Diag(Main.range(
"A"),
"'A' should be initialized in a member "
894 "initializer of the constructor"),
895 withFix(equalToFix(ExpectedAFix))),
896 AllOf(Diag(Main.range(
"B"),
"'B' should be initialized in a member "
897 "initializer of the constructor"),
898 withFix(equalToFix(ExpectedBFix))),
899 AllOf(Diag(Main.range(
"C"),
"variable 'C' is not initialized"),
900 withFix(equalToFix(ExpectedCFix))),
901 AllOf(Diag(Main.range(
"D"),
"variable 'D' is not initialized"),
902 withFix(equalToFix(ExpectedDFix))))));
905TEST(DiagnosticTest, ClangTidySelfContainedDiagsFormatting) {
906 Annotations Main(R
"cpp(
909 virtual void Reset1() = 0;
910 virtual void Reset2() = 0;
912 class A : public Interface {
913 // This will be marked by clangd to use override instead of virtual
914 $virtual1[[virtual ]]void $Reset1[[Reset1]]()$override1[[]];
915 $virtual2[[virtual ]]/**/void $Reset2[[Reset2]]()$override2[[]];
919 TU.ClangTidyProvider =
920 addTidyChecks("cppcoreguidelines-explicit-virtual-functions,");
921 clangd::Fix
const ExpectedFix1{
922 "prefer using 'override' or (rarely) 'final' "
923 "instead of 'virtual'",
924 {TextEdit{Main.range(
"override1"),
" override"},
925 TextEdit{Main.range(
"virtual1"),
""}},
927 clangd::Fix
const ExpectedFix2{
928 "prefer using 'override' or (rarely) 'final' "
929 "instead of 'virtual'",
930 {TextEdit{Main.range(
"override2"),
" override"},
931 TextEdit{Main.range(
"virtual2"),
""}},
935 EXPECT_THAT(TU.build().getDiagnostics(),
936 ifTidyChecks(UnorderedElementsAre(
937 AllOf(Diag(Main.range(
"Reset1"),
938 "prefer using 'override' or (rarely) 'final' "
939 "instead of 'virtual'"),
940 withFix(equalToFix(ExpectedFix1))),
941 AllOf(Diag(Main.range(
"Reset2"),
942 "prefer using 'override' or (rarely) 'final' "
943 "instead of 'virtual'"),
944 withFix(equalToFix(ExpectedFix2))))));
947TEST(DiagnosticsTest, ClangTidyCallingIntoPreprocessor) {
948 std::string Main = R
"cpp(
953 std::string Header = R"cpp(
954 #define EXTERN extern
958 TU.AdditionalFiles[
"b.h"] = Header;
959 TU.ClangTidyProvider =
addTidyChecks(
"modernize-use-trailing-return-type");
964TEST(DiagnosticsTest, Preprocessor) {
970 Annotations Test(R
"cpp(
973 int a = [[b]]; // error-ok
980 ElementsAre(Diag(Test.range(), "use of undeclared identifier 'b'")));
983TEST(DiagnosticsTest, IgnoreVerify) {
985 int a; // expected-error {{}}
987 TU.ExtraArgs.push_back("-Xclang");
988 TU.ExtraArgs.push_back(
"-verify");
989 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
992TEST(DiagnosticTest, IgnoreBEFilelistOptions) {
994 TU.ExtraArgs.push_back(
"-Xclang");
995 for (
const auto *DisableOption :
996 {
"-fsanitize-ignorelist=null",
"-fprofile-list=null",
997 "-fxray-always-instrument=null",
"-fxray-never-instrument=null",
998 "-fxray-attr-list=null"}) {
999 TU.ExtraArgs.push_back(DisableOption);
1000 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
1001 TU.ExtraArgs.pop_back();
1006TEST(DiagnosticsTest, RecursivePreamble) {
1008 #include "foo.h" // error-ok
1011 TU.Filename = "foo.h";
1012 EXPECT_THAT(TU.build().getDiagnostics(),
1013 ElementsAre(diagName(
"pp_including_mainfile_in_preamble")));
1014 EXPECT_THAT(TU.build().getLocalTopLevelDecls(), SizeIs(1));
1018TEST(DiagnosticsTest, RecursivePreamblePragmaOnce) {
1024 TU.Filename = "foo.h";
1025 EXPECT_THAT(TU.build().getDiagnostics(),
1026 Not(Contains(diagName(
"pp_including_mainfile_in_preamble"))));
1027 EXPECT_THAT(TU.build().getLocalTopLevelDecls(), SizeIs(1));
1032TEST(DiagnosticsTest, RecursivePreambleIfndefGuard) {
1036 #include "foo.h" // error-ok
1040 TU.Filename = "foo.h";
1042 EXPECT_THAT(TU.build().getDiagnostics(),
1043 ElementsAre(diagName(
"pp_including_mainfile_in_preamble")));
1044 EXPECT_THAT(TU.build().getLocalTopLevelDecls(), SizeIs(1));
1047TEST(DiagnosticsTest, PreambleWithPragmaAssumeNonnull) {
1049#pragma clang assume_nonnull begin
1051#pragma clang assume_nonnull end
1053 auto AST = TU.build();
1054 EXPECT_THAT(
AST.getDiagnostics(), IsEmpty());
1055 const auto *
X = cast<FunctionDecl>(
findDecl(
AST,
"foo")).getParamDecl(0);
1056 ASSERT_TRUE(
X->getOriginalType()->getNullability() ==
1057 NullabilityKind::NonNull);
1060TEST(DiagnosticsTest, PreambleHeaderWithBadPragmaAssumeNonnull) {
1061 Annotations Header(R
"cpp(
1062#pragma clang assume_nonnull begin // error-ok
1066#include "foo.h" // unterminated assume_nonnull should not affect bar.
1069 TU.AdditionalFiles = {{"foo.h", std::string(Header.code())}};
1070 auto AST = TU.build();
1071 EXPECT_THAT(
AST.getDiagnostics(),
1072 ElementsAre(diagName(
"pp_eof_in_assume_nonnull")));
1073 const auto *
X = cast<FunctionDecl>(
findDecl(
AST,
"foo")).getParamDecl(0);
1074 ASSERT_TRUE(
X->getOriginalType()->getNullability() ==
1075 NullabilityKind::NonNull);
1076 const auto *Y = cast<FunctionDecl>(
findDecl(
AST,
"bar")).getParamDecl(0);
1077 ASSERT_FALSE(Y->getOriginalType()->getNullability());
1080TEST(DiagnosticsTest, InsideMacros) {
1081 Annotations Test(R
"cpp(
1083 #define RET(x) return x + 10
1086 RET($foo[[0]]); // error-ok
1093 ElementsAre(Diag(Test.range("foo"),
1094 "cannot initialize return object of type "
1095 "'int *' with an rvalue of type 'int'"),
1096 Diag(Test.range(
"bar"),
1097 "cannot initialize return object of type "
1098 "'int *' with an rvalue of type 'int'")));
1101TEST(DiagnosticsTest, NoFixItInMacro) {
1102 Annotations Test(R
"cpp(
1103 #define Define(name) void name() {}
1105 [[Define]](main) // error-ok
1108 EXPECT_THAT(TU.build().getDiagnostics(),
1109 ElementsAre(AllOf(Diag(Test.range(),
"'main' must return 'int'"),
1113TEST(DiagnosticsTest, PragmaSystemHeader) {
1114 Annotations Test(
"#pragma clang [[system_header]]\n");
1117 TU.build().getDiagnostics(),
1119 Diag(Test.range(),
"#pragma system_header ignored in main file"))));
1120 TU.Filename =
"TestTU.h";
1121 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
1124TEST(ClangdTest, MSAsm) {
1127 llvm::InitializeAllTargetInfos();
1129 TU.ExtraArgs = {
"-fms-extensions"};
1130 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
1133TEST(DiagnosticsTest, ToLSP) {
1136 URIForFile HeaderFile =
1140 D.ID = clang::diag::err_undeclared_var_use;
1142 D.Name =
"undeclared_var_use";
1144 D.Message =
"something terrible happened";
1145 D.Range = {pos(1, 2), pos(3, 4)};
1146 D.InsideMainFile =
true;
1147 D.Severity = DiagnosticsEngine::Error;
1148 D.File =
"foo/bar/main.cpp";
1149 D.AbsFile = std::string(
MainFile.file());
1150 D.OpaqueData[
"test"] =
"bar";
1152 clangd::Note NoteInMain;
1153 NoteInMain.Message =
"declared somewhere in the main file";
1154 NoteInMain.Range = {pos(5, 6), pos(7, 8)};
1155 NoteInMain.Severity = DiagnosticsEngine::Remark;
1156 NoteInMain.File =
"../foo/bar/main.cpp";
1157 NoteInMain.InsideMainFile =
true;
1158 NoteInMain.AbsFile = std::string(
MainFile.file());
1160 D.Notes.push_back(NoteInMain);
1162 clangd::Note NoteInHeader;
1163 NoteInHeader.Message =
"declared somewhere in the header file";
1164 NoteInHeader.Range = {pos(9, 10), pos(11, 12)};
1165 NoteInHeader.Severity = DiagnosticsEngine::Note;
1166 NoteInHeader.File =
"../foo/baz/header.h";
1167 NoteInHeader.InsideMainFile =
false;
1168 NoteInHeader.AbsFile = std::string(HeaderFile.file());
1169 D.Notes.push_back(NoteInHeader);
1172 F.Message =
"do something";
1173 D.Fixes.push_back(F);
1176 clangd::Diagnostic MainLSP;
1177 MainLSP.range = D.Range;
1178 MainLSP.severity =
getSeverity(DiagnosticsEngine::Error);
1179 MainLSP.code =
"undeclared_var_use";
1180 MainLSP.source =
"clang";
1182 R
"(Something terrible happened (fix available)
1184main.cpp:6:7: remark: declared somewhere in the main file
1186../foo/baz/header.h:10:11:
1187note: declared somewhere in the header file)";
1189 MainLSP.data = D.OpaqueData;
1191 clangd::Diagnostic NoteInMainLSP;
1192 NoteInMainLSP.range = NoteInMain.Range;
1193 NoteInMainLSP.severity = getSeverity(DiagnosticsEngine::Remark);
1194 NoteInMainLSP.message = R"(Declared somewhere in the main file
1196main.cpp:2:3: error: something terrible happened)";
1198 ClangdDiagnosticOptions Opts;
1200 std::vector<std::pair<clangd::Diagnostic, std::vector<clangd::Fix>>> LSPDiags;
1202 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix> Fixes) {
1204 {std::move(LSPDiag),
1205 std::vector<clangd::Fix>(Fixes.begin(), Fixes.end())});
1210 ElementsAre(
Pair(equalToLSPDiag(MainLSP), ElementsAre(equalToFix(F))),
1211 Pair(equalToLSPDiag(NoteInMainLSP), IsEmpty())));
1212 EXPECT_EQ(LSPDiags[0].first.code,
"undeclared_var_use");
1213 EXPECT_EQ(LSPDiags[0].first.source,
"clang");
1214 EXPECT_EQ(LSPDiags[1].first.code,
"");
1215 EXPECT_EQ(LSPDiags[1].first.source,
"");
1219 Opts.EmitRelatedLocations =
true;
1221 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix> Fixes) {
1223 {std::move(LSPDiag),
1224 std::vector<clangd::Fix>(Fixes.begin(), Fixes.end())});
1226 MainLSP.message =
"Something terrible happened (fix available)";
1227 DiagnosticRelatedInformation NoteInMainDRI;
1228 NoteInMainDRI.message =
"Declared somewhere in the main file";
1229 NoteInMainDRI.location.range = NoteInMain.Range;
1230 NoteInMainDRI.location.uri =
MainFile;
1231 MainLSP.relatedInformation = {NoteInMainDRI};
1232 DiagnosticRelatedInformation NoteInHeaderDRI;
1233 NoteInHeaderDRI.message =
"Declared somewhere in the header file";
1234 NoteInHeaderDRI.location.range = NoteInHeader.Range;
1235 NoteInHeaderDRI.location.uri = HeaderFile;
1236 MainLSP.relatedInformation = {NoteInMainDRI, NoteInHeaderDRI};
1237 EXPECT_THAT(LSPDiags, ElementsAre(
Pair(equalToLSPDiag(MainLSP),
1238 ElementsAre(equalToFix(F)))));
1241struct SymbolWithHeader {
1243 std::string DeclaringFile;
1244 std::string IncludeHeader;
1247std::unique_ptr<SymbolIndex>
1248buildIndexWithSymbol(llvm::ArrayRef<SymbolWithHeader> Syms) {
1249 SymbolSlab::Builder Slab;
1250 for (
const auto &S : Syms) {
1251 Symbol Sym =
cls(S.QName);
1253 Sym.CanonicalDeclaration.FileURI = S.DeclaringFile.c_str();
1254 Sym.Definition.FileURI = S.DeclaringFile.c_str();
1258 return MemIndex::build(std::move(Slab).build(), RefSlab(), RelationSlab());
1261TEST(IncludeFixerTest, IncompleteType) {
1263 TU.ExtraArgs.push_back(
"-std=c++20");
1264 auto Index = buildIndexWithSymbol(
1265 {SymbolWithHeader{
"ns::X",
"unittest:///x.h",
"\"x.h\""}});
1266 TU.ExternalIndex = Index.get();
1268 std::vector<std::pair<llvm::StringRef, llvm::StringRef>> Tests{
1269 {
"incomplete_nested_name_spec",
"[[ns::X::]]Nested n;"},
1270 {
"incomplete_base_class",
"class Y : [[ns::X]] {};"},
1271 {
"incomplete_member_access",
"auto i = x[[->]]f();"},
1272 {
"incomplete_type",
"auto& [[[]]m] = *x;"},
1273 {
"init_incomplete_type",
1274 "struct C { static int f(ns::X&); }; int i = C::f([[{]]});"},
1275 {
"bad_cast_incomplete",
"auto a = [[static_cast]]<ns::X>(0);"},
1276 {
"template_nontype_parm_incomplete",
"template <ns::X [[foo]]> int a;"},
1277 {
"typecheck_decl_incomplete_type",
"ns::X [[var]];"},
1278 {
"typecheck_incomplete_tag",
"auto i = [[(*x)]]->f();"},
1279 {
"typecheck_nonviable_condition_incomplete",
1280 "struct A { operator ns::X(); } a; const ns::X &[[b]] = a;"},
1281 {
"invalid_incomplete_type_use",
"auto var = [[ns::X()]];"},
1282 {
"sizeof_alignof_incomplete_or_sizeless_type",
1283 "auto s = [[sizeof]](ns::X);"},
1284 {
"for_range_incomplete_type",
"void foo() { for (auto i : [[*]]x ) {} }"},
1285 {
"func_def_incomplete_result",
"ns::X [[func]] () {}"},
1286 {
"field_incomplete_or_sizeless",
"class M { ns::X [[member]]; };"},
1287 {
"array_incomplete_or_sizeless_type",
"auto s = [[(ns::X[]){}]];"},
1288 {
"call_incomplete_return",
"ns::X f(); auto fp = &f; auto z = [[fp()]];"},
1289 {
"call_function_incomplete_return",
"ns::X foo(); auto a = [[foo()]];"},
1290 {
"call_incomplete_argument",
"int m(ns::X); int i = m([[*x]]);"},
1291 {
"switch_incomplete_class_type",
"void a() { [[switch]](*x) {} }"},
1292 {
"delete_incomplete_class_type",
"void f() { [[delete]] *x; }"},
1293 {
"-Wdelete-incomplete",
"void f() { [[delete]] x; }"},
1294 {
"dereference_incomplete_type",
1295 R
"cpp(void f() { asm("" : "=r"([[*]]x)::); })cpp"},
1297 for (
auto Case : Tests) {
1298 Annotations Main(Case.second);
1299 TU.Code = Main.code().str() +
"\n // error-ok";
1301 TU.build().getDiagnostics(),
1302 ElementsAre(AllOf(diagName(Case.first), hasRange(Main.range()),
1303 withFix(Fix(Range{},
"#include \"x.h\"\n",
1304 "Include \"x.h\" for symbol ns::X")))))
1309TEST(IncludeFixerTest, IncompleteEnum) {
1310 Symbol Sym =
enm(
"X");
1312 Sym.CanonicalDeclaration.FileURI = Sym.Definition.FileURI =
"unittest:///x.h";
1314 SymbolSlab::Builder Slab;
1320 TU.ExternalIndex = Index.get();
1321 TU.ExtraArgs.push_back(
"-std=c++20");
1322 TU.ExtraArgs.push_back(
"-fno-ms-compatibility");
1324 std::vector<std::pair<llvm::StringRef, llvm::StringRef>> Tests{
1325 {
"incomplete_enum",
"enum class X : int; using enum [[X]];"},
1326 {
"underlying_type_of_incomplete_enum",
1327 "[[__underlying_type]](enum X) i;"},
1329 for (
auto Case : Tests) {
1330 Annotations Main(Case.second);
1331 TU.Code = Main.code().str() +
"\n // error-ok";
1332 EXPECT_THAT(TU.build().getDiagnostics(),
1333 Contains(AllOf(diagName(Case.first), hasRange(Main.range()),
1334 withFix(Fix(Range{},
"#include \"x.h\"\n",
1335 "Include \"x.h\" for symbol X")))))
1340TEST(IncludeFixerTest, NoSuggestIncludeWhenNoDefinitionInHeader) {
1341 Annotations Test(R
"cpp(// error-ok
1342$insert[[]]namespace ns {
1345class Y : $base[[public ns::X]] {};
1352 Symbol Sym =
cls(
"ns::X");
1354 Sym.CanonicalDeclaration.FileURI =
"unittest:///x.h";
1355 Sym.Definition.FileURI =
"unittest:///x.cc";
1358 SymbolSlab::Builder Slab;
1362 TU.ExternalIndex = Index.get();
1364 EXPECT_THAT(TU.build().getDiagnostics(),
1365 UnorderedElementsAre(
1366 Diag(Test.range(
"base"),
"base class has incomplete type"),
1367 Diag(Test.range(
"access"),
1368 "member access into incomplete type 'ns::X'")));
1371TEST(IncludeFixerTest, Typo) {
1372 Annotations Test(R
"cpp(// error-ok
1373$insert[[]]namespace ns {
1375 $unqualified1[[X]] x;
1376 // No fix if the unresolved type is used as specifier. (ns::)X::Nested will be
1377 // considered the unresolved type.
1378 $unqualified2[[X]]::Nested n;
1380struct S : $base[[X]] {};
1383 ns::$qualified1[[X]] x; // ns:: is valid.
1384 ns::$qualified2[[X]](); // Error: no member in namespace
1386 ::$global[[Global]] glob;
1388using Type = ns::$template[[Foo]]<int>;
1391 auto Index = buildIndexWithSymbol(
1392 {SymbolWithHeader{
"ns::X",
"unittest:///x.h",
"\"x.h\""},
1393 SymbolWithHeader{
"Global",
"unittest:///global.h",
"\"global.h\""},
1394 SymbolWithHeader{
"ns::Foo",
"unittest:///foo.h",
"\"foo.h\""}});
1395 TU.ExternalIndex = Index.get();
1398 TU.build().getDiagnostics(),
1399 UnorderedElementsAre(
1400 AllOf(Diag(Test.range(
"unqualified1"),
"unknown type name 'X'"),
1401 diagName(
"unknown_typename"),
1402 withFix(Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
1403 "Include \"x.h\" for symbol ns::X"))),
1404 Diag(Test.range(
"unqualified2"),
"use of undeclared identifier 'X'"),
1405 AllOf(Diag(Test.range(
"qualified1"),
1406 "no type named 'X' in namespace 'ns'"),
1407 diagName(
"typename_nested_not_found"),
1408 withFix(Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
1409 "Include \"x.h\" for symbol ns::X"))),
1410 AllOf(Diag(Test.range(
"qualified2"),
1411 "no member named 'X' in namespace 'ns'"),
1412 diagName(
"no_member"),
1413 withFix(Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
1414 "Include \"x.h\" for symbol ns::X"))),
1415 AllOf(Diag(Test.range(
"global"),
1416 "no type named 'Global' in the global namespace"),
1417 diagName(
"typename_nested_not_found"),
1418 withFix(Fix(Test.range(
"insert"),
"#include \"global.h\"\n",
1419 "Include \"global.h\" for symbol Global"))),
1420 AllOf(Diag(Test.range(
"template"),
1421 "no template named 'Foo' in namespace 'ns'"),
1422 diagName(
"no_member_template"),
1423 withFix(Fix(Test.range(
"insert"),
"#include \"foo.h\"\n",
1424 "Include \"foo.h\" for symbol ns::Foo"))),
1425 AllOf(Diag(Test.range(
"base"),
"expected class name"),
1426 diagName(
"expected_class_name"),
1427 withFix(Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
1428 "Include \"x.h\" for symbol ns::X")))));
1431TEST(IncludeFixerTest, TypoInMacro) {
1442 auto Index = buildIndexWithSymbol(
1443 {SymbolWithHeader{
"X",
"unittest:///x.h",
"\"x.h\""},
1444 SymbolWithHeader{
"ns::X",
"unittest:///ns.h",
"\"x.h\""}});
1445 TU.ExternalIndex = Index.get();
1448 TU.ExtraArgs = {
"-fno-ms-compatibility"};
1449 EXPECT_THAT(TU.build().getDiagnostics(), Each(withFix(_)));
1452TEST(IncludeFixerTest, MultipleMatchedSymbols) {
1453 Annotations Test(R
"cpp(// error-ok
1454$insert[[]]namespace na {
1457 $unqualified[[X]] x;
1463 auto Index = buildIndexWithSymbol(
1464 {SymbolWithHeader{
"na::X",
"unittest:///a.h",
"\"a.h\""},
1465 SymbolWithHeader{
"na::nb::X",
"unittest:///b.h",
"\"b.h\""}});
1466 TU.ExternalIndex = Index.get();
1468 EXPECT_THAT(TU.build().getDiagnostics(),
1469 UnorderedElementsAre(AllOf(
1470 Diag(Test.range(
"unqualified"),
"unknown type name 'X'"),
1471 diagName(
"unknown_typename"),
1472 withFix(Fix(Test.range(
"insert"),
"#include \"a.h\"\n",
1473 "Include \"a.h\" for symbol na::X"),
1474 Fix(Test.range(
"insert"),
"#include \"b.h\"\n",
1475 "Include \"b.h\" for symbol na::nb::X")))));
1478TEST(IncludeFixerTest, NoCrashMemberAccess) {
1479 Annotations Test(R
"cpp(// error-ok
1480 struct X { int xyz; };
1481 void g() { X x; x.$[[xy]]; }
1484 auto Index = buildIndexWithSymbol(
1485 SymbolWithHeader{
"na::X",
"unittest:///a.h",
"\"a.h\""});
1486 TU.ExternalIndex = Index.get();
1489 TU.build().getDiagnostics(),
1490 UnorderedElementsAre(Diag(Test.range(),
"no member named 'xy' in 'X'")));
1493TEST(IncludeFixerTest, UseCachedIndexResults) {
1496 Annotations Test(R
"cpp(// error-ok
1497$insert[[]]void foo() {
1520 buildIndexWithSymbol(SymbolWithHeader{
"X",
"unittest:///a.h",
"\"a.h\""});
1521 TU.ExternalIndex = Index.get();
1523 auto Parsed = TU.build();
1524 for (
const auto &D : Parsed.getDiagnostics()) {
1525 if (D.Fixes.size() != 1) {
1526 ADD_FAILURE() <<
"D.Fixes.size() != 1";
1529 EXPECT_EQ(D.Fixes[0].Message, std::string(
"Include \"a.h\" for symbol X"));
1533TEST(IncludeFixerTest, UnresolvedNameAsSpecifier) {
1534 Annotations Test(R
"cpp(// error-ok
1535$insert[[]]namespace ns {
1537void g() { ns::$[[scope]]::X_Y(); }
1540 TU.Code = std::string(Test.code());
1542 TU.ExtraArgs.push_back(
"-fno-ms-compatibility");
1543 auto Index = buildIndexWithSymbol(
1544 SymbolWithHeader{
"ns::scope::X_Y",
"unittest:///x.h",
"\"x.h\""});
1545 TU.ExternalIndex = Index.get();
1548 TU.build().getDiagnostics(),
1549 UnorderedElementsAre(
1550 AllOf(Diag(Test.range(),
"no member named 'scope' in namespace 'ns'"),
1551 diagName(
"no_member"),
1552 withFix(Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
1553 "Include \"x.h\" for symbol ns::scope::X_Y")))));
1556TEST(IncludeFixerTest, UnresolvedSpecifierWithSemaCorrection) {
1557 Annotations Test(R
"cpp(// error-ok
1558$insert[[]]namespace clang {
1560 // "clangd::" will be corrected to "clang::" by Sema.
1561 $q1[[clangd]]::$x[[X]] x;
1562 $q2[[clangd]]::$ns[[ns]]::Y y;
1567 TU.Code = std::string(Test.code());
1569 TU.ExtraArgs.push_back(
"-fno-ms-compatibility");
1570 auto Index = buildIndexWithSymbol(
1571 {SymbolWithHeader{
"clang::clangd::X",
"unittest:///x.h",
"\"x.h\""},
1572 SymbolWithHeader{
"clang::clangd::ns::Y",
"unittest:///y.h",
"\"y.h\""}});
1573 TU.ExternalIndex = Index.get();
1576 TU.build().getDiagnostics(),
1577 UnorderedElementsAre(
1578 AllOf(Diag(Test.range(
"q1"),
"use of undeclared identifier 'clangd'; "
1579 "did you mean 'clang'?"),
1580 diagName(
"undeclared_var_use_suggest"),
1582 Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
1583 "Include \"x.h\" for symbol clang::clangd::X"))),
1584 AllOf(Diag(Test.range(
"x"),
"no type named 'X' in namespace 'clang'"),
1585 diagName(
"typename_nested_not_found"),
1586 withFix(Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
1587 "Include \"x.h\" for symbol clang::clangd::X"))),
1589 Diag(Test.range(
"q2"),
"use of undeclared identifier 'clangd'; "
1590 "did you mean 'clang'?"),
1591 diagName(
"undeclared_var_use_suggest"),
1593 Fix(Test.range(
"insert"),
"#include \"y.h\"\n",
1594 "Include \"y.h\" for symbol clang::clangd::ns::Y"))),
1595 AllOf(Diag(Test.range(
"ns"),
1596 "no member named 'ns' in namespace 'clang'"),
1597 diagName(
"no_member"),
1599 Fix(Test.range(
"insert"),
"#include \"y.h\"\n",
1600 "Include \"y.h\" for symbol clang::clangd::ns::Y")))));
1603TEST(IncludeFixerTest, SpecifiedScopeIsNamespaceAlias) {
1604 Annotations Test(R
"cpp(// error-ok
1605$insert[[]]namespace a {}
1612 auto Index = buildIndexWithSymbol(
1613 SymbolWithHeader{
"a::X",
"unittest:///x.h",
"\"x.h\""});
1614 TU.ExternalIndex = Index.get();
1616 EXPECT_THAT(TU.build().getDiagnostics(),
1617 UnorderedElementsAre(AllOf(
1618 Diag(Test.range(),
"no type named 'X' in namespace 'a'"),
1619 diagName(
"typename_nested_not_found"),
1620 withFix(Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
1621 "Include \"x.h\" for symbol a::X")))));
1624TEST(IncludeFixerTest, NoCrashOnTemplateInstantiations) {
1625 Annotations Test(R
"cpp(
1626 template <typename T> struct Templ {
1627 template <typename U>
1628 typename U::type operator=(const U &);
1633 A() { [[a]]; /*error-ok*/ } // crash if we compute scopes lazily.
1638 auto Index = buildIndexWithSymbol({});
1639 TU.ExternalIndex = Index.get();
1642 TU.build().getDiagnostics(),
1643 ElementsAre(Diag(Test.range(),
"use of undeclared identifier 'a'")));
1646TEST(IncludeFixerTest, HeaderNamedInDiag) {
1647 Annotations Test(R
"cpp(
1648 $insert[[]]int main() {
1653 TU.ExtraArgs = {
"-xc",
"-std=c99",
1654 "-Wno-error=implicit-function-declaration"};
1655 auto Index = buildIndexWithSymbol({});
1656 TU.ExternalIndex = Index.get();
1659 TU.build().getDiagnostics(),
1661 Diag(Test.range(),
"call to undeclared library function 'printf' "
1662 "with type 'int (const char *, ...)'; ISO C99 "
1663 "and later do not support implicit function "
1665 withFix(Fix(Test.range(
"insert"),
"#include <stdio.h>\n",
1666 "Include <stdio.h> for symbol printf")))));
1668 TU.ExtraArgs = {
"-xc",
"-std=c89"};
1670 TU.build().getDiagnostics(),
1672 Diag(Test.range(),
"implicitly declaring library function 'printf' "
1673 "with type 'int (const char *, ...)'"),
1674 withFix(Fix(Test.range(
"insert"),
"#include <stdio.h>\n",
1675 "Include <stdio.h> for symbol printf")))));
1678TEST(IncludeFixerTest, CImplicitFunctionDecl) {
1679 Annotations Test(
"void x() { [[foo]](); }");
1681 TU.Filename =
"test.c";
1682 TU.ExtraArgs = {
"-std=c99",
"-Wno-error=implicit-function-declaration"};
1684 Symbol Sym =
func(
"foo");
1686 Sym.CanonicalDeclaration.FileURI =
"unittest:///foo.h";
1689 SymbolSlab::Builder Slab;
1693 TU.ExternalIndex = Index.get();
1696 TU.build().getDiagnostics(),
1699 "call to undeclared function 'foo'; ISO C99 and later do not "
1700 "support implicit function declarations"),
1701 withFix(Fix(Range{},
"#include \"foo.h\"\n",
1702 "Include \"foo.h\" for symbol foo")))));
1704 TU.ExtraArgs = {
"-std=c89",
"-Wall"};
1705 EXPECT_THAT(TU.build().getDiagnostics(),
1707 Diag(Test.range(),
"implicit declaration of function 'foo'"),
1708 withFix(Fix(Range{},
"#include \"foo.h\"\n",
1709 "Include \"foo.h\" for symbol foo")))));
1712TEST(DiagsInHeaders, DiagInsideHeader) {
1713 Annotations Main(R
"cpp(
1715 void foo() {})cpp");
1716 Annotations Header("[[no_type_spec]]; // error-ok");
1718 TU.AdditionalFiles = {{
"a.h", std::string(Header.code())}};
1719 EXPECT_THAT(TU.build().getDiagnostics(),
1720 UnorderedElementsAre(AllOf(
1721 Diag(Main.range(),
"in included file: a type specifier is "
1722 "required for all declarations"),
1723 withNote(Diag(Header.range(),
"error occurred here")))));
1726TEST(DiagsInHeaders, DiagInTransitiveInclude) {
1727 Annotations Main(R
"cpp(
1729 void foo() {})cpp");
1731 TU.AdditionalFiles = {{"a.h",
"#include \"b.h\""},
1732 {
"b.h",
"no_type_spec; // error-ok"}};
1733 EXPECT_THAT(TU.build().getDiagnostics(),
1734 UnorderedElementsAre(Diag(Main.range(),
1735 "in included file: a type specifier is "
1736 "required for all declarations")));
1739TEST(DiagsInHeaders, DiagInMultipleHeaders) {
1740 Annotations Main(R
"cpp(
1741 #include $a[["a.h"]]
1742 #include $b[["b.h"]]
1743 void foo() {})cpp");
1745 TU.AdditionalFiles = {{"a.h",
"no_type_spec; // error-ok"},
1746 {
"b.h",
"no_type_spec; // error-ok"}};
1747 EXPECT_THAT(TU.build().getDiagnostics(),
1748 UnorderedElementsAre(
1749 Diag(Main.range(
"a"),
"in included file: a type specifier is "
1750 "required for all declarations"),
1751 Diag(Main.range(
"b"),
"in included file: a type specifier is "
1752 "required for all declarations")));
1755TEST(DiagsInHeaders, PreferExpansionLocation) {
1756 Annotations Main(R
"cpp(
1759 void foo() {})cpp");
1761 TU.AdditionalFiles = {
1762 {"a.h",
"#include \"b.h\"\n"},
1763 {
"b.h",
"#ifndef X\n#define X\nno_type_spec; // error-ok\n#endif"}};
1764 EXPECT_THAT(TU.build().getDiagnostics(),
1765 Contains(Diag(Main.range(),
"in included file: a type specifier "
1766 "is required for all declarations")));
1769TEST(DiagsInHeaders, PreferExpansionLocationMacros) {
1770 Annotations Main(R
"cpp(
1775 void foo() {})cpp");
1777 TU.AdditionalFiles = {
1778 {"a.h",
"#include \"c.h\"\n"},
1779 {
"b.h",
"#include \"c.h\"\n"},
1780 {
"c.h",
"#ifndef X\n#define X\nno_type_spec; // error-ok\n#endif"}};
1781 EXPECT_THAT(TU.build().getDiagnostics(),
1782 UnorderedElementsAre(Diag(Main.range(),
1783 "in included file: a type specifier is "
1784 "required for all declarations")));
1787TEST(DiagsInHeaders, LimitDiagsOutsideMainFile) {
1788 Annotations Main(R
"cpp(
1791 void foo() {})cpp");
1793 TU.AdditionalFiles = {{"a.h",
"#include \"c.h\"\n"},
1794 {
"b.h",
"#include \"c.h\"\n"},
1798 no_type_spec_0; // error-ok
1810 EXPECT_THAT(TU.build().getDiagnostics(),
1811 UnorderedElementsAre(Diag(Main.range(),
1812 "in included file: a type specifier is "
1813 "required for all declarations")));
1816TEST(DiagsInHeaders, OnlyErrorOrFatal) {
1817 Annotations Main(R
"cpp(
1819 void foo() {})cpp");
1820 Annotations Header(R"cpp(
1821 [[no_type_spec]]; // error-ok
1824 TU.AdditionalFiles = {{"a.h", std::string(Header.code())}};
1825 EXPECT_THAT(TU.build().getDiagnostics(),
1826 UnorderedElementsAre(AllOf(
1827 Diag(Main.range(),
"in included file: a type specifier is "
1828 "required for all declarations"),
1829 withNote(Diag(Header.range(),
"error occurred here")))));
1832TEST(DiagsInHeaders, OnlyDefaultErrorOrFatal) {
1833 Annotations Main(R
"cpp(
1834 #include [["a.h"]] // get unused "foo" warning when building preamble.
1836 Annotations Header(R"cpp(
1837 namespace { void foo() {} }
1838 void func() {foo();} ;)cpp");
1840 TU.AdditionalFiles = {{"a.h", std::string(Header.code())}};
1842 TU.ExtraArgs = {
"-Werror",
"-Wunused"};
1843 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
1846TEST(DiagsInHeaders, FromNonWrittenSources) {
1847 Annotations Main(R
"cpp(
1849 void foo() {})cpp");
1850 Annotations Header(R"cpp(
1852 int b = [[FOO]]; // error-ok)cpp");
1854 TU.AdditionalFiles = {{"a.h", std::string(Header.code())}};
1855 TU.ExtraArgs = {
"-DFOO=NOOO"};
1856 EXPECT_THAT(TU.build().getDiagnostics(),
1857 UnorderedElementsAre(AllOf(
1859 "in included file: use of undeclared identifier 'NOOO'"),
1860 withNote(Diag(Header.range(),
"error occurred here")))));
1863TEST(DiagsInHeaders, ErrorFromMacroExpansion) {
1864 Annotations Main(R
"cpp(
1869 Annotations Header(R"cpp(
1873 TU.AdditionalFiles = {{"a.h", std::string(Header.code())}};
1874 EXPECT_THAT(TU.build().getDiagnostics(),
1875 UnorderedElementsAre(
1876 Diag(Main.range(),
"in included file: use of undeclared "
1877 "identifier 'foo'; did you mean 'fo'?")));
1880TEST(DiagsInHeaders, ErrorFromMacroArgument) {
1881 Annotations Main(R
"cpp(
1886 Annotations Header(R"cpp(
1890 TU.AdditionalFiles = {{"a.h", std::string(Header.code())}};
1891 EXPECT_THAT(TU.build().getDiagnostics(),
1892 UnorderedElementsAre(
1893 Diag(Main.range(),
"in included file: use of undeclared "
1894 "identifier 'foo'; did you mean 'fo'?")));
1899 TU.ExtraArgs.push_back(
"--include=a.h");
1900 TU.AdditionalFiles = {{
"a.h",
"void main();"}};
1903 EXPECT_THAT(TU.build().getDiagnostics(), UnorderedElementsAre());
1906TEST(ToLSPDiag, RangeIsInMain) {
1907 ClangdDiagnosticOptions Opts;
1909 D.Range = {pos(1, 2), pos(3, 4)};
1910 D.Notes.emplace_back();
1911 Note &N = D.Notes.back();
1912 N.Range = {pos(2, 3), pos(3, 4)};
1914 D.InsideMainFile =
true;
1915 N.InsideMainFile =
false;
1917 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix>) {
1918 EXPECT_EQ(LSPDiag.range, D.Range);
1921 D.InsideMainFile =
false;
1922 N.InsideMainFile =
true;
1924 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix>) {
1925 EXPECT_EQ(LSPDiag.range, N.Range);
1929TEST(ParsedASTTest, ModuleSawDiag) {
1932 auto AST = TU.build();
1934 EXPECT_THAT(
AST.getDiagnostics(),
1935 testing::Contains(Diag(Code.range(), KDiagMsg.str())));
1941 TU.ExtraArgs = {
"-Wnewline-eof"};
1944 TU.Code =
"#define FOO\n void bar();\n";
1945 auto AST = TU.build();
1946 EXPECT_THAT(
AST.getDiagnostics(), IsEmpty());
1949 Annotations Code(
"#define FOO[[]]");
1950 TU.Code = Code.code().str();
1951 auto AST = TU.build();
1953 AST.getDiagnostics(),
1954 testing::Contains(Diag(Code.range(),
"no newline at end of file")));
1960 TU.ExtraArgs = {
"-Wunused",
"-Wdeprecated"};
1961 Annotations Test(R
"cpp(
1962 void bar() __attribute__((deprecated));
1965 $deprecated[[bar]]();
1967 TU.Code = Test.code().str();
1968 EXPECT_THAT(TU.build().getDiagnostics(),
1969 UnorderedElementsAre(
1970 AllOf(Diag(Test.range("unused"),
"unused variable 'x'"),
1972 AllOf(Diag(Test.range(
"deprecated"),
"'bar' is deprecated"),
1975 Test = Annotations(R
"cpp(
1976 $typedef[[typedef int INT]];
1978 TU.Code = Test.code();
1979 TU.ClangTidyProvider = addTidyChecks("modernize-use-using");
1981 TU.build().getDiagnostics(),
1982 ifTidyChecks(UnorderedElementsAre(
1983 AllOf(Diag(Test.range(
"typedef"),
"use 'using' instead of 'typedef'"),
1989 TU.ExtraArgs = {
"-Werror"};
1990 Annotations Test(R
"cpp($typedef[[typedef int INT]]; // error-ok)cpp");
1991 TU.Code = Test.code().str();
1992 TU.ClangTidyProvider = addTidyChecks("modernize-use-using");
1994 TU.build().getDiagnostics(),
1995 ifTidyChecks(UnorderedElementsAre(
1996 AllOf(Diag(Test.range(
"typedef"),
"use 'using' instead of 'typedef'"),
1999 diagSeverity(DiagnosticsEngine::Warning)))));
2001 TU.ClangTidyProvider =
2004 TU.build().getDiagnostics(),
2005 ifTidyChecks(UnorderedElementsAre(
2006 AllOf(Diag(Test.range(
"typedef"),
"use 'using' instead of 'typedef'"),
2008 diagSeverity(DiagnosticsEngine::Error)))));
2012 ClangdDiagnosticOptions Opts;
2013 std::optional<clangd::Diagnostic> Diag;
2015 D.Range = {pos(1, 2), pos(3, 4)};
2016 D.InsideMainFile =
true;
2020 D.Severity = DiagnosticsEngine::Warning;
2022 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix>) {
2023 Diag = std::move(LSPDiag);
2025 EXPECT_EQ(Diag->severity,
getSeverity(DiagnosticsEngine::Remark));
2029 D.Severity = DiagnosticsEngine::Error;
2031 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix>) {
2032 Diag = std::move(LSPDiag);
2034 EXPECT_EQ(Diag->severity,
getSeverity(DiagnosticsEngine::Error));
2039 D.Severity = DiagnosticsEngine::Warning;
2041 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix>) {
2042 Diag = std::move(LSPDiag);
2044 EXPECT_EQ(Diag->severity,
getSeverity(DiagnosticsEngine::Warning));
2047TEST(DiagnosticsTest, IncludeCleaner) {
2048 Annotations Test(R
"cpp(
2049$fix[[ $diag[[#include "unused.h"]]
2055 #include <system_header.h>
2062 TU.Code = Test.code().str();
2063 TU.AdditionalFiles["unused.h"] = R
"cpp(
2067 TU.AdditionalFiles["used.h"] = R
"cpp(
2071 TU.AdditionalFiles["ignore.h"] = R
"cpp(
2075 TU.AdditionalFiles["system/system_header.h"] =
"";
2076 TU.ExtraArgs = {
"-isystem" +
testPath(
"system")};
2081 [](llvm::StringRef Header) {
return Header.ends_with(
"ignore.h"); });
2082 WithContextValue WithCfg(
Config::Key, std::move(Cfg));
2083 auto AST = TU.build();
2085 AST.getDiagnostics(),
2087 Diag(Test.range(
"diag"),
2088 "included header unused.h is not used directly"),
2090 withFix(Fix(Test.range(
"fix"),
"",
"remove #include directive")))));
2091 auto &Diag =
AST.getDiagnostics().front();
2093 llvm::ValueIs(Not(IsEmpty())));
2095 WithContextValue SuppressAllWithCfg(
Config::Key, std::move(Cfg));
2096 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
2099 WithContextValue SuppressFilterWithCfg(
Config::Key, std::move(Cfg));
2100 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
2103TEST(DiagnosticsTest, FixItFromHeader) {
2104 llvm::StringLiteral Header(R
"cpp(
2106 void foo(int *, int);)cpp");
2107 Annotations Source(R"cpp(
2111 $diag[[foo]]($fix[[]]x, 1);
2114 TU.Code = Source.code().str();
2115 TU.HeaderCode = Header.str();
2117 TU.build().getDiagnostics(),
2118 UnorderedElementsAre(AllOf(
2119 Diag(Source.range("diag"),
"no matching function for call to 'foo'"),
2120 withFix(Fix(Source.range(
"fix"),
"&",
2121 "candidate function not viable: no known conversion from "
2122 "'int' to 'int *' for 1st argument; take the address of "
2123 "the argument with &")))));
2126TEST(DiagnosticsTest, UnusedInHeader) {
2129 TU.ExtraArgs.push_back(
"-Wunused-function");
2130 TU.Filename =
"test.c";
2131 EXPECT_THAT(TU.build().getDiagnostics(),
2132 ElementsAre(withID(diag::warn_unused_function)));
2135 TU.Filename =
"test.h";
2136 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.