9#include "../clang-tidy/ClangTidyOptions.h"
22#include "clang-tidy-config.h"
29#include "clang/AST/Decl.h"
30#include "clang/Basic/Diagnostic.h"
31#include "clang/Basic/DiagnosticSema.h"
32#include "clang/Basic/LLVM.h"
33#include "clang/Basic/SourceManager.h"
34#include "clang/Basic/Specifiers.h"
35#include "llvm/ADT/ArrayRef.h"
36#include "llvm/ADT/StringRef.h"
37#include "llvm/Support/Error.h"
38#include "llvm/Support/JSON.h"
39#include "llvm/Support/ScopedPrinter.h"
40#include "llvm/Support/TargetSelect.h"
41#include "llvm/Testing/Support/SupportHelpers.h"
42#include "gmock/gmock.h"
43#include "gtest/gtest.h"
56using ::testing::AllOf;
57using ::testing::Contains;
59using ::testing::ElementsAre;
60using ::testing::Field;
61using ::testing::IsEmpty;
64using ::testing::SizeIs;
65using ::testing::UnorderedElementsAre;
67::testing::Matcher<const Diag &> withFix(::testing::Matcher<Fix> FixMatcher) {
71::testing::Matcher<const Diag &> withFix(::testing::Matcher<Fix> FixMatcher1,
72 ::testing::Matcher<Fix> FixMatcher2) {
76::testing::Matcher<const Diag &> withID(
unsigned ID) {
79::testing::Matcher<const Diag &>
80withNote(::testing::Matcher<Note> NoteMatcher) {
84::testing::Matcher<const Diag &>
85withNote(::testing::Matcher<Note> NoteMatcher1,
86 ::testing::Matcher<Note> NoteMatcher2) {
87 return Field(&
Diag::Notes, UnorderedElementsAre(NoteMatcher1, NoteMatcher2));
90::testing::Matcher<const Diag &>
91withTag(::testing::Matcher<DiagnosticTag> TagMatcher) {
98 "Diag at " + llvm::to_string(
Range) +
" = [" + Message +
"]") {
99 return arg.Range ==
Range && arg.Message == Message;
102MATCHER_P3(
Fix,
Range, Replacement, Message,
103 "Fix " + llvm::to_string(
Range) +
" => " +
104 ::testing::PrintToString(Replacement) +
" = [" + Message +
"]") {
105 return arg.Message == Message && arg.Edits.size() == 1 &&
106 arg.Edits[0].range ==
Range && arg.Edits[0].newText == Replacement;
109MATCHER_P(fixMessage, Message,
"") {
return arg.Message == Message; }
112 "LSP diagnostic " + llvm::to_string(LSPDiag)) {
114 *result_listener << llvm::formatv(
"expected:\n{0:2}\ngot\n{1:2}",
122MATCHER_P(diagSource, S,
"") {
return arg.Source == S; }
123MATCHER_P(diagName, N,
"") {
return arg.Name == N; }
124MATCHER_P(diagSeverity, S,
"") {
return arg.Severity == S; }
129 if (arg.Edits.size() !=
Fix.
Edits.size())
131 for (std::size_t I = 0; I < arg.Edits.size(); ++I) {
132 if (arg.Edits[I].range !=
Fix.
Edits[I].range ||
133 arg.Edits[I].newText !=
Fix.
Edits[I].newText)
140Position pos(
int Line,
int Character) {
152::testing::Matcher<std::vector<clangd::Diag>>
153ifTidyChecks(::testing::Matcher<std::vector<clangd::Diag>> M) {
154 if (!CLANGD_TIDY_CHECKS)
159TEST(DiagnosticsTest, DiagnosticRanges) {
167 struct Container { int* begin(); int* end(); } *container;
168 for (auto i : $insertstar[[]]$range[[container]]) {
173 foo()$semicolon[[]]//with comments
175 double $type[[bar]] = "foo";
176 struct Foo { int x; }; Foo a;
178 test::$nomembernamespace[[test]];
179 $macro[[ID($macroarg[[fod]])]]();
184 TU.build().getDiagnostics(),
187 AllOf(
Diag(Test.range(
"range"),
188 "invalid range expression of type 'struct Container *'; "
189 "did you mean to dereference it with '*'?"),
190 withFix(
Fix(Test.range(
"insertstar"),
"*",
"insert '*'"))),
192 AllOf(
Diag(Test.range(
"typo"),
193 "use of undeclared identifier 'goo'; did you mean 'foo'?"),
194 diagSource(
Diag::Clang), diagName(
"undeclared_var_use_suggest"),
196 Fix(Test.range(
"typo"),
"foo",
"change 'go\\…' to 'foo'")),
198 withNote(
Diag(Test.range(
"decl"),
"'foo' declared here"))),
202 AllOf(
Diag(Test.range(
"semicolon"),
"expected ';' after expression"),
203 withFix(
Fix(Test.range(
"semicolon"),
";",
"insert ';'"))),
205 Diag(Test.range(
"unk"),
"use of undeclared identifier 'unknown'"),
206 Diag(Test.range(
"type"),
207 "cannot initialize a variable of type 'double' with an lvalue "
208 "of type 'const char[4]'"),
209 Diag(Test.range(
"nomember"),
"no member named 'y' in 'Foo'"),
210 Diag(Test.range(
"nomembernamespace"),
211 "no member named 'test' in namespace 'test'"),
212 AllOf(
Diag(Test.range(
"macro"),
213 "use of undeclared identifier 'fod'; did you mean 'foo'?"),
214 withFix(
Fix(Test.range(
"macroarg"),
"foo",
215 "change 'fod' to 'foo'")))));
223TEST(DiagnosticsTest, WSwitch) {
232 TU.ExtraArgs = {
"-Wswitch"};
233 EXPECT_THAT(TU.build().getDiagnostics(),
234 ElementsAre(
Diag(Test.range(),
235 "enumeration value 'X' not handled in switch")));
238TEST(DiagnosticsTest, FlagsMatter) {
239 Annotations Test(
"[[void]] main() {} // error-ok");
241 EXPECT_THAT(TU.build().getDiagnostics(),
242 ElementsAre(AllOf(
Diag(Test.range(),
"'main' must return 'int'"),
243 withFix(
Fix(Test.range(),
"int",
244 "change 'void' to 'int'")))));
246 TU.Filename =
"Plain.c";
248 TU.build().getDiagnostics(),
250 Diag(Test.range(),
"return type of 'main' is not 'int'"),
251 withFix(
Fix(Test.range(),
"int",
"change return type to 'int'")))));
254TEST(DiagnosticsTest, DiagnosticPreamble) {
256 #include $[["not-found.h"]] // error-ok
260 EXPECT_THAT(TU.build().getDiagnostics(),
261 ElementsAre(::testing::AllOf(
262 Diag(Test.range(),
"'not-found.h' file not found"),
263 diagSource(
Diag::Clang), diagName(
"pp_file_not_found"))));
266TEST(DiagnosticsTest, DeduplicatedClangTidyDiagnostics) {
268 float foo = [[0.1f]];
273 TU.ClangTidyProvider =
addTidyChecks(
"readability-uppercase-literal-suffix,"
277 TU.build().getDiagnostics(),
278 ifTidyChecks(UnorderedElementsAre(::testing::AllOf(
280 "floating point literal has suffix 'f', which is not uppercase"),
293 TU.Code = std::string(Test.code());
297 TU.build().getDiagnostics(),
298 ifTidyChecks(UnorderedElementsAre(::testing::AllOf(
300 "floating point literal has suffix 'f', which is not uppercase"),
304TEST(DiagnosticsTest, ClangTidy) {
306 #include $deprecated[["assert.h"]]
308 #define $macrodef[[SQUARE]](X) (X)*(X)
309 int $main[[main]]() {
311 return SQUARE($macroarg[[++]]y);
312 return $doubled[[sizeof(sizeof(int))]];
315 // misc-no-recursion uses a custom traversal from the TUDecl
325 TU.AdditionalFiles[
"system/assert.h"] =
"";
326 TU.ClangTidyProvider =
addTidyChecks(
"bugprone-sizeof-expression,"
327 "bugprone-macro-repeated-side-effects,"
328 "modernize-deprecated-headers,"
329 "modernize-use-trailing-return-type,"
330 "misc-no-recursion");
331 TU.ExtraArgs.push_back(
"-isystem" +
testPath(
"system"));
332 TU.ExtraArgs.push_back(
"-Wno-unsequenced");
334 TU.build().getDiagnostics(),
335 ifTidyChecks(UnorderedElementsAre(
336 AllOf(
Diag(Test.range(
"deprecated"),
337 "inclusion of deprecated C++ header 'assert.h'; consider "
338 "using 'cassert' instead"),
340 diagName(
"modernize-deprecated-headers"),
341 withFix(
Fix(Test.range(
"deprecated"),
"<cassert>",
342 "change '\"assert.h\"' to '<cassert>'"))),
343 Diag(Test.range(
"doubled"),
344 "suspicious usage of 'sizeof(sizeof(...))'"),
345 AllOf(
Diag(Test.range(
"macroarg"),
346 "side effects in the 1st macro argument 'X' are "
350 diagName(
"bugprone-macro-repeated-side-effects"),
351 withNote(
Diag(Test.range(
"macrodef"),
352 "macro 'SQUARE' defined here"))),
353 AllOf(
Diag(Test.range(
"main"),
354 "use a trailing return type for this function"),
356 diagName(
"modernize-use-trailing-return-type"),
359 "use a trailing return type for this function"))),
360 Diag(Test.range(
"foo"),
361 "function 'foo' is within a recursive call chain"),
362 Diag(Test.range(
"bar"),
363 "function 'bar' is within a recursive call chain"))));
366#if CLANGD_TIDY_CHECKS && CLANG_TIDY_ENABLE_QUERY_BASED_CUSTOM_CHECKS
367TEST(DiagnosticsTest, ClangTidyExperimentalCustomChecks) {
369 int x = $literal[[0]];
372 TU.ClangTidyProvider = [](tidy::ClangTidyOptions &Opts, llvm::StringRef) {
373 Opts.Checks =
"-*,custom-integer-literal";
374 tidy::ClangTidyOptions::CustomCheckValue Check;
375 Check.Name =
"integer-literal";
376 Check.Query = R
"(match integerLiteral().bind("literal"))";
377 tidy::ClangTidyOptions::CustomCheckDiag Diagnostic;
382 Opts.CustomChecks.emplace();
383 Opts.CustomChecks->push_back(std::move(Check));
389 Cfg.Diagnostics.ClangTidy.FastCheckFilter = Policy;
391 auto AST = TU.build();
392 return std::vector<clangd::Diag>(
AST.getDiagnostics());
394 auto CustomDiagnostic =
395 AllOf(
Diag(Test.range(
"literal"),
"integer literal found"),
397 diagSeverity(DiagnosticsEngine::Warning));
402 ElementsAre(CustomDiagnostic));
404 ElementsAre(CustomDiagnostic));
408TEST(DiagnosticsTest, ClangTidyRedundantParenthesesFix) {
411 return$lparen[[(]]0$rparen[[)]];
415 TU.ClangTidyProvider =
addTidyChecks(
"readability-redundant-parentheses");
417 clangd::Fix ExpectedFix;
418 ExpectedFix.Message =
"redundant parentheses around expression";
419 ExpectedFix.Edits.push_back(
TextEdit{Test.range(
"lparen"),
" "});
420 ExpectedFix.Edits.push_back(
TextEdit{Test.range(
"rparen"),
""});
423 TU.build().getDiagnostics(),
424 ifTidyChecks(ElementsAre(AllOf(
425 Diag(Test.range(
"lparen"),
"redundant parentheses around expression"),
427 diagName(
"readability-redundant-parentheses"),
428 withFix(equalToFix(ExpectedFix))))));
431TEST(DiagnosticsTest, ClangTidyEOF) {
435 #include "a.h")cpp");
438 TU.ExtraArgs = {
"-isystem."};
439 TU.AdditionalFiles[
"a.h"] = TU.AdditionalFiles[
"b.h"] =
"";
442 TU.build().getDiagnostics(),
443 ifTidyChecks(Contains(
444 AllOf(
Diag(Test.range(),
"#includes are not sorted properly"),
448TEST(DiagnosticTest, TemplatesInHeaders) {
451 Derived<int> [[y]]; // error-ok
454 template <typename T>
455 struct Derived : [[T]] {};
458 TU.HeaderCode = Header.code().str();
460 TU.build().getDiagnostics(),
462 Diag(Main.range(), "in template: base specifier must name a class"),
463 withNote(
Diag(Header.range(),
"error occurred here"),
464 Diag(Main.range(),
"in instantiation of template class "
465 "'Derived<int>' requested here")))));
468TEST(DiagnosticTest, MakeUnique) {
472 struct S { S(char*); };
473 auto x = std::[[make_unique]]<S>(42); // error-ok
476 TU.HeaderCode = R"cpp(
478 // These mocks aren't quite right - we omit unique_ptr for simplicity.
479 // forward is included to show its body is not needed to get the diagnostic.
480 template <typename T> T&& forward(T& t);
481 template <typename T, typename... A> T* make_unique(A&&... args) {
482 return new T(std::forward<A>(args)...);
486 EXPECT_THAT(TU.build().getDiagnostics(),
487 UnorderedElementsAre(
490 "no matching constructor for initialization of 'S'")));
493TEST(DiagnosticTest, CoroutineInHeader) {
494 StringRef CoroutineH = R
"cpp(
496template <class Ret, typename... T>
497struct coroutine_traits { using promise_type = typename Ret::promise_type; };
499template <class Promise = void>
500struct coroutine_handle {
501 static coroutine_handle from_address(void *) noexcept;
502 static coroutine_handle from_promise(Promise &promise);
503 constexpr void* address() const noexcept;
506struct coroutine_handle<void> {
507 template <class PromiseType>
508 coroutine_handle(coroutine_handle<PromiseType>) noexcept;
509 static coroutine_handle from_address(void *);
510 constexpr void* address() const noexcept;
514 bool await_ready() noexcept { return false; }
515 void await_suspend(coroutine_handle<>) noexcept {}
516 void await_resume() noexcept {}
521 StringRef Header = R"cpp(
522#include "coroutine.h"
523template <typename T> struct [[clang::coro_return_type]] Gen {
524 struct promise_type {
525 Gen<T> get_return_object() {
528 std::awaitable initial_suspend();
529 std::awaitable final_suspend() noexcept;
530 void unhandled_exception();
531 void return_value(T t);
535Gen<int> foo_coro(int b) { co_return b; }
540Gen<int> $[[bar_coro]](int b) { return foo_coro(b); }
543 TU.AdditionalFiles["coroutine.h"] = std::string(CoroutineH);
544 TU.AdditionalFiles[
"header.hpp"] = std::string(Header);
545 TU.ExtraArgs.push_back(
"--std=c++20");
546 EXPECT_THAT(TU.build().getDiagnostics(), ElementsAre(hasRange(Main.range())));
549TEST(DiagnosticTest, MakeShared) {
553 struct S { S(char*); };
554 auto x = std::[[make_shared]]<S>(42); // error-ok
557 TU.HeaderCode = R"cpp(
559 // These mocks aren't quite right - we omit shared_ptr for simplicity.
560 // forward is included to show its body is not needed to get the diagnostic.
561 template <typename T> T&& forward(T& t);
562 template <typename T, typename... A> T* make_shared(A&&... args) {
563 return new T(std::forward<A>(args)...);
567 TU.ParseOpts.PreambleParseForwardingFunctions = true;
568 EXPECT_THAT(TU.build().getDiagnostics(),
569 UnorderedElementsAre(
572 "no matching constructor for initialization of 'S'")));
575TEST(DiagnosticTest, NoMultipleDiagnosticInFlight) {
577 template <typename T> struct Foo {
587 Foo<LabelInfo> label_info_map;
588 [[for]] (auto it = label_info_map.begin(); it != label_info_map.end(); ++it) {
594 TU.ClangTidyProvider = addTidyChecks("modernize-loop-convert");
596 TU.build().getDiagnostics(),
597 ifTidyChecks(UnorderedElementsAre(::testing::AllOf(
598 Diag(Main.range(),
"use range-based for loop instead"),
602TEST(DiagnosticTest, RespectsDiagnosticConfig) {
612 TU.build().getDiagnostics(),
613 ElementsAre(
Diag(Main.range(),
"use of undeclared identifier 'unknown'"),
614 Diag(Main.range(
"ret"),
615 "void function 'x' should not return a value")));
619 EXPECT_THAT(TU.build().getDiagnostics(),
620 ElementsAre(
Diag(Main.range(),
621 "use of undeclared identifier 'unknown'")));
624TEST(DiagnosticTest, RespectsDiagnosticConfigInHeader) {
626 int x = "42"; // error-ok
629 #include "header.hpp"
632 TU.AdditionalFiles[
"header.hpp"] = std::string(Header.code());
636 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
639TEST(DiagnosticTest, ClangTidySuppressionComment) {
643 double d = 8 / i; // NOLINT
647 double f = BAD; // NOLINT
648 double g = [[8]] / i;
650 double h = BAD2; // NOLINT
656 // verify no crashes on unmatched nolints.
661 TU.ClangTidyProvider = addTidyChecks("bugprone-integer-division");
663 TU.build().getDiagnostics(),
664 ifTidyChecks(UnorderedElementsAre(::testing::AllOf(
665 Diag(Main.range(),
"result of integer division used in a floating "
666 "point context; possible loss of precision"),
668 diagName(
"bugprone-integer-division")))));
671TEST(DiagnosticTest, ClangTidySystemMacro) {
676 double x = $inline[[8]] / i;
677 double y = $user[[DIVIDE_USER]](i);
678 double z = DIVIDE_SYS(i);
682 TU.AdditionalFiles[
"user.h"] = R
"cpp(
683 #define DIVIDE_USER(Y) 8/Y
685 TU.AdditionalFiles["system.h"] = R
"cpp(
686 #pragma clang system_header
687 #define DIVIDE_SYS(Y) 8/Y
690 TU.ClangTidyProvider = addTidyChecks("bugprone-integer-division");
691 std::string BadDivision =
"result of integer division used in a floating "
692 "point context; possible loss of precision";
696 EXPECT_THAT(TU.build().getDiagnostics(),
698 UnorderedElementsAre(
Diag(Main.range(
"inline"), BadDivision),
699 Diag(Main.range(
"user"), BadDivision))));
702TEST(DiagnosticTest, ClangTidyWarningAsError) {
706 double f = [[8]] / i; // error-ok
710 TU.ClangTidyProvider =
711 addTidyChecks("bugprone-integer-division",
"bugprone-integer-division");
713 TU.build().getDiagnostics(),
714 ifTidyChecks(UnorderedElementsAre(::testing::AllOf(
715 Diag(Main.range(),
"result of integer division used in a floating "
716 "point context; possible loss of precision"),
718 diagSeverity(DiagnosticsEngine::Error)))));
721TidyProvider addClangArgs(std::vector<llvm::StringRef> ExtraArgs,
723 return [ExtraArgs = std::move(ExtraArgs),
Checks =
Checks.str()](
724 tidy::ClangTidyOptions &Opts, llvm::StringRef) {
726 Opts.ExtraArgs.emplace();
727 for (llvm::StringRef Arg : ExtraArgs)
728 Opts.ExtraArgs->emplace_back(Arg);
734TEST(DiagnosticTest, ClangTidyEnablesClangWarning) {
736 static void [[foo]]() {}
740 auto UnusedFooWarning =
741 AllOf(
Diag(Main.range(),
"unused function 'foo'"),
742 diagName(
"-Wunused-function"), diagSource(
Diag::Clang),
743 diagSeverity(DiagnosticsEngine::Warning));
746 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
750 TU.ClangTidyProvider =
751 addClangArgs({
"-Wunused"},
"clang-diagnostic-unused-function");
752 EXPECT_THAT(TU.build().getDiagnostics(), ElementsAre(UnusedFooWarning));
755 TU.ClangTidyProvider = addClangArgs({
"-Wunused"},
"clang-diagnostic-*");
756 EXPECT_THAT(TU.build().getDiagnostics(), ElementsAre(UnusedFooWarning));
758 TU.ClangTidyProvider = addClangArgs({
"-Wunused"},
"*");
759 EXPECT_THAT(TU.build().getDiagnostics(), Contains(UnusedFooWarning));
761 TU.ClangTidyProvider = addClangArgs(
762 {
"-Wunused"},
"clang-diagnostic-*,-clang-diagnostic-unused-function");
763 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
766 TU.ClangTidyProvider = addClangArgs({
"-Wunused"},
"clang-diagnostic-unused");
767 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
770 TU.ClangTidyProvider = addClangArgs({
"-Wunused",
"-Dfoo=bar"},
771 "clang-diagnostic-unused-function");
772 EXPECT_THAT(TU.build().getDiagnostics(), ElementsAre(UnusedFooWarning))
773 <<
"Not unused function 'bar'!";
776 TU.ExtraArgs = {
"-Werror"};
777 TU.ClangTidyProvider =
778 addClangArgs({
"-Wunused"},
"clang-diagnostic-unused-function");
779 EXPECT_THAT(TU.build().getDiagnostics(),
780 ElementsAre(diagSeverity(DiagnosticsEngine::Warning)));
783 TU.ExtraArgs = {
"-Wunused",
"-Werror"};
784 TU.ClangTidyProvider =
785 addClangArgs({
"-Wunused"},
"clang-diagnostic-unused-function");
786 EXPECT_THAT(TU.build().getDiagnostics(),
787 ElementsAre(diagSeverity(DiagnosticsEngine::Error)));
790 TU.ExtraArgs = {
"-Wunused-function",
"-Werror"};
791 TU.ClangTidyProvider =
792 addClangArgs({
"-Wunused"},
"clang-diagnostic-unused-label");
793 EXPECT_THAT(TU.build().getDiagnostics(),
794 ElementsAre(diagSeverity(DiagnosticsEngine::Warning)));
799 TU.ClangTidyProvider = addClangArgs({
"-Wunused",
"-Wno-unused"},
800 "clang-diagnostic-unused-function");
801 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
804 TU.ClangTidyProvider =
805 addClangArgs({
"-Wunused"}, {
"clang-diagnostic-unused-function"});
806 EXPECT_THAT(TU.build().getDiagnostics(), SizeIs(1));
809 TU.ClangTidyProvider = addClangArgs({
"-Wunused",
"-Wno-unused-function"},
810 {
"clang-diagnostic-unused-function"});
811 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
812 TU.ClangTidyProvider = addClangArgs({
"-Wunused-function",
"-Wno-unused"},
813 {
"clang-diagnostic-unused-function"});
814 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
818 TU.ExtraArgs = {
"-Wunused"};
819 TU.ClangTidyProvider = addClangArgs({
"-Wno-unused"}, {});
820 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
822 TU.ExtraArgs = {
"-Wno-unused"};
823 TU.ClangTidyProvider = addClangArgs({
"-Wunused"}, {
"-*, clang-diagnostic-*"});
824 EXPECT_THAT(TU.build().getDiagnostics(), SizeIs(1));
827TEST(DiagnosticTest, LongFixMessages) {
832 int somereallyreallyreallyreallyreallyreallyreallyreallylongidentifier;
833 [[omereallyreallyreallyreallyreallyreallyreallyreallylongidentifier]]= 10;
838 TU.build().getDiagnostics(),
839 ElementsAre(withFix(Fix(
841 "somereallyreallyreallyreallyreallyreallyreallyreallylongidentifier",
842 "change 'omereallyreallyreallyreallyreallyreallyreallyreall…' to "
843 "'somereallyreallyreallyreallyreallyreallyreallyreal…'"))));
853 TU.Code = std::string(Source.code());
854 EXPECT_THAT(TU.build().getDiagnostics(),
856 Fix(Source.range(), "ident",
"change 'ide\\…' to 'ident'"))));
859TEST(DiagnosticTest, NewLineFixMessage) {
862 TU.ExtraArgs = {
"-Wnewline-eof"};
864 TU.build().getDiagnostics(),
865 ElementsAre(withFix((
Fix(Source.range(),
"\n",
"insert '\\n'")))));
868TEST(DiagnosticTest, ClangTidySuppressionCommentTrumpsWarningAsError) {
872 double f = [[8]] / i; // NOLINT
876 TU.ClangTidyProvider =
877 addTidyChecks("bugprone-integer-division",
"bugprone-integer-division");
878 EXPECT_THAT(TU.build().getDiagnostics(), UnorderedElementsAre());
881TEST(DiagnosticTest, ClangTidyNoLiteralDataInMacroToken) {
884 using pthread_t = int;
885 int pthread_kill(pthread_t thread, int sig);
888 return pthread_kill(thread, 0);
892 TU.ClangTidyProvider = addTidyChecks("bugprone-bad-signal-to-kill-thread");
893 EXPECT_THAT(TU.build().getDiagnostics(), UnorderedElementsAre());
896TEST(DiagnosticTest, BadSignalToKillThreadInPreamble) {
899 using pthread_t = int;
900 int pthread_kill(pthread_t thread, int sig);
903 return pthread_kill(thread, 15);
907 TU.HeaderFilename = "signal.h";
908 TU.HeaderCode =
"#define SIGTERM 15";
909 TU.ClangTidyProvider =
addTidyChecks(
"bugprone-bad-signal-to-kill-thread");
910 EXPECT_THAT(TU.build().getDiagnostics(),
911 ifTidyChecks(UnorderedElementsAre(
912 diagName(
"bugprone-bad-signal-to-kill-thread"))));
915TEST(DiagnosticTest, ClangTidyMacroToEnumCheck) {
922 std::vector<TidyProvider> Providers;
924 addTidyChecks("cppcoreguidelines-macro-to-enum,modernize-macro-to-enum"));
926 TU.ClangTidyProvider =
combine(std::move(Providers));
927 EXPECT_THAT(TU.build().getDiagnostics(), UnorderedElementsAre());
930TEST(DiagnosticTest, ElseAfterReturnRange) {
935 } [[else]] if (cond == 2) {
942 TU.ClangTidyProvider = addTidyChecks("llvm-else-after-return");
943 EXPECT_THAT(TU.build().getDiagnostics(),
944 ifTidyChecks(ElementsAre(
945 Diag(Main.range(),
"do not use 'else' after 'return'"))));
948TEST(DiagnosticTest, ClangTidySelfContainedDiags) {
957 void InitVariables() {
958 float $C[[C]]$CFix[[]];
959 double $D[[D]]$DFix[[]];
963 TU.ClangTidyProvider =
965 "cppcoreguidelines-init-variables");
966 clangd::Fix ExpectedAFix;
967 ExpectedAFix.Message =
968 "'A' should be initialized in a member initializer of the constructor";
969 ExpectedAFix.Edits.push_back(
TextEdit{Main.range(
"Fix"),
" : A(1)"});
970 ExpectedAFix.Edits.push_back(
TextEdit{Main.range(
"A"),
""});
975 clangd::Fix ExpectedBFix;
976 ExpectedBFix.Message =
977 "'B' should be initialized in a member initializer of the constructor";
978 ExpectedBFix.Edits.push_back(
TextEdit{Main.range(
"Fix"),
" : B(1)"});
979 ExpectedBFix.Edits.push_back(
TextEdit{Main.range(
"B"),
""});
981 clangd::Fix ExpectedCFix;
982 ExpectedCFix.Message =
"variable 'C' is not initialized";
983 ExpectedCFix.Edits.push_back(
984 TextEdit{Main.range(
"MathHeader"),
"#include <math.h>\n\n"});
985 ExpectedCFix.Edits.push_back(
TextEdit{Main.range(
"CFix"),
" = NAN"});
989 clangd::Fix ExpectedDFix;
990 ExpectedDFix.Message =
"variable 'D' is not initialized";
991 ExpectedDFix.Edits.push_back(
992 TextEdit{Main.range(
"MathHeader"),
"#include <math.h>\n\n"});
993 ExpectedDFix.Edits.push_back(
TextEdit{Main.range(
"DFix"),
" = NAN"});
995 TU.build().getDiagnostics(),
996 ifTidyChecks(UnorderedElementsAre(
997 AllOf(
Diag(Main.range(
"A"),
"'A' should be initialized in a member "
998 "initializer of the constructor"),
999 withFix(equalToFix(ExpectedAFix))),
1000 AllOf(
Diag(Main.range(
"B"),
"'B' should be initialized in a member "
1001 "initializer of the constructor"),
1002 withFix(equalToFix(ExpectedBFix))),
1003 AllOf(
Diag(Main.range(
"C"),
"variable 'C' is not initialized"),
1004 withFix(equalToFix(ExpectedCFix))),
1005 AllOf(
Diag(Main.range(
"D"),
"variable 'D' is not initialized"),
1006 withFix(equalToFix(ExpectedDFix))))));
1009TEST(DiagnosticTest, ClangTidySelfContainedDiagsFormatting) {
1013 virtual void Reset1() = 0;
1014 virtual void Reset2() = 0;
1016 class A : public Interface {
1017 // This will be marked by clangd to use override instead of virtual
1018 $virtual1[[virtual ]]void $Reset1[[Reset1]]()$override1[[]];
1019 $virtual2[[virtual ]]/**/void $Reset2[[Reset2]]()$override2[[]];
1023 TU.ClangTidyProvider =
1024 addTidyChecks("cppcoreguidelines-explicit-virtual-functions,");
1025 clangd::Fix
const ExpectedFix1{
1026 "prefer using 'override' or (rarely) 'final' "
1027 "instead of 'virtual'",
1028 {
TextEdit{Main.range(
"virtual1"),
""},
1029 TextEdit{Main.range(
"override1"),
" override"}},
1031 clangd::Fix
const ExpectedFix2{
1032 "prefer using 'override' or (rarely) 'final' "
1033 "instead of 'virtual'",
1034 {
TextEdit{Main.range(
"virtual2"),
""},
1035 TextEdit{Main.range(
"override2"),
" override"}},
1039 EXPECT_THAT(TU.build().getDiagnostics(),
1040 ifTidyChecks(UnorderedElementsAre(
1041 AllOf(
Diag(Main.range(
"Reset1"),
1042 "prefer using 'override' or (rarely) 'final' "
1043 "instead of 'virtual'"),
1044 withFix(equalToFix(ExpectedFix1))),
1045 AllOf(
Diag(Main.range(
"Reset2"),
1046 "prefer using 'override' or (rarely) 'final' "
1047 "instead of 'virtual'"),
1048 withFix(equalToFix(ExpectedFix2))))));
1051TEST(DiagnosticsTest, ClangTidyCallingIntoPreprocessor) {
1052 std::string Main = R
"cpp(
1057 std::string Header = R"cpp(
1058 #define EXTERN extern
1062 TU.AdditionalFiles[
"b.h"] = Header;
1063 TU.ClangTidyProvider =
addTidyChecks(
"modernize-use-trailing-return-type");
1068TEST(DiagnosticsTest, Preprocessor) {
1077 int a = [[b]]; // error-ok
1084 ElementsAre(Diag(Test.range(), "use of undeclared identifier 'b'")));
1087TEST(DiagnosticsTest, IgnoreVerify) {
1089 int a; // expected-error {{}}
1091 TU.ExtraArgs.push_back("-Xclang");
1092 TU.ExtraArgs.push_back(
"-verify");
1093 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
1096TEST(DiagnosticTest, IgnoreBEFilelistOptions) {
1098 TU.ExtraArgs.push_back(
"-Xclang");
1099 for (
const auto *DisableOption :
1100 {
"-fsanitize-ignorelist=null",
"-fprofile-list=null",
1101 "-fxray-always-instrument=null",
"-fxray-never-instrument=null",
1102 "-fxray-attr-list=null"}) {
1103 TU.ExtraArgs.push_back(DisableOption);
1104 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
1105 TU.ExtraArgs.pop_back();
1110TEST(DiagnosticsTest, RecursivePreamble) {
1112 #include "foo.h" // error-ok
1115 TU.Filename = "foo.h";
1116 EXPECT_THAT(TU.build().getDiagnostics(),
1117 ElementsAre(diagName(
"pp_including_mainfile_in_preamble")));
1118 EXPECT_THAT(TU.build().getLocalTopLevelDecls(), SizeIs(1));
1122TEST(DiagnosticsTest, RecursivePreamblePragmaOnce) {
1128 TU.Filename = "foo.h";
1129 EXPECT_THAT(TU.build().getDiagnostics(),
1130 Not(Contains(diagName(
"pp_including_mainfile_in_preamble"))));
1131 EXPECT_THAT(TU.build().getLocalTopLevelDecls(), SizeIs(1));
1136TEST(DiagnosticsTest, RecursivePreambleIfndefGuard) {
1140 #include "foo.h" // error-ok
1144 TU.Filename = "foo.h";
1146 EXPECT_THAT(TU.build().getDiagnostics(),
1147 ElementsAre(diagName(
"pp_including_mainfile_in_preamble")));
1148 EXPECT_THAT(TU.build().getLocalTopLevelDecls(), SizeIs(1));
1151TEST(DiagnosticsTest, PreambleWithPragmaAssumeNonnull) {
1153#pragma clang assume_nonnull begin
1155#pragma clang assume_nonnull end
1157 auto AST = TU.build();
1158 EXPECT_THAT(
AST.getDiagnostics(), IsEmpty());
1159 const auto *
X = cast<FunctionDecl>(
findDecl(
AST,
"foo")).getParamDecl(0);
1160 ASSERT_TRUE(
X->getOriginalType()->getNullability() ==
1161 NullabilityKind::NonNull);
1164TEST(DiagnosticsTest, PreamblePragmaDiagnosticPushPop) {
1166#pragma clang diagnostic push
1170#pragma clang diagnostic pop
1172 auto AST = TU.build();
1173 EXPECT_THAT(
AST.getDiagnostics(), IsEmpty());
1176TEST(DiagnosticsTest, PreambleHeaderWithBadPragmaAssumeNonnull) {
1178#pragma clang assume_nonnull begin // error-ok
1182#include "foo.h" // unterminated assume_nonnull should not affect bar.
1185 TU.AdditionalFiles = {{"foo.h", std::string(Header.code())}};
1186 auto AST = TU.build();
1187 EXPECT_THAT(
AST.getDiagnostics(),
1188 ElementsAre(diagName(
"pp_eof_in_assume_nonnull")));
1189 const auto *
X = cast<FunctionDecl>(
findDecl(
AST,
"foo")).getParamDecl(0);
1190 ASSERT_TRUE(
X->getOriginalType()->getNullability() ==
1191 NullabilityKind::NonNull);
1192 const auto *Y = cast<FunctionDecl>(
findDecl(
AST,
"bar")).getParamDecl(0);
1193 ASSERT_FALSE(Y->getOriginalType()->getNullability());
1196TEST(DiagnosticsTest, InsideMacros) {
1199 #define RET(x) return x + 10
1202 RET($foo[[0]]); // error-ok
1209 ElementsAre(Diag(Test.range("foo"),
1210 "cannot initialize return object of type "
1211 "'int *' with an rvalue of type 'int'"),
1212 Diag(Test.range(
"bar"),
1213 "cannot initialize return object of type "
1214 "'int *' with an rvalue of type 'int'")));
1217TEST(DiagnosticsTest, NoFixItInMacro) {
1219 #define Define(name) void name() {}
1221 [[Define]](main) // error-ok
1224 EXPECT_THAT(TU.build().getDiagnostics(),
1225 ElementsAre(AllOf(
Diag(Test.range(),
"'main' must return 'int'"),
1229TEST(DiagnosticsTest, PragmaSystemHeader) {
1230 Annotations Test(
"#pragma clang [[system_header]]\n");
1233 TU.build().getDiagnostics(),
1235 Diag(Test.range(),
"#pragma system_header ignored in main file"))));
1236 TU.Filename =
"TestTU.h";
1237 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
1240TEST(ClangdTest, MSAsm) {
1243 llvm::InitializeAllTargetInfos();
1245 TU.ExtraArgs = {
"-fms-extensions"};
1246 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
1249TEST(DiagnosticsTest, ToLSP) {
1256 D.ID = clang::diag::err_undeclared_var_use;
1258 D.Name =
"undeclared_var_use";
1260 D.Message =
"something terrible happened";
1261 D.Range = {pos(1, 2), pos(3, 4)};
1262 D.InsideMainFile =
true;
1263 D.Severity = DiagnosticsEngine::Error;
1264 D.File =
"foo/bar/main.cpp";
1265 D.AbsFile = std::string(MainFile.file());
1266 D.OpaqueData[
"test"] =
"bar";
1268 clangd::Note NoteInMain;
1269 NoteInMain.Message =
"declared somewhere in the main file";
1270 NoteInMain.Range = {pos(5, 6), pos(7, 8)};
1271 NoteInMain.Severity = DiagnosticsEngine::Remark;
1272 NoteInMain.File =
"../foo/bar/main.cpp";
1273 NoteInMain.InsideMainFile =
true;
1274 NoteInMain.AbsFile = std::string(MainFile.file());
1276 D.Notes.push_back(NoteInMain);
1278 clangd::Note NoteInHeader;
1279 NoteInHeader.Message =
"declared somewhere in the header file";
1280 NoteInHeader.Range = {pos(9, 10), pos(11, 12)};
1281 NoteInHeader.Severity = DiagnosticsEngine::Note;
1282 NoteInHeader.File =
"../foo/baz/header.h";
1283 NoteInHeader.InsideMainFile =
false;
1284 NoteInHeader.AbsFile = std::string(
HeaderFile.file());
1285 D.Notes.push_back(NoteInHeader);
1288 F.Message =
"do something";
1289 D.Fixes.push_back(F);
1292 clangd::Diagnostic MainLSP;
1293 MainLSP.range =
D.Range;
1294 MainLSP.severity =
getSeverity(DiagnosticsEngine::Error);
1295 MainLSP.code =
"undeclared_var_use";
1296 MainLSP.source =
"clang";
1298 R
"(Something terrible happened (fix available)
1300main.cpp:6:7: remark: declared somewhere in the main file
1302../foo/baz/header.h:10:11:
1303note: declared somewhere in the header file)";
1305 MainLSP.data = D.OpaqueData;
1307 clangd::Diagnostic NoteInMainLSP;
1308 NoteInMainLSP.range = NoteInMain.Range;
1309 NoteInMainLSP.severity = getSeverity(DiagnosticsEngine::Remark);
1310 NoteInMainLSP.message = R"(Declared somewhere in the main file
1312main.cpp:2:3: error: something terrible happened)";
1316 std::vector<std::pair<clangd::Diagnostic, std::vector<clangd::Fix>>> LSPDiags;
1318 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix> Fixes) {
1320 {std::move(LSPDiag),
1321 std::vector<clangd::Fix>(Fixes.begin(), Fixes.end())});
1326 ElementsAre(Pair(equalToLSPDiag(MainLSP), ElementsAre(equalToFix(F))),
1327 Pair(equalToLSPDiag(NoteInMainLSP), IsEmpty())));
1328 EXPECT_EQ(LSPDiags[0].first.code,
"undeclared_var_use");
1329 EXPECT_EQ(LSPDiags[0].first.source,
"clang");
1330 EXPECT_EQ(LSPDiags[1].first.code,
"");
1331 EXPECT_EQ(LSPDiags[1].first.source,
"");
1335 Opts.EmitRelatedLocations =
true;
1337 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix> Fixes) {
1339 {std::move(LSPDiag),
1340 std::vector<clangd::Fix>(Fixes.begin(), Fixes.end())});
1342 MainLSP.message =
"Something terrible happened (fix available)";
1344 NoteInMainDRI.
message =
"Declared somewhere in the main file";
1345 NoteInMainDRI.location.range = NoteInMain.Range;
1346 NoteInMainDRI.location.uri = MainFile;
1347 MainLSP.relatedInformation = {NoteInMainDRI};
1349 NoteInHeaderDRI.
message =
"Declared somewhere in the header file";
1350 NoteInHeaderDRI.location.range = NoteInHeader.Range;
1352 MainLSP.relatedInformation = {NoteInMainDRI, NoteInHeaderDRI};
1353 EXPECT_THAT(LSPDiags, ElementsAre(Pair(equalToLSPDiag(MainLSP),
1354 ElementsAre(equalToFix(F)))));
1357struct SymbolWithHeader {
1359 std::string DeclaringFile;
1360 std::string IncludeHeader;
1363std::unique_ptr<SymbolIndex>
1364buildIndexWithSymbol(llvm::ArrayRef<SymbolWithHeader> Syms) {
1366 for (
const auto &S : Syms) {
1369 Sym.CanonicalDeclaration.FileURI = S.DeclaringFile.c_str();
1370 Sym.Definition.FileURI = S.DeclaringFile.c_str();
1377TEST(IncludeFixerTest, IncompleteType) {
1379 TU.ExtraArgs.push_back(
"-std=c++20");
1380 auto Index = buildIndexWithSymbol(
1381 {SymbolWithHeader{
"ns::X",
"unittest:///x.h",
"\"x.h\""}});
1382 TU.ExternalIndex = Index.get();
1384 std::vector<std::pair<llvm::StringRef, llvm::StringRef>> Tests{
1385 {
"incomplete_nested_name_spec",
"[[ns::X::]]Nested n;"},
1386 {
"incomplete_base_class",
"class Y : [[ns::X]] {};"},
1387 {
"incomplete_member_access",
"auto i = x[[->]]f();"},
1388 {
"incomplete_type",
"auto& [[[]]m] = *x;"},
1389 {
"init_incomplete_type",
1390 "struct C { static int f(ns::X&); }; int i = C::f([[{]]});"},
1391 {
"bad_cast_incomplete",
"auto a = [[static_cast]]<ns::X>(0);"},
1392 {
"template_nontype_parm_incomplete",
"template <ns::X [[foo]]> int a;"},
1393 {
"typecheck_decl_incomplete_type",
"ns::X [[var]];"},
1394 {
"typecheck_incomplete_tag",
"auto i = [[(*x)]]->f();"},
1395 {
"typecheck_nonviable_condition_incomplete",
1396 "struct A { operator ns::X(); } a; const ns::X &[[b]] = a;"},
1397 {
"invalid_incomplete_type_use",
"auto var = [[ns::X()]];"},
1398 {
"sizeof_alignof_incomplete_or_sizeless_type",
1399 "auto s = [[sizeof]](ns::X);"},
1400 {
"for_range_incomplete_type",
"void foo() { for (auto i : [[*]]x ) {} }"},
1401 {
"func_def_incomplete_result",
"ns::X [[func]] () {}"},
1402 {
"field_incomplete_or_sizeless",
"class M { ns::X [[member]]; };"},
1403 {
"array_incomplete_or_sizeless_type",
"auto s = [[(ns::X[]){}]];"},
1404 {
"call_incomplete_return",
"ns::X f(); auto fp = &f; auto z = [[fp()]];"},
1405 {
"call_function_incomplete_return",
"ns::X foo(); auto a = [[foo()]];"},
1406 {
"call_incomplete_argument",
"int m(ns::X); int i = m([[*x]]);"},
1407 {
"switch_incomplete_class_type",
"void a() { [[switch]](*x) {} }"},
1408 {
"delete_incomplete_class_type",
"void f() { [[delete]] *x; }"},
1409 {
"-Wdelete-incomplete",
"void f() { [[delete]] x; }"},
1410 {
"dereference_incomplete_type",
1411 R
"cpp(void f() { asm("" : "=r"([[*]]x)::); })cpp"},
1413 for (
auto Case : Tests) {
1415 TU.Code = Main.code().str() +
"\n // error-ok";
1417 TU.build().getDiagnostics(),
1418 ElementsAre(AllOf(diagName(Case.first), hasRange(Main.range()),
1419 withFix(
Fix(
Range{},
"#include \"x.h\"\n",
1420 "Include \"x.h\" for symbol ns::X")))))
1425TEST(IncludeFixerTest, IncompleteEnum) {
1428 Sym.CanonicalDeclaration.FileURI = Sym.Definition.FileURI =
"unittest:///x.h";
1437 TU.ExtraArgs.push_back(
"-std=c++20");
1438 TU.ExtraArgs.push_back(
"-fno-ms-compatibility");
1440 std::vector<std::pair<llvm::StringRef, llvm::StringRef>> Tests{
1441 {
"incomplete_enum",
"enum class X : int; using enum [[X]];"},
1442 {
"underlying_type_of_incomplete_enum",
1443 "[[__underlying_type]](enum X) i;"},
1445 for (
auto Case : Tests) {
1447 TU.Code = Main.code().str() +
"\n // error-ok";
1448 EXPECT_THAT(TU.build().getDiagnostics(),
1449 Contains(AllOf(diagName(Case.first), hasRange(Main.range()),
1450 withFix(
Fix(
Range{},
"#include \"x.h\"\n",
1451 "Include \"x.h\" for symbol X")))))
1456TEST(IncludeFixerTest, NoSuggestIncludeWhenNoDefinitionInHeader) {
1458$insert[[]]namespace ns {
1461class Y : $base[[public ns::X]] {};
1470 Sym.CanonicalDeclaration.FileURI =
"unittest:///x.h";
1471 Sym.Definition.FileURI =
"unittest:///x.cc";
1478 TU.ExternalIndex = Index.get();
1480 EXPECT_THAT(TU.build().getDiagnostics(),
1481 UnorderedElementsAre(
1482 Diag(Test.range(
"base"),
"base class has incomplete type"),
1483 Diag(Test.range(
"access"),
1484 "member access into incomplete type 'ns::X'")));
1487TEST(IncludeFixerTest, Typo) {
1489$insert[[]]namespace ns {
1491 $unqualified1[[X]] x;
1492 // No fix if the unresolved type is used as specifier. (ns::)X::Nested will be
1493 // considered the unresolved type.
1494 $unqualified2[[X]]::Nested n;
1496struct S : $base[[X]] {};
1499 ns::$qualified1[[X]] x; // ns:: is valid.
1500 ns::$qualified2[[X]](); // Error: no member in namespace
1502 ::$global[[Global]] glob;
1504using Type = ns::$template[[Foo]]<int>;
1507 auto Index = buildIndexWithSymbol(
1508 {SymbolWithHeader{
"ns::X",
"unittest:///x.h",
"\"x.h\""},
1509 SymbolWithHeader{
"Global",
"unittest:///global.h",
"\"global.h\""},
1510 SymbolWithHeader{
"ns::Foo",
"unittest:///foo.h",
"\"foo.h\""}});
1511 TU.ExternalIndex = Index.get();
1514 TU.build().getDiagnostics(),
1515 UnorderedElementsAre(
1516 AllOf(
Diag(Test.range(
"unqualified1"),
"unknown type name 'X'"),
1517 diagName(
"unknown_typename"),
1518 withFix(
Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
1519 "Include \"x.h\" for symbol ns::X"))),
1520 Diag(Test.range(
"unqualified2"),
"use of undeclared identifier 'X'"),
1521 AllOf(
Diag(Test.range(
"qualified1"),
1522 "no type named 'X' in namespace 'ns'"),
1523 diagName(
"typename_nested_not_found"),
1524 withFix(
Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
1525 "Include \"x.h\" for symbol ns::X"))),
1526 AllOf(
Diag(Test.range(
"qualified2"),
1527 "no member named 'X' in namespace 'ns'"),
1528 diagName(
"no_member"),
1529 withFix(
Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
1530 "Include \"x.h\" for symbol ns::X"))),
1531 AllOf(
Diag(Test.range(
"global"),
1532 "no type named 'Global' in the global namespace"),
1533 diagName(
"typename_nested_not_found"),
1534 withFix(
Fix(Test.range(
"insert"),
"#include \"global.h\"\n",
1535 "Include \"global.h\" for symbol Global"))),
1536 AllOf(
Diag(Test.range(
"template"),
1537 "no template named 'Foo' in namespace 'ns'"),
1538 diagName(
"no_member_template"),
1539 withFix(
Fix(Test.range(
"insert"),
"#include \"foo.h\"\n",
1540 "Include \"foo.h\" for symbol ns::Foo"))),
1541 AllOf(
Diag(Test.range(
"base"),
"expected class name"),
1542 diagName(
"expected_class_name"),
1543 withFix(
Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
1544 "Include \"x.h\" for symbol ns::X")))));
1547TEST(IncludeFixerTest, TypoInMacro) {
1558 auto Index = buildIndexWithSymbol(
1559 {SymbolWithHeader{
"X",
"unittest:///x.h",
"\"x.h\""},
1560 SymbolWithHeader{
"ns::X",
"unittest:///ns.h",
"\"x.h\""}});
1561 TU.ExternalIndex = Index.get();
1564 TU.ExtraArgs = {
"-fno-ms-compatibility"};
1565 EXPECT_THAT(TU.build().getDiagnostics(), Each(withFix(_)));
1568TEST(IncludeFixerTest, MultipleMatchedSymbols) {
1570$insert[[]]namespace na {
1573 $unqualified[[X]] x;
1579 auto Index = buildIndexWithSymbol(
1580 {SymbolWithHeader{
"na::X",
"unittest:///a.h",
"\"a.h\""},
1581 SymbolWithHeader{
"na::nb::X",
"unittest:///b.h",
"\"b.h\""}});
1582 TU.ExternalIndex = Index.get();
1584 EXPECT_THAT(TU.build().getDiagnostics(),
1585 UnorderedElementsAre(AllOf(
1586 Diag(Test.range(
"unqualified"),
"unknown type name 'X'"),
1587 diagName(
"unknown_typename"),
1588 withFix(
Fix(Test.range(
"insert"),
"#include \"a.h\"\n",
1589 "Include \"a.h\" for symbol na::X"),
1590 Fix(Test.range(
"insert"),
"#include \"b.h\"\n",
1591 "Include \"b.h\" for symbol na::nb::X")))));
1594TEST(IncludeFixerTest, NoCrashMemberAccess) {
1596 struct X { int xyz; };
1597 void g() { X x; x.$[[xy]]; }
1600 auto Index = buildIndexWithSymbol(
1601 SymbolWithHeader{
"na::X",
"unittest:///a.h",
"\"a.h\""});
1602 TU.ExternalIndex = Index.get();
1605 TU.build().getDiagnostics(),
1606 UnorderedElementsAre(
Diag(Test.range(),
"no member named 'xy' in 'X'")));
1609TEST(IncludeFixerTest, NoCrashOnQualifiedConversionFunctionName) {
1612void f() { operator new[](0, operator::align_val_t{}); }
1614 TU.ExtraArgs.push_back("-std=c++17");
1615 auto Index = buildIndexWithSymbol(
1616 SymbolWithHeader{
"std::align_val_t",
"unittest:///new.h",
"<new>"});
1617 TU.ExternalIndex = Index.get();
1619 EXPECT_THAT(TU.build().getDiagnostics(), Not(IsEmpty()));
1622TEST(IncludeFixerTest, FixConversionFunctionTargetType) {
1624$insert[[]]struct Wrapper {
1625 template <typename T>
1626 operator T() const { return T(); }
1631 auto V = W.operator::$target[[Something]]();
1635 auto Index = buildIndexWithSymbol(
1636 SymbolWithHeader{
"Something",
"unittest:///test.h",
"\"test.h\""});
1637 TU.ExternalIndex = Index.get();
1639 EXPECT_THAT(TU.build().getDiagnostics(),
1642 Contains(
Fix(Test.range(
"insert"),
"#include \"test.h\"\n",
1643 "Include \"test.h\" for symbol Something")))));
1646TEST(IncludeFixerTest, UseCachedIndexResults) {
1650$insert[[]]void foo() {
1673 buildIndexWithSymbol(SymbolWithHeader{
"X",
"unittest:///a.h",
"\"a.h\""});
1674 TU.ExternalIndex = Index.get();
1676 auto Parsed = TU.build();
1677 for (
const auto &D : Parsed.getDiagnostics()) {
1678 if (
D.Fixes.size() != 1) {
1679 ADD_FAILURE() <<
"D.Fixes.size() != 1";
1682 EXPECT_EQ(
D.Fixes[0].Message, std::string(
"Include \"a.h\" for symbol X"));
1686TEST(IncludeFixerTest, UnresolvedNameAsSpecifier) {
1688$insert[[]]namespace ns {
1690void g() { ns::$[[scope]]::X_Y(); }
1693 TU.Code = std::string(Test.code());
1695 TU.ExtraArgs.push_back(
"-fno-ms-compatibility");
1696 auto Index = buildIndexWithSymbol(
1697 SymbolWithHeader{
"ns::scope::X_Y",
"unittest:///x.h",
"\"x.h\""});
1698 TU.ExternalIndex = Index.get();
1701 TU.build().getDiagnostics(),
1702 UnorderedElementsAre(
1703 AllOf(
Diag(Test.range(),
"no member named 'scope' in namespace 'ns'"),
1704 diagName(
"no_member"),
1705 withFix(
Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
1706 "Include \"x.h\" for symbol ns::scope::X_Y")))));
1709TEST(IncludeFixerTest, UnresolvedSpecifierWithSemaCorrection) {
1711$insert[[]]namespace clang {
1713 // "clangd::" will be corrected to "clang::" by Sema.
1714 $q1[[clangd]]::$x[[X]] x;
1715 $q2[[clangd]]::$ns[[ns]]::Y y;
1720 TU.Code = std::string(Test.code());
1722 TU.ExtraArgs.push_back(
"-fno-ms-compatibility");
1723 auto Index = buildIndexWithSymbol(
1724 {SymbolWithHeader{
"clang::clangd::X",
"unittest:///x.h",
"\"x.h\""},
1725 SymbolWithHeader{
"clang::clangd::ns::Y",
"unittest:///y.h",
"\"y.h\""}});
1726 TU.ExternalIndex = Index.get();
1729 TU.build().getDiagnostics(),
1730 UnorderedElementsAre(
1731 AllOf(
Diag(Test.range(
"q1"),
"use of undeclared identifier 'clangd'; "
1732 "did you mean 'clang'?"),
1733 diagName(
"undeclared_var_use_suggest"),
1735 Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
1736 "Include \"x.h\" for symbol clang::clangd::X"))),
1737 AllOf(
Diag(Test.range(
"x"),
"no type named 'X' in namespace 'clang'"),
1738 diagName(
"typename_nested_not_found"),
1739 withFix(
Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
1740 "Include \"x.h\" for symbol clang::clangd::X"))),
1742 Diag(Test.range(
"q2"),
"use of undeclared identifier 'clangd'; "
1743 "did you mean 'clang'?"),
1744 diagName(
"undeclared_var_use_suggest"),
1746 Fix(Test.range(
"insert"),
"#include \"y.h\"\n",
1747 "Include \"y.h\" for symbol clang::clangd::ns::Y"))),
1748 AllOf(
Diag(Test.range(
"ns"),
1749 "no member named 'ns' in namespace 'clang'"),
1750 diagName(
"no_member"),
1752 Fix(Test.range(
"insert"),
"#include \"y.h\"\n",
1753 "Include \"y.h\" for symbol clang::clangd::ns::Y")))));
1756TEST(IncludeFixerTest, SpecifiedScopeIsNamespaceAlias) {
1758$insert[[]]namespace a {}
1765 auto Index = buildIndexWithSymbol(
1766 SymbolWithHeader{
"a::X",
"unittest:///x.h",
"\"x.h\""});
1767 TU.ExternalIndex = Index.get();
1769 EXPECT_THAT(TU.build().getDiagnostics(),
1770 UnorderedElementsAre(AllOf(
1771 Diag(Test.range(),
"no type named 'X' in namespace 'a'"),
1772 diagName(
"typename_nested_not_found"),
1773 withFix(
Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
1774 "Include \"x.h\" for symbol a::X")))));
1777TEST(IncludeFixerTest, NoCrashOnTemplateInstantiations) {
1779 template <typename T> struct Templ {
1780 template <typename U>
1781 typename U::type operator=(const U &);
1786 A() { [[a]]; /*error-ok*/ } // crash if we compute scopes lazily.
1791 auto Index = buildIndexWithSymbol({});
1792 TU.ExternalIndex = Index.get();
1795 TU.build().getDiagnostics(),
1796 ElementsAre(
Diag(Test.range(),
"use of undeclared identifier 'a'")));
1799TEST(IncludeFixerTest, HeaderNamedInDiag) {
1801 $insert[[]]int main() {
1806 TU.ExtraArgs = {
"-xc",
"-std=c99",
1807 "-Wno-error=implicit-function-declaration"};
1808 auto Index = buildIndexWithSymbol({});
1809 TU.ExternalIndex = Index.get();
1812 TU.build().getDiagnostics(),
1814 Diag(Test.range(),
"call to undeclared library function 'printf' "
1815 "with type 'int (const char *, ...)'; ISO C99 "
1816 "and later do not support implicit function "
1818 withFix(
Fix(Test.range(
"insert"),
"#include <stdio.h>\n",
1819 "Include <stdio.h> for symbol printf")))));
1821 TU.ExtraArgs = {
"-xc",
"-std=c89"};
1823 TU.build().getDiagnostics(),
1825 Diag(Test.range(),
"implicitly declaring library function 'printf' "
1826 "with type 'int (const char *, ...)'"),
1827 withFix(
Fix(Test.range(
"insert"),
"#include <stdio.h>\n",
1828 "Include <stdio.h> for symbol printf")))));
1831TEST(IncludeFixerTest, CImplicitFunctionDecl) {
1834 TU.Filename =
"test.c";
1835 TU.ExtraArgs = {
"-std=c99",
"-Wno-error=implicit-function-declaration"};
1839 Sym.CanonicalDeclaration.FileURI =
"unittest:///foo.h";
1846 TU.ExternalIndex = Index.get();
1849 TU.build().getDiagnostics(),
1852 "call to undeclared function 'foo'; ISO C99 and later do not "
1853 "support implicit function declarations"),
1854 withFix(
Fix(
Range{},
"#include \"foo.h\"\n",
1855 "Include \"foo.h\" for symbol foo")))));
1857 TU.ExtraArgs = {
"-std=c89",
"-Wall"};
1858 EXPECT_THAT(TU.build().getDiagnostics(),
1860 Diag(Test.range(),
"implicit declaration of function 'foo'"),
1861 withFix(
Fix(
Range{},
"#include \"foo.h\"\n",
1862 "Include \"foo.h\" for symbol foo")))));
1865TEST(DiagsInHeaders, DiagInsideHeader) {
1868 void foo() {})cpp");
1869 Annotations Header("[[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, DiagInTransitiveInclude) {
1882 void foo() {})cpp");
1884 TU.AdditionalFiles = {{"a.h",
"#include \"b.h\""},
1885 {
"b.h",
"no_type_spec; // error-ok"}};
1886 EXPECT_THAT(TU.build().getDiagnostics(),
1887 UnorderedElementsAre(
Diag(Main.range(),
1888 "in included file: a type specifier is "
1889 "required for all declarations")));
1892TEST(DiagsInHeaders, DiagInMultipleHeaders) {
1894 #include $a[["a.h"]]
1895 #include $b[["b.h"]]
1896 void foo() {})cpp");
1898 TU.AdditionalFiles = {{"a.h",
"no_type_spec; // error-ok"},
1899 {
"b.h",
"no_type_spec; // error-ok"}};
1900 EXPECT_THAT(TU.build().getDiagnostics(),
1901 UnorderedElementsAre(
1902 Diag(Main.range(
"a"),
"in included file: a type specifier is "
1903 "required for all declarations"),
1904 Diag(Main.range(
"b"),
"in included file: a type specifier is "
1905 "required for all declarations")));
1908TEST(DiagsInHeaders, PreferExpansionLocation) {
1912 void foo() {})cpp");
1914 TU.AdditionalFiles = {
1915 {"a.h",
"#include \"b.h\"\n"},
1916 {
"b.h",
"#ifndef X\n#define X\nno_type_spec; // error-ok\n#endif"}};
1917 EXPECT_THAT(TU.build().getDiagnostics(),
1918 Contains(
Diag(Main.range(),
"in included file: a type specifier "
1919 "is required for all declarations")));
1922TEST(DiagsInHeaders, PreferExpansionLocationMacros) {
1928 void foo() {})cpp");
1930 TU.AdditionalFiles = {
1931 {"a.h",
"#include \"c.h\"\n"},
1932 {
"b.h",
"#include \"c.h\"\n"},
1933 {
"c.h",
"#ifndef X\n#define X\nno_type_spec; // error-ok\n#endif"}};
1934 EXPECT_THAT(TU.build().getDiagnostics(),
1935 UnorderedElementsAre(
Diag(Main.range(),
1936 "in included file: a type specifier is "
1937 "required for all declarations")));
1940TEST(DiagsInHeaders, LimitDiagsOutsideMainFile) {
1944 void foo() {})cpp");
1946 TU.AdditionalFiles = {{"a.h",
"#include \"c.h\"\n"},
1947 {
"b.h",
"#include \"c.h\"\n"},
1951 no_type_spec_0; // error-ok
1963 EXPECT_THAT(TU.build().getDiagnostics(),
1964 UnorderedElementsAre(Diag(Main.range(),
1965 "in included file: a type specifier is "
1966 "required for all declarations")));
1969TEST(DiagsInHeaders, OnlyErrorOrFatal) {
1972 void foo() {})cpp");
1974 [[no_type_spec]]; // error-ok
1977 TU.AdditionalFiles = {{"a.h", std::string(Header.code())}};
1978 EXPECT_THAT(TU.build().getDiagnostics(),
1979 UnorderedElementsAre(AllOf(
1980 Diag(Main.range(),
"in included file: a type specifier is "
1981 "required for all declarations"),
1982 withNote(
Diag(Header.range(),
"error occurred here")))));
1985TEST(DiagsInHeaders, OnlyDefaultErrorOrFatal) {
1987 #include [["a.h"]] // get unused "foo" warning when building preamble.
1990 namespace { void foo() {} }
1991 void func() {foo();} ;)cpp");
1993 TU.AdditionalFiles = {{"a.h", std::string(Header.code())}};
1995 TU.ExtraArgs = {
"-Werror",
"-Wunused"};
1996 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
1999TEST(DiagsInHeaders, FromNonWrittenSources) {
2002 void foo() {})cpp");
2005 int b = [[FOO]]; // error-ok)cpp");
2007 TU.AdditionalFiles = {{"a.h", std::string(Header.code())}};
2008 TU.ExtraArgs = {
"-DFOO=NOOO"};
2009 EXPECT_THAT(TU.build().getDiagnostics(),
2010 UnorderedElementsAre(AllOf(
2012 "in included file: use of undeclared identifier 'NOOO'"),
2013 withNote(
Diag(Header.range(),
"error occurred here")))));
2016TEST(DiagsInHeaders, ErrorFromMacroExpansion) {
2026 TU.AdditionalFiles = {{"a.h", std::string(Header.code())}};
2027 EXPECT_THAT(TU.build().getDiagnostics(),
2028 UnorderedElementsAre(
2029 Diag(Main.range(),
"in included file: use of undeclared "
2030 "identifier 'foo'; did you mean 'fo'?")));
2033TEST(DiagsInHeaders, ErrorFromMacroArgument) {
2043 TU.AdditionalFiles = {{"a.h", std::string(Header.code())}};
2044 EXPECT_THAT(TU.build().getDiagnostics(),
2045 UnorderedElementsAre(
2046 Diag(Main.range(),
"in included file: use of undeclared "
2047 "identifier 'foo'; did you mean 'fo'?")));
2050TEST(IgnoreDiags, FromNonWrittenInclude) {
2052 TU.
ExtraArgs.push_back(
"--include=a.h");
2053 TU.AdditionalFiles = {{
"a.h",
"void main();"}};
2056 EXPECT_THAT(TU.build().getDiagnostics(), UnorderedElementsAre());
2059TEST(ToLSPDiag, RangeIsInMain) {
2062 D.Range = {pos(1, 2), pos(3, 4)};
2063 D.Notes.emplace_back();
2064 Note &N =
D.Notes.back();
2065 N.Range = {pos(2, 3), pos(3, 4)};
2067 D.InsideMainFile =
true;
2068 N.InsideMainFile =
false;
2070 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix>) {
2071 EXPECT_EQ(LSPDiag.range,
D.Range);
2074 D.InsideMainFile =
false;
2075 N.InsideMainFile =
true;
2077 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix>) {
2078 EXPECT_EQ(LSPDiag.range, N.Range);
2082TEST(ParsedASTTest, ModuleSawDiag) {
2085 auto AST = TU.build();
2087 EXPECT_THAT(
AST.getDiagnostics(),
2088 testing::Contains(
Diag(Code.range(), KDiagMsg.str())));
2097 TU.Code =
"#define FOO\n void bar();\n";
2098 auto AST = TU.build();
2099 EXPECT_THAT(
AST.getDiagnostics(), IsEmpty());
2103 TU.Code = Code.code().str();
2104 auto AST = TU.build();
2106 AST.getDiagnostics(),
2107 testing::Contains(
Diag(Code.range(),
"no newline at end of file")));
2111TEST(Diagnostics, Tags) {
2113 TU.
ExtraArgs = {
"-Wunused",
"-Wdeprecated"};
2115 void bar() __attribute__((deprecated));
2118 $deprecated[[bar]]();
2120 TU.Code = Test.code().str();
2121 EXPECT_THAT(TU.build().getDiagnostics(),
2122 UnorderedElementsAre(
2123 AllOf(Diag(Test.range("unused"),
"unused variable 'x'"),
2125 AllOf(
Diag(Test.range(
"deprecated"),
"'bar' is deprecated"),
2129 $typedef[[typedef int INT]];
2131 TU.Code = Test.code();
2132 TU.ClangTidyProvider = addTidyChecks("modernize-use-using");
2134 TU.build().getDiagnostics(),
2135 ifTidyChecks(UnorderedElementsAre(
2136 AllOf(
Diag(Test.range(
"typedef"),
"use 'using' instead of 'typedef'"),
2140TEST(Diagnostics, TidyDiagsArentAffectedFromWerror) {
2143 Annotations Test(R
"cpp($typedef[[typedef int INT]]; // error-ok)cpp");
2144 TU.Code = Test.code().str();
2145 TU.ClangTidyProvider = addTidyChecks("modernize-use-using");
2147 TU.build().getDiagnostics(),
2148 ifTidyChecks(UnorderedElementsAre(
2149 AllOf(
Diag(Test.range(
"typedef"),
"use 'using' instead of 'typedef'"),
2152 diagSeverity(DiagnosticsEngine::Warning)))));
2154 TU.ClangTidyProvider =
2157 TU.build().getDiagnostics(),
2158 ifTidyChecks(UnorderedElementsAre(
2159 AllOf(
Diag(Test.range(
"typedef"),
"use 'using' instead of 'typedef'"),
2161 diagSeverity(DiagnosticsEngine::Error)))));
2164TEST(Diagnostics, DeprecatedDiagsAreHints) {
2166 std::optional<clangd::Diagnostic>
Diag;
2168 D.Range = {pos(1, 2), pos(3, 4)};
2169 D.InsideMainFile =
true;
2173 D.Severity = DiagnosticsEngine::Warning;
2175 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix>) {
2176 Diag = std::move(LSPDiag);
2182 D.Severity = DiagnosticsEngine::Error;
2184 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix>) {
2185 Diag = std::move(LSPDiag);
2192 D.Severity = DiagnosticsEngine::Warning;
2194 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix>) {
2195 Diag = std::move(LSPDiag);
2200TEST(DiagnosticsTest, IncludeCleaner) {
2202$fix[[ $diag[[#include "unused.h"]]
2208 #include <system_header.h>
2215 TU.Code = Test.code().str();
2216 TU.AdditionalFiles["unused.h"] = R
"cpp(
2220 TU.AdditionalFiles["used.h"] = R
"cpp(
2224 TU.AdditionalFiles["ignore.h"] = R
"cpp(
2228 TU.AdditionalFiles["system/system_header.h"] =
"";
2229 TU.ExtraArgs = {
"-isystem" +
testPath(
"system")};
2233 Cfg.Diagnostics.Includes.IgnoreHeader.emplace_back(
2234 [](llvm::StringRef Header) {
return Header.ends_with(
"ignore.h"); });
2236 auto AST = TU.build();
2238 AST.getDiagnostics(),
2240 Diag(Test.range(
"diag"),
2241 "included header unused.h is not used directly"),
2243 withFix(
Fix(Test.range(
"fix"),
"",
"remove #include directive")))));
2244 auto &
Diag =
AST.getDiagnostics().front();
2246 llvm::ValueIs(Not(IsEmpty())));
2247 Cfg.Diagnostics.SuppressAll =
true;
2249 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
2250 Cfg.Diagnostics.SuppressAll =
false;
2251 Cfg.Diagnostics.Suppress = {
"unused-includes"};
2253 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
2256TEST(DiagnosticsTest, FixItFromHeader) {
2257 llvm::StringLiteral Header(R
"cpp(
2259 void foo(int *, int);)cpp");
2264 $diag[[foo]]($fix[[]]x, 1);
2267 TU.Code = Source.code().str();
2268 TU.HeaderCode = Header.str();
2270 TU.build().getDiagnostics(),
2271 UnorderedElementsAre(AllOf(
2272 Diag(Source.range("diag"),
"no matching function for call to 'foo'"),
2273 withFix(
Fix(Source.range(
"fix"),
"&",
2274 "candidate function not viable: no known conversion from "
2275 "'int' to 'int *' for 1st argument; take the address of "
2276 "the argument with &")))));
2279TEST(DiagnosticsTest, UnusedInHeader) {
2282 TU.ExtraArgs.push_back(
"-Wunused-function");
2283 TU.Filename =
"test.c";
2284 EXPECT_THAT(TU.build().getDiagnostics(),
2285 ElementsAre(withID(diag::warn_unused_function)));
2288 TU.Filename =
"test.h";
2289 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
2292TEST(DiagnosticsTest, DontSuppressSubcategories) {
2303 TU.ExtraArgs.push_back("-Wunreachable-code-aggressive");
2304 TU.Code = Source.code().str();
2309 EXPECT_THAT(TU.build().getDiagnostics(),
2310 ElementsAre(diagName(
"-Wunreachable-code-break")));
2314TEST(DiagnosticsTest, TokenMergeFixit) {
2317 return$lparen[[(]]0$rparen[[)]];
2321 SourceManagerForFile SM("TestTU.cpp", Test.code());
2322 DiagnosticsEngine &DE = SM.get().getDiagnostics();
2324 DE.setClient(&DiagConsumer,
false);
2326 LangOptions LangOpts;
2327 DiagConsumer.BeginSourceFile(LangOpts,
nullptr);
2332 SourceLocation LParenBegin = Loc(Test.range(
"lparen").start);
2333 SourceLocation LParenEnd = Loc(Test.range(
"lparen").end);
2334 SourceLocation RParenBegin = Loc(Test.range(
"rparen").start);
2335 SourceLocation RParenEnd = Loc(Test.range(
"rparen").end);
2337 unsigned ID = DE.getCustomDiagID(DiagnosticsEngine::Warning,
2338 "redundant parentheses around expression");
2339 DiagnosticBuilder DB = DE.Report(LParenBegin, ID);
2340 DB.AddFixItHint(FixItHint::CreateRemoval(
2341 CharSourceRange::getCharRange(LParenBegin, LParenEnd)));
2342 DB.AddFixItHint(FixItHint::CreateRemoval(
2343 CharSourceRange::getCharRange(RParenBegin, RParenEnd)));
2345 DiagConsumer.EndSourceFile();
2347 auto Diags = DiagConsumer.take();
2349 ASSERT_EQ(Diags.size(), 1u);
2350 ASSERT_EQ(Diags[0].Fixes.size(), 1u);
2351 const auto &
Fix = Diags[0].Fixes[0];
2354 EXPECT_EQ(
Fix.
Edits[0].range, Test.range(
"lparen"));
2355 EXPECT_EQ(
Fix.
Edits[0].newText,
" ");
2356 EXPECT_EQ(
Fix.
Edits[1].range, Test.range(
"rparen"));
2357 EXPECT_EQ(
Fix.
Edits[1].newText,
"");
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.
StoreDiags collects the diagnostics that can later be reported by clangd.
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)
llvm::Expected< SourceLocation > sourceLocationInMainFile(const SourceManager &SM, Position P)
Return the file location, corresponding to P.
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.
cppcoreguidelines::ProBoundsAvoidUncheckedContainerAccessCheck P
===– 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
struct clang::clangd::Config::@343034053122374337352226322054223376344037116252::@107156241027253143221327255130274177352007274355 ClangTidy
Configures what clang-tidy checks to run and options to use with them.
bool ExperimentalCustomChecks
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).
Ensure we have enough bits to represent all SymbolTag values.
@ 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.