9#include "../clang-tidy/ClangTidyOptions.h"
28#include "clang/AST/Decl.h"
29#include "clang/Basic/Diagnostic.h"
30#include "clang/Basic/DiagnosticSema.h"
31#include "clang/Basic/LLVM.h"
32#include "clang/Basic/SourceManager.h"
33#include "clang/Basic/Specifiers.h"
34#include "llvm/ADT/ArrayRef.h"
35#include "llvm/ADT/StringRef.h"
36#include "llvm/Support/Error.h"
37#include "llvm/Support/JSON.h"
38#include "llvm/Support/ScopedPrinter.h"
39#include "llvm/Support/TargetSelect.h"
40#include "llvm/Testing/Support/SupportHelpers.h"
41#include "gmock/gmock.h"
42#include "gtest/gtest.h"
55using ::testing::AllOf;
56using ::testing::Contains;
58using ::testing::ElementsAre;
59using ::testing::Field;
60using ::testing::IsEmpty;
63using ::testing::SizeIs;
64using ::testing::UnorderedElementsAre;
66::testing::Matcher<const Diag &> withFix(::testing::Matcher<Fix> FixMatcher) {
70::testing::Matcher<const Diag &> withFix(::testing::Matcher<Fix> FixMatcher1,
71 ::testing::Matcher<Fix> FixMatcher2) {
75::testing::Matcher<const Diag &> withID(
unsigned ID) {
78::testing::Matcher<const Diag &>
79withNote(::testing::Matcher<Note> NoteMatcher) {
83::testing::Matcher<const Diag &>
84withNote(::testing::Matcher<Note> NoteMatcher1,
85 ::testing::Matcher<Note> NoteMatcher2) {
86 return Field(&
Diag::Notes, UnorderedElementsAre(NoteMatcher1, NoteMatcher2));
89::testing::Matcher<const Diag &>
90withTag(::testing::Matcher<DiagnosticTag> TagMatcher) {
97 "Diag at " + llvm::to_string(
Range) +
" = [" + Message +
"]") {
98 return arg.Range ==
Range && arg.Message == Message;
101MATCHER_P3(
Fix,
Range, Replacement, Message,
102 "Fix " + llvm::to_string(
Range) +
" => " +
103 ::testing::PrintToString(Replacement) +
" = [" + Message +
"]") {
104 return arg.Message == Message && arg.Edits.size() == 1 &&
105 arg.Edits[0].range ==
Range && arg.Edits[0].newText == Replacement;
108MATCHER_P(fixMessage, Message,
"") {
return arg.Message == Message; }
111 "LSP diagnostic " + llvm::to_string(LSPDiag)) {
113 *result_listener << llvm::formatv(
"expected:\n{0:2}\ngot\n{1:2}",
121MATCHER_P(diagSource, S,
"") {
return arg.Source == S; }
122MATCHER_P(diagName, N,
"") {
return arg.Name == N; }
123MATCHER_P(diagSeverity, S,
"") {
return arg.Severity == S; }
128 if (arg.Edits.size() !=
Fix.
Edits.size())
130 for (std::size_t I = 0; I < arg.Edits.size(); ++I) {
131 if (arg.Edits[I].range !=
Fix.
Edits[I].range ||
132 arg.Edits[I].newText !=
Fix.
Edits[I].newText)
139Position pos(
int Line,
int Character) {
151::testing::Matcher<std::vector<clangd::Diag>>
152ifTidyChecks(::testing::Matcher<std::vector<clangd::Diag>> M) {
153 if (!CLANGD_TIDY_CHECKS)
158TEST(DiagnosticsTest, DiagnosticRanges) {
166 struct Container { int* begin(); int* end(); } *container;
167 for (auto i : $insertstar[[]]$range[[container]]) {
172 foo()$semicolon[[]]//with comments
174 double $type[[bar]] = "foo";
175 struct Foo { int x; }; Foo a;
177 test::$nomembernamespace[[test]];
178 $macro[[ID($macroarg[[fod]])]]();
183 TU.build().getDiagnostics(),
186 AllOf(
Diag(Test.range(
"range"),
187 "invalid range expression of type 'struct Container *'; "
188 "did you mean to dereference it with '*'?"),
189 withFix(
Fix(Test.range(
"insertstar"),
"*",
"insert '*'"))),
191 AllOf(
Diag(Test.range(
"typo"),
192 "use of undeclared identifier 'goo'; did you mean 'foo'?"),
193 diagSource(
Diag::Clang), diagName(
"undeclared_var_use_suggest"),
195 Fix(Test.range(
"typo"),
"foo",
"change 'go\\…' to 'foo'")),
197 withNote(
Diag(Test.range(
"decl"),
"'foo' declared here"))),
201 AllOf(
Diag(Test.range(
"semicolon"),
"expected ';' after expression"),
202 withFix(
Fix(Test.range(
"semicolon"),
";",
"insert ';'"))),
204 Diag(Test.range(
"unk"),
"use of undeclared identifier 'unknown'"),
205 Diag(Test.range(
"type"),
206 "cannot initialize a variable of type 'double' with an lvalue "
207 "of type 'const char[4]'"),
208 Diag(Test.range(
"nomember"),
"no member named 'y' in 'Foo'"),
209 Diag(Test.range(
"nomembernamespace"),
210 "no member named 'test' in namespace 'test'"),
211 AllOf(
Diag(Test.range(
"macro"),
212 "use of undeclared identifier 'fod'; did you mean 'foo'?"),
213 withFix(
Fix(Test.range(
"macroarg"),
"foo",
214 "change 'fod' to 'foo'")))));
222TEST(DiagnosticsTest, WSwitch) {
231 TU.ExtraArgs = {
"-Wswitch"};
232 EXPECT_THAT(TU.build().getDiagnostics(),
233 ElementsAre(
Diag(Test.range(),
234 "enumeration value 'X' not handled in switch")));
237TEST(DiagnosticsTest, FlagsMatter) {
238 Annotations Test(
"[[void]] main() {} // error-ok");
240 EXPECT_THAT(TU.build().getDiagnostics(),
241 ElementsAre(AllOf(
Diag(Test.range(),
"'main' must return 'int'"),
242 withFix(
Fix(Test.range(),
"int",
243 "change 'void' to 'int'")))));
245 TU.Filename =
"Plain.c";
247 TU.build().getDiagnostics(),
249 Diag(Test.range(),
"return type of 'main' is not 'int'"),
250 withFix(
Fix(Test.range(),
"int",
"change return type to 'int'")))));
253TEST(DiagnosticsTest, DiagnosticPreamble) {
255 #include $[["not-found.h"]] // error-ok
259 EXPECT_THAT(TU.build().getDiagnostics(),
260 ElementsAre(::testing::AllOf(
261 Diag(Test.range(),
"'not-found.h' file not found"),
262 diagSource(
Diag::Clang), diagName(
"pp_file_not_found"))));
265TEST(DiagnosticsTest, DeduplicatedClangTidyDiagnostics) {
267 float foo = [[0.1f]];
272 TU.ClangTidyProvider =
addTidyChecks(
"readability-uppercase-literal-suffix,"
276 TU.build().getDiagnostics(),
277 ifTidyChecks(UnorderedElementsAre(::testing::AllOf(
279 "floating point literal has suffix 'f', which is not uppercase"),
292 TU.Code = std::string(Test.code());
296 TU.build().getDiagnostics(),
297 ifTidyChecks(UnorderedElementsAre(::testing::AllOf(
299 "floating point literal has suffix 'f', which is not uppercase"),
303TEST(DiagnosticsTest, ClangTidy) {
305 #include $deprecated[["assert.h"]]
307 #define $macrodef[[SQUARE]](X) (X)*(X)
308 int $main[[main]]() {
310 return SQUARE($macroarg[[++]]y);
311 return $doubled[[sizeof(sizeof(int))]];
314 // misc-no-recursion uses a custom traversal from the TUDecl
324 TU.AdditionalFiles[
"system/assert.h"] =
"";
325 TU.ClangTidyProvider =
addTidyChecks(
"bugprone-sizeof-expression,"
326 "bugprone-macro-repeated-side-effects,"
327 "modernize-deprecated-headers,"
328 "modernize-use-trailing-return-type,"
329 "misc-no-recursion");
330 TU.ExtraArgs.push_back(
"-isystem" +
testPath(
"system"));
331 TU.ExtraArgs.push_back(
"-Wno-unsequenced");
333 TU.build().getDiagnostics(),
334 ifTidyChecks(UnorderedElementsAre(
335 AllOf(
Diag(Test.range(
"deprecated"),
336 "inclusion of deprecated C++ header 'assert.h'; consider "
337 "using 'cassert' instead"),
339 diagName(
"modernize-deprecated-headers"),
340 withFix(
Fix(Test.range(
"deprecated"),
"<cassert>",
341 "change '\"assert.h\"' to '<cassert>'"))),
342 Diag(Test.range(
"doubled"),
343 "suspicious usage of 'sizeof(sizeof(...))'"),
344 AllOf(
Diag(Test.range(
"macroarg"),
345 "side effects in the 1st macro argument 'X' are "
349 diagName(
"bugprone-macro-repeated-side-effects"),
350 withNote(
Diag(Test.range(
"macrodef"),
351 "macro 'SQUARE' defined here"))),
352 AllOf(
Diag(Test.range(
"main"),
353 "use a trailing return type for this function"),
355 diagName(
"modernize-use-trailing-return-type"),
358 "use a trailing return type for this function"))),
359 Diag(Test.range(
"foo"),
360 "function 'foo' is within a recursive call chain"),
361 Diag(Test.range(
"bar"),
362 "function 'bar' is within a recursive call chain"))));
365TEST(DiagnosticsTest, ClangTidyRedundantParenthesesFix) {
368 return$lparen[[(]]0$rparen[[)]];
372 TU.ClangTidyProvider =
addTidyChecks(
"readability-redundant-parentheses");
374 clangd::Fix ExpectedFix;
375 ExpectedFix.Message =
"redundant parentheses around expression";
376 ExpectedFix.Edits.push_back(
TextEdit{Test.range(
"lparen"),
" "});
377 ExpectedFix.Edits.push_back(
TextEdit{Test.range(
"rparen"),
""});
380 TU.build().getDiagnostics(),
381 ifTidyChecks(ElementsAre(AllOf(
382 Diag(Test.range(
"lparen"),
"redundant parentheses around expression"),
384 diagName(
"readability-redundant-parentheses"),
385 withFix(equalToFix(ExpectedFix))))));
388TEST(DiagnosticsTest, ClangTidyEOF) {
392 #include "a.h")cpp");
395 TU.ExtraArgs = {
"-isystem."};
396 TU.AdditionalFiles[
"a.h"] = TU.AdditionalFiles[
"b.h"] =
"";
399 TU.build().getDiagnostics(),
400 ifTidyChecks(Contains(
401 AllOf(
Diag(Test.range(),
"#includes are not sorted properly"),
405TEST(DiagnosticTest, TemplatesInHeaders) {
408 Derived<int> [[y]]; // error-ok
411 template <typename T>
412 struct Derived : [[T]] {};
415 TU.HeaderCode = Header.code().str();
417 TU.build().getDiagnostics(),
419 Diag(Main.range(), "in template: base specifier must name a class"),
420 withNote(
Diag(Header.range(),
"error occurred here"),
421 Diag(Main.range(),
"in instantiation of template class "
422 "'Derived<int>' requested here")))));
425TEST(DiagnosticTest, MakeUnique) {
429 struct S { S(char*); };
430 auto x = std::[[make_unique]]<S>(42); // error-ok
433 TU.HeaderCode = R"cpp(
435 // These mocks aren't quite right - we omit unique_ptr for simplicity.
436 // forward is included to show its body is not needed to get the diagnostic.
437 template <typename T> T&& forward(T& t);
438 template <typename T, typename... A> T* make_unique(A&&... args) {
439 return new T(std::forward<A>(args)...);
443 EXPECT_THAT(TU.build().getDiagnostics(),
444 UnorderedElementsAre(
447 "no matching constructor for initialization of 'S'")));
450TEST(DiagnosticTest, CoroutineInHeader) {
451 StringRef CoroutineH = R
"cpp(
453template <class Ret, typename... T>
454struct coroutine_traits { using promise_type = typename Ret::promise_type; };
456template <class Promise = void>
457struct coroutine_handle {
458 static coroutine_handle from_address(void *) noexcept;
459 static coroutine_handle from_promise(Promise &promise);
460 constexpr void* address() const noexcept;
463struct coroutine_handle<void> {
464 template <class PromiseType>
465 coroutine_handle(coroutine_handle<PromiseType>) noexcept;
466 static coroutine_handle from_address(void *);
467 constexpr void* address() const noexcept;
471 bool await_ready() noexcept { return false; }
472 void await_suspend(coroutine_handle<>) noexcept {}
473 void await_resume() noexcept {}
478 StringRef Header = R"cpp(
479#include "coroutine.h"
480template <typename T> struct [[clang::coro_return_type]] Gen {
481 struct promise_type {
482 Gen<T> get_return_object() {
485 std::awaitable initial_suspend();
486 std::awaitable final_suspend() noexcept;
487 void unhandled_exception();
488 void return_value(T t);
492Gen<int> foo_coro(int b) { co_return b; }
497Gen<int> $[[bar_coro]](int b) { return foo_coro(b); }
500 TU.AdditionalFiles["coroutine.h"] = std::string(CoroutineH);
501 TU.AdditionalFiles[
"header.hpp"] = std::string(Header);
502 TU.ExtraArgs.push_back(
"--std=c++20");
503 EXPECT_THAT(TU.build().getDiagnostics(), ElementsAre(hasRange(Main.range())));
506TEST(DiagnosticTest, MakeShared) {
510 struct S { S(char*); };
511 auto x = std::[[make_shared]]<S>(42); // error-ok
514 TU.HeaderCode = R"cpp(
516 // These mocks aren't quite right - we omit shared_ptr for simplicity.
517 // forward is included to show its body is not needed to get the diagnostic.
518 template <typename T> T&& forward(T& t);
519 template <typename T, typename... A> T* make_shared(A&&... args) {
520 return new T(std::forward<A>(args)...);
524 TU.ParseOpts.PreambleParseForwardingFunctions = true;
525 EXPECT_THAT(TU.build().getDiagnostics(),
526 UnorderedElementsAre(
529 "no matching constructor for initialization of 'S'")));
532TEST(DiagnosticTest, NoMultipleDiagnosticInFlight) {
534 template <typename T> struct Foo {
544 Foo<LabelInfo> label_info_map;
545 [[for]] (auto it = label_info_map.begin(); it != label_info_map.end(); ++it) {
551 TU.ClangTidyProvider = addTidyChecks("modernize-loop-convert");
553 TU.build().getDiagnostics(),
554 ifTidyChecks(UnorderedElementsAre(::testing::AllOf(
555 Diag(Main.range(),
"use range-based for loop instead"),
559TEST(DiagnosticTest, RespectsDiagnosticConfig) {
569 TU.build().getDiagnostics(),
570 ElementsAre(
Diag(Main.range(),
"use of undeclared identifier 'unknown'"),
571 Diag(Main.range(
"ret"),
572 "void function 'x' should not return a value")));
576 EXPECT_THAT(TU.build().getDiagnostics(),
577 ElementsAre(
Diag(Main.range(),
578 "use of undeclared identifier 'unknown'")));
581TEST(DiagnosticTest, RespectsDiagnosticConfigInHeader) {
583 int x = "42"; // error-ok
586 #include "header.hpp"
589 TU.AdditionalFiles[
"header.hpp"] = std::string(Header.code());
593 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
596TEST(DiagnosticTest, ClangTidySuppressionComment) {
600 double d = 8 / i; // NOLINT
604 double f = BAD; // NOLINT
605 double g = [[8]] / i;
607 double h = BAD2; // NOLINT
613 // verify no crashes on unmatched nolints.
618 TU.ClangTidyProvider = addTidyChecks("bugprone-integer-division");
620 TU.build().getDiagnostics(),
621 ifTidyChecks(UnorderedElementsAre(::testing::AllOf(
622 Diag(Main.range(),
"result of integer division used in a floating "
623 "point context; possible loss of precision"),
625 diagName(
"bugprone-integer-division")))));
628TEST(DiagnosticTest, ClangTidySystemMacro) {
633 double x = $inline[[8]] / i;
634 double y = $user[[DIVIDE_USER]](i);
635 double z = DIVIDE_SYS(i);
639 TU.AdditionalFiles[
"user.h"] = R
"cpp(
640 #define DIVIDE_USER(Y) 8/Y
642 TU.AdditionalFiles["system.h"] = R
"cpp(
643 #pragma clang system_header
644 #define DIVIDE_SYS(Y) 8/Y
647 TU.ClangTidyProvider = addTidyChecks("bugprone-integer-division");
648 std::string BadDivision =
"result of integer division used in a floating "
649 "point context; possible loss of precision";
653 EXPECT_THAT(TU.build().getDiagnostics(),
655 UnorderedElementsAre(
Diag(Main.range(
"inline"), BadDivision),
656 Diag(Main.range(
"user"), BadDivision))));
659TEST(DiagnosticTest, ClangTidyWarningAsError) {
663 double f = [[8]] / i; // error-ok
667 TU.ClangTidyProvider =
668 addTidyChecks("bugprone-integer-division",
"bugprone-integer-division");
670 TU.build().getDiagnostics(),
671 ifTidyChecks(UnorderedElementsAre(::testing::AllOf(
672 Diag(Main.range(),
"result of integer division used in a floating "
673 "point context; possible loss of precision"),
675 diagSeverity(DiagnosticsEngine::Error)))));
678TidyProvider addClangArgs(std::vector<llvm::StringRef> ExtraArgs,
680 return [ExtraArgs = std::move(ExtraArgs),
Checks =
Checks.str()](
681 tidy::ClangTidyOptions &Opts, llvm::StringRef) {
683 Opts.ExtraArgs.emplace();
684 for (llvm::StringRef Arg : ExtraArgs)
685 Opts.ExtraArgs->emplace_back(Arg);
691TEST(DiagnosticTest, ClangTidyEnablesClangWarning) {
693 static void [[foo]]() {}
697 auto UnusedFooWarning =
698 AllOf(
Diag(Main.range(),
"unused function 'foo'"),
699 diagName(
"-Wunused-function"), diagSource(
Diag::Clang),
700 diagSeverity(DiagnosticsEngine::Warning));
703 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
707 TU.ClangTidyProvider =
708 addClangArgs({
"-Wunused"},
"clang-diagnostic-unused-function");
709 EXPECT_THAT(TU.build().getDiagnostics(), ElementsAre(UnusedFooWarning));
712 TU.ClangTidyProvider = addClangArgs({
"-Wunused"},
"clang-diagnostic-*");
713 EXPECT_THAT(TU.build().getDiagnostics(), ElementsAre(UnusedFooWarning));
715 TU.ClangTidyProvider = addClangArgs({
"-Wunused"},
"*");
716 EXPECT_THAT(TU.build().getDiagnostics(), Contains(UnusedFooWarning));
718 TU.ClangTidyProvider = addClangArgs(
719 {
"-Wunused"},
"clang-diagnostic-*,-clang-diagnostic-unused-function");
720 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
723 TU.ClangTidyProvider = addClangArgs({
"-Wunused"},
"clang-diagnostic-unused");
724 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
727 TU.ClangTidyProvider = addClangArgs({
"-Wunused",
"-Dfoo=bar"},
728 "clang-diagnostic-unused-function");
729 EXPECT_THAT(TU.build().getDiagnostics(), ElementsAre(UnusedFooWarning))
730 <<
"Not unused function 'bar'!";
733 TU.ExtraArgs = {
"-Werror"};
734 TU.ClangTidyProvider =
735 addClangArgs({
"-Wunused"},
"clang-diagnostic-unused-function");
736 EXPECT_THAT(TU.build().getDiagnostics(),
737 ElementsAre(diagSeverity(DiagnosticsEngine::Warning)));
740 TU.ExtraArgs = {
"-Wunused",
"-Werror"};
741 TU.ClangTidyProvider =
742 addClangArgs({
"-Wunused"},
"clang-diagnostic-unused-function");
743 EXPECT_THAT(TU.build().getDiagnostics(),
744 ElementsAre(diagSeverity(DiagnosticsEngine::Error)));
747 TU.ExtraArgs = {
"-Wunused-function",
"-Werror"};
748 TU.ClangTidyProvider =
749 addClangArgs({
"-Wunused"},
"clang-diagnostic-unused-label");
750 EXPECT_THAT(TU.build().getDiagnostics(),
751 ElementsAre(diagSeverity(DiagnosticsEngine::Warning)));
756 TU.ClangTidyProvider = addClangArgs({
"-Wunused",
"-Wno-unused"},
757 "clang-diagnostic-unused-function");
758 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
761 TU.ClangTidyProvider =
762 addClangArgs({
"-Wunused"}, {
"clang-diagnostic-unused-function"});
763 EXPECT_THAT(TU.build().getDiagnostics(), SizeIs(1));
766 TU.ClangTidyProvider = addClangArgs({
"-Wunused",
"-Wno-unused-function"},
767 {
"clang-diagnostic-unused-function"});
768 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
769 TU.ClangTidyProvider = addClangArgs({
"-Wunused-function",
"-Wno-unused"},
770 {
"clang-diagnostic-unused-function"});
771 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
775 TU.ExtraArgs = {
"-Wunused"};
776 TU.ClangTidyProvider = addClangArgs({
"-Wno-unused"}, {});
777 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
779 TU.ExtraArgs = {
"-Wno-unused"};
780 TU.ClangTidyProvider = addClangArgs({
"-Wunused"}, {
"-*, clang-diagnostic-*"});
781 EXPECT_THAT(TU.build().getDiagnostics(), SizeIs(1));
784TEST(DiagnosticTest, LongFixMessages) {
789 int somereallyreallyreallyreallyreallyreallyreallyreallylongidentifier;
790 [[omereallyreallyreallyreallyreallyreallyreallyreallylongidentifier]]= 10;
795 TU.build().getDiagnostics(),
796 ElementsAre(withFix(Fix(
798 "somereallyreallyreallyreallyreallyreallyreallyreallylongidentifier",
799 "change 'omereallyreallyreallyreallyreallyreallyreallyreall…' to "
800 "'somereallyreallyreallyreallyreallyreallyreallyreal…'"))));
810 TU.Code = std::string(Source.code());
811 EXPECT_THAT(TU.build().getDiagnostics(),
813 Fix(Source.range(), "ident",
"change 'ide\\…' to 'ident'"))));
816TEST(DiagnosticTest, NewLineFixMessage) {
819 TU.ExtraArgs = {
"-Wnewline-eof"};
821 TU.build().getDiagnostics(),
822 ElementsAre(withFix((
Fix(Source.range(),
"\n",
"insert '\\n'")))));
825TEST(DiagnosticTest, ClangTidySuppressionCommentTrumpsWarningAsError) {
829 double f = [[8]] / i; // NOLINT
833 TU.ClangTidyProvider =
834 addTidyChecks("bugprone-integer-division",
"bugprone-integer-division");
835 EXPECT_THAT(TU.build().getDiagnostics(), UnorderedElementsAre());
838TEST(DiagnosticTest, ClangTidyNoLiteralDataInMacroToken) {
841 using pthread_t = int;
842 int pthread_kill(pthread_t thread, int sig);
845 return pthread_kill(thread, 0);
849 TU.ClangTidyProvider = addTidyChecks("bugprone-bad-signal-to-kill-thread");
850 EXPECT_THAT(TU.build().getDiagnostics(), UnorderedElementsAre());
853TEST(DiagnosticTest, BadSignalToKillThreadInPreamble) {
856 using pthread_t = int;
857 int pthread_kill(pthread_t thread, int sig);
860 return pthread_kill(thread, 15);
864 TU.HeaderFilename = "signal.h";
865 TU.HeaderCode =
"#define SIGTERM 15";
866 TU.ClangTidyProvider =
addTidyChecks(
"bugprone-bad-signal-to-kill-thread");
867 EXPECT_THAT(TU.build().getDiagnostics(),
868 ifTidyChecks(UnorderedElementsAre(
869 diagName(
"bugprone-bad-signal-to-kill-thread"))));
872TEST(DiagnosticTest, ClangTidyMacroToEnumCheck) {
879 std::vector<TidyProvider> Providers;
881 addTidyChecks("cppcoreguidelines-macro-to-enum,modernize-macro-to-enum"));
883 TU.ClangTidyProvider =
combine(std::move(Providers));
884 EXPECT_THAT(TU.build().getDiagnostics(), UnorderedElementsAre());
887TEST(DiagnosticTest, ElseAfterReturnRange) {
892 } [[else]] if (cond == 2) {
899 TU.ClangTidyProvider = addTidyChecks("llvm-else-after-return");
900 EXPECT_THAT(TU.build().getDiagnostics(),
901 ifTidyChecks(ElementsAre(
902 Diag(Main.range(),
"do not use 'else' after 'return'"))));
905TEST(DiagnosticTest, ClangTidySelfContainedDiags) {
914 void InitVariables() {
915 float $C[[C]]$CFix[[]];
916 double $D[[D]]$DFix[[]];
920 TU.ClangTidyProvider =
922 "cppcoreguidelines-init-variables");
923 clangd::Fix ExpectedAFix;
924 ExpectedAFix.Message =
925 "'A' should be initialized in a member initializer of the constructor";
926 ExpectedAFix.Edits.push_back(
TextEdit{Main.range(
"Fix"),
" : A(1)"});
927 ExpectedAFix.Edits.push_back(
TextEdit{Main.range(
"A"),
""});
932 clangd::Fix ExpectedBFix;
933 ExpectedBFix.Message =
934 "'B' should be initialized in a member initializer of the constructor";
935 ExpectedBFix.Edits.push_back(
TextEdit{Main.range(
"Fix"),
" : B(1)"});
936 ExpectedBFix.Edits.push_back(
TextEdit{Main.range(
"B"),
""});
938 clangd::Fix ExpectedCFix;
939 ExpectedCFix.Message =
"variable 'C' is not initialized";
940 ExpectedCFix.Edits.push_back(
941 TextEdit{Main.range(
"MathHeader"),
"#include <math.h>\n\n"});
942 ExpectedCFix.Edits.push_back(
TextEdit{Main.range(
"CFix"),
" = NAN"});
946 clangd::Fix ExpectedDFix;
947 ExpectedDFix.Message =
"variable 'D' is not initialized";
948 ExpectedDFix.Edits.push_back(
949 TextEdit{Main.range(
"MathHeader"),
"#include <math.h>\n\n"});
950 ExpectedDFix.Edits.push_back(
TextEdit{Main.range(
"DFix"),
" = NAN"});
952 TU.build().getDiagnostics(),
953 ifTidyChecks(UnorderedElementsAre(
954 AllOf(
Diag(Main.range(
"A"),
"'A' should be initialized in a member "
955 "initializer of the constructor"),
956 withFix(equalToFix(ExpectedAFix))),
957 AllOf(
Diag(Main.range(
"B"),
"'B' should be initialized in a member "
958 "initializer of the constructor"),
959 withFix(equalToFix(ExpectedBFix))),
960 AllOf(
Diag(Main.range(
"C"),
"variable 'C' is not initialized"),
961 withFix(equalToFix(ExpectedCFix))),
962 AllOf(
Diag(Main.range(
"D"),
"variable 'D' is not initialized"),
963 withFix(equalToFix(ExpectedDFix))))));
966TEST(DiagnosticTest, ClangTidySelfContainedDiagsFormatting) {
970 virtual void Reset1() = 0;
971 virtual void Reset2() = 0;
973 class A : public Interface {
974 // This will be marked by clangd to use override instead of virtual
975 $virtual1[[virtual ]]void $Reset1[[Reset1]]()$override1[[]];
976 $virtual2[[virtual ]]/**/void $Reset2[[Reset2]]()$override2[[]];
980 TU.ClangTidyProvider =
981 addTidyChecks("cppcoreguidelines-explicit-virtual-functions,");
982 clangd::Fix
const ExpectedFix1{
983 "prefer using 'override' or (rarely) 'final' "
984 "instead of 'virtual'",
985 {
TextEdit{Main.range(
"virtual1"),
""},
986 TextEdit{Main.range(
"override1"),
" override"}},
988 clangd::Fix
const ExpectedFix2{
989 "prefer using 'override' or (rarely) 'final' "
990 "instead of 'virtual'",
991 {
TextEdit{Main.range(
"virtual2"),
""},
992 TextEdit{Main.range(
"override2"),
" override"}},
996 EXPECT_THAT(TU.build().getDiagnostics(),
997 ifTidyChecks(UnorderedElementsAre(
998 AllOf(
Diag(Main.range(
"Reset1"),
999 "prefer using 'override' or (rarely) 'final' "
1000 "instead of 'virtual'"),
1001 withFix(equalToFix(ExpectedFix1))),
1002 AllOf(
Diag(Main.range(
"Reset2"),
1003 "prefer using 'override' or (rarely) 'final' "
1004 "instead of 'virtual'"),
1005 withFix(equalToFix(ExpectedFix2))))));
1008TEST(DiagnosticsTest, ClangTidyCallingIntoPreprocessor) {
1009 std::string Main = R
"cpp(
1014 std::string Header = R"cpp(
1015 #define EXTERN extern
1019 TU.AdditionalFiles[
"b.h"] = Header;
1020 TU.ClangTidyProvider =
addTidyChecks(
"modernize-use-trailing-return-type");
1025TEST(DiagnosticsTest, Preprocessor) {
1034 int a = [[b]]; // error-ok
1041 ElementsAre(Diag(Test.range(), "use of undeclared identifier 'b'")));
1044TEST(DiagnosticsTest, IgnoreVerify) {
1046 int a; // expected-error {{}}
1048 TU.ExtraArgs.push_back("-Xclang");
1049 TU.ExtraArgs.push_back(
"-verify");
1050 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
1053TEST(DiagnosticTest, IgnoreBEFilelistOptions) {
1055 TU.ExtraArgs.push_back(
"-Xclang");
1056 for (
const auto *DisableOption :
1057 {
"-fsanitize-ignorelist=null",
"-fprofile-list=null",
1058 "-fxray-always-instrument=null",
"-fxray-never-instrument=null",
1059 "-fxray-attr-list=null"}) {
1060 TU.ExtraArgs.push_back(DisableOption);
1061 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
1062 TU.ExtraArgs.pop_back();
1067TEST(DiagnosticsTest, RecursivePreamble) {
1069 #include "foo.h" // error-ok
1072 TU.Filename = "foo.h";
1073 EXPECT_THAT(TU.build().getDiagnostics(),
1074 ElementsAre(diagName(
"pp_including_mainfile_in_preamble")));
1075 EXPECT_THAT(TU.build().getLocalTopLevelDecls(), SizeIs(1));
1079TEST(DiagnosticsTest, RecursivePreamblePragmaOnce) {
1085 TU.Filename = "foo.h";
1086 EXPECT_THAT(TU.build().getDiagnostics(),
1087 Not(Contains(diagName(
"pp_including_mainfile_in_preamble"))));
1088 EXPECT_THAT(TU.build().getLocalTopLevelDecls(), SizeIs(1));
1093TEST(DiagnosticsTest, RecursivePreambleIfndefGuard) {
1097 #include "foo.h" // error-ok
1101 TU.Filename = "foo.h";
1103 EXPECT_THAT(TU.build().getDiagnostics(),
1104 ElementsAre(diagName(
"pp_including_mainfile_in_preamble")));
1105 EXPECT_THAT(TU.build().getLocalTopLevelDecls(), SizeIs(1));
1108TEST(DiagnosticsTest, PreambleWithPragmaAssumeNonnull) {
1110#pragma clang assume_nonnull begin
1112#pragma clang assume_nonnull end
1114 auto AST = TU.build();
1115 EXPECT_THAT(
AST.getDiagnostics(), IsEmpty());
1116 const auto *
X = cast<FunctionDecl>(
findDecl(
AST,
"foo")).getParamDecl(0);
1117 ASSERT_TRUE(
X->getOriginalType()->getNullability() ==
1118 NullabilityKind::NonNull);
1121TEST(DiagnosticsTest, PreamblePragmaDiagnosticPushPop) {
1123#pragma clang diagnostic push
1127#pragma clang diagnostic pop
1129 auto AST = TU.build();
1130 EXPECT_THAT(
AST.getDiagnostics(), IsEmpty());
1133TEST(DiagnosticsTest, PreambleHeaderWithBadPragmaAssumeNonnull) {
1135#pragma clang assume_nonnull begin // error-ok
1139#include "foo.h" // unterminated assume_nonnull should not affect bar.
1142 TU.AdditionalFiles = {{"foo.h", std::string(Header.code())}};
1143 auto AST = TU.build();
1144 EXPECT_THAT(
AST.getDiagnostics(),
1145 ElementsAre(diagName(
"pp_eof_in_assume_nonnull")));
1146 const auto *
X = cast<FunctionDecl>(
findDecl(
AST,
"foo")).getParamDecl(0);
1147 ASSERT_TRUE(
X->getOriginalType()->getNullability() ==
1148 NullabilityKind::NonNull);
1149 const auto *Y = cast<FunctionDecl>(
findDecl(
AST,
"bar")).getParamDecl(0);
1150 ASSERT_FALSE(Y->getOriginalType()->getNullability());
1153TEST(DiagnosticsTest, InsideMacros) {
1156 #define RET(x) return x + 10
1159 RET($foo[[0]]); // error-ok
1166 ElementsAre(Diag(Test.range("foo"),
1167 "cannot initialize return object of type "
1168 "'int *' with an rvalue of type 'int'"),
1169 Diag(Test.range(
"bar"),
1170 "cannot initialize return object of type "
1171 "'int *' with an rvalue of type 'int'")));
1174TEST(DiagnosticsTest, NoFixItInMacro) {
1176 #define Define(name) void name() {}
1178 [[Define]](main) // error-ok
1181 EXPECT_THAT(TU.build().getDiagnostics(),
1182 ElementsAre(AllOf(
Diag(Test.range(),
"'main' must return 'int'"),
1186TEST(DiagnosticsTest, PragmaSystemHeader) {
1187 Annotations Test(
"#pragma clang [[system_header]]\n");
1190 TU.build().getDiagnostics(),
1192 Diag(Test.range(),
"#pragma system_header ignored in main file"))));
1193 TU.Filename =
"TestTU.h";
1194 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
1197TEST(ClangdTest, MSAsm) {
1200 llvm::InitializeAllTargetInfos();
1202 TU.ExtraArgs = {
"-fms-extensions"};
1203 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
1206TEST(DiagnosticsTest, ToLSP) {
1213 D.ID = clang::diag::err_undeclared_var_use;
1215 D.Name =
"undeclared_var_use";
1217 D.Message =
"something terrible happened";
1218 D.Range = {pos(1, 2), pos(3, 4)};
1219 D.InsideMainFile =
true;
1220 D.Severity = DiagnosticsEngine::Error;
1221 D.File =
"foo/bar/main.cpp";
1222 D.AbsFile = std::string(MainFile.file());
1223 D.OpaqueData[
"test"] =
"bar";
1225 clangd::Note NoteInMain;
1226 NoteInMain.Message =
"declared somewhere in the main file";
1227 NoteInMain.Range = {pos(5, 6), pos(7, 8)};
1228 NoteInMain.Severity = DiagnosticsEngine::Remark;
1229 NoteInMain.File =
"../foo/bar/main.cpp";
1230 NoteInMain.InsideMainFile =
true;
1231 NoteInMain.AbsFile = std::string(MainFile.file());
1233 D.Notes.push_back(NoteInMain);
1235 clangd::Note NoteInHeader;
1236 NoteInHeader.Message =
"declared somewhere in the header file";
1237 NoteInHeader.Range = {pos(9, 10), pos(11, 12)};
1238 NoteInHeader.Severity = DiagnosticsEngine::Note;
1239 NoteInHeader.File =
"../foo/baz/header.h";
1240 NoteInHeader.InsideMainFile =
false;
1241 NoteInHeader.AbsFile = std::string(
HeaderFile.file());
1242 D.Notes.push_back(NoteInHeader);
1245 F.Message =
"do something";
1246 D.Fixes.push_back(F);
1249 clangd::Diagnostic MainLSP;
1250 MainLSP.range =
D.Range;
1251 MainLSP.severity =
getSeverity(DiagnosticsEngine::Error);
1252 MainLSP.code =
"undeclared_var_use";
1253 MainLSP.source =
"clang";
1255 R
"(Something terrible happened (fix available)
1257main.cpp:6:7: remark: declared somewhere in the main file
1259../foo/baz/header.h:10:11:
1260note: declared somewhere in the header file)";
1262 MainLSP.data = D.OpaqueData;
1264 clangd::Diagnostic NoteInMainLSP;
1265 NoteInMainLSP.range = NoteInMain.Range;
1266 NoteInMainLSP.severity = getSeverity(DiagnosticsEngine::Remark);
1267 NoteInMainLSP.message = R"(Declared somewhere in the main file
1269main.cpp:2:3: error: something terrible happened)";
1273 std::vector<std::pair<clangd::Diagnostic, std::vector<clangd::Fix>>> LSPDiags;
1275 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix> Fixes) {
1277 {std::move(LSPDiag),
1278 std::vector<clangd::Fix>(Fixes.begin(), Fixes.end())});
1283 ElementsAre(Pair(equalToLSPDiag(MainLSP), ElementsAre(equalToFix(F))),
1284 Pair(equalToLSPDiag(NoteInMainLSP), IsEmpty())));
1285 EXPECT_EQ(LSPDiags[0].first.code,
"undeclared_var_use");
1286 EXPECT_EQ(LSPDiags[0].first.source,
"clang");
1287 EXPECT_EQ(LSPDiags[1].first.code,
"");
1288 EXPECT_EQ(LSPDiags[1].first.source,
"");
1292 Opts.EmitRelatedLocations =
true;
1294 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix> Fixes) {
1296 {std::move(LSPDiag),
1297 std::vector<clangd::Fix>(Fixes.begin(), Fixes.end())});
1299 MainLSP.message =
"Something terrible happened (fix available)";
1301 NoteInMainDRI.
message =
"Declared somewhere in the main file";
1302 NoteInMainDRI.location.range = NoteInMain.Range;
1303 NoteInMainDRI.location.uri = MainFile;
1304 MainLSP.relatedInformation = {NoteInMainDRI};
1306 NoteInHeaderDRI.
message =
"Declared somewhere in the header file";
1307 NoteInHeaderDRI.location.range = NoteInHeader.Range;
1309 MainLSP.relatedInformation = {NoteInMainDRI, NoteInHeaderDRI};
1310 EXPECT_THAT(LSPDiags, ElementsAre(Pair(equalToLSPDiag(MainLSP),
1311 ElementsAre(equalToFix(F)))));
1314struct SymbolWithHeader {
1316 std::string DeclaringFile;
1317 std::string IncludeHeader;
1320std::unique_ptr<SymbolIndex>
1321buildIndexWithSymbol(llvm::ArrayRef<SymbolWithHeader> Syms) {
1323 for (
const auto &S : Syms) {
1326 Sym.CanonicalDeclaration.FileURI = S.DeclaringFile.c_str();
1327 Sym.Definition.FileURI = S.DeclaringFile.c_str();
1334TEST(IncludeFixerTest, IncompleteType) {
1336 TU.ExtraArgs.push_back(
"-std=c++20");
1337 auto Index = buildIndexWithSymbol(
1338 {SymbolWithHeader{
"ns::X",
"unittest:///x.h",
"\"x.h\""}});
1339 TU.ExternalIndex = Index.get();
1341 std::vector<std::pair<llvm::StringRef, llvm::StringRef>> Tests{
1342 {
"incomplete_nested_name_spec",
"[[ns::X::]]Nested n;"},
1343 {
"incomplete_base_class",
"class Y : [[ns::X]] {};"},
1344 {
"incomplete_member_access",
"auto i = x[[->]]f();"},
1345 {
"incomplete_type",
"auto& [[[]]m] = *x;"},
1346 {
"init_incomplete_type",
1347 "struct C { static int f(ns::X&); }; int i = C::f([[{]]});"},
1348 {
"bad_cast_incomplete",
"auto a = [[static_cast]]<ns::X>(0);"},
1349 {
"template_nontype_parm_incomplete",
"template <ns::X [[foo]]> int a;"},
1350 {
"typecheck_decl_incomplete_type",
"ns::X [[var]];"},
1351 {
"typecheck_incomplete_tag",
"auto i = [[(*x)]]->f();"},
1352 {
"typecheck_nonviable_condition_incomplete",
1353 "struct A { operator ns::X(); } a; const ns::X &[[b]] = a;"},
1354 {
"invalid_incomplete_type_use",
"auto var = [[ns::X()]];"},
1355 {
"sizeof_alignof_incomplete_or_sizeless_type",
1356 "auto s = [[sizeof]](ns::X);"},
1357 {
"for_range_incomplete_type",
"void foo() { for (auto i : [[*]]x ) {} }"},
1358 {
"func_def_incomplete_result",
"ns::X [[func]] () {}"},
1359 {
"field_incomplete_or_sizeless",
"class M { ns::X [[member]]; };"},
1360 {
"array_incomplete_or_sizeless_type",
"auto s = [[(ns::X[]){}]];"},
1361 {
"call_incomplete_return",
"ns::X f(); auto fp = &f; auto z = [[fp()]];"},
1362 {
"call_function_incomplete_return",
"ns::X foo(); auto a = [[foo()]];"},
1363 {
"call_incomplete_argument",
"int m(ns::X); int i = m([[*x]]);"},
1364 {
"switch_incomplete_class_type",
"void a() { [[switch]](*x) {} }"},
1365 {
"delete_incomplete_class_type",
"void f() { [[delete]] *x; }"},
1366 {
"-Wdelete-incomplete",
"void f() { [[delete]] x; }"},
1367 {
"dereference_incomplete_type",
1368 R
"cpp(void f() { asm("" : "=r"([[*]]x)::); })cpp"},
1370 for (
auto Case : Tests) {
1372 TU.Code = Main.code().str() +
"\n // error-ok";
1374 TU.build().getDiagnostics(),
1375 ElementsAre(AllOf(diagName(Case.first), hasRange(Main.range()),
1376 withFix(
Fix(
Range{},
"#include \"x.h\"\n",
1377 "Include \"x.h\" for symbol ns::X")))))
1382TEST(IncludeFixerTest, IncompleteEnum) {
1385 Sym.CanonicalDeclaration.FileURI = Sym.Definition.FileURI =
"unittest:///x.h";
1394 TU.ExtraArgs.push_back(
"-std=c++20");
1395 TU.ExtraArgs.push_back(
"-fno-ms-compatibility");
1397 std::vector<std::pair<llvm::StringRef, llvm::StringRef>> Tests{
1398 {
"incomplete_enum",
"enum class X : int; using enum [[X]];"},
1399 {
"underlying_type_of_incomplete_enum",
1400 "[[__underlying_type]](enum X) i;"},
1402 for (
auto Case : Tests) {
1404 TU.Code = Main.code().str() +
"\n // error-ok";
1405 EXPECT_THAT(TU.build().getDiagnostics(),
1406 Contains(AllOf(diagName(Case.first), hasRange(Main.range()),
1407 withFix(
Fix(
Range{},
"#include \"x.h\"\n",
1408 "Include \"x.h\" for symbol X")))))
1413TEST(IncludeFixerTest, NoSuggestIncludeWhenNoDefinitionInHeader) {
1415$insert[[]]namespace ns {
1418class Y : $base[[public ns::X]] {};
1427 Sym.CanonicalDeclaration.FileURI =
"unittest:///x.h";
1428 Sym.Definition.FileURI =
"unittest:///x.cc";
1435 TU.ExternalIndex = Index.get();
1437 EXPECT_THAT(TU.build().getDiagnostics(),
1438 UnorderedElementsAre(
1439 Diag(Test.range(
"base"),
"base class has incomplete type"),
1440 Diag(Test.range(
"access"),
1441 "member access into incomplete type 'ns::X'")));
1444TEST(IncludeFixerTest, Typo) {
1446$insert[[]]namespace ns {
1448 $unqualified1[[X]] x;
1449 // No fix if the unresolved type is used as specifier. (ns::)X::Nested will be
1450 // considered the unresolved type.
1451 $unqualified2[[X]]::Nested n;
1453struct S : $base[[X]] {};
1456 ns::$qualified1[[X]] x; // ns:: is valid.
1457 ns::$qualified2[[X]](); // Error: no member in namespace
1459 ::$global[[Global]] glob;
1461using Type = ns::$template[[Foo]]<int>;
1464 auto Index = buildIndexWithSymbol(
1465 {SymbolWithHeader{
"ns::X",
"unittest:///x.h",
"\"x.h\""},
1466 SymbolWithHeader{
"Global",
"unittest:///global.h",
"\"global.h\""},
1467 SymbolWithHeader{
"ns::Foo",
"unittest:///foo.h",
"\"foo.h\""}});
1468 TU.ExternalIndex = Index.get();
1471 TU.build().getDiagnostics(),
1472 UnorderedElementsAre(
1473 AllOf(
Diag(Test.range(
"unqualified1"),
"unknown type name 'X'"),
1474 diagName(
"unknown_typename"),
1475 withFix(
Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
1476 "Include \"x.h\" for symbol ns::X"))),
1477 Diag(Test.range(
"unqualified2"),
"use of undeclared identifier 'X'"),
1478 AllOf(
Diag(Test.range(
"qualified1"),
1479 "no type named 'X' in namespace 'ns'"),
1480 diagName(
"typename_nested_not_found"),
1481 withFix(
Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
1482 "Include \"x.h\" for symbol ns::X"))),
1483 AllOf(
Diag(Test.range(
"qualified2"),
1484 "no member named 'X' in namespace 'ns'"),
1485 diagName(
"no_member"),
1486 withFix(
Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
1487 "Include \"x.h\" for symbol ns::X"))),
1488 AllOf(
Diag(Test.range(
"global"),
1489 "no type named 'Global' in the global namespace"),
1490 diagName(
"typename_nested_not_found"),
1491 withFix(
Fix(Test.range(
"insert"),
"#include \"global.h\"\n",
1492 "Include \"global.h\" for symbol Global"))),
1493 AllOf(
Diag(Test.range(
"template"),
1494 "no template named 'Foo' in namespace 'ns'"),
1495 diagName(
"no_member_template"),
1496 withFix(
Fix(Test.range(
"insert"),
"#include \"foo.h\"\n",
1497 "Include \"foo.h\" for symbol ns::Foo"))),
1498 AllOf(
Diag(Test.range(
"base"),
"expected class name"),
1499 diagName(
"expected_class_name"),
1500 withFix(
Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
1501 "Include \"x.h\" for symbol ns::X")))));
1504TEST(IncludeFixerTest, TypoInMacro) {
1515 auto Index = buildIndexWithSymbol(
1516 {SymbolWithHeader{
"X",
"unittest:///x.h",
"\"x.h\""},
1517 SymbolWithHeader{
"ns::X",
"unittest:///ns.h",
"\"x.h\""}});
1518 TU.ExternalIndex = Index.get();
1521 TU.ExtraArgs = {
"-fno-ms-compatibility"};
1522 EXPECT_THAT(TU.build().getDiagnostics(), Each(withFix(_)));
1525TEST(IncludeFixerTest, MultipleMatchedSymbols) {
1527$insert[[]]namespace na {
1530 $unqualified[[X]] x;
1536 auto Index = buildIndexWithSymbol(
1537 {SymbolWithHeader{
"na::X",
"unittest:///a.h",
"\"a.h\""},
1538 SymbolWithHeader{
"na::nb::X",
"unittest:///b.h",
"\"b.h\""}});
1539 TU.ExternalIndex = Index.get();
1541 EXPECT_THAT(TU.build().getDiagnostics(),
1542 UnorderedElementsAre(AllOf(
1543 Diag(Test.range(
"unqualified"),
"unknown type name 'X'"),
1544 diagName(
"unknown_typename"),
1545 withFix(
Fix(Test.range(
"insert"),
"#include \"a.h\"\n",
1546 "Include \"a.h\" for symbol na::X"),
1547 Fix(Test.range(
"insert"),
"#include \"b.h\"\n",
1548 "Include \"b.h\" for symbol na::nb::X")))));
1551TEST(IncludeFixerTest, NoCrashMemberAccess) {
1553 struct X { int xyz; };
1554 void g() { X x; x.$[[xy]]; }
1557 auto Index = buildIndexWithSymbol(
1558 SymbolWithHeader{
"na::X",
"unittest:///a.h",
"\"a.h\""});
1559 TU.ExternalIndex = Index.get();
1562 TU.build().getDiagnostics(),
1563 UnorderedElementsAre(
Diag(Test.range(),
"no member named 'xy' in 'X'")));
1566TEST(IncludeFixerTest, NoCrashOnQualifiedConversionFunctionName) {
1569void f() { operator new[](0, operator::align_val_t{}); }
1571 TU.ExtraArgs.push_back("-std=c++17");
1572 auto Index = buildIndexWithSymbol(
1573 SymbolWithHeader{
"std::align_val_t",
"unittest:///new.h",
"<new>"});
1574 TU.ExternalIndex = Index.get();
1576 EXPECT_THAT(TU.build().getDiagnostics(), Not(IsEmpty()));
1579TEST(IncludeFixerTest, FixConversionFunctionTargetType) {
1581$insert[[]]struct Wrapper {
1582 template <typename T>
1583 operator T() const { return T(); }
1588 auto V = W.operator::$target[[Something]]();
1592 auto Index = buildIndexWithSymbol(
1593 SymbolWithHeader{
"Something",
"unittest:///test.h",
"\"test.h\""});
1594 TU.ExternalIndex = Index.get();
1596 EXPECT_THAT(TU.build().getDiagnostics(),
1599 Contains(
Fix(Test.range(
"insert"),
"#include \"test.h\"\n",
1600 "Include \"test.h\" for symbol Something")))));
1603TEST(IncludeFixerTest, UseCachedIndexResults) {
1607$insert[[]]void foo() {
1630 buildIndexWithSymbol(SymbolWithHeader{
"X",
"unittest:///a.h",
"\"a.h\""});
1631 TU.ExternalIndex = Index.get();
1633 auto Parsed = TU.build();
1634 for (
const auto &D : Parsed.getDiagnostics()) {
1635 if (
D.Fixes.size() != 1) {
1636 ADD_FAILURE() <<
"D.Fixes.size() != 1";
1639 EXPECT_EQ(
D.Fixes[0].Message, std::string(
"Include \"a.h\" for symbol X"));
1643TEST(IncludeFixerTest, UnresolvedNameAsSpecifier) {
1645$insert[[]]namespace ns {
1647void g() { ns::$[[scope]]::X_Y(); }
1650 TU.Code = std::string(Test.code());
1652 TU.ExtraArgs.push_back(
"-fno-ms-compatibility");
1653 auto Index = buildIndexWithSymbol(
1654 SymbolWithHeader{
"ns::scope::X_Y",
"unittest:///x.h",
"\"x.h\""});
1655 TU.ExternalIndex = Index.get();
1658 TU.build().getDiagnostics(),
1659 UnorderedElementsAre(
1660 AllOf(
Diag(Test.range(),
"no member named 'scope' in namespace 'ns'"),
1661 diagName(
"no_member"),
1662 withFix(
Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
1663 "Include \"x.h\" for symbol ns::scope::X_Y")))));
1666TEST(IncludeFixerTest, UnresolvedSpecifierWithSemaCorrection) {
1668$insert[[]]namespace clang {
1670 // "clangd::" will be corrected to "clang::" by Sema.
1671 $q1[[clangd]]::$x[[X]] x;
1672 $q2[[clangd]]::$ns[[ns]]::Y y;
1677 TU.Code = std::string(Test.code());
1679 TU.ExtraArgs.push_back(
"-fno-ms-compatibility");
1680 auto Index = buildIndexWithSymbol(
1681 {SymbolWithHeader{
"clang::clangd::X",
"unittest:///x.h",
"\"x.h\""},
1682 SymbolWithHeader{
"clang::clangd::ns::Y",
"unittest:///y.h",
"\"y.h\""}});
1683 TU.ExternalIndex = Index.get();
1686 TU.build().getDiagnostics(),
1687 UnorderedElementsAre(
1688 AllOf(
Diag(Test.range(
"q1"),
"use of undeclared identifier 'clangd'; "
1689 "did you mean 'clang'?"),
1690 diagName(
"undeclared_var_use_suggest"),
1692 Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
1693 "Include \"x.h\" for symbol clang::clangd::X"))),
1694 AllOf(
Diag(Test.range(
"x"),
"no type named 'X' in namespace 'clang'"),
1695 diagName(
"typename_nested_not_found"),
1696 withFix(
Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
1697 "Include \"x.h\" for symbol clang::clangd::X"))),
1699 Diag(Test.range(
"q2"),
"use of undeclared identifier 'clangd'; "
1700 "did you mean 'clang'?"),
1701 diagName(
"undeclared_var_use_suggest"),
1703 Fix(Test.range(
"insert"),
"#include \"y.h\"\n",
1704 "Include \"y.h\" for symbol clang::clangd::ns::Y"))),
1705 AllOf(
Diag(Test.range(
"ns"),
1706 "no member named 'ns' in namespace 'clang'"),
1707 diagName(
"no_member"),
1709 Fix(Test.range(
"insert"),
"#include \"y.h\"\n",
1710 "Include \"y.h\" for symbol clang::clangd::ns::Y")))));
1713TEST(IncludeFixerTest, SpecifiedScopeIsNamespaceAlias) {
1715$insert[[]]namespace a {}
1722 auto Index = buildIndexWithSymbol(
1723 SymbolWithHeader{
"a::X",
"unittest:///x.h",
"\"x.h\""});
1724 TU.ExternalIndex = Index.get();
1726 EXPECT_THAT(TU.build().getDiagnostics(),
1727 UnorderedElementsAre(AllOf(
1728 Diag(Test.range(),
"no type named 'X' in namespace 'a'"),
1729 diagName(
"typename_nested_not_found"),
1730 withFix(
Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
1731 "Include \"x.h\" for symbol a::X")))));
1734TEST(IncludeFixerTest, NoCrashOnTemplateInstantiations) {
1736 template <typename T> struct Templ {
1737 template <typename U>
1738 typename U::type operator=(const U &);
1743 A() { [[a]]; /*error-ok*/ } // crash if we compute scopes lazily.
1748 auto Index = buildIndexWithSymbol({});
1749 TU.ExternalIndex = Index.get();
1752 TU.build().getDiagnostics(),
1753 ElementsAre(
Diag(Test.range(),
"use of undeclared identifier 'a'")));
1756TEST(IncludeFixerTest, HeaderNamedInDiag) {
1758 $insert[[]]int main() {
1763 TU.ExtraArgs = {
"-xc",
"-std=c99",
1764 "-Wno-error=implicit-function-declaration"};
1765 auto Index = buildIndexWithSymbol({});
1766 TU.ExternalIndex = Index.get();
1769 TU.build().getDiagnostics(),
1771 Diag(Test.range(),
"call to undeclared library function 'printf' "
1772 "with type 'int (const char *, ...)'; ISO C99 "
1773 "and later do not support implicit function "
1775 withFix(
Fix(Test.range(
"insert"),
"#include <stdio.h>\n",
1776 "Include <stdio.h> for symbol printf")))));
1778 TU.ExtraArgs = {
"-xc",
"-std=c89"};
1780 TU.build().getDiagnostics(),
1782 Diag(Test.range(),
"implicitly declaring library function 'printf' "
1783 "with type 'int (const char *, ...)'"),
1784 withFix(
Fix(Test.range(
"insert"),
"#include <stdio.h>\n",
1785 "Include <stdio.h> for symbol printf")))));
1788TEST(IncludeFixerTest, CImplicitFunctionDecl) {
1791 TU.Filename =
"test.c";
1792 TU.ExtraArgs = {
"-std=c99",
"-Wno-error=implicit-function-declaration"};
1796 Sym.CanonicalDeclaration.FileURI =
"unittest:///foo.h";
1803 TU.ExternalIndex = Index.get();
1806 TU.build().getDiagnostics(),
1809 "call to undeclared function 'foo'; ISO C99 and later do not "
1810 "support implicit function declarations"),
1811 withFix(
Fix(
Range{},
"#include \"foo.h\"\n",
1812 "Include \"foo.h\" for symbol foo")))));
1814 TU.ExtraArgs = {
"-std=c89",
"-Wall"};
1815 EXPECT_THAT(TU.build().getDiagnostics(),
1817 Diag(Test.range(),
"implicit declaration of function 'foo'"),
1818 withFix(
Fix(
Range{},
"#include \"foo.h\"\n",
1819 "Include \"foo.h\" for symbol foo")))));
1822TEST(DiagsInHeaders, DiagInsideHeader) {
1825 void foo() {})cpp");
1826 Annotations Header("[[no_type_spec]]; // error-ok");
1828 TU.AdditionalFiles = {{
"a.h", std::string(Header.code())}};
1829 EXPECT_THAT(TU.build().getDiagnostics(),
1830 UnorderedElementsAre(AllOf(
1831 Diag(Main.range(),
"in included file: a type specifier is "
1832 "required for all declarations"),
1833 withNote(
Diag(Header.range(),
"error occurred here")))));
1836TEST(DiagsInHeaders, DiagInTransitiveInclude) {
1839 void foo() {})cpp");
1841 TU.AdditionalFiles = {{"a.h",
"#include \"b.h\""},
1842 {
"b.h",
"no_type_spec; // error-ok"}};
1843 EXPECT_THAT(TU.build().getDiagnostics(),
1844 UnorderedElementsAre(
Diag(Main.range(),
1845 "in included file: a type specifier is "
1846 "required for all declarations")));
1849TEST(DiagsInHeaders, DiagInMultipleHeaders) {
1851 #include $a[["a.h"]]
1852 #include $b[["b.h"]]
1853 void foo() {})cpp");
1855 TU.AdditionalFiles = {{"a.h",
"no_type_spec; // error-ok"},
1856 {
"b.h",
"no_type_spec; // error-ok"}};
1857 EXPECT_THAT(TU.build().getDiagnostics(),
1858 UnorderedElementsAre(
1859 Diag(Main.range(
"a"),
"in included file: a type specifier is "
1860 "required for all declarations"),
1861 Diag(Main.range(
"b"),
"in included file: a type specifier is "
1862 "required for all declarations")));
1865TEST(DiagsInHeaders, PreferExpansionLocation) {
1869 void foo() {})cpp");
1871 TU.AdditionalFiles = {
1872 {"a.h",
"#include \"b.h\"\n"},
1873 {
"b.h",
"#ifndef X\n#define X\nno_type_spec; // error-ok\n#endif"}};
1874 EXPECT_THAT(TU.build().getDiagnostics(),
1875 Contains(
Diag(Main.range(),
"in included file: a type specifier "
1876 "is required for all declarations")));
1879TEST(DiagsInHeaders, PreferExpansionLocationMacros) {
1885 void foo() {})cpp");
1887 TU.AdditionalFiles = {
1888 {"a.h",
"#include \"c.h\"\n"},
1889 {
"b.h",
"#include \"c.h\"\n"},
1890 {
"c.h",
"#ifndef X\n#define X\nno_type_spec; // error-ok\n#endif"}};
1891 EXPECT_THAT(TU.build().getDiagnostics(),
1892 UnorderedElementsAre(
Diag(Main.range(),
1893 "in included file: a type specifier is "
1894 "required for all declarations")));
1897TEST(DiagsInHeaders, LimitDiagsOutsideMainFile) {
1901 void foo() {})cpp");
1903 TU.AdditionalFiles = {{"a.h",
"#include \"c.h\"\n"},
1904 {
"b.h",
"#include \"c.h\"\n"},
1908 no_type_spec_0; // error-ok
1920 EXPECT_THAT(TU.build().getDiagnostics(),
1921 UnorderedElementsAre(Diag(Main.range(),
1922 "in included file: a type specifier is "
1923 "required for all declarations")));
1926TEST(DiagsInHeaders, OnlyErrorOrFatal) {
1929 void foo() {})cpp");
1931 [[no_type_spec]]; // error-ok
1934 TU.AdditionalFiles = {{"a.h", std::string(Header.code())}};
1935 EXPECT_THAT(TU.build().getDiagnostics(),
1936 UnorderedElementsAre(AllOf(
1937 Diag(Main.range(),
"in included file: a type specifier is "
1938 "required for all declarations"),
1939 withNote(
Diag(Header.range(),
"error occurred here")))));
1942TEST(DiagsInHeaders, OnlyDefaultErrorOrFatal) {
1944 #include [["a.h"]] // get unused "foo" warning when building preamble.
1947 namespace { void foo() {} }
1948 void func() {foo();} ;)cpp");
1950 TU.AdditionalFiles = {{"a.h", std::string(Header.code())}};
1952 TU.ExtraArgs = {
"-Werror",
"-Wunused"};
1953 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
1956TEST(DiagsInHeaders, FromNonWrittenSources) {
1959 void foo() {})cpp");
1962 int b = [[FOO]]; // error-ok)cpp");
1964 TU.AdditionalFiles = {{"a.h", std::string(Header.code())}};
1965 TU.ExtraArgs = {
"-DFOO=NOOO"};
1966 EXPECT_THAT(TU.build().getDiagnostics(),
1967 UnorderedElementsAre(AllOf(
1969 "in included file: use of undeclared identifier 'NOOO'"),
1970 withNote(
Diag(Header.range(),
"error occurred here")))));
1973TEST(DiagsInHeaders, ErrorFromMacroExpansion) {
1983 TU.AdditionalFiles = {{"a.h", std::string(Header.code())}};
1984 EXPECT_THAT(TU.build().getDiagnostics(),
1985 UnorderedElementsAre(
1986 Diag(Main.range(),
"in included file: use of undeclared "
1987 "identifier 'foo'; did you mean 'fo'?")));
1990TEST(DiagsInHeaders, ErrorFromMacroArgument) {
2000 TU.AdditionalFiles = {{"a.h", std::string(Header.code())}};
2001 EXPECT_THAT(TU.build().getDiagnostics(),
2002 UnorderedElementsAre(
2003 Diag(Main.range(),
"in included file: use of undeclared "
2004 "identifier 'foo'; did you mean 'fo'?")));
2007TEST(IgnoreDiags, FromNonWrittenInclude) {
2009 TU.
ExtraArgs.push_back(
"--include=a.h");
2010 TU.AdditionalFiles = {{
"a.h",
"void main();"}};
2013 EXPECT_THAT(TU.build().getDiagnostics(), UnorderedElementsAre());
2016TEST(ToLSPDiag, RangeIsInMain) {
2019 D.Range = {pos(1, 2), pos(3, 4)};
2020 D.Notes.emplace_back();
2021 Note &N =
D.Notes.back();
2022 N.Range = {pos(2, 3), pos(3, 4)};
2024 D.InsideMainFile =
true;
2025 N.InsideMainFile =
false;
2027 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix>) {
2028 EXPECT_EQ(LSPDiag.range,
D.Range);
2031 D.InsideMainFile =
false;
2032 N.InsideMainFile =
true;
2034 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix>) {
2035 EXPECT_EQ(LSPDiag.range, N.Range);
2039TEST(ParsedASTTest, ModuleSawDiag) {
2042 auto AST = TU.build();
2044 EXPECT_THAT(
AST.getDiagnostics(),
2045 testing::Contains(
Diag(Code.range(), KDiagMsg.str())));
2054 TU.Code =
"#define FOO\n void bar();\n";
2055 auto AST = TU.build();
2056 EXPECT_THAT(
AST.getDiagnostics(), IsEmpty());
2060 TU.Code = Code.code().str();
2061 auto AST = TU.build();
2063 AST.getDiagnostics(),
2064 testing::Contains(
Diag(Code.range(),
"no newline at end of file")));
2068TEST(Diagnostics, Tags) {
2070 TU.
ExtraArgs = {
"-Wunused",
"-Wdeprecated"};
2072 void bar() __attribute__((deprecated));
2075 $deprecated[[bar]]();
2077 TU.Code = Test.code().str();
2078 EXPECT_THAT(TU.build().getDiagnostics(),
2079 UnorderedElementsAre(
2080 AllOf(Diag(Test.range("unused"),
"unused variable 'x'"),
2082 AllOf(
Diag(Test.range(
"deprecated"),
"'bar' is deprecated"),
2086 $typedef[[typedef int INT]];
2088 TU.Code = Test.code();
2089 TU.ClangTidyProvider = addTidyChecks("modernize-use-using");
2091 TU.build().getDiagnostics(),
2092 ifTidyChecks(UnorderedElementsAre(
2093 AllOf(
Diag(Test.range(
"typedef"),
"use 'using' instead of 'typedef'"),
2097TEST(Diagnostics, TidyDiagsArentAffectedFromWerror) {
2100 Annotations Test(R
"cpp($typedef[[typedef int INT]]; // error-ok)cpp");
2101 TU.Code = Test.code().str();
2102 TU.ClangTidyProvider = addTidyChecks("modernize-use-using");
2104 TU.build().getDiagnostics(),
2105 ifTidyChecks(UnorderedElementsAre(
2106 AllOf(
Diag(Test.range(
"typedef"),
"use 'using' instead of 'typedef'"),
2109 diagSeverity(DiagnosticsEngine::Warning)))));
2111 TU.ClangTidyProvider =
2114 TU.build().getDiagnostics(),
2115 ifTidyChecks(UnorderedElementsAre(
2116 AllOf(
Diag(Test.range(
"typedef"),
"use 'using' instead of 'typedef'"),
2118 diagSeverity(DiagnosticsEngine::Error)))));
2121TEST(Diagnostics, DeprecatedDiagsAreHints) {
2123 std::optional<clangd::Diagnostic>
Diag;
2125 D.Range = {pos(1, 2), pos(3, 4)};
2126 D.InsideMainFile =
true;
2130 D.Severity = DiagnosticsEngine::Warning;
2132 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix>) {
2133 Diag = std::move(LSPDiag);
2139 D.Severity = DiagnosticsEngine::Error;
2141 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix>) {
2142 Diag = std::move(LSPDiag);
2149 D.Severity = DiagnosticsEngine::Warning;
2151 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix>) {
2152 Diag = std::move(LSPDiag);
2157TEST(DiagnosticsTest, IncludeCleaner) {
2159$fix[[ $diag[[#include "unused.h"]]
2165 #include <system_header.h>
2172 TU.Code = Test.code().str();
2173 TU.AdditionalFiles["unused.h"] = R
"cpp(
2177 TU.AdditionalFiles["used.h"] = R
"cpp(
2181 TU.AdditionalFiles["ignore.h"] = R
"cpp(
2185 TU.AdditionalFiles["system/system_header.h"] =
"";
2186 TU.ExtraArgs = {
"-isystem" +
testPath(
"system")};
2190 Cfg.Diagnostics.Includes.IgnoreHeader.emplace_back(
2191 [](llvm::StringRef Header) {
return Header.ends_with(
"ignore.h"); });
2193 auto AST = TU.build();
2195 AST.getDiagnostics(),
2197 Diag(Test.range(
"diag"),
2198 "included header unused.h is not used directly"),
2200 withFix(
Fix(Test.range(
"fix"),
"",
"remove #include directive")))));
2201 auto &
Diag =
AST.getDiagnostics().front();
2203 llvm::ValueIs(Not(IsEmpty())));
2204 Cfg.Diagnostics.SuppressAll =
true;
2206 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
2207 Cfg.Diagnostics.SuppressAll =
false;
2208 Cfg.Diagnostics.Suppress = {
"unused-includes"};
2210 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
2213TEST(DiagnosticsTest, FixItFromHeader) {
2214 llvm::StringLiteral Header(R
"cpp(
2216 void foo(int *, int);)cpp");
2221 $diag[[foo]]($fix[[]]x, 1);
2224 TU.Code = Source.code().str();
2225 TU.HeaderCode = Header.str();
2227 TU.build().getDiagnostics(),
2228 UnorderedElementsAre(AllOf(
2229 Diag(Source.range("diag"),
"no matching function for call to 'foo'"),
2230 withFix(
Fix(Source.range(
"fix"),
"&",
2231 "candidate function not viable: no known conversion from "
2232 "'int' to 'int *' for 1st argument; take the address of "
2233 "the argument with &")))));
2236TEST(DiagnosticsTest, UnusedInHeader) {
2239 TU.ExtraArgs.push_back(
"-Wunused-function");
2240 TU.Filename =
"test.c";
2241 EXPECT_THAT(TU.build().getDiagnostics(),
2242 ElementsAre(withID(diag::warn_unused_function)));
2245 TU.Filename =
"test.h";
2246 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
2249TEST(DiagnosticsTest, DontSuppressSubcategories) {
2260 TU.ExtraArgs.push_back("-Wunreachable-code-aggressive");
2261 TU.Code = Source.code().str();
2266 EXPECT_THAT(TU.build().getDiagnostics(),
2267 ElementsAre(diagName(
"-Wunreachable-code-break")));
2271TEST(DiagnosticsTest, TokenMergeFixit) {
2274 return$lparen[[(]]0$rparen[[)]];
2278 SourceManagerForFile SM("TestTU.cpp", Test.code());
2279 DiagnosticsEngine &DE = SM.get().getDiagnostics();
2281 DE.setClient(&DiagConsumer,
false);
2283 LangOptions LangOpts;
2284 DiagConsumer.BeginSourceFile(LangOpts,
nullptr);
2289 SourceLocation LParenBegin = Loc(Test.range(
"lparen").start);
2290 SourceLocation LParenEnd = Loc(Test.range(
"lparen").end);
2291 SourceLocation RParenBegin = Loc(Test.range(
"rparen").start);
2292 SourceLocation RParenEnd = Loc(Test.range(
"rparen").end);
2294 unsigned ID = DE.getCustomDiagID(DiagnosticsEngine::Warning,
2295 "redundant parentheses around expression");
2296 DiagnosticBuilder DB = DE.Report(LParenBegin, ID);
2297 DB.AddFixItHint(FixItHint::CreateRemoval(
2298 CharSourceRange::getCharRange(LParenBegin, LParenEnd)));
2299 DB.AddFixItHint(FixItHint::CreateRemoval(
2300 CharSourceRange::getCharRange(RParenBegin, RParenEnd)));
2302 DiagConsumer.EndSourceFile();
2304 auto Diags = DiagConsumer.take();
2306 ASSERT_EQ(Diags.size(), 1u);
2307 ASSERT_EQ(Diags[0].Fixes.size(), 1u);
2308 const auto &
Fix = Diags[0].Fixes[0];
2311 EXPECT_EQ(
Fix.
Edits[0].range, Test.range(
"lparen"));
2312 EXPECT_EQ(
Fix.
Edits[0].newText,
" ");
2313 EXPECT_EQ(
Fix.
Edits[1].range, Test.range(
"rparen"));
2314 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
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.