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,"
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"),
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.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) {
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) {
381 Derived<int> [[y]]; // error-ok
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) {
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; }
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) {
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) {
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) {
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")));
549 EXPECT_THAT(TU.build().getDiagnostics(),
550 ElementsAre(
Diag(Main.range(),
551 "use of undeclared identifier 'unknown'")));
554TEST(DiagnosticTest, RespectsDiagnosticConfigInHeader) {
556 int x = "42"; // error-ok
559 #include "header.hpp"
562 TU.AdditionalFiles[
"header.hpp"] = std::string(Header.code());
566 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
569TEST(DiagnosticTest, ClangTidySuppressionComment) {
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) {
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) {
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,
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);
664TEST(DiagnosticTest, ClangTidyEnablesClangWarning) {
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) {
762 int somereallyreallyreallyreallyreallyreallyreallyreallylongidentifier;
763 [[omereallyreallyreallyreallyreallyreallyreallyreallylongidentifier]]= 10;
768 TU.build().getDiagnostics(),
769 ElementsAre(withFix(Fix(
771 "somereallyreallyreallyreallyreallyreallyreallyreallylongidentifier",
772 "change 'omereallyreallyreallyreallyreallyreallyreallyreall…' to "
773 "'somereallyreallyreallyreallyreallyreallyreallyreal…'"))));
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) {
792 TU.ExtraArgs = {
"-Wnewline-eof"};
794 TU.build().getDiagnostics(),
795 ElementsAre(withFix((
Fix(Source.range(),
"\n",
"insert '\\n'")))));
798TEST(DiagnosticTest, ClangTidySuppressionCommentTrumpsWarningAsError) {
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) {
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, BadSignalToKillThreadInPreamble) {
829 using pthread_t = int;
830 int pthread_kill(pthread_t thread, int sig);
833 return pthread_kill(thread, 15);
837 TU.HeaderFilename = "signal.h";
838 TU.HeaderCode =
"#define SIGTERM 15";
839 TU.ClangTidyProvider =
addTidyChecks(
"bugprone-bad-signal-to-kill-thread");
840 EXPECT_THAT(TU.build().getDiagnostics(),
841 ifTidyChecks(UnorderedElementsAre(
842 diagName(
"bugprone-bad-signal-to-kill-thread"))));
845TEST(DiagnosticTest, ClangTidyMacroToEnumCheck) {
852 std::vector<TidyProvider> Providers;
854 addTidyChecks("cppcoreguidelines-macro-to-enum,modernize-macro-to-enum"));
856 TU.ClangTidyProvider =
combine(std::move(Providers));
857 EXPECT_THAT(TU.build().getDiagnostics(), UnorderedElementsAre());
860TEST(DiagnosticTest, ElseAfterReturnRange) {
865 } [[else]] if (cond == 2) {
872 TU.ClangTidyProvider = addTidyChecks("llvm-else-after-return");
873 EXPECT_THAT(TU.build().getDiagnostics(),
874 ifTidyChecks(ElementsAre(
875 Diag(Main.range(),
"do not use 'else' after 'return'"))));
878TEST(DiagnosticTest, ClangTidySelfContainedDiags) {
887 void InitVariables() {
888 float $C[[C]]$CFix[[]];
889 double $D[[D]]$DFix[[]];
893 TU.ClangTidyProvider =
895 "cppcoreguidelines-init-variables");
896 clangd::Fix ExpectedAFix;
897 ExpectedAFix.Message =
898 "'A' should be initialized in a member initializer of the constructor";
899 ExpectedAFix.Edits.push_back(
TextEdit{Main.range(
"Fix"),
" : A(1)"});
900 ExpectedAFix.Edits.push_back(
TextEdit{Main.range(
"A"),
""});
905 clangd::Fix ExpectedBFix;
906 ExpectedBFix.Message =
907 "'B' should be initialized in a member initializer of the constructor";
908 ExpectedBFix.Edits.push_back(
TextEdit{Main.range(
"Fix"),
" : B(1)"});
909 ExpectedBFix.Edits.push_back(
TextEdit{Main.range(
"B"),
""});
911 clangd::Fix ExpectedCFix;
912 ExpectedCFix.Message =
"variable 'C' is not initialized";
913 ExpectedCFix.Edits.push_back(
TextEdit{Main.range(
"CFix"),
" = NAN"});
914 ExpectedCFix.Edits.push_back(
915 TextEdit{Main.range(
"MathHeader"),
"#include <math.h>\n\n"});
919 clangd::Fix ExpectedDFix;
920 ExpectedDFix.Message =
"variable 'D' is not initialized";
921 ExpectedDFix.Edits.push_back(
TextEdit{Main.range(
"DFix"),
" = NAN"});
922 ExpectedDFix.Edits.push_back(
923 TextEdit{Main.range(
"MathHeader"),
"#include <math.h>\n\n"});
925 TU.build().getDiagnostics(),
926 ifTidyChecks(UnorderedElementsAre(
927 AllOf(
Diag(Main.range(
"A"),
"'A' should be initialized in a member "
928 "initializer of the constructor"),
929 withFix(equalToFix(ExpectedAFix))),
930 AllOf(
Diag(Main.range(
"B"),
"'B' should be initialized in a member "
931 "initializer of the constructor"),
932 withFix(equalToFix(ExpectedBFix))),
933 AllOf(
Diag(Main.range(
"C"),
"variable 'C' is not initialized"),
934 withFix(equalToFix(ExpectedCFix))),
935 AllOf(
Diag(Main.range(
"D"),
"variable 'D' is not initialized"),
936 withFix(equalToFix(ExpectedDFix))))));
939TEST(DiagnosticTest, ClangTidySelfContainedDiagsFormatting) {
943 virtual void Reset1() = 0;
944 virtual void Reset2() = 0;
946 class A : public Interface {
947 // This will be marked by clangd to use override instead of virtual
948 $virtual1[[virtual ]]void $Reset1[[Reset1]]()$override1[[]];
949 $virtual2[[virtual ]]/**/void $Reset2[[Reset2]]()$override2[[]];
953 TU.ClangTidyProvider =
954 addTidyChecks("cppcoreguidelines-explicit-virtual-functions,");
955 clangd::Fix
const ExpectedFix1{
956 "prefer using 'override' or (rarely) 'final' "
957 "instead of 'virtual'",
958 {
TextEdit{Main.range(
"override1"),
" override"},
959 TextEdit{Main.range(
"virtual1"),
""}},
961 clangd::Fix
const ExpectedFix2{
962 "prefer using 'override' or (rarely) 'final' "
963 "instead of 'virtual'",
964 {
TextEdit{Main.range(
"override2"),
" override"},
965 TextEdit{Main.range(
"virtual2"),
""}},
969 EXPECT_THAT(TU.build().getDiagnostics(),
970 ifTidyChecks(UnorderedElementsAre(
971 AllOf(
Diag(Main.range(
"Reset1"),
972 "prefer using 'override' or (rarely) 'final' "
973 "instead of 'virtual'"),
974 withFix(equalToFix(ExpectedFix1))),
975 AllOf(
Diag(Main.range(
"Reset2"),
976 "prefer using 'override' or (rarely) 'final' "
977 "instead of 'virtual'"),
978 withFix(equalToFix(ExpectedFix2))))));
981TEST(DiagnosticsTest, ClangTidyCallingIntoPreprocessor) {
982 std::string Main = R
"cpp(
987 std::string Header = R"cpp(
988 #define EXTERN extern
992 TU.AdditionalFiles[
"b.h"] = Header;
993 TU.ClangTidyProvider =
addTidyChecks(
"modernize-use-trailing-return-type");
998TEST(DiagnosticsTest, Preprocessor) {
1007 int a = [[b]]; // error-ok
1014 ElementsAre(Diag(Test.range(), "use of undeclared identifier 'b'")));
1017TEST(DiagnosticsTest, IgnoreVerify) {
1019 int a; // expected-error {{}}
1021 TU.ExtraArgs.push_back("-Xclang");
1022 TU.ExtraArgs.push_back(
"-verify");
1023 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
1026TEST(DiagnosticTest, IgnoreBEFilelistOptions) {
1028 TU.ExtraArgs.push_back(
"-Xclang");
1029 for (
const auto *DisableOption :
1030 {
"-fsanitize-ignorelist=null",
"-fprofile-list=null",
1031 "-fxray-always-instrument=null",
"-fxray-never-instrument=null",
1032 "-fxray-attr-list=null"}) {
1033 TU.ExtraArgs.push_back(DisableOption);
1034 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
1035 TU.ExtraArgs.pop_back();
1040TEST(DiagnosticsTest, RecursivePreamble) {
1042 #include "foo.h" // error-ok
1045 TU.Filename = "foo.h";
1046 EXPECT_THAT(TU.build().getDiagnostics(),
1047 ElementsAre(diagName(
"pp_including_mainfile_in_preamble")));
1048 EXPECT_THAT(TU.build().getLocalTopLevelDecls(), SizeIs(1));
1052TEST(DiagnosticsTest, RecursivePreamblePragmaOnce) {
1058 TU.Filename = "foo.h";
1059 EXPECT_THAT(TU.build().getDiagnostics(),
1060 Not(Contains(diagName(
"pp_including_mainfile_in_preamble"))));
1061 EXPECT_THAT(TU.build().getLocalTopLevelDecls(), SizeIs(1));
1066TEST(DiagnosticsTest, RecursivePreambleIfndefGuard) {
1070 #include "foo.h" // error-ok
1074 TU.Filename = "foo.h";
1076 EXPECT_THAT(TU.build().getDiagnostics(),
1077 ElementsAre(diagName(
"pp_including_mainfile_in_preamble")));
1078 EXPECT_THAT(TU.build().getLocalTopLevelDecls(), SizeIs(1));
1081TEST(DiagnosticsTest, PreambleWithPragmaAssumeNonnull) {
1083#pragma clang assume_nonnull begin
1085#pragma clang assume_nonnull end
1087 auto AST = TU.build();
1088 EXPECT_THAT(
AST.getDiagnostics(), IsEmpty());
1089 const auto *
X = cast<FunctionDecl>(
findDecl(
AST,
"foo")).getParamDecl(0);
1090 ASSERT_TRUE(
X->getOriginalType()->getNullability() ==
1091 NullabilityKind::NonNull);
1094TEST(DiagnosticsTest, PreamblePragmaDiagnosticPushPop) {
1096#pragma clang diagnostic push
1100#pragma clang diagnostic pop
1102 auto AST = TU.build();
1103 EXPECT_THAT(
AST.getDiagnostics(), IsEmpty());
1106TEST(DiagnosticsTest, PreambleHeaderWithBadPragmaAssumeNonnull) {
1108#pragma clang assume_nonnull begin // error-ok
1112#include "foo.h" // unterminated assume_nonnull should not affect bar.
1115 TU.AdditionalFiles = {{"foo.h", std::string(Header.code())}};
1116 auto AST = TU.build();
1117 EXPECT_THAT(
AST.getDiagnostics(),
1118 ElementsAre(diagName(
"pp_eof_in_assume_nonnull")));
1119 const auto *
X = cast<FunctionDecl>(
findDecl(
AST,
"foo")).getParamDecl(0);
1120 ASSERT_TRUE(
X->getOriginalType()->getNullability() ==
1121 NullabilityKind::NonNull);
1122 const auto *Y = cast<FunctionDecl>(
findDecl(
AST,
"bar")).getParamDecl(0);
1123 ASSERT_FALSE(Y->getOriginalType()->getNullability());
1126TEST(DiagnosticsTest, InsideMacros) {
1129 #define RET(x) return x + 10
1132 RET($foo[[0]]); // error-ok
1139 ElementsAre(Diag(Test.range("foo"),
1140 "cannot initialize return object of type "
1141 "'int *' with an rvalue of type 'int'"),
1142 Diag(Test.range(
"bar"),
1143 "cannot initialize return object of type "
1144 "'int *' with an rvalue of type 'int'")));
1147TEST(DiagnosticsTest, NoFixItInMacro) {
1149 #define Define(name) void name() {}
1151 [[Define]](main) // error-ok
1154 EXPECT_THAT(TU.build().getDiagnostics(),
1155 ElementsAre(AllOf(
Diag(Test.range(),
"'main' must return 'int'"),
1159TEST(DiagnosticsTest, PragmaSystemHeader) {
1160 Annotations Test(
"#pragma clang [[system_header]]\n");
1163 TU.build().getDiagnostics(),
1165 Diag(Test.range(),
"#pragma system_header ignored in main file"))));
1166 TU.Filename =
"TestTU.h";
1167 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
1170TEST(ClangdTest, MSAsm) {
1173 llvm::InitializeAllTargetInfos();
1175 TU.ExtraArgs = {
"-fms-extensions"};
1176 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
1179TEST(DiagnosticsTest, ToLSP) {
1186 D.ID = clang::diag::err_undeclared_var_use;
1188 D.Name =
"undeclared_var_use";
1190 D.Message =
"something terrible happened";
1191 D.Range = {pos(1, 2), pos(3, 4)};
1192 D.InsideMainFile =
true;
1193 D.Severity = DiagnosticsEngine::Error;
1194 D.File =
"foo/bar/main.cpp";
1195 D.AbsFile = std::string(MainFile.file());
1196 D.OpaqueData[
"test"] =
"bar";
1198 clangd::Note NoteInMain;
1199 NoteInMain.Message =
"declared somewhere in the main file";
1200 NoteInMain.Range = {pos(5, 6), pos(7, 8)};
1201 NoteInMain.Severity = DiagnosticsEngine::Remark;
1202 NoteInMain.File =
"../foo/bar/main.cpp";
1203 NoteInMain.InsideMainFile =
true;
1204 NoteInMain.AbsFile = std::string(MainFile.file());
1206 D.Notes.push_back(NoteInMain);
1208 clangd::Note NoteInHeader;
1209 NoteInHeader.Message =
"declared somewhere in the header file";
1210 NoteInHeader.Range = {pos(9, 10), pos(11, 12)};
1211 NoteInHeader.Severity = DiagnosticsEngine::Note;
1212 NoteInHeader.File =
"../foo/baz/header.h";
1213 NoteInHeader.InsideMainFile =
false;
1214 NoteInHeader.AbsFile = std::string(
HeaderFile.file());
1215 D.Notes.push_back(NoteInHeader);
1218 F.Message =
"do something";
1219 D.Fixes.push_back(F);
1222 clangd::Diagnostic MainLSP;
1223 MainLSP.range =
D.Range;
1224 MainLSP.severity =
getSeverity(DiagnosticsEngine::Error);
1225 MainLSP.code =
"undeclared_var_use";
1226 MainLSP.source =
"clang";
1228 R
"(Something terrible happened (fix available)
1230main.cpp:6:7: remark: declared somewhere in the main file
1232../foo/baz/header.h:10:11:
1233note: declared somewhere in the header file)";
1235 MainLSP.data = D.OpaqueData;
1237 clangd::Diagnostic NoteInMainLSP;
1238 NoteInMainLSP.range = NoteInMain.Range;
1239 NoteInMainLSP.severity = getSeverity(DiagnosticsEngine::Remark);
1240 NoteInMainLSP.message = R"(Declared somewhere in the main file
1242main.cpp:2:3: error: something terrible happened)";
1246 std::vector<std::pair<clangd::Diagnostic, std::vector<clangd::Fix>>> LSPDiags;
1248 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix> Fixes) {
1250 {std::move(LSPDiag),
1251 std::vector<clangd::Fix>(Fixes.begin(), Fixes.end())});
1256 ElementsAre(Pair(equalToLSPDiag(MainLSP), ElementsAre(equalToFix(F))),
1257 Pair(equalToLSPDiag(NoteInMainLSP), IsEmpty())));
1258 EXPECT_EQ(LSPDiags[0].first.code,
"undeclared_var_use");
1259 EXPECT_EQ(LSPDiags[0].first.source,
"clang");
1260 EXPECT_EQ(LSPDiags[1].first.code,
"");
1261 EXPECT_EQ(LSPDiags[1].first.source,
"");
1265 Opts.EmitRelatedLocations =
true;
1267 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix> Fixes) {
1269 {std::move(LSPDiag),
1270 std::vector<clangd::Fix>(Fixes.begin(), Fixes.end())});
1272 MainLSP.message =
"Something terrible happened (fix available)";
1274 NoteInMainDRI.
message =
"Declared somewhere in the main file";
1275 NoteInMainDRI.location.range = NoteInMain.Range;
1276 NoteInMainDRI.location.uri = MainFile;
1277 MainLSP.relatedInformation = {NoteInMainDRI};
1279 NoteInHeaderDRI.
message =
"Declared somewhere in the header file";
1280 NoteInHeaderDRI.location.range = NoteInHeader.Range;
1282 MainLSP.relatedInformation = {NoteInMainDRI, NoteInHeaderDRI};
1283 EXPECT_THAT(LSPDiags, ElementsAre(Pair(equalToLSPDiag(MainLSP),
1284 ElementsAre(equalToFix(F)))));
1287struct SymbolWithHeader {
1289 std::string DeclaringFile;
1290 std::string IncludeHeader;
1293std::unique_ptr<SymbolIndex>
1294buildIndexWithSymbol(llvm::ArrayRef<SymbolWithHeader> Syms) {
1296 for (
const auto &S : Syms) {
1299 Sym.CanonicalDeclaration.FileURI = S.DeclaringFile.c_str();
1300 Sym.Definition.FileURI = S.DeclaringFile.c_str();
1307TEST(IncludeFixerTest, IncompleteType) {
1309 TU.ExtraArgs.push_back(
"-std=c++20");
1310 auto Index = buildIndexWithSymbol(
1311 {SymbolWithHeader{
"ns::X",
"unittest:///x.h",
"\"x.h\""}});
1312 TU.ExternalIndex = Index.get();
1314 std::vector<std::pair<llvm::StringRef, llvm::StringRef>> Tests{
1315 {
"incomplete_nested_name_spec",
"[[ns::X::]]Nested n;"},
1316 {
"incomplete_base_class",
"class Y : [[ns::X]] {};"},
1317 {
"incomplete_member_access",
"auto i = x[[->]]f();"},
1318 {
"incomplete_type",
"auto& [[[]]m] = *x;"},
1319 {
"init_incomplete_type",
1320 "struct C { static int f(ns::X&); }; int i = C::f([[{]]});"},
1321 {
"bad_cast_incomplete",
"auto a = [[static_cast]]<ns::X>(0);"},
1322 {
"template_nontype_parm_incomplete",
"template <ns::X [[foo]]> int a;"},
1323 {
"typecheck_decl_incomplete_type",
"ns::X [[var]];"},
1324 {
"typecheck_incomplete_tag",
"auto i = [[(*x)]]->f();"},
1325 {
"typecheck_nonviable_condition_incomplete",
1326 "struct A { operator ns::X(); } a; const ns::X &[[b]] = a;"},
1327 {
"invalid_incomplete_type_use",
"auto var = [[ns::X()]];"},
1328 {
"sizeof_alignof_incomplete_or_sizeless_type",
1329 "auto s = [[sizeof]](ns::X);"},
1330 {
"for_range_incomplete_type",
"void foo() { for (auto i : [[*]]x ) {} }"},
1331 {
"func_def_incomplete_result",
"ns::X [[func]] () {}"},
1332 {
"field_incomplete_or_sizeless",
"class M { ns::X [[member]]; };"},
1333 {
"array_incomplete_or_sizeless_type",
"auto s = [[(ns::X[]){}]];"},
1334 {
"call_incomplete_return",
"ns::X f(); auto fp = &f; auto z = [[fp()]];"},
1335 {
"call_function_incomplete_return",
"ns::X foo(); auto a = [[foo()]];"},
1336 {
"call_incomplete_argument",
"int m(ns::X); int i = m([[*x]]);"},
1337 {
"switch_incomplete_class_type",
"void a() { [[switch]](*x) {} }"},
1338 {
"delete_incomplete_class_type",
"void f() { [[delete]] *x; }"},
1339 {
"-Wdelete-incomplete",
"void f() { [[delete]] x; }"},
1340 {
"dereference_incomplete_type",
1341 R
"cpp(void f() { asm("" : "=r"([[*]]x)::); })cpp"},
1343 for (
auto Case : Tests) {
1345 TU.Code = Main.code().str() +
"\n // error-ok";
1347 TU.build().getDiagnostics(),
1348 ElementsAre(AllOf(diagName(Case.first), hasRange(Main.range()),
1349 withFix(
Fix(
Range{},
"#include \"x.h\"\n",
1350 "Include \"x.h\" for symbol ns::X")))))
1355TEST(IncludeFixerTest, IncompleteEnum) {
1358 Sym.CanonicalDeclaration.FileURI = Sym.Definition.FileURI =
"unittest:///x.h";
1367 TU.ExtraArgs.push_back(
"-std=c++20");
1368 TU.ExtraArgs.push_back(
"-fno-ms-compatibility");
1370 std::vector<std::pair<llvm::StringRef, llvm::StringRef>> Tests{
1371 {
"incomplete_enum",
"enum class X : int; using enum [[X]];"},
1372 {
"underlying_type_of_incomplete_enum",
1373 "[[__underlying_type]](enum X) i;"},
1375 for (
auto Case : Tests) {
1377 TU.Code = Main.code().str() +
"\n // error-ok";
1378 EXPECT_THAT(TU.build().getDiagnostics(),
1379 Contains(AllOf(diagName(Case.first), hasRange(Main.range()),
1380 withFix(
Fix(
Range{},
"#include \"x.h\"\n",
1381 "Include \"x.h\" for symbol X")))))
1386TEST(IncludeFixerTest, NoSuggestIncludeWhenNoDefinitionInHeader) {
1388$insert[[]]namespace ns {
1391class Y : $base[[public ns::X]] {};
1400 Sym.CanonicalDeclaration.FileURI =
"unittest:///x.h";
1401 Sym.Definition.FileURI =
"unittest:///x.cc";
1408 TU.ExternalIndex = Index.get();
1410 EXPECT_THAT(TU.build().getDiagnostics(),
1411 UnorderedElementsAre(
1412 Diag(Test.range(
"base"),
"base class has incomplete type"),
1413 Diag(Test.range(
"access"),
1414 "member access into incomplete type 'ns::X'")));
1417TEST(IncludeFixerTest, Typo) {
1419$insert[[]]namespace ns {
1421 $unqualified1[[X]] x;
1422 // No fix if the unresolved type is used as specifier. (ns::)X::Nested will be
1423 // considered the unresolved type.
1424 $unqualified2[[X]]::Nested n;
1426struct S : $base[[X]] {};
1429 ns::$qualified1[[X]] x; // ns:: is valid.
1430 ns::$qualified2[[X]](); // Error: no member in namespace
1432 ::$global[[Global]] glob;
1434using Type = ns::$template[[Foo]]<int>;
1437 auto Index = buildIndexWithSymbol(
1438 {SymbolWithHeader{
"ns::X",
"unittest:///x.h",
"\"x.h\""},
1439 SymbolWithHeader{
"Global",
"unittest:///global.h",
"\"global.h\""},
1440 SymbolWithHeader{
"ns::Foo",
"unittest:///foo.h",
"\"foo.h\""}});
1441 TU.ExternalIndex = Index.get();
1444 TU.build().getDiagnostics(),
1445 UnorderedElementsAre(
1446 AllOf(
Diag(Test.range(
"unqualified1"),
"unknown type name 'X'"),
1447 diagName(
"unknown_typename"),
1448 withFix(
Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
1449 "Include \"x.h\" for symbol ns::X"))),
1450 Diag(Test.range(
"unqualified2"),
"use of undeclared identifier 'X'"),
1451 AllOf(
Diag(Test.range(
"qualified1"),
1452 "no type named 'X' in namespace 'ns'"),
1453 diagName(
"typename_nested_not_found"),
1454 withFix(
Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
1455 "Include \"x.h\" for symbol ns::X"))),
1456 AllOf(
Diag(Test.range(
"qualified2"),
1457 "no member named 'X' in namespace 'ns'"),
1458 diagName(
"no_member"),
1459 withFix(
Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
1460 "Include \"x.h\" for symbol ns::X"))),
1461 AllOf(
Diag(Test.range(
"global"),
1462 "no type named 'Global' in the global namespace"),
1463 diagName(
"typename_nested_not_found"),
1464 withFix(
Fix(Test.range(
"insert"),
"#include \"global.h\"\n",
1465 "Include \"global.h\" for symbol Global"))),
1466 AllOf(
Diag(Test.range(
"template"),
1467 "no template named 'Foo' in namespace 'ns'"),
1468 diagName(
"no_member_template"),
1469 withFix(
Fix(Test.range(
"insert"),
"#include \"foo.h\"\n",
1470 "Include \"foo.h\" for symbol ns::Foo"))),
1471 AllOf(
Diag(Test.range(
"base"),
"expected class name"),
1472 diagName(
"expected_class_name"),
1473 withFix(
Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
1474 "Include \"x.h\" for symbol ns::X")))));
1477TEST(IncludeFixerTest, TypoInMacro) {
1488 auto Index = buildIndexWithSymbol(
1489 {SymbolWithHeader{
"X",
"unittest:///x.h",
"\"x.h\""},
1490 SymbolWithHeader{
"ns::X",
"unittest:///ns.h",
"\"x.h\""}});
1491 TU.ExternalIndex = Index.get();
1494 TU.ExtraArgs = {
"-fno-ms-compatibility"};
1495 EXPECT_THAT(TU.build().getDiagnostics(), Each(withFix(_)));
1498TEST(IncludeFixerTest, MultipleMatchedSymbols) {
1500$insert[[]]namespace na {
1503 $unqualified[[X]] x;
1509 auto Index = buildIndexWithSymbol(
1510 {SymbolWithHeader{
"na::X",
"unittest:///a.h",
"\"a.h\""},
1511 SymbolWithHeader{
"na::nb::X",
"unittest:///b.h",
"\"b.h\""}});
1512 TU.ExternalIndex = Index.get();
1514 EXPECT_THAT(TU.build().getDiagnostics(),
1515 UnorderedElementsAre(AllOf(
1516 Diag(Test.range(
"unqualified"),
"unknown type name 'X'"),
1517 diagName(
"unknown_typename"),
1518 withFix(
Fix(Test.range(
"insert"),
"#include \"a.h\"\n",
1519 "Include \"a.h\" for symbol na::X"),
1520 Fix(Test.range(
"insert"),
"#include \"b.h\"\n",
1521 "Include \"b.h\" for symbol na::nb::X")))));
1524TEST(IncludeFixerTest, NoCrashMemberAccess) {
1526 struct X { int xyz; };
1527 void g() { X x; x.$[[xy]]; }
1530 auto Index = buildIndexWithSymbol(
1531 SymbolWithHeader{
"na::X",
"unittest:///a.h",
"\"a.h\""});
1532 TU.ExternalIndex = Index.get();
1535 TU.build().getDiagnostics(),
1536 UnorderedElementsAre(
Diag(Test.range(),
"no member named 'xy' in 'X'")));
1539TEST(IncludeFixerTest, UseCachedIndexResults) {
1543$insert[[]]void foo() {
1566 buildIndexWithSymbol(SymbolWithHeader{
"X",
"unittest:///a.h",
"\"a.h\""});
1567 TU.ExternalIndex = Index.get();
1569 auto Parsed = TU.build();
1570 for (
const auto &D : Parsed.getDiagnostics()) {
1571 if (
D.Fixes.size() != 1) {
1572 ADD_FAILURE() <<
"D.Fixes.size() != 1";
1575 EXPECT_EQ(
D.Fixes[0].Message, std::string(
"Include \"a.h\" for symbol X"));
1579TEST(IncludeFixerTest, UnresolvedNameAsSpecifier) {
1581$insert[[]]namespace ns {
1583void g() { ns::$[[scope]]::X_Y(); }
1586 TU.Code = std::string(Test.code());
1588 TU.ExtraArgs.push_back(
"-fno-ms-compatibility");
1589 auto Index = buildIndexWithSymbol(
1590 SymbolWithHeader{
"ns::scope::X_Y",
"unittest:///x.h",
"\"x.h\""});
1591 TU.ExternalIndex = Index.get();
1594 TU.build().getDiagnostics(),
1595 UnorderedElementsAre(
1596 AllOf(
Diag(Test.range(),
"no member named 'scope' in namespace 'ns'"),
1597 diagName(
"no_member"),
1598 withFix(
Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
1599 "Include \"x.h\" for symbol ns::scope::X_Y")))));
1602TEST(IncludeFixerTest, UnresolvedSpecifierWithSemaCorrection) {
1604$insert[[]]namespace clang {
1606 // "clangd::" will be corrected to "clang::" by Sema.
1607 $q1[[clangd]]::$x[[X]] x;
1608 $q2[[clangd]]::$ns[[ns]]::Y y;
1613 TU.Code = std::string(Test.code());
1615 TU.ExtraArgs.push_back(
"-fno-ms-compatibility");
1616 auto Index = buildIndexWithSymbol(
1617 {SymbolWithHeader{
"clang::clangd::X",
"unittest:///x.h",
"\"x.h\""},
1618 SymbolWithHeader{
"clang::clangd::ns::Y",
"unittest:///y.h",
"\"y.h\""}});
1619 TU.ExternalIndex = Index.get();
1622 TU.build().getDiagnostics(),
1623 UnorderedElementsAre(
1624 AllOf(
Diag(Test.range(
"q1"),
"use of undeclared identifier 'clangd'; "
1625 "did you mean 'clang'?"),
1626 diagName(
"undeclared_var_use_suggest"),
1628 Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
1629 "Include \"x.h\" for symbol clang::clangd::X"))),
1630 AllOf(
Diag(Test.range(
"x"),
"no type named 'X' in namespace 'clang'"),
1631 diagName(
"typename_nested_not_found"),
1632 withFix(
Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
1633 "Include \"x.h\" for symbol clang::clangd::X"))),
1635 Diag(Test.range(
"q2"),
"use of undeclared identifier 'clangd'; "
1636 "did you mean 'clang'?"),
1637 diagName(
"undeclared_var_use_suggest"),
1639 Fix(Test.range(
"insert"),
"#include \"y.h\"\n",
1640 "Include \"y.h\" for symbol clang::clangd::ns::Y"))),
1641 AllOf(
Diag(Test.range(
"ns"),
1642 "no member named 'ns' in namespace 'clang'"),
1643 diagName(
"no_member"),
1645 Fix(Test.range(
"insert"),
"#include \"y.h\"\n",
1646 "Include \"y.h\" for symbol clang::clangd::ns::Y")))));
1649TEST(IncludeFixerTest, SpecifiedScopeIsNamespaceAlias) {
1651$insert[[]]namespace a {}
1658 auto Index = buildIndexWithSymbol(
1659 SymbolWithHeader{
"a::X",
"unittest:///x.h",
"\"x.h\""});
1660 TU.ExternalIndex = Index.get();
1662 EXPECT_THAT(TU.build().getDiagnostics(),
1663 UnorderedElementsAre(AllOf(
1664 Diag(Test.range(),
"no type named 'X' in namespace 'a'"),
1665 diagName(
"typename_nested_not_found"),
1666 withFix(
Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
1667 "Include \"x.h\" for symbol a::X")))));
1670TEST(IncludeFixerTest, NoCrashOnTemplateInstantiations) {
1672 template <typename T> struct Templ {
1673 template <typename U>
1674 typename U::type operator=(const U &);
1679 A() { [[a]]; /*error-ok*/ } // crash if we compute scopes lazily.
1684 auto Index = buildIndexWithSymbol({});
1685 TU.ExternalIndex = Index.get();
1688 TU.build().getDiagnostics(),
1689 ElementsAre(
Diag(Test.range(),
"use of undeclared identifier 'a'")));
1692TEST(IncludeFixerTest, HeaderNamedInDiag) {
1694 $insert[[]]int main() {
1699 TU.ExtraArgs = {
"-xc",
"-std=c99",
1700 "-Wno-error=implicit-function-declaration"};
1701 auto Index = buildIndexWithSymbol({});
1702 TU.ExternalIndex = Index.get();
1705 TU.build().getDiagnostics(),
1707 Diag(Test.range(),
"call to undeclared library function 'printf' "
1708 "with type 'int (const char *, ...)'; ISO C99 "
1709 "and later do not support implicit function "
1711 withFix(
Fix(Test.range(
"insert"),
"#include <stdio.h>\n",
1712 "Include <stdio.h> for symbol printf")))));
1714 TU.ExtraArgs = {
"-xc",
"-std=c89"};
1716 TU.build().getDiagnostics(),
1718 Diag(Test.range(),
"implicitly declaring library function 'printf' "
1719 "with type 'int (const char *, ...)'"),
1720 withFix(
Fix(Test.range(
"insert"),
"#include <stdio.h>\n",
1721 "Include <stdio.h> for symbol printf")))));
1724TEST(IncludeFixerTest, CImplicitFunctionDecl) {
1727 TU.Filename =
"test.c";
1728 TU.ExtraArgs = {
"-std=c99",
"-Wno-error=implicit-function-declaration"};
1732 Sym.CanonicalDeclaration.FileURI =
"unittest:///foo.h";
1739 TU.ExternalIndex = Index.get();
1742 TU.build().getDiagnostics(),
1745 "call to undeclared function 'foo'; ISO C99 and later do not "
1746 "support implicit function declarations"),
1747 withFix(
Fix(
Range{},
"#include \"foo.h\"\n",
1748 "Include \"foo.h\" for symbol foo")))));
1750 TU.ExtraArgs = {
"-std=c89",
"-Wall"};
1751 EXPECT_THAT(TU.build().getDiagnostics(),
1753 Diag(Test.range(),
"implicit declaration of function 'foo'"),
1754 withFix(
Fix(
Range{},
"#include \"foo.h\"\n",
1755 "Include \"foo.h\" for symbol foo")))));
1758TEST(DiagsInHeaders, DiagInsideHeader) {
1761 void foo() {})cpp");
1762 Annotations Header("[[no_type_spec]]; // error-ok");
1764 TU.AdditionalFiles = {{
"a.h", std::string(Header.code())}};
1765 EXPECT_THAT(TU.build().getDiagnostics(),
1766 UnorderedElementsAre(AllOf(
1767 Diag(Main.range(),
"in included file: a type specifier is "
1768 "required for all declarations"),
1769 withNote(
Diag(Header.range(),
"error occurred here")))));
1772TEST(DiagsInHeaders, DiagInTransitiveInclude) {
1775 void foo() {})cpp");
1777 TU.AdditionalFiles = {{"a.h",
"#include \"b.h\""},
1778 {
"b.h",
"no_type_spec; // error-ok"}};
1779 EXPECT_THAT(TU.build().getDiagnostics(),
1780 UnorderedElementsAre(
Diag(Main.range(),
1781 "in included file: a type specifier is "
1782 "required for all declarations")));
1785TEST(DiagsInHeaders, DiagInMultipleHeaders) {
1787 #include $a[["a.h"]]
1788 #include $b[["b.h"]]
1789 void foo() {})cpp");
1791 TU.AdditionalFiles = {{"a.h",
"no_type_spec; // error-ok"},
1792 {
"b.h",
"no_type_spec; // error-ok"}};
1793 EXPECT_THAT(TU.build().getDiagnostics(),
1794 UnorderedElementsAre(
1795 Diag(Main.range(
"a"),
"in included file: a type specifier is "
1796 "required for all declarations"),
1797 Diag(Main.range(
"b"),
"in included file: a type specifier is "
1798 "required for all declarations")));
1801TEST(DiagsInHeaders, PreferExpansionLocation) {
1805 void foo() {})cpp");
1807 TU.AdditionalFiles = {
1808 {"a.h",
"#include \"b.h\"\n"},
1809 {
"b.h",
"#ifndef X\n#define X\nno_type_spec; // error-ok\n#endif"}};
1810 EXPECT_THAT(TU.build().getDiagnostics(),
1811 Contains(
Diag(Main.range(),
"in included file: a type specifier "
1812 "is required for all declarations")));
1815TEST(DiagsInHeaders, PreferExpansionLocationMacros) {
1821 void foo() {})cpp");
1823 TU.AdditionalFiles = {
1824 {"a.h",
"#include \"c.h\"\n"},
1825 {
"b.h",
"#include \"c.h\"\n"},
1826 {
"c.h",
"#ifndef X\n#define X\nno_type_spec; // error-ok\n#endif"}};
1827 EXPECT_THAT(TU.build().getDiagnostics(),
1828 UnorderedElementsAre(
Diag(Main.range(),
1829 "in included file: a type specifier is "
1830 "required for all declarations")));
1833TEST(DiagsInHeaders, LimitDiagsOutsideMainFile) {
1837 void foo() {})cpp");
1839 TU.AdditionalFiles = {{"a.h",
"#include \"c.h\"\n"},
1840 {
"b.h",
"#include \"c.h\"\n"},
1844 no_type_spec_0; // error-ok
1856 EXPECT_THAT(TU.build().getDiagnostics(),
1857 UnorderedElementsAre(Diag(Main.range(),
1858 "in included file: a type specifier is "
1859 "required for all declarations")));
1862TEST(DiagsInHeaders, OnlyErrorOrFatal) {
1865 void foo() {})cpp");
1867 [[no_type_spec]]; // error-ok
1870 TU.AdditionalFiles = {{"a.h", std::string(Header.code())}};
1871 EXPECT_THAT(TU.build().getDiagnostics(),
1872 UnorderedElementsAre(AllOf(
1873 Diag(Main.range(),
"in included file: a type specifier is "
1874 "required for all declarations"),
1875 withNote(
Diag(Header.range(),
"error occurred here")))));
1878TEST(DiagsInHeaders, OnlyDefaultErrorOrFatal) {
1880 #include [["a.h"]] // get unused "foo" warning when building preamble.
1883 namespace { void foo() {} }
1884 void func() {foo();} ;)cpp");
1886 TU.AdditionalFiles = {{"a.h", std::string(Header.code())}};
1888 TU.ExtraArgs = {
"-Werror",
"-Wunused"};
1889 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
1892TEST(DiagsInHeaders, FromNonWrittenSources) {
1895 void foo() {})cpp");
1898 int b = [[FOO]]; // error-ok)cpp");
1900 TU.AdditionalFiles = {{"a.h", std::string(Header.code())}};
1901 TU.ExtraArgs = {
"-DFOO=NOOO"};
1902 EXPECT_THAT(TU.build().getDiagnostics(),
1903 UnorderedElementsAre(AllOf(
1905 "in included file: use of undeclared identifier 'NOOO'"),
1906 withNote(
Diag(Header.range(),
"error occurred here")))));
1909TEST(DiagsInHeaders, ErrorFromMacroExpansion) {
1919 TU.AdditionalFiles = {{"a.h", std::string(Header.code())}};
1920 EXPECT_THAT(TU.build().getDiagnostics(),
1921 UnorderedElementsAre(
1922 Diag(Main.range(),
"in included file: use of undeclared "
1923 "identifier 'foo'; did you mean 'fo'?")));
1926TEST(DiagsInHeaders, ErrorFromMacroArgument) {
1936 TU.AdditionalFiles = {{"a.h", std::string(Header.code())}};
1937 EXPECT_THAT(TU.build().getDiagnostics(),
1938 UnorderedElementsAre(
1939 Diag(Main.range(),
"in included file: use of undeclared "
1940 "identifier 'foo'; did you mean 'fo'?")));
1943TEST(IgnoreDiags, FromNonWrittenInclude) {
1945 TU.
ExtraArgs.push_back(
"--include=a.h");
1946 TU.AdditionalFiles = {{
"a.h",
"void main();"}};
1949 EXPECT_THAT(TU.build().getDiagnostics(), UnorderedElementsAre());
1952TEST(ToLSPDiag, RangeIsInMain) {
1955 D.Range = {pos(1, 2), pos(3, 4)};
1956 D.Notes.emplace_back();
1957 Note &N =
D.Notes.back();
1958 N.Range = {pos(2, 3), pos(3, 4)};
1960 D.InsideMainFile =
true;
1961 N.InsideMainFile =
false;
1963 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix>) {
1964 EXPECT_EQ(LSPDiag.range,
D.Range);
1967 D.InsideMainFile =
false;
1968 N.InsideMainFile =
true;
1970 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix>) {
1971 EXPECT_EQ(LSPDiag.range, N.Range);
1975TEST(ParsedASTTest, ModuleSawDiag) {
1978 auto AST = TU.build();
1980 EXPECT_THAT(
AST.getDiagnostics(),
1981 testing::Contains(
Diag(Code.range(), KDiagMsg.str())));
1990 TU.Code =
"#define FOO\n void bar();\n";
1991 auto AST = TU.build();
1992 EXPECT_THAT(
AST.getDiagnostics(), IsEmpty());
1996 TU.Code = Code.code().str();
1997 auto AST = TU.build();
1999 AST.getDiagnostics(),
2000 testing::Contains(
Diag(Code.range(),
"no newline at end of file")));
2004TEST(Diagnostics, Tags) {
2006 TU.
ExtraArgs = {
"-Wunused",
"-Wdeprecated"};
2008 void bar() __attribute__((deprecated));
2011 $deprecated[[bar]]();
2013 TU.Code = Test.code().str();
2014 EXPECT_THAT(TU.build().getDiagnostics(),
2015 UnorderedElementsAre(
2016 AllOf(Diag(Test.range("unused"),
"unused variable 'x'"),
2018 AllOf(
Diag(Test.range(
"deprecated"),
"'bar' is deprecated"),
2022 $typedef[[typedef int INT]];
2024 TU.Code = Test.code();
2025 TU.ClangTidyProvider = addTidyChecks("modernize-use-using");
2027 TU.build().getDiagnostics(),
2028 ifTidyChecks(UnorderedElementsAre(
2029 AllOf(
Diag(Test.range(
"typedef"),
"use 'using' instead of 'typedef'"),
2033TEST(Diagnostics, TidyDiagsArentAffectedFromWerror) {
2036 Annotations Test(R
"cpp($typedef[[typedef int INT]]; // error-ok)cpp");
2037 TU.Code = Test.code().str();
2038 TU.ClangTidyProvider = addTidyChecks("modernize-use-using");
2040 TU.build().getDiagnostics(),
2041 ifTidyChecks(UnorderedElementsAre(
2042 AllOf(
Diag(Test.range(
"typedef"),
"use 'using' instead of 'typedef'"),
2045 diagSeverity(DiagnosticsEngine::Warning)))));
2047 TU.ClangTidyProvider =
2050 TU.build().getDiagnostics(),
2051 ifTidyChecks(UnorderedElementsAre(
2052 AllOf(
Diag(Test.range(
"typedef"),
"use 'using' instead of 'typedef'"),
2054 diagSeverity(DiagnosticsEngine::Error)))));
2057TEST(Diagnostics, DeprecatedDiagsAreHints) {
2059 std::optional<clangd::Diagnostic>
Diag;
2061 D.Range = {pos(1, 2), pos(3, 4)};
2062 D.InsideMainFile =
true;
2066 D.Severity = DiagnosticsEngine::Warning;
2068 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix>) {
2069 Diag = std::move(LSPDiag);
2075 D.Severity = DiagnosticsEngine::Error;
2077 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix>) {
2078 Diag = std::move(LSPDiag);
2085 D.Severity = DiagnosticsEngine::Warning;
2087 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix>) {
2088 Diag = std::move(LSPDiag);
2093TEST(DiagnosticsTest, IncludeCleaner) {
2095$fix[[ $diag[[#include "unused.h"]]
2101 #include <system_header.h>
2108 TU.Code = Test.code().str();
2109 TU.AdditionalFiles["unused.h"] = R
"cpp(
2113 TU.AdditionalFiles["used.h"] = R
"cpp(
2117 TU.AdditionalFiles["ignore.h"] = R
"cpp(
2121 TU.AdditionalFiles["system/system_header.h"] =
"";
2122 TU.ExtraArgs = {
"-isystem" +
testPath(
"system")};
2126 Cfg.Diagnostics.Includes.IgnoreHeader.emplace_back(
2127 [](llvm::StringRef Header) {
return Header.ends_with(
"ignore.h"); });
2129 auto AST = TU.build();
2131 AST.getDiagnostics(),
2133 Diag(Test.range(
"diag"),
2134 "included header unused.h is not used directly"),
2136 withFix(
Fix(Test.range(
"fix"),
"",
"remove #include directive")))));
2137 auto &
Diag =
AST.getDiagnostics().front();
2139 llvm::ValueIs(Not(IsEmpty())));
2140 Cfg.Diagnostics.SuppressAll =
true;
2142 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
2143 Cfg.Diagnostics.SuppressAll =
false;
2144 Cfg.Diagnostics.Suppress = {
"unused-includes"};
2146 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
2149TEST(DiagnosticsTest, FixItFromHeader) {
2150 llvm::StringLiteral Header(R
"cpp(
2152 void foo(int *, int);)cpp");
2157 $diag[[foo]]($fix[[]]x, 1);
2160 TU.Code = Source.code().str();
2161 TU.HeaderCode = Header.str();
2163 TU.build().getDiagnostics(),
2164 UnorderedElementsAre(AllOf(
2165 Diag(Source.range("diag"),
"no matching function for call to 'foo'"),
2166 withFix(
Fix(Source.range(
"fix"),
"&",
2167 "candidate function not viable: no known conversion from "
2168 "'int' to 'int *' for 1st argument; take the address of "
2169 "the argument with &")))));
2172TEST(DiagnosticsTest, UnusedInHeader) {
2175 TU.ExtraArgs.push_back(
"-Wunused-function");
2176 TU.Filename =
"test.c";
2177 EXPECT_THAT(TU.build().getDiagnostics(),
2178 ElementsAre(withID(diag::warn_unused_function)));
2181 TU.Filename =
"test.h";
2182 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
2185TEST(DiagnosticsTest, DontSuppressSubcategories) {
2196 TU.ExtraArgs.push_back("-Wunreachable-code-aggressive");
2197 TU.Code = Source.code().str();
2202 EXPECT_THAT(TU.build().getDiagnostics(),
2203 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.