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 +
"]") {
95 return arg.Range ==
Range && arg.Message == 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;
105MATCHER_P(fixMessage, Message,
"") {
return arg.Message == Message; }
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; }
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) {
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) {
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) {
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) {
264 float foo = [[0.1f]];
269 TU.ClangTidyProvider =
addTidyChecks(
"readability-uppercase-literal-suffix,"
273 TU.build().getDiagnostics(),
274 ifTidyChecks(UnorderedElementsAre(::testing::AllOf(
276 "floating point literal has suffix 'f', which is not uppercase"),
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) {
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.AdditionalFiles[
"system/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(
"-isystem" +
testPath(
"system"));
328 TU.ExtraArgs.push_back(
"-Wno-unsequenced");
330 TU.build().getDiagnostics(),
331 ifTidyChecks(UnorderedElementsAre(
332 AllOf(
Diag(Test.range(
"deprecated"),
333 "inclusion of deprecated C++ header 'assert.h'; consider "
334 "using 'cassert' instead"),
336 diagName(
"modernize-deprecated-headers"),
337 withFix(
Fix(Test.range(
"deprecated"),
"<cassert>",
338 "change '\"assert.h\"' to '<cassert>'"))),
339 Diag(Test.range(
"doubled"),
340 "suspicious usage of 'sizeof(sizeof(...))'"),
341 AllOf(
Diag(Test.range(
"macroarg"),
342 "side effects in the 1st macro argument 'X' are "
346 diagName(
"bugprone-macro-repeated-side-effects"),
347 withNote(
Diag(Test.range(
"macrodef"),
348 "macro 'SQUARE' defined here"))),
349 AllOf(
Diag(Test.range(
"main"),
350 "use a trailing return type for this function"),
352 diagName(
"modernize-use-trailing-return-type"),
355 "use a trailing return type for this function"))),
356 Diag(Test.range(
"foo"),
357 "function 'foo' is within a recursive call chain"),
358 Diag(Test.range(
"bar"),
359 "function 'bar' is within a recursive call chain"))));
362TEST(DiagnosticsTest, ClangTidyEOF) {
366 #include "a.h")cpp");
369 TU.ExtraArgs = {
"-isystem."};
370 TU.AdditionalFiles[
"a.h"] = TU.AdditionalFiles[
"b.h"] =
"";
373 TU.build().getDiagnostics(),
374 ifTidyChecks(Contains(
375 AllOf(
Diag(Test.range(),
"#includes are not sorted properly"),
379TEST(DiagnosticTest, TemplatesInHeaders) {
382 Derived<int> [[y]]; // error-ok
385 template <typename T>
386 struct Derived : [[T]] {};
389 TU.HeaderCode = Header.code().str();
391 TU.build().getDiagnostics(),
393 Diag(Main.range(), "in template: base specifier must name a class"),
394 withNote(
Diag(Header.range(),
"error occurred here"),
395 Diag(Main.range(),
"in instantiation of template class "
396 "'Derived<int>' requested here")))));
399TEST(DiagnosticTest, MakeUnique) {
403 struct S { S(char*); };
404 auto x = std::[[make_unique]]<S>(42); // error-ok
407 TU.HeaderCode = R"cpp(
409 // These mocks aren't quite right - we omit unique_ptr for simplicity.
410 // forward is included to show its body is not needed to get the diagnostic.
411 template <typename T> T&& forward(T& t);
412 template <typename T, typename... A> T* make_unique(A&&... args) {
413 return new T(std::forward<A>(args)...);
417 EXPECT_THAT(TU.build().getDiagnostics(),
418 UnorderedElementsAre(
421 "no matching constructor for initialization of 'S'")));
424TEST(DiagnosticTest, CoroutineInHeader) {
425 StringRef CoroutineH = R
"cpp(
427template <class Ret, typename... T>
428struct coroutine_traits { using promise_type = typename Ret::promise_type; };
430template <class Promise = void>
431struct coroutine_handle {
432 static coroutine_handle from_address(void *) noexcept;
433 static coroutine_handle from_promise(Promise &promise);
434 constexpr void* address() const noexcept;
437struct coroutine_handle<void> {
438 template <class PromiseType>
439 coroutine_handle(coroutine_handle<PromiseType>) noexcept;
440 static coroutine_handle from_address(void *);
441 constexpr void* address() const noexcept;
445 bool await_ready() noexcept { return false; }
446 void await_suspend(coroutine_handle<>) noexcept {}
447 void await_resume() noexcept {}
452 StringRef Header = R"cpp(
453#include "coroutine.h"
454template <typename T> struct [[clang::coro_return_type]] Gen {
455 struct promise_type {
456 Gen<T> get_return_object() {
459 std::awaitable initial_suspend();
460 std::awaitable final_suspend() noexcept;
461 void unhandled_exception();
462 void return_value(T t);
466Gen<int> foo_coro(int b) { co_return b; }
471Gen<int> $[[bar_coro]](int b) { return foo_coro(b); }
474 TU.AdditionalFiles["coroutine.h"] = std::string(CoroutineH);
475 TU.AdditionalFiles[
"header.hpp"] = std::string(Header);
476 TU.ExtraArgs.push_back(
"--std=c++20");
477 EXPECT_THAT(TU.build().getDiagnostics(), ElementsAre(hasRange(Main.range())));
480TEST(DiagnosticTest, MakeShared) {
484 struct S { S(char*); };
485 auto x = std::[[make_shared]]<S>(42); // error-ok
488 TU.HeaderCode = R"cpp(
490 // These mocks aren't quite right - we omit shared_ptr for simplicity.
491 // forward is included to show its body is not needed to get the diagnostic.
492 template <typename T> T&& forward(T& t);
493 template <typename T, typename... A> T* make_shared(A&&... args) {
494 return new T(std::forward<A>(args)...);
498 TU.ParseOpts.PreambleParseForwardingFunctions = true;
499 EXPECT_THAT(TU.build().getDiagnostics(),
500 UnorderedElementsAre(
503 "no matching constructor for initialization of 'S'")));
506TEST(DiagnosticTest, NoMultipleDiagnosticInFlight) {
508 template <typename T> struct Foo {
518 Foo<LabelInfo> label_info_map;
519 [[for]] (auto it = label_info_map.begin(); it != label_info_map.end(); ++it) {
525 TU.ClangTidyProvider = addTidyChecks("modernize-loop-convert");
527 TU.build().getDiagnostics(),
528 ifTidyChecks(UnorderedElementsAre(::testing::AllOf(
529 Diag(Main.range(),
"use range-based for loop instead"),
533TEST(DiagnosticTest, RespectsDiagnosticConfig) {
543 TU.build().getDiagnostics(),
544 ElementsAre(
Diag(Main.range(),
"use of undeclared identifier 'unknown'"),
545 Diag(Main.range(
"ret"),
546 "void function 'x' should not return a value")));
550 EXPECT_THAT(TU.build().getDiagnostics(),
551 ElementsAre(
Diag(Main.range(),
552 "use of undeclared identifier 'unknown'")));
555TEST(DiagnosticTest, RespectsDiagnosticConfigInHeader) {
557 int x = "42"; // error-ok
560 #include "header.hpp"
563 TU.AdditionalFiles[
"header.hpp"] = std::string(Header.code());
567 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
570TEST(DiagnosticTest, ClangTidySuppressionComment) {
574 double d = 8 / i; // NOLINT
578 double f = BAD; // NOLINT
579 double g = [[8]] / i;
581 double h = BAD2; // NOLINT
587 // verify no crashes on unmatched nolints.
592 TU.ClangTidyProvider = addTidyChecks("bugprone-integer-division");
594 TU.build().getDiagnostics(),
595 ifTidyChecks(UnorderedElementsAre(::testing::AllOf(
596 Diag(Main.range(),
"result of integer division used in a floating "
597 "point context; possible loss of precision"),
599 diagName(
"bugprone-integer-division")))));
602TEST(DiagnosticTest, ClangTidySystemMacro) {
607 double x = $inline[[8]] / i;
608 double y = $user[[DIVIDE_USER]](i);
609 double z = DIVIDE_SYS(i);
613 TU.AdditionalFiles[
"user.h"] = R
"cpp(
614 #define DIVIDE_USER(Y) 8/Y
616 TU.AdditionalFiles["system.h"] = R
"cpp(
617 #pragma clang system_header
618 #define DIVIDE_SYS(Y) 8/Y
621 TU.ClangTidyProvider = addTidyChecks("bugprone-integer-division");
622 std::string BadDivision =
"result of integer division used in a floating "
623 "point context; possible loss of precision";
627 EXPECT_THAT(TU.build().getDiagnostics(),
629 UnorderedElementsAre(
Diag(Main.range(
"inline"), BadDivision),
630 Diag(Main.range(
"user"), BadDivision))));
633TEST(DiagnosticTest, ClangTidyWarningAsError) {
637 double f = [[8]] / i; // error-ok
641 TU.ClangTidyProvider =
642 addTidyChecks("bugprone-integer-division",
"bugprone-integer-division");
644 TU.build().getDiagnostics(),
645 ifTidyChecks(UnorderedElementsAre(::testing::AllOf(
646 Diag(Main.range(),
"result of integer division used in a floating "
647 "point context; possible loss of precision"),
649 diagSeverity(DiagnosticsEngine::Error)))));
652TidyProvider addClangArgs(std::vector<llvm::StringRef> ExtraArgs,
654 return [ExtraArgs = std::move(ExtraArgs),
Checks =
Checks.str()](
655 tidy::ClangTidyOptions &Opts, llvm::StringRef) {
657 Opts.ExtraArgs.emplace();
658 for (llvm::StringRef Arg : ExtraArgs)
659 Opts.ExtraArgs->emplace_back(Arg);
665TEST(DiagnosticTest, ClangTidyEnablesClangWarning) {
667 static void [[foo]]() {}
671 auto UnusedFooWarning =
672 AllOf(
Diag(Main.range(),
"unused function 'foo'"),
673 diagName(
"-Wunused-function"), diagSource(
Diag::Clang),
674 diagSeverity(DiagnosticsEngine::Warning));
677 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
681 TU.ClangTidyProvider =
682 addClangArgs({
"-Wunused"},
"clang-diagnostic-unused-function");
683 EXPECT_THAT(TU.build().getDiagnostics(), ElementsAre(UnusedFooWarning));
686 TU.ClangTidyProvider = addClangArgs({
"-Wunused"},
"clang-diagnostic-*");
687 EXPECT_THAT(TU.build().getDiagnostics(), ElementsAre(UnusedFooWarning));
689 TU.ClangTidyProvider = addClangArgs({
"-Wunused"},
"*");
690 EXPECT_THAT(TU.build().getDiagnostics(), Contains(UnusedFooWarning));
692 TU.ClangTidyProvider = addClangArgs(
693 {
"-Wunused"},
"clang-diagnostic-*,-clang-diagnostic-unused-function");
694 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
697 TU.ClangTidyProvider = addClangArgs({
"-Wunused"},
"clang-diagnostic-unused");
698 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
701 TU.ClangTidyProvider = addClangArgs({
"-Wunused",
"-Dfoo=bar"},
702 "clang-diagnostic-unused-function");
703 EXPECT_THAT(TU.build().getDiagnostics(), ElementsAre(UnusedFooWarning))
704 <<
"Not unused function 'bar'!";
707 TU.ExtraArgs = {
"-Werror"};
708 TU.ClangTidyProvider =
709 addClangArgs({
"-Wunused"},
"clang-diagnostic-unused-function");
710 EXPECT_THAT(TU.build().getDiagnostics(),
711 ElementsAre(diagSeverity(DiagnosticsEngine::Warning)));
714 TU.ExtraArgs = {
"-Wunused",
"-Werror"};
715 TU.ClangTidyProvider =
716 addClangArgs({
"-Wunused"},
"clang-diagnostic-unused-function");
717 EXPECT_THAT(TU.build().getDiagnostics(),
718 ElementsAre(diagSeverity(DiagnosticsEngine::Error)));
721 TU.ExtraArgs = {
"-Wunused-function",
"-Werror"};
722 TU.ClangTidyProvider =
723 addClangArgs({
"-Wunused"},
"clang-diagnostic-unused-label");
724 EXPECT_THAT(TU.build().getDiagnostics(),
725 ElementsAre(diagSeverity(DiagnosticsEngine::Warning)));
730 TU.ClangTidyProvider = addClangArgs({
"-Wunused",
"-Wno-unused"},
731 "clang-diagnostic-unused-function");
732 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
735 TU.ClangTidyProvider =
736 addClangArgs({
"-Wunused"}, {
"clang-diagnostic-unused-function"});
737 EXPECT_THAT(TU.build().getDiagnostics(), SizeIs(1));
740 TU.ClangTidyProvider = addClangArgs({
"-Wunused",
"-Wno-unused-function"},
741 {
"clang-diagnostic-unused-function"});
742 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
743 TU.ClangTidyProvider = addClangArgs({
"-Wunused-function",
"-Wno-unused"},
744 {
"clang-diagnostic-unused-function"});
745 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
749 TU.ExtraArgs = {
"-Wunused"};
750 TU.ClangTidyProvider = addClangArgs({
"-Wno-unused"}, {});
751 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
753 TU.ExtraArgs = {
"-Wno-unused"};
754 TU.ClangTidyProvider = addClangArgs({
"-Wunused"}, {
"-*, clang-diagnostic-*"});
755 EXPECT_THAT(TU.build().getDiagnostics(), SizeIs(1));
758TEST(DiagnosticTest, LongFixMessages) {
763 int somereallyreallyreallyreallyreallyreallyreallyreallylongidentifier;
764 [[omereallyreallyreallyreallyreallyreallyreallyreallylongidentifier]]= 10;
769 TU.build().getDiagnostics(),
770 ElementsAre(withFix(Fix(
772 "somereallyreallyreallyreallyreallyreallyreallyreallylongidentifier",
773 "change 'omereallyreallyreallyreallyreallyreallyreallyreall…' to "
774 "'somereallyreallyreallyreallyreallyreallyreallyreal…'"))));
784 TU.Code = std::string(Source.code());
785 EXPECT_THAT(TU.build().getDiagnostics(),
787 Fix(Source.range(), "ident",
"change 'ide\\…' to 'ident'"))));
790TEST(DiagnosticTest, NewLineFixMessage) {
793 TU.ExtraArgs = {
"-Wnewline-eof"};
795 TU.build().getDiagnostics(),
796 ElementsAre(withFix((
Fix(Source.range(),
"\n",
"insert '\\n'")))));
799TEST(DiagnosticTest, ClangTidySuppressionCommentTrumpsWarningAsError) {
803 double f = [[8]] / i; // NOLINT
807 TU.ClangTidyProvider =
808 addTidyChecks("bugprone-integer-division",
"bugprone-integer-division");
809 EXPECT_THAT(TU.build().getDiagnostics(), UnorderedElementsAre());
812TEST(DiagnosticTest, ClangTidyNoLiteralDataInMacroToken) {
815 using pthread_t = int;
816 int pthread_kill(pthread_t thread, int sig);
819 return pthread_kill(thread, 0);
823 TU.ClangTidyProvider = addTidyChecks("bugprone-bad-signal-to-kill-thread");
824 EXPECT_THAT(TU.build().getDiagnostics(), UnorderedElementsAre());
827TEST(DiagnosticTest, BadSignalToKillThreadInPreamble) {
830 using pthread_t = int;
831 int pthread_kill(pthread_t thread, int sig);
834 return pthread_kill(thread, 15);
838 TU.HeaderFilename = "signal.h";
839 TU.HeaderCode =
"#define SIGTERM 15";
840 TU.ClangTidyProvider =
addTidyChecks(
"bugprone-bad-signal-to-kill-thread");
841 EXPECT_THAT(TU.build().getDiagnostics(),
842 ifTidyChecks(UnorderedElementsAre(
843 diagName(
"bugprone-bad-signal-to-kill-thread"))));
846TEST(DiagnosticTest, ClangTidyMacroToEnumCheck) {
853 std::vector<TidyProvider> Providers;
855 addTidyChecks("cppcoreguidelines-macro-to-enum,modernize-macro-to-enum"));
857 TU.ClangTidyProvider =
combine(std::move(Providers));
858 EXPECT_THAT(TU.build().getDiagnostics(), UnorderedElementsAre());
861TEST(DiagnosticTest, ElseAfterReturnRange) {
866 } [[else]] if (cond == 2) {
873 TU.ClangTidyProvider = addTidyChecks("llvm-else-after-return");
874 EXPECT_THAT(TU.build().getDiagnostics(),
875 ifTidyChecks(ElementsAre(
876 Diag(Main.range(),
"do not use 'else' after 'return'"))));
879TEST(DiagnosticTest, ClangTidySelfContainedDiags) {
888 void InitVariables() {
889 float $C[[C]]$CFix[[]];
890 double $D[[D]]$DFix[[]];
894 TU.ClangTidyProvider =
896 "cppcoreguidelines-init-variables");
897 clangd::Fix ExpectedAFix;
898 ExpectedAFix.Message =
899 "'A' should be initialized in a member initializer of the constructor";
900 ExpectedAFix.Edits.push_back(
TextEdit{Main.range(
"Fix"),
" : A(1)"});
901 ExpectedAFix.Edits.push_back(
TextEdit{Main.range(
"A"),
""});
906 clangd::Fix ExpectedBFix;
907 ExpectedBFix.Message =
908 "'B' should be initialized in a member initializer of the constructor";
909 ExpectedBFix.Edits.push_back(
TextEdit{Main.range(
"Fix"),
" : B(1)"});
910 ExpectedBFix.Edits.push_back(
TextEdit{Main.range(
"B"),
""});
912 clangd::Fix ExpectedCFix;
913 ExpectedCFix.Message =
"variable 'C' is not initialized";
914 ExpectedCFix.Edits.push_back(
TextEdit{Main.range(
"CFix"),
" = NAN"});
915 ExpectedCFix.Edits.push_back(
916 TextEdit{Main.range(
"MathHeader"),
"#include <math.h>\n\n"});
920 clangd::Fix ExpectedDFix;
921 ExpectedDFix.Message =
"variable 'D' is not initialized";
922 ExpectedDFix.Edits.push_back(
TextEdit{Main.range(
"DFix"),
" = NAN"});
923 ExpectedDFix.Edits.push_back(
924 TextEdit{Main.range(
"MathHeader"),
"#include <math.h>\n\n"});
926 TU.build().getDiagnostics(),
927 ifTidyChecks(UnorderedElementsAre(
928 AllOf(
Diag(Main.range(
"A"),
"'A' should be initialized in a member "
929 "initializer of the constructor"),
930 withFix(equalToFix(ExpectedAFix))),
931 AllOf(
Diag(Main.range(
"B"),
"'B' should be initialized in a member "
932 "initializer of the constructor"),
933 withFix(equalToFix(ExpectedBFix))),
934 AllOf(
Diag(Main.range(
"C"),
"variable 'C' is not initialized"),
935 withFix(equalToFix(ExpectedCFix))),
936 AllOf(
Diag(Main.range(
"D"),
"variable 'D' is not initialized"),
937 withFix(equalToFix(ExpectedDFix))))));
940TEST(DiagnosticTest, ClangTidySelfContainedDiagsFormatting) {
944 virtual void Reset1() = 0;
945 virtual void Reset2() = 0;
947 class A : public Interface {
948 // This will be marked by clangd to use override instead of virtual
949 $virtual1[[virtual ]]void $Reset1[[Reset1]]()$override1[[]];
950 $virtual2[[virtual ]]/**/void $Reset2[[Reset2]]()$override2[[]];
954 TU.ClangTidyProvider =
955 addTidyChecks("cppcoreguidelines-explicit-virtual-functions,");
956 clangd::Fix
const ExpectedFix1{
957 "prefer using 'override' or (rarely) 'final' "
958 "instead of 'virtual'",
959 {
TextEdit{Main.range(
"override1"),
" override"},
960 TextEdit{Main.range(
"virtual1"),
""}},
962 clangd::Fix
const ExpectedFix2{
963 "prefer using 'override' or (rarely) 'final' "
964 "instead of 'virtual'",
965 {
TextEdit{Main.range(
"override2"),
" override"},
966 TextEdit{Main.range(
"virtual2"),
""}},
970 EXPECT_THAT(TU.build().getDiagnostics(),
971 ifTidyChecks(UnorderedElementsAre(
972 AllOf(
Diag(Main.range(
"Reset1"),
973 "prefer using 'override' or (rarely) 'final' "
974 "instead of 'virtual'"),
975 withFix(equalToFix(ExpectedFix1))),
976 AllOf(
Diag(Main.range(
"Reset2"),
977 "prefer using 'override' or (rarely) 'final' "
978 "instead of 'virtual'"),
979 withFix(equalToFix(ExpectedFix2))))));
982TEST(DiagnosticsTest, ClangTidyCallingIntoPreprocessor) {
983 std::string Main = R
"cpp(
988 std::string Header = R"cpp(
989 #define EXTERN extern
993 TU.AdditionalFiles[
"b.h"] = Header;
994 TU.ClangTidyProvider =
addTidyChecks(
"modernize-use-trailing-return-type");
999TEST(DiagnosticsTest, Preprocessor) {
1008 int a = [[b]]; // error-ok
1015 ElementsAre(Diag(Test.range(), "use of undeclared identifier 'b'")));
1018TEST(DiagnosticsTest, IgnoreVerify) {
1020 int a; // expected-error {{}}
1022 TU.ExtraArgs.push_back("-Xclang");
1023 TU.ExtraArgs.push_back(
"-verify");
1024 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
1027TEST(DiagnosticTest, IgnoreBEFilelistOptions) {
1029 TU.ExtraArgs.push_back(
"-Xclang");
1030 for (
const auto *DisableOption :
1031 {
"-fsanitize-ignorelist=null",
"-fprofile-list=null",
1032 "-fxray-always-instrument=null",
"-fxray-never-instrument=null",
1033 "-fxray-attr-list=null"}) {
1034 TU.ExtraArgs.push_back(DisableOption);
1035 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
1036 TU.ExtraArgs.pop_back();
1041TEST(DiagnosticsTest, RecursivePreamble) {
1043 #include "foo.h" // error-ok
1046 TU.Filename = "foo.h";
1047 EXPECT_THAT(TU.build().getDiagnostics(),
1048 ElementsAre(diagName(
"pp_including_mainfile_in_preamble")));
1049 EXPECT_THAT(TU.build().getLocalTopLevelDecls(), SizeIs(1));
1053TEST(DiagnosticsTest, RecursivePreamblePragmaOnce) {
1059 TU.Filename = "foo.h";
1060 EXPECT_THAT(TU.build().getDiagnostics(),
1061 Not(Contains(diagName(
"pp_including_mainfile_in_preamble"))));
1062 EXPECT_THAT(TU.build().getLocalTopLevelDecls(), SizeIs(1));
1067TEST(DiagnosticsTest, RecursivePreambleIfndefGuard) {
1071 #include "foo.h" // error-ok
1075 TU.Filename = "foo.h";
1077 EXPECT_THAT(TU.build().getDiagnostics(),
1078 ElementsAre(diagName(
"pp_including_mainfile_in_preamble")));
1079 EXPECT_THAT(TU.build().getLocalTopLevelDecls(), SizeIs(1));
1082TEST(DiagnosticsTest, PreambleWithPragmaAssumeNonnull) {
1084#pragma clang assume_nonnull begin
1086#pragma clang assume_nonnull end
1088 auto AST = TU.build();
1089 EXPECT_THAT(
AST.getDiagnostics(), IsEmpty());
1090 const auto *
X = cast<FunctionDecl>(
findDecl(
AST,
"foo")).getParamDecl(0);
1091 ASSERT_TRUE(
X->getOriginalType()->getNullability() ==
1092 NullabilityKind::NonNull);
1095TEST(DiagnosticsTest, PreamblePragmaDiagnosticPushPop) {
1097#pragma clang diagnostic push
1101#pragma clang diagnostic pop
1103 auto AST = TU.build();
1104 EXPECT_THAT(
AST.getDiagnostics(), IsEmpty());
1107TEST(DiagnosticsTest, PreambleHeaderWithBadPragmaAssumeNonnull) {
1109#pragma clang assume_nonnull begin // error-ok
1113#include "foo.h" // unterminated assume_nonnull should not affect bar.
1116 TU.AdditionalFiles = {{"foo.h", std::string(Header.code())}};
1117 auto AST = TU.build();
1118 EXPECT_THAT(
AST.getDiagnostics(),
1119 ElementsAre(diagName(
"pp_eof_in_assume_nonnull")));
1120 const auto *
X = cast<FunctionDecl>(
findDecl(
AST,
"foo")).getParamDecl(0);
1121 ASSERT_TRUE(
X->getOriginalType()->getNullability() ==
1122 NullabilityKind::NonNull);
1123 const auto *Y = cast<FunctionDecl>(
findDecl(
AST,
"bar")).getParamDecl(0);
1124 ASSERT_FALSE(Y->getOriginalType()->getNullability());
1127TEST(DiagnosticsTest, InsideMacros) {
1130 #define RET(x) return x + 10
1133 RET($foo[[0]]); // error-ok
1140 ElementsAre(Diag(Test.range("foo"),
1141 "cannot initialize return object of type "
1142 "'int *' with an rvalue of type 'int'"),
1143 Diag(Test.range(
"bar"),
1144 "cannot initialize return object of type "
1145 "'int *' with an rvalue of type 'int'")));
1148TEST(DiagnosticsTest, NoFixItInMacro) {
1150 #define Define(name) void name() {}
1152 [[Define]](main) // error-ok
1155 EXPECT_THAT(TU.build().getDiagnostics(),
1156 ElementsAre(AllOf(
Diag(Test.range(),
"'main' must return 'int'"),
1160TEST(DiagnosticsTest, PragmaSystemHeader) {
1161 Annotations Test(
"#pragma clang [[system_header]]\n");
1164 TU.build().getDiagnostics(),
1166 Diag(Test.range(),
"#pragma system_header ignored in main file"))));
1167 TU.Filename =
"TestTU.h";
1168 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
1171TEST(ClangdTest, MSAsm) {
1174 llvm::InitializeAllTargetInfos();
1176 TU.ExtraArgs = {
"-fms-extensions"};
1177 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
1180TEST(DiagnosticsTest, ToLSP) {
1187 D.ID = clang::diag::err_undeclared_var_use;
1189 D.Name =
"undeclared_var_use";
1191 D.Message =
"something terrible happened";
1192 D.Range = {pos(1, 2), pos(3, 4)};
1193 D.InsideMainFile =
true;
1194 D.Severity = DiagnosticsEngine::Error;
1195 D.File =
"foo/bar/main.cpp";
1196 D.AbsFile = std::string(MainFile.file());
1197 D.OpaqueData[
"test"] =
"bar";
1199 clangd::Note NoteInMain;
1200 NoteInMain.Message =
"declared somewhere in the main file";
1201 NoteInMain.Range = {pos(5, 6), pos(7, 8)};
1202 NoteInMain.Severity = DiagnosticsEngine::Remark;
1203 NoteInMain.File =
"../foo/bar/main.cpp";
1204 NoteInMain.InsideMainFile =
true;
1205 NoteInMain.AbsFile = std::string(MainFile.file());
1207 D.Notes.push_back(NoteInMain);
1209 clangd::Note NoteInHeader;
1210 NoteInHeader.Message =
"declared somewhere in the header file";
1211 NoteInHeader.Range = {pos(9, 10), pos(11, 12)};
1212 NoteInHeader.Severity = DiagnosticsEngine::Note;
1213 NoteInHeader.File =
"../foo/baz/header.h";
1214 NoteInHeader.InsideMainFile =
false;
1215 NoteInHeader.AbsFile = std::string(
HeaderFile.file());
1216 D.Notes.push_back(NoteInHeader);
1219 F.Message =
"do something";
1220 D.Fixes.push_back(F);
1223 clangd::Diagnostic MainLSP;
1224 MainLSP.range =
D.Range;
1225 MainLSP.severity =
getSeverity(DiagnosticsEngine::Error);
1226 MainLSP.code =
"undeclared_var_use";
1227 MainLSP.source =
"clang";
1229 R
"(Something terrible happened (fix available)
1231main.cpp:6:7: remark: declared somewhere in the main file
1233../foo/baz/header.h:10:11:
1234note: declared somewhere in the header file)";
1236 MainLSP.data = D.OpaqueData;
1238 clangd::Diagnostic NoteInMainLSP;
1239 NoteInMainLSP.range = NoteInMain.Range;
1240 NoteInMainLSP.severity = getSeverity(DiagnosticsEngine::Remark);
1241 NoteInMainLSP.message = R"(Declared somewhere in the main file
1243main.cpp:2:3: error: something terrible happened)";
1247 std::vector<std::pair<clangd::Diagnostic, std::vector<clangd::Fix>>> LSPDiags;
1249 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix> Fixes) {
1251 {std::move(LSPDiag),
1252 std::vector<clangd::Fix>(Fixes.begin(), Fixes.end())});
1257 ElementsAre(Pair(equalToLSPDiag(MainLSP), ElementsAre(equalToFix(F))),
1258 Pair(equalToLSPDiag(NoteInMainLSP), IsEmpty())));
1259 EXPECT_EQ(LSPDiags[0].first.code,
"undeclared_var_use");
1260 EXPECT_EQ(LSPDiags[0].first.source,
"clang");
1261 EXPECT_EQ(LSPDiags[1].first.code,
"");
1262 EXPECT_EQ(LSPDiags[1].first.source,
"");
1266 Opts.EmitRelatedLocations =
true;
1268 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix> Fixes) {
1270 {std::move(LSPDiag),
1271 std::vector<clangd::Fix>(Fixes.begin(), Fixes.end())});
1273 MainLSP.message =
"Something terrible happened (fix available)";
1275 NoteInMainDRI.
message =
"Declared somewhere in the main file";
1276 NoteInMainDRI.location.range = NoteInMain.Range;
1277 NoteInMainDRI.location.uri = MainFile;
1278 MainLSP.relatedInformation = {NoteInMainDRI};
1280 NoteInHeaderDRI.
message =
"Declared somewhere in the header file";
1281 NoteInHeaderDRI.location.range = NoteInHeader.Range;
1283 MainLSP.relatedInformation = {NoteInMainDRI, NoteInHeaderDRI};
1284 EXPECT_THAT(LSPDiags, ElementsAre(Pair(equalToLSPDiag(MainLSP),
1285 ElementsAre(equalToFix(F)))));
1288struct SymbolWithHeader {
1290 std::string DeclaringFile;
1291 std::string IncludeHeader;
1294std::unique_ptr<SymbolIndex>
1295buildIndexWithSymbol(llvm::ArrayRef<SymbolWithHeader> Syms) {
1297 for (
const auto &S : Syms) {
1300 Sym.CanonicalDeclaration.FileURI = S.DeclaringFile.c_str();
1301 Sym.Definition.FileURI = S.DeclaringFile.c_str();
1308TEST(IncludeFixerTest, IncompleteType) {
1310 TU.ExtraArgs.push_back(
"-std=c++20");
1311 auto Index = buildIndexWithSymbol(
1312 {SymbolWithHeader{
"ns::X",
"unittest:///x.h",
"\"x.h\""}});
1313 TU.ExternalIndex = Index.get();
1315 std::vector<std::pair<llvm::StringRef, llvm::StringRef>> Tests{
1316 {
"incomplete_nested_name_spec",
"[[ns::X::]]Nested n;"},
1317 {
"incomplete_base_class",
"class Y : [[ns::X]] {};"},
1318 {
"incomplete_member_access",
"auto i = x[[->]]f();"},
1319 {
"incomplete_type",
"auto& [[[]]m] = *x;"},
1320 {
"init_incomplete_type",
1321 "struct C { static int f(ns::X&); }; int i = C::f([[{]]});"},
1322 {
"bad_cast_incomplete",
"auto a = [[static_cast]]<ns::X>(0);"},
1323 {
"template_nontype_parm_incomplete",
"template <ns::X [[foo]]> int a;"},
1324 {
"typecheck_decl_incomplete_type",
"ns::X [[var]];"},
1325 {
"typecheck_incomplete_tag",
"auto i = [[(*x)]]->f();"},
1326 {
"typecheck_nonviable_condition_incomplete",
1327 "struct A { operator ns::X(); } a; const ns::X &[[b]] = a;"},
1328 {
"invalid_incomplete_type_use",
"auto var = [[ns::X()]];"},
1329 {
"sizeof_alignof_incomplete_or_sizeless_type",
1330 "auto s = [[sizeof]](ns::X);"},
1331 {
"for_range_incomplete_type",
"void foo() { for (auto i : [[*]]x ) {} }"},
1332 {
"func_def_incomplete_result",
"ns::X [[func]] () {}"},
1333 {
"field_incomplete_or_sizeless",
"class M { ns::X [[member]]; };"},
1334 {
"array_incomplete_or_sizeless_type",
"auto s = [[(ns::X[]){}]];"},
1335 {
"call_incomplete_return",
"ns::X f(); auto fp = &f; auto z = [[fp()]];"},
1336 {
"call_function_incomplete_return",
"ns::X foo(); auto a = [[foo()]];"},
1337 {
"call_incomplete_argument",
"int m(ns::X); int i = m([[*x]]);"},
1338 {
"switch_incomplete_class_type",
"void a() { [[switch]](*x) {} }"},
1339 {
"delete_incomplete_class_type",
"void f() { [[delete]] *x; }"},
1340 {
"-Wdelete-incomplete",
"void f() { [[delete]] x; }"},
1341 {
"dereference_incomplete_type",
1342 R
"cpp(void f() { asm("" : "=r"([[*]]x)::); })cpp"},
1344 for (
auto Case : Tests) {
1346 TU.Code = Main.code().str() +
"\n // error-ok";
1348 TU.build().getDiagnostics(),
1349 ElementsAre(AllOf(diagName(Case.first), hasRange(Main.range()),
1350 withFix(
Fix(
Range{},
"#include \"x.h\"\n",
1351 "Include \"x.h\" for symbol ns::X")))))
1356TEST(IncludeFixerTest, IncompleteEnum) {
1359 Sym.CanonicalDeclaration.FileURI = Sym.Definition.FileURI =
"unittest:///x.h";
1368 TU.ExtraArgs.push_back(
"-std=c++20");
1369 TU.ExtraArgs.push_back(
"-fno-ms-compatibility");
1371 std::vector<std::pair<llvm::StringRef, llvm::StringRef>> Tests{
1372 {
"incomplete_enum",
"enum class X : int; using enum [[X]];"},
1373 {
"underlying_type_of_incomplete_enum",
1374 "[[__underlying_type]](enum X) i;"},
1376 for (
auto Case : Tests) {
1378 TU.Code = Main.code().str() +
"\n // error-ok";
1379 EXPECT_THAT(TU.build().getDiagnostics(),
1380 Contains(AllOf(diagName(Case.first), hasRange(Main.range()),
1381 withFix(
Fix(
Range{},
"#include \"x.h\"\n",
1382 "Include \"x.h\" for symbol X")))))
1387TEST(IncludeFixerTest, NoSuggestIncludeWhenNoDefinitionInHeader) {
1389$insert[[]]namespace ns {
1392class Y : $base[[public ns::X]] {};
1401 Sym.CanonicalDeclaration.FileURI =
"unittest:///x.h";
1402 Sym.Definition.FileURI =
"unittest:///x.cc";
1409 TU.ExternalIndex = Index.get();
1411 EXPECT_THAT(TU.build().getDiagnostics(),
1412 UnorderedElementsAre(
1413 Diag(Test.range(
"base"),
"base class has incomplete type"),
1414 Diag(Test.range(
"access"),
1415 "member access into incomplete type 'ns::X'")));
1418TEST(IncludeFixerTest, Typo) {
1420$insert[[]]namespace ns {
1422 $unqualified1[[X]] x;
1423 // No fix if the unresolved type is used as specifier. (ns::)X::Nested will be
1424 // considered the unresolved type.
1425 $unqualified2[[X]]::Nested n;
1427struct S : $base[[X]] {};
1430 ns::$qualified1[[X]] x; // ns:: is valid.
1431 ns::$qualified2[[X]](); // Error: no member in namespace
1433 ::$global[[Global]] glob;
1435using Type = ns::$template[[Foo]]<int>;
1438 auto Index = buildIndexWithSymbol(
1439 {SymbolWithHeader{
"ns::X",
"unittest:///x.h",
"\"x.h\""},
1440 SymbolWithHeader{
"Global",
"unittest:///global.h",
"\"global.h\""},
1441 SymbolWithHeader{
"ns::Foo",
"unittest:///foo.h",
"\"foo.h\""}});
1442 TU.ExternalIndex = Index.get();
1445 TU.build().getDiagnostics(),
1446 UnorderedElementsAre(
1447 AllOf(
Diag(Test.range(
"unqualified1"),
"unknown type name 'X'"),
1448 diagName(
"unknown_typename"),
1449 withFix(
Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
1450 "Include \"x.h\" for symbol ns::X"))),
1451 Diag(Test.range(
"unqualified2"),
"use of undeclared identifier 'X'"),
1452 AllOf(
Diag(Test.range(
"qualified1"),
1453 "no type named 'X' in namespace 'ns'"),
1454 diagName(
"typename_nested_not_found"),
1455 withFix(
Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
1456 "Include \"x.h\" for symbol ns::X"))),
1457 AllOf(
Diag(Test.range(
"qualified2"),
1458 "no member named 'X' in namespace 'ns'"),
1459 diagName(
"no_member"),
1460 withFix(
Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
1461 "Include \"x.h\" for symbol ns::X"))),
1462 AllOf(
Diag(Test.range(
"global"),
1463 "no type named 'Global' in the global namespace"),
1464 diagName(
"typename_nested_not_found"),
1465 withFix(
Fix(Test.range(
"insert"),
"#include \"global.h\"\n",
1466 "Include \"global.h\" for symbol Global"))),
1467 AllOf(
Diag(Test.range(
"template"),
1468 "no template named 'Foo' in namespace 'ns'"),
1469 diagName(
"no_member_template"),
1470 withFix(
Fix(Test.range(
"insert"),
"#include \"foo.h\"\n",
1471 "Include \"foo.h\" for symbol ns::Foo"))),
1472 AllOf(
Diag(Test.range(
"base"),
"expected class name"),
1473 diagName(
"expected_class_name"),
1474 withFix(
Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
1475 "Include \"x.h\" for symbol ns::X")))));
1478TEST(IncludeFixerTest, TypoInMacro) {
1489 auto Index = buildIndexWithSymbol(
1490 {SymbolWithHeader{
"X",
"unittest:///x.h",
"\"x.h\""},
1491 SymbolWithHeader{
"ns::X",
"unittest:///ns.h",
"\"x.h\""}});
1492 TU.ExternalIndex = Index.get();
1495 TU.ExtraArgs = {
"-fno-ms-compatibility"};
1496 EXPECT_THAT(TU.build().getDiagnostics(), Each(withFix(_)));
1499TEST(IncludeFixerTest, MultipleMatchedSymbols) {
1501$insert[[]]namespace na {
1504 $unqualified[[X]] x;
1510 auto Index = buildIndexWithSymbol(
1511 {SymbolWithHeader{
"na::X",
"unittest:///a.h",
"\"a.h\""},
1512 SymbolWithHeader{
"na::nb::X",
"unittest:///b.h",
"\"b.h\""}});
1513 TU.ExternalIndex = Index.get();
1515 EXPECT_THAT(TU.build().getDiagnostics(),
1516 UnorderedElementsAre(AllOf(
1517 Diag(Test.range(
"unqualified"),
"unknown type name 'X'"),
1518 diagName(
"unknown_typename"),
1519 withFix(
Fix(Test.range(
"insert"),
"#include \"a.h\"\n",
1520 "Include \"a.h\" for symbol na::X"),
1521 Fix(Test.range(
"insert"),
"#include \"b.h\"\n",
1522 "Include \"b.h\" for symbol na::nb::X")))));
1525TEST(IncludeFixerTest, NoCrashMemberAccess) {
1527 struct X { int xyz; };
1528 void g() { X x; x.$[[xy]]; }
1531 auto Index = buildIndexWithSymbol(
1532 SymbolWithHeader{
"na::X",
"unittest:///a.h",
"\"a.h\""});
1533 TU.ExternalIndex = Index.get();
1536 TU.build().getDiagnostics(),
1537 UnorderedElementsAre(
Diag(Test.range(),
"no member named 'xy' in 'X'")));
1540TEST(IncludeFixerTest, UseCachedIndexResults) {
1544$insert[[]]void foo() {
1567 buildIndexWithSymbol(SymbolWithHeader{
"X",
"unittest:///a.h",
"\"a.h\""});
1568 TU.ExternalIndex = Index.get();
1570 auto Parsed = TU.build();
1571 for (
const auto &D : Parsed.getDiagnostics()) {
1572 if (
D.Fixes.size() != 1) {
1573 ADD_FAILURE() <<
"D.Fixes.size() != 1";
1576 EXPECT_EQ(
D.Fixes[0].Message, std::string(
"Include \"a.h\" for symbol X"));
1580TEST(IncludeFixerTest, UnresolvedNameAsSpecifier) {
1582$insert[[]]namespace ns {
1584void g() { ns::$[[scope]]::X_Y(); }
1587 TU.Code = std::string(Test.code());
1589 TU.ExtraArgs.push_back(
"-fno-ms-compatibility");
1590 auto Index = buildIndexWithSymbol(
1591 SymbolWithHeader{
"ns::scope::X_Y",
"unittest:///x.h",
"\"x.h\""});
1592 TU.ExternalIndex = Index.get();
1595 TU.build().getDiagnostics(),
1596 UnorderedElementsAre(
1597 AllOf(
Diag(Test.range(),
"no member named 'scope' in namespace 'ns'"),
1598 diagName(
"no_member"),
1599 withFix(
Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
1600 "Include \"x.h\" for symbol ns::scope::X_Y")))));
1603TEST(IncludeFixerTest, UnresolvedSpecifierWithSemaCorrection) {
1605$insert[[]]namespace clang {
1607 // "clangd::" will be corrected to "clang::" by Sema.
1608 $q1[[clangd]]::$x[[X]] x;
1609 $q2[[clangd]]::$ns[[ns]]::Y y;
1614 TU.Code = std::string(Test.code());
1616 TU.ExtraArgs.push_back(
"-fno-ms-compatibility");
1617 auto Index = buildIndexWithSymbol(
1618 {SymbolWithHeader{
"clang::clangd::X",
"unittest:///x.h",
"\"x.h\""},
1619 SymbolWithHeader{
"clang::clangd::ns::Y",
"unittest:///y.h",
"\"y.h\""}});
1620 TU.ExternalIndex = Index.get();
1623 TU.build().getDiagnostics(),
1624 UnorderedElementsAre(
1625 AllOf(
Diag(Test.range(
"q1"),
"use of undeclared identifier 'clangd'; "
1626 "did you mean 'clang'?"),
1627 diagName(
"undeclared_var_use_suggest"),
1629 Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
1630 "Include \"x.h\" for symbol clang::clangd::X"))),
1631 AllOf(
Diag(Test.range(
"x"),
"no type named 'X' in namespace 'clang'"),
1632 diagName(
"typename_nested_not_found"),
1633 withFix(
Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
1634 "Include \"x.h\" for symbol clang::clangd::X"))),
1636 Diag(Test.range(
"q2"),
"use of undeclared identifier 'clangd'; "
1637 "did you mean 'clang'?"),
1638 diagName(
"undeclared_var_use_suggest"),
1640 Fix(Test.range(
"insert"),
"#include \"y.h\"\n",
1641 "Include \"y.h\" for symbol clang::clangd::ns::Y"))),
1642 AllOf(
Diag(Test.range(
"ns"),
1643 "no member named 'ns' in namespace 'clang'"),
1644 diagName(
"no_member"),
1646 Fix(Test.range(
"insert"),
"#include \"y.h\"\n",
1647 "Include \"y.h\" for symbol clang::clangd::ns::Y")))));
1650TEST(IncludeFixerTest, SpecifiedScopeIsNamespaceAlias) {
1652$insert[[]]namespace a {}
1659 auto Index = buildIndexWithSymbol(
1660 SymbolWithHeader{
"a::X",
"unittest:///x.h",
"\"x.h\""});
1661 TU.ExternalIndex = Index.get();
1663 EXPECT_THAT(TU.build().getDiagnostics(),
1664 UnorderedElementsAre(AllOf(
1665 Diag(Test.range(),
"no type named 'X' in namespace 'a'"),
1666 diagName(
"typename_nested_not_found"),
1667 withFix(
Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
1668 "Include \"x.h\" for symbol a::X")))));
1671TEST(IncludeFixerTest, NoCrashOnTemplateInstantiations) {
1673 template <typename T> struct Templ {
1674 template <typename U>
1675 typename U::type operator=(const U &);
1680 A() { [[a]]; /*error-ok*/ } // crash if we compute scopes lazily.
1685 auto Index = buildIndexWithSymbol({});
1686 TU.ExternalIndex = Index.get();
1689 TU.build().getDiagnostics(),
1690 ElementsAre(
Diag(Test.range(),
"use of undeclared identifier 'a'")));
1693TEST(IncludeFixerTest, HeaderNamedInDiag) {
1695 $insert[[]]int main() {
1700 TU.ExtraArgs = {
"-xc",
"-std=c99",
1701 "-Wno-error=implicit-function-declaration"};
1702 auto Index = buildIndexWithSymbol({});
1703 TU.ExternalIndex = Index.get();
1706 TU.build().getDiagnostics(),
1708 Diag(Test.range(),
"call to undeclared library function 'printf' "
1709 "with type 'int (const char *, ...)'; ISO C99 "
1710 "and later do not support implicit function "
1712 withFix(
Fix(Test.range(
"insert"),
"#include <stdio.h>\n",
1713 "Include <stdio.h> for symbol printf")))));
1715 TU.ExtraArgs = {
"-xc",
"-std=c89"};
1717 TU.build().getDiagnostics(),
1719 Diag(Test.range(),
"implicitly declaring library function 'printf' "
1720 "with type 'int (const char *, ...)'"),
1721 withFix(
Fix(Test.range(
"insert"),
"#include <stdio.h>\n",
1722 "Include <stdio.h> for symbol printf")))));
1725TEST(IncludeFixerTest, CImplicitFunctionDecl) {
1728 TU.Filename =
"test.c";
1729 TU.ExtraArgs = {
"-std=c99",
"-Wno-error=implicit-function-declaration"};
1733 Sym.CanonicalDeclaration.FileURI =
"unittest:///foo.h";
1740 TU.ExternalIndex = Index.get();
1743 TU.build().getDiagnostics(),
1746 "call to undeclared function 'foo'; ISO C99 and later do not "
1747 "support implicit function declarations"),
1748 withFix(
Fix(
Range{},
"#include \"foo.h\"\n",
1749 "Include \"foo.h\" for symbol foo")))));
1751 TU.ExtraArgs = {
"-std=c89",
"-Wall"};
1752 EXPECT_THAT(TU.build().getDiagnostics(),
1754 Diag(Test.range(),
"implicit declaration of function 'foo'"),
1755 withFix(
Fix(
Range{},
"#include \"foo.h\"\n",
1756 "Include \"foo.h\" for symbol foo")))));
1759TEST(DiagsInHeaders, DiagInsideHeader) {
1762 void foo() {})cpp");
1763 Annotations Header("[[no_type_spec]]; // error-ok");
1765 TU.AdditionalFiles = {{
"a.h", std::string(Header.code())}};
1766 EXPECT_THAT(TU.build().getDiagnostics(),
1767 UnorderedElementsAre(AllOf(
1768 Diag(Main.range(),
"in included file: a type specifier is "
1769 "required for all declarations"),
1770 withNote(
Diag(Header.range(),
"error occurred here")))));
1773TEST(DiagsInHeaders, DiagInTransitiveInclude) {
1776 void foo() {})cpp");
1778 TU.AdditionalFiles = {{"a.h",
"#include \"b.h\""},
1779 {
"b.h",
"no_type_spec; // error-ok"}};
1780 EXPECT_THAT(TU.build().getDiagnostics(),
1781 UnorderedElementsAre(
Diag(Main.range(),
1782 "in included file: a type specifier is "
1783 "required for all declarations")));
1786TEST(DiagsInHeaders, DiagInMultipleHeaders) {
1788 #include $a[["a.h"]]
1789 #include $b[["b.h"]]
1790 void foo() {})cpp");
1792 TU.AdditionalFiles = {{"a.h",
"no_type_spec; // error-ok"},
1793 {
"b.h",
"no_type_spec; // error-ok"}};
1794 EXPECT_THAT(TU.build().getDiagnostics(),
1795 UnorderedElementsAre(
1796 Diag(Main.range(
"a"),
"in included file: a type specifier is "
1797 "required for all declarations"),
1798 Diag(Main.range(
"b"),
"in included file: a type specifier is "
1799 "required for all declarations")));
1802TEST(DiagsInHeaders, PreferExpansionLocation) {
1806 void foo() {})cpp");
1808 TU.AdditionalFiles = {
1809 {"a.h",
"#include \"b.h\"\n"},
1810 {
"b.h",
"#ifndef X\n#define X\nno_type_spec; // error-ok\n#endif"}};
1811 EXPECT_THAT(TU.build().getDiagnostics(),
1812 Contains(
Diag(Main.range(),
"in included file: a type specifier "
1813 "is required for all declarations")));
1816TEST(DiagsInHeaders, PreferExpansionLocationMacros) {
1822 void foo() {})cpp");
1824 TU.AdditionalFiles = {
1825 {"a.h",
"#include \"c.h\"\n"},
1826 {
"b.h",
"#include \"c.h\"\n"},
1827 {
"c.h",
"#ifndef X\n#define X\nno_type_spec; // error-ok\n#endif"}};
1828 EXPECT_THAT(TU.build().getDiagnostics(),
1829 UnorderedElementsAre(
Diag(Main.range(),
1830 "in included file: a type specifier is "
1831 "required for all declarations")));
1834TEST(DiagsInHeaders, LimitDiagsOutsideMainFile) {
1838 void foo() {})cpp");
1840 TU.AdditionalFiles = {{"a.h",
"#include \"c.h\"\n"},
1841 {
"b.h",
"#include \"c.h\"\n"},
1845 no_type_spec_0; // error-ok
1857 EXPECT_THAT(TU.build().getDiagnostics(),
1858 UnorderedElementsAre(Diag(Main.range(),
1859 "in included file: a type specifier is "
1860 "required for all declarations")));
1863TEST(DiagsInHeaders, OnlyErrorOrFatal) {
1866 void foo() {})cpp");
1868 [[no_type_spec]]; // error-ok
1871 TU.AdditionalFiles = {{"a.h", std::string(Header.code())}};
1872 EXPECT_THAT(TU.build().getDiagnostics(),
1873 UnorderedElementsAre(AllOf(
1874 Diag(Main.range(),
"in included file: a type specifier is "
1875 "required for all declarations"),
1876 withNote(
Diag(Header.range(),
"error occurred here")))));
1879TEST(DiagsInHeaders, OnlyDefaultErrorOrFatal) {
1881 #include [["a.h"]] // get unused "foo" warning when building preamble.
1884 namespace { void foo() {} }
1885 void func() {foo();} ;)cpp");
1887 TU.AdditionalFiles = {{"a.h", std::string(Header.code())}};
1889 TU.ExtraArgs = {
"-Werror",
"-Wunused"};
1890 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
1893TEST(DiagsInHeaders, FromNonWrittenSources) {
1896 void foo() {})cpp");
1899 int b = [[FOO]]; // error-ok)cpp");
1901 TU.AdditionalFiles = {{"a.h", std::string(Header.code())}};
1902 TU.ExtraArgs = {
"-DFOO=NOOO"};
1903 EXPECT_THAT(TU.build().getDiagnostics(),
1904 UnorderedElementsAre(AllOf(
1906 "in included file: use of undeclared identifier 'NOOO'"),
1907 withNote(
Diag(Header.range(),
"error occurred here")))));
1910TEST(DiagsInHeaders, ErrorFromMacroExpansion) {
1920 TU.AdditionalFiles = {{"a.h", std::string(Header.code())}};
1921 EXPECT_THAT(TU.build().getDiagnostics(),
1922 UnorderedElementsAre(
1923 Diag(Main.range(),
"in included file: use of undeclared "
1924 "identifier 'foo'; did you mean 'fo'?")));
1927TEST(DiagsInHeaders, ErrorFromMacroArgument) {
1937 TU.AdditionalFiles = {{"a.h", std::string(Header.code())}};
1938 EXPECT_THAT(TU.build().getDiagnostics(),
1939 UnorderedElementsAre(
1940 Diag(Main.range(),
"in included file: use of undeclared "
1941 "identifier 'foo'; did you mean 'fo'?")));
1944TEST(IgnoreDiags, FromNonWrittenInclude) {
1946 TU.
ExtraArgs.push_back(
"--include=a.h");
1947 TU.AdditionalFiles = {{
"a.h",
"void main();"}};
1950 EXPECT_THAT(TU.build().getDiagnostics(), UnorderedElementsAre());
1953TEST(ToLSPDiag, RangeIsInMain) {
1956 D.Range = {pos(1, 2), pos(3, 4)};
1957 D.Notes.emplace_back();
1958 Note &N =
D.Notes.back();
1959 N.Range = {pos(2, 3), pos(3, 4)};
1961 D.InsideMainFile =
true;
1962 N.InsideMainFile =
false;
1964 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix>) {
1965 EXPECT_EQ(LSPDiag.range,
D.Range);
1968 D.InsideMainFile =
false;
1969 N.InsideMainFile =
true;
1971 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix>) {
1972 EXPECT_EQ(LSPDiag.range, N.Range);
1976TEST(ParsedASTTest, ModuleSawDiag) {
1979 auto AST = TU.build();
1981 EXPECT_THAT(
AST.getDiagnostics(),
1982 testing::Contains(
Diag(Code.range(), KDiagMsg.str())));
1991 TU.Code =
"#define FOO\n void bar();\n";
1992 auto AST = TU.build();
1993 EXPECT_THAT(
AST.getDiagnostics(), IsEmpty());
1997 TU.Code = Code.code().str();
1998 auto AST = TU.build();
2000 AST.getDiagnostics(),
2001 testing::Contains(
Diag(Code.range(),
"no newline at end of file")));
2005TEST(Diagnostics, Tags) {
2007 TU.
ExtraArgs = {
"-Wunused",
"-Wdeprecated"};
2009 void bar() __attribute__((deprecated));
2012 $deprecated[[bar]]();
2014 TU.Code = Test.code().str();
2015 EXPECT_THAT(TU.build().getDiagnostics(),
2016 UnorderedElementsAre(
2017 AllOf(Diag(Test.range("unused"),
"unused variable 'x'"),
2019 AllOf(
Diag(Test.range(
"deprecated"),
"'bar' is deprecated"),
2023 $typedef[[typedef int INT]];
2025 TU.Code = Test.code();
2026 TU.ClangTidyProvider = addTidyChecks("modernize-use-using");
2028 TU.build().getDiagnostics(),
2029 ifTidyChecks(UnorderedElementsAre(
2030 AllOf(
Diag(Test.range(
"typedef"),
"use 'using' instead of 'typedef'"),
2034TEST(Diagnostics, TidyDiagsArentAffectedFromWerror) {
2037 Annotations Test(R
"cpp($typedef[[typedef int INT]]; // error-ok)cpp");
2038 TU.Code = Test.code().str();
2039 TU.ClangTidyProvider = addTidyChecks("modernize-use-using");
2041 TU.build().getDiagnostics(),
2042 ifTidyChecks(UnorderedElementsAre(
2043 AllOf(
Diag(Test.range(
"typedef"),
"use 'using' instead of 'typedef'"),
2046 diagSeverity(DiagnosticsEngine::Warning)))));
2048 TU.ClangTidyProvider =
2051 TU.build().getDiagnostics(),
2052 ifTidyChecks(UnorderedElementsAre(
2053 AllOf(
Diag(Test.range(
"typedef"),
"use 'using' instead of 'typedef'"),
2055 diagSeverity(DiagnosticsEngine::Error)))));
2058TEST(Diagnostics, DeprecatedDiagsAreHints) {
2060 std::optional<clangd::Diagnostic>
Diag;
2062 D.Range = {pos(1, 2), pos(3, 4)};
2063 D.InsideMainFile =
true;
2067 D.Severity = DiagnosticsEngine::Warning;
2069 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix>) {
2070 Diag = std::move(LSPDiag);
2076 D.Severity = DiagnosticsEngine::Error;
2078 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix>) {
2079 Diag = std::move(LSPDiag);
2086 D.Severity = DiagnosticsEngine::Warning;
2088 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix>) {
2089 Diag = std::move(LSPDiag);
2094TEST(DiagnosticsTest, IncludeCleaner) {
2096$fix[[ $diag[[#include "unused.h"]]
2102 #include <system_header.h>
2109 TU.Code = Test.code().str();
2110 TU.AdditionalFiles["unused.h"] = R
"cpp(
2114 TU.AdditionalFiles["used.h"] = R
"cpp(
2118 TU.AdditionalFiles["ignore.h"] = R
"cpp(
2122 TU.AdditionalFiles["system/system_header.h"] =
"";
2123 TU.ExtraArgs = {
"-isystem" +
testPath(
"system")};
2127 Cfg.Diagnostics.Includes.IgnoreHeader.emplace_back(
2128 [](llvm::StringRef Header) {
return Header.ends_with(
"ignore.h"); });
2130 auto AST = TU.build();
2132 AST.getDiagnostics(),
2134 Diag(Test.range(
"diag"),
2135 "included header unused.h is not used directly"),
2137 withFix(
Fix(Test.range(
"fix"),
"",
"remove #include directive")))));
2138 auto &
Diag =
AST.getDiagnostics().front();
2140 llvm::ValueIs(Not(IsEmpty())));
2141 Cfg.Diagnostics.SuppressAll =
true;
2143 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
2144 Cfg.Diagnostics.SuppressAll =
false;
2145 Cfg.Diagnostics.Suppress = {
"unused-includes"};
2147 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
2150TEST(DiagnosticsTest, FixItFromHeader) {
2151 llvm::StringLiteral Header(R
"cpp(
2153 void foo(int *, int);)cpp");
2158 $diag[[foo]]($fix[[]]x, 1);
2161 TU.Code = Source.code().str();
2162 TU.HeaderCode = Header.str();
2164 TU.build().getDiagnostics(),
2165 UnorderedElementsAre(AllOf(
2166 Diag(Source.range("diag"),
"no matching function for call to 'foo'"),
2167 withFix(
Fix(Source.range(
"fix"),
"&",
2168 "candidate function not viable: no known conversion from "
2169 "'int' to 'int *' for 1st argument; take the address of "
2170 "the argument with &")))));
2173TEST(DiagnosticsTest, UnusedInHeader) {
2176 TU.ExtraArgs.push_back(
"-Wunused-function");
2177 TU.Filename =
"test.c";
2178 EXPECT_THAT(TU.build().getDiagnostics(),
2179 ElementsAre(withID(diag::warn_unused_function)));
2182 TU.Filename =
"test.h";
2183 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
2186TEST(DiagnosticsTest, DontSuppressSubcategories) {
2197 TU.ExtraArgs.push_back("-Wunreachable-code-aggressive");
2198 TU.Code = Source.code().str();
2203 EXPECT_THAT(TU.build().getDiagnostics(),
2204 ElementsAre(diagName(
"-Wunreachable-code-break")));
static cl::opt< bool > Fix("fix", desc(R"(
Apply suggested fixes. Without -fix-errors
clang-tidy will bail out if any compilation
errors were found.
)"), cl::init(false), cl::cat(ClangTidyCategory))
static cl::opt< std::string > Checks("checks", desc(R"(
Comma-separated list of globs with optional '-'
prefix. Globs are processed in order of
appearance in the list. Globs without '-'
prefix add checks with matching names to the
set, globs with the '-' prefix remove checks
with matching names from the set of enabled
checks. This option's value is appended to the
value of the 'Checks' option in .clang-tidy
file, if any.
)"), cl::init(""), cl::cat(ClangTidyCategory))
Same as llvm::Annotations, but adjusts functions to LSP-specific types for positions and ranges.
static std::unique_ptr< SymbolIndex > build(SymbolSlab Symbols, RefSlab Refs, RelationSlab Relations)
Builds an index from slabs. The index takes ownership of the data.
An efficient structure of storing large set of symbol references in memory.
SymbolSlab::Builder is a mutable container that can 'freeze' to SymbolSlab.
void insert(const Symbol &S)
Adds a symbol, overwriting any existing one with the same ID.
WithContextValue extends Context::current() with a single value.
FIXME: Skip testing on windows temporarily due to the different escaping code mode.
llvm::unique_function< void(tidy::ClangTidyOptions &, llvm::StringRef) const > TidyProvider
A factory to modify a tidy::ClangTidyOptions.
Symbol func(llvm::StringRef Name)
const NamedDecl & findDecl(ParsedAST &AST, llvm::StringRef QName)
Symbol cls(llvm::StringRef Name)
TidyProvider combine(std::vector< TidyProvider > Providers)
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)
static URISchemeRegistry::Add< TestScheme > X(TestScheme::Scheme, "Test schema")
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)
TidyProvider disableUnusableChecks(llvm::ArrayRef< std::string > ExtraBadChecks)
Provider that will disable checks known to not work with clangd.
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.
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Settings that express user/project preferences and control clangd behavior.
static clangd::Key< Config > Key
Context key which can be used to set the current Config.
@ Strict
Diagnose missing and unused includes.
struct clang::clangd::Config::@343034053122374337352226322054223376344037116252 Diagnostics
Controls warnings and errors when parsing code.
IncludesPolicy UnusedIncludes
A top-level diagnostic that may have Notes and Fixes.
std::vector< Fix > Fixes
Alternative fixes for this diagnostic, one should be chosen.
llvm::SmallVector< DiagnosticTag, 1 > Tags
enum clang::clangd::Diag::DiagSource Source
std::vector< Note > Notes
Elaborate on the problem, usually pointing to a related piece of code.
Represents a single fix-it that editor can apply to fix the error.
std::string Message
Message for the fix-it.
llvm::SmallVector< TextEdit, 1 > Edits
TextEdits from clang's fix-its. Must be non-empty.
Represents a note for the diagnostic.
int line
Line position in a document (zero-based).
The class presents a C++ symbol, e.g.
@ IndexedForCodeCompletion
Whether or not this symbol is meant to be used for the code completion.
@ Include
#include "header.h"
std::vector< std::string > ExtraArgs
static TestTU withHeaderCode(llvm::StringRef HeaderCode)
static TestTU withCode(llvm::StringRef Code)
const SymbolIndex * ExternalIndex
static URIForFile canonicalize(llvm::StringRef AbsPath, llvm::StringRef TUPath)
Canonicalizes AbsPath via URI.