9#include "../clang-tidy/ClangTidyOptions.h"
24#include "clang-tidy-config.h"
31#include "clang/AST/Decl.h"
32#include "clang/Basic/Diagnostic.h"
33#include "clang/Basic/DiagnosticSema.h"
34#include "clang/Basic/LLVM.h"
35#include "clang/Basic/SourceManager.h"
36#include "clang/Basic/Specifiers.h"
37#include "clang/Frontend/CompilerInvocation.h"
38#include "clang/Frontend/FrontendOptions.h"
39#include "clang/Lex/Preprocessor.h"
40#include "llvm/ADT/ArrayRef.h"
41#include "llvm/ADT/StringRef.h"
42#include "llvm/Support/Error.h"
43#include "llvm/Support/JSON.h"
44#include "llvm/Support/ScopedPrinter.h"
45#include "llvm/Support/TargetSelect.h"
46#include "llvm/Testing/Support/SupportHelpers.h"
47#include "gmock/gmock.h"
48#include "gtest/gtest.h"
61using ::testing::AllOf;
62using ::testing::Contains;
64using ::testing::ElementsAre;
65using ::testing::Field;
66using ::testing::IsEmpty;
69using ::testing::SizeIs;
70using ::testing::UnorderedElementsAre;
72::testing::Matcher<const Diag &> withFix(::testing::Matcher<Fix> FixMatcher) {
76::testing::Matcher<const Diag &> withFix(::testing::Matcher<Fix> FixMatcher1,
77 ::testing::Matcher<Fix> FixMatcher2) {
81::testing::Matcher<const Diag &> withID(
unsigned ID) {
84::testing::Matcher<const Diag &>
85withNote(::testing::Matcher<Note> NoteMatcher) {
89::testing::Matcher<const Diag &>
90withNote(::testing::Matcher<Note> NoteMatcher1,
91 ::testing::Matcher<Note> NoteMatcher2) {
92 return Field(&
Diag::Notes, UnorderedElementsAre(NoteMatcher1, NoteMatcher2));
95::testing::Matcher<const Diag &>
96withTag(::testing::Matcher<DiagnosticTag> TagMatcher) {
103 "Diag at " + llvm::to_string(
Range) +
" = [" + Message +
"]") {
104 return arg.Range ==
Range && arg.Message == Message;
107MATCHER_P3(
Fix,
Range, Replacement, Message,
108 "Fix " + llvm::to_string(
Range) +
" => " +
109 ::testing::PrintToString(Replacement) +
" = [" + Message +
"]") {
110 return arg.Message == Message && arg.Edits.size() == 1 &&
111 arg.Edits[0].range ==
Range && arg.Edits[0].newText == Replacement;
114MATCHER_P(fixMessage, Message,
"") {
return arg.Message == Message; }
117 "LSP diagnostic " + llvm::to_string(LSPDiag)) {
119 *result_listener << llvm::formatv(
"expected:\n{0:2}\ngot\n{1:2}",
127MATCHER_P(diagSource, S,
"") {
return arg.Source == S; }
128MATCHER_P(diagName, N,
"") {
return arg.Name == N; }
129MATCHER_P(diagSeverity, S,
"") {
return arg.Severity == S; }
134 if (arg.Edits.size() !=
Fix.
Edits.size())
136 for (std::size_t I = 0; I < arg.Edits.size(); ++I) {
137 if (arg.Edits[I].range !=
Fix.
Edits[I].range ||
138 arg.Edits[I].newText !=
Fix.
Edits[I].newText)
145Position pos(
int Line,
int Character) {
157::testing::Matcher<std::vector<clangd::Diag>>
158ifTidyChecks(::testing::Matcher<std::vector<clangd::Diag>> M) {
159 if (!CLANGD_TIDY_CHECKS)
164TEST(DiagnosticsTest, DiagnosticRanges) {
172 struct Container { int* begin(); int* end(); } *container;
173 for (auto i : $insertstar[[]]$range[[container]]) {
178 foo()$semicolon[[]]//with comments
180 double $type[[bar]] = "foo";
181 struct Foo { int x; }; Foo a;
183 test::$nomembernamespace[[test]];
184 $macro[[ID($macroarg[[fod]])]]();
189 TU.build().getDiagnostics(),
192 AllOf(
Diag(Test.range(
"range"),
193 "invalid range expression of type 'struct Container *'; "
194 "did you mean to dereference it with '*'?"),
195 withFix(
Fix(Test.range(
"insertstar"),
"*",
"insert '*'"))),
197 AllOf(
Diag(Test.range(
"typo"),
198 "use of undeclared identifier 'goo'; did you mean 'foo'?"),
199 diagSource(
Diag::Clang), diagName(
"undeclared_var_use_suggest"),
201 Fix(Test.range(
"typo"),
"foo",
"change 'go\\…' to 'foo'")),
203 withNote(
Diag(Test.range(
"decl"),
"'foo' declared here"))),
207 AllOf(
Diag(Test.range(
"semicolon"),
"expected ';' after expression"),
208 withFix(
Fix(Test.range(
"semicolon"),
";",
"insert ';'"))),
210 Diag(Test.range(
"unk"),
"use of undeclared identifier 'unknown'"),
211 Diag(Test.range(
"type"),
212 "cannot initialize a variable of type 'double' with an lvalue "
213 "of type 'const char[4]'"),
214 Diag(Test.range(
"nomember"),
"no member named 'y' in 'Foo'"),
215 Diag(Test.range(
"nomembernamespace"),
216 "no member named 'test' in namespace 'test'"),
217 AllOf(
Diag(Test.range(
"macro"),
218 "use of undeclared identifier 'fod'; did you mean 'foo'?"),
219 withFix(
Fix(Test.range(
"macroarg"),
"foo",
220 "change 'fod' to 'foo'")))));
228TEST(DiagnosticsTest, WSwitch) {
237 TU.ExtraArgs = {
"-Wswitch"};
238 EXPECT_THAT(TU.build().getDiagnostics(),
239 ElementsAre(
Diag(Test.range(),
240 "enumeration value 'X' not handled in switch")));
243TEST(DiagnosticsTest, FlagsMatter) {
244 Annotations Test(
"[[void]] main() {} // error-ok");
246 EXPECT_THAT(TU.build().getDiagnostics(),
247 ElementsAre(AllOf(
Diag(Test.range(),
"'main' must return 'int'"),
248 withFix(
Fix(Test.range(),
"int",
249 "change 'void' to 'int'")))));
251 TU.Filename =
"Plain.c";
253 TU.build().getDiagnostics(),
255 Diag(Test.range(),
"return type of 'main' is not 'int'"),
256 withFix(
Fix(Test.range(),
"int",
"change return type to 'int'")))));
259TEST(DiagnosticsTest, DiagnosticPreamble) {
261 #include $[["not-found.h"]] // error-ok
265 EXPECT_THAT(TU.build().getDiagnostics(),
266 ElementsAre(::testing::AllOf(
267 Diag(Test.range(),
"'not-found.h' file not found"),
268 diagSource(
Diag::Clang), diagName(
"pp_file_not_found"))));
271TEST(DiagnosticsTest, DeduplicatedClangTidyDiagnostics) {
273 float foo = [[0.1f]];
278 TU.ClangTidyProvider =
addTidyChecks(
"readability-uppercase-literal-suffix,"
282 TU.build().getDiagnostics(),
283 ifTidyChecks(UnorderedElementsAre(::testing::AllOf(
285 "floating point literal has suffix 'f', which is not uppercase"),
298 TU.Code = std::string(Test.code());
302 TU.build().getDiagnostics(),
303 ifTidyChecks(UnorderedElementsAre(::testing::AllOf(
305 "floating point literal has suffix 'f', which is not uppercase"),
309TEST(DiagnosticsTest, ClangTidy) {
311 #include $deprecated[["assert.h"]]
313 #define $macrodef[[SQUARE]](X) (X)*(X)
314 int $main[[main]]() {
316 return SQUARE($macroarg[[++]]y);
317 return $doubled[[sizeof(sizeof(int))]];
320 // misc-no-recursion uses a custom traversal from the TUDecl
330 TU.AdditionalFiles[
"system/assert.h"] =
"";
331 TU.ClangTidyProvider =
addTidyChecks(
"bugprone-sizeof-expression,"
332 "bugprone-macro-repeated-side-effects,"
333 "modernize-deprecated-headers,"
334 "modernize-use-trailing-return-type,"
335 "misc-no-recursion");
336 TU.ExtraArgs.push_back(
"-isystem" +
testPath(
"system"));
337 TU.ExtraArgs.push_back(
"-Wno-unsequenced");
339 TU.build().getDiagnostics(),
340 ifTidyChecks(UnorderedElementsAre(
341 AllOf(
Diag(Test.range(
"deprecated"),
342 "inclusion of deprecated C++ header 'assert.h'; consider "
343 "using 'cassert' instead"),
345 diagName(
"modernize-deprecated-headers"),
346 withFix(
Fix(Test.range(
"deprecated"),
"<cassert>",
347 "change '\"assert.h\"' to '<cassert>'"))),
348 Diag(Test.range(
"doubled"),
349 "suspicious usage of 'sizeof(sizeof(...))'"),
350 AllOf(
Diag(Test.range(
"macroarg"),
351 "side effects in the 1st macro argument 'X' are "
355 diagName(
"bugprone-macro-repeated-side-effects"),
356 withNote(
Diag(Test.range(
"macrodef"),
357 "macro 'SQUARE' defined here"))),
358 AllOf(
Diag(Test.range(
"main"),
359 "use a trailing return type for this function"),
361 diagName(
"modernize-use-trailing-return-type"),
364 "use a trailing return type for this function"))),
365 Diag(Test.range(
"foo"),
366 "function 'foo' is within a recursive call chain"),
367 Diag(Test.range(
"bar"),
368 "function 'bar' is within a recursive call chain"))));
371#if CLANGD_TIDY_CHECKS && CLANG_TIDY_ENABLE_QUERY_BASED_CUSTOM_CHECKS
372TEST(DiagnosticsTest, ClangTidyExperimentalCustomChecks) {
374 int x = $literal[[0]];
377 TU.ClangTidyProvider = [](tidy::ClangTidyOptions &Opts, llvm::StringRef) {
378 Opts.Checks =
"-*,custom-integer-literal";
379 tidy::ClangTidyOptions::CustomCheckValue Check;
380 Check.Name =
"integer-literal";
381 Check.Query = R
"(match integerLiteral().bind("literal"))";
382 tidy::ClangTidyOptions::CustomCheckDiag Diagnostic;
387 Opts.CustomChecks.emplace();
388 Opts.CustomChecks->push_back(std::move(Check));
394 Cfg.Diagnostics.ClangTidy.FastCheckFilter = Policy;
396 auto AST = TU.build();
397 return std::vector<clangd::Diag>(
AST.getDiagnostics());
399 auto CustomDiagnostic =
400 AllOf(
Diag(Test.range(
"literal"),
"integer literal found"),
402 diagSeverity(DiagnosticsEngine::Warning));
407 ElementsAre(CustomDiagnostic));
409 ElementsAre(CustomDiagnostic));
413TEST(DiagnosticsTest, ClangTidyRedundantParenthesesFix) {
416 return$lparen[[(]]0$rparen[[)]];
420 TU.ClangTidyProvider =
addTidyChecks(
"readability-redundant-parentheses");
422 clangd::Fix ExpectedFix;
423 ExpectedFix.Message =
"redundant parentheses around expression";
424 ExpectedFix.Edits.push_back(
TextEdit{Test.range(
"lparen"),
" "});
425 ExpectedFix.Edits.push_back(
TextEdit{Test.range(
"rparen"),
""});
428 TU.build().getDiagnostics(),
429 ifTidyChecks(ElementsAre(AllOf(
430 Diag(Test.range(
"lparen"),
"redundant parentheses around expression"),
432 diagName(
"readability-redundant-parentheses"),
433 withFix(equalToFix(ExpectedFix))))));
436TEST(DiagnosticsTest, ClangTidyEOF) {
440 #include "a.h")cpp");
443 TU.ExtraArgs = {
"-isystem."};
444 TU.AdditionalFiles[
"a.h"] = TU.AdditionalFiles[
"b.h"] =
"";
447 TU.build().getDiagnostics(),
448 ifTidyChecks(Contains(
449 AllOf(
Diag(Test.range(),
"#includes are not sorted properly"),
453TEST(DiagnosticTest, TemplatesInHeaders) {
456 Derived<int> [[y]]; // error-ok
459 template <typename T>
460 struct Derived : [[T]] {};
463 TU.HeaderCode = Header.code().str();
465 TU.build().getDiagnostics(),
467 Diag(Main.range(), "in template: base specifier must name a class"),
468 withNote(
Diag(Header.range(),
"error occurred here"),
469 Diag(Main.range(),
"in instantiation of template class "
470 "'Derived<int>' requested here")))));
473TEST(DiagnosticTest, MakeUnique) {
477 struct S { S(char*); };
478 auto x = std::[[make_unique]]<S>(42); // error-ok
481 TU.HeaderCode = R"cpp(
483 // These mocks aren't quite right - we omit unique_ptr for simplicity.
484 // forward is included to show its body is not needed to get the diagnostic.
485 template <typename T> T&& forward(T& t);
486 template <typename T, typename... A> T* make_unique(A&&... args) {
487 return new T(std::forward<A>(args)...);
491 EXPECT_THAT(TU.build().getDiagnostics(),
492 UnorderedElementsAre(
495 "no matching constructor for initialization of 'S'")));
498TEST(DiagnosticTest, CoroutineInHeader) {
499 StringRef CoroutineH = R
"cpp(
501template <class Ret, typename... T>
502struct coroutine_traits { using promise_type = typename Ret::promise_type; };
504template <class Promise = void>
505struct coroutine_handle {
506 static coroutine_handle from_address(void *) noexcept;
507 static coroutine_handle from_promise(Promise &promise);
508 constexpr void* address() const noexcept;
511struct coroutine_handle<void> {
512 template <class PromiseType>
513 coroutine_handle(coroutine_handle<PromiseType>) noexcept;
514 static coroutine_handle from_address(void *);
515 constexpr void* address() const noexcept;
519 bool await_ready() noexcept { return false; }
520 void await_suspend(coroutine_handle<>) noexcept {}
521 void await_resume() noexcept {}
526 StringRef Header = R"cpp(
527#include "coroutine.h"
528template <typename T> struct [[clang::coro_return_type]] Gen {
529 struct promise_type {
530 Gen<T> get_return_object() {
533 std::awaitable initial_suspend();
534 std::awaitable final_suspend() noexcept;
535 void unhandled_exception();
536 void return_value(T t);
540Gen<int> foo_coro(int b) { co_return b; }
545Gen<int> $[[bar_coro]](int b) { return foo_coro(b); }
548 TU.AdditionalFiles["coroutine.h"] = std::string(CoroutineH);
549 TU.AdditionalFiles[
"header.hpp"] = std::string(Header);
550 TU.ExtraArgs.push_back(
"--std=c++20");
551 EXPECT_THAT(TU.build().getDiagnostics(), ElementsAre(hasRange(Main.range())));
554TEST(DiagnosticTest, MakeShared) {
558 struct S { S(char*); };
559 auto x = std::[[make_shared]]<S>(42); // error-ok
562 TU.HeaderCode = R"cpp(
564 // These mocks aren't quite right - we omit shared_ptr for simplicity.
565 // forward is included to show its body is not needed to get the diagnostic.
566 template <typename T> T&& forward(T& t);
567 template <typename T, typename... A> T* make_shared(A&&... args) {
568 return new T(std::forward<A>(args)...);
572 TU.ParseOpts.PreambleParseForwardingFunctions = true;
573 EXPECT_THAT(TU.build().getDiagnostics(),
574 UnorderedElementsAre(
577 "no matching constructor for initialization of 'S'")));
580TEST(DiagnosticTest, NoMultipleDiagnosticInFlight) {
582 template <typename T> struct Foo {
592 Foo<LabelInfo> label_info_map;
593 [[for]] (auto it = label_info_map.begin(); it != label_info_map.end(); ++it) {
599 TU.ClangTidyProvider = addTidyChecks("modernize-loop-convert");
601 TU.build().getDiagnostics(),
602 ifTidyChecks(UnorderedElementsAre(::testing::AllOf(
603 Diag(Main.range(),
"use range-based for loop instead"),
607TEST(DiagnosticTest, RespectsDiagnosticConfig) {
617 TU.build().getDiagnostics(),
618 ElementsAre(
Diag(Main.range(),
"use of undeclared identifier 'unknown'"),
619 Diag(Main.range(
"ret"),
620 "void function 'x' should not return a value")));
624 EXPECT_THAT(TU.build().getDiagnostics(),
625 ElementsAre(
Diag(Main.range(),
626 "use of undeclared identifier 'unknown'")));
629TEST(DiagnosticTest, RespectsDiagnosticConfigInHeader) {
631 int x = "42"; // error-ok
634 #include "header.hpp"
637 TU.AdditionalFiles[
"header.hpp"] = std::string(Header.code());
641 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
644TEST(DiagnosticTest, ClangTidySuppressionComment) {
648 double d = 8 / i; // NOLINT
652 double f = BAD; // NOLINT
653 double g = [[8]] / i;
655 double h = BAD2; // NOLINT
661 // verify no crashes on unmatched nolints.
666 TU.ClangTidyProvider = addTidyChecks("bugprone-integer-division");
668 TU.build().getDiagnostics(),
669 ifTidyChecks(UnorderedElementsAre(::testing::AllOf(
670 Diag(Main.range(),
"result of integer division used in a floating "
671 "point context; possible loss of precision"),
673 diagName(
"bugprone-integer-division")))));
676TEST(DiagnosticTest, ClangTidySystemMacro) {
681 double x = $inline[[8]] / i;
682 double y = $user[[DIVIDE_USER]](i);
683 double z = DIVIDE_SYS(i);
687 TU.AdditionalFiles[
"user.h"] = R
"cpp(
688 #define DIVIDE_USER(Y) 8/Y
690 TU.AdditionalFiles["system.h"] = R
"cpp(
691 #pragma clang system_header
692 #define DIVIDE_SYS(Y) 8/Y
695 TU.ClangTidyProvider = addTidyChecks("bugprone-integer-division");
696 std::string BadDivision =
"result of integer division used in a floating "
697 "point context; possible loss of precision";
701 EXPECT_THAT(TU.build().getDiagnostics(),
703 UnorderedElementsAre(
Diag(Main.range(
"inline"), BadDivision),
704 Diag(Main.range(
"user"), BadDivision))));
707TEST(DiagnosticTest, ClangTidyWarningAsError) {
711 double f = [[8]] / i; // error-ok
715 TU.ClangTidyProvider =
716 addTidyChecks("bugprone-integer-division",
"bugprone-integer-division");
718 TU.build().getDiagnostics(),
719 ifTidyChecks(UnorderedElementsAre(::testing::AllOf(
720 Diag(Main.range(),
"result of integer division used in a floating "
721 "point context; possible loss of precision"),
723 diagSeverity(DiagnosticsEngine::Error)))));
726TidyProvider addClangArgs(std::vector<llvm::StringRef> ExtraArgs,
728 return [ExtraArgs = std::move(ExtraArgs),
Checks =
Checks.str()](
729 tidy::ClangTidyOptions &Opts, llvm::StringRef) {
731 Opts.ExtraArgs.emplace();
732 for (llvm::StringRef Arg : ExtraArgs)
733 Opts.ExtraArgs->emplace_back(Arg);
739TEST(DiagnosticTest, ClangTidyEnablesClangWarning) {
741 static void [[foo]]() {}
745 auto UnusedFooWarning =
746 AllOf(
Diag(Main.range(),
"unused function 'foo'"),
747 diagName(
"-Wunused-function"), diagSource(
Diag::Clang),
748 diagSeverity(DiagnosticsEngine::Warning));
751 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
755 TU.ClangTidyProvider =
756 addClangArgs({
"-Wunused"},
"clang-diagnostic-unused-function");
757 EXPECT_THAT(TU.build().getDiagnostics(), ElementsAre(UnusedFooWarning));
760 TU.ClangTidyProvider = addClangArgs({
"-Wunused"},
"clang-diagnostic-*");
761 EXPECT_THAT(TU.build().getDiagnostics(), ElementsAre(UnusedFooWarning));
763 TU.ClangTidyProvider = addClangArgs({
"-Wunused"},
"*");
764 EXPECT_THAT(TU.build().getDiagnostics(), Contains(UnusedFooWarning));
766 TU.ClangTidyProvider = addClangArgs(
767 {
"-Wunused"},
"clang-diagnostic-*,-clang-diagnostic-unused-function");
768 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
771 TU.ClangTidyProvider = addClangArgs({
"-Wunused"},
"clang-diagnostic-unused");
772 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
775 TU.ClangTidyProvider = addClangArgs({
"-Wunused",
"-Dfoo=bar"},
776 "clang-diagnostic-unused-function");
777 EXPECT_THAT(TU.build().getDiagnostics(), ElementsAre(UnusedFooWarning))
778 <<
"Not unused function 'bar'!";
781 TU.ExtraArgs = {
"-Werror"};
782 TU.ClangTidyProvider =
783 addClangArgs({
"-Wunused"},
"clang-diagnostic-unused-function");
784 EXPECT_THAT(TU.build().getDiagnostics(),
785 ElementsAre(diagSeverity(DiagnosticsEngine::Warning)));
788 TU.ExtraArgs = {
"-Wunused",
"-Werror"};
789 TU.ClangTidyProvider =
790 addClangArgs({
"-Wunused"},
"clang-diagnostic-unused-function");
791 EXPECT_THAT(TU.build().getDiagnostics(),
792 ElementsAre(diagSeverity(DiagnosticsEngine::Error)));
795 TU.ExtraArgs = {
"-Wunused-function",
"-Werror"};
796 TU.ClangTidyProvider =
797 addClangArgs({
"-Wunused"},
"clang-diagnostic-unused-label");
798 EXPECT_THAT(TU.build().getDiagnostics(),
799 ElementsAre(diagSeverity(DiagnosticsEngine::Warning)));
804 TU.ClangTidyProvider = addClangArgs({
"-Wunused",
"-Wno-unused"},
805 "clang-diagnostic-unused-function");
806 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
809 TU.ClangTidyProvider =
810 addClangArgs({
"-Wunused"}, {
"clang-diagnostic-unused-function"});
811 EXPECT_THAT(TU.build().getDiagnostics(), SizeIs(1));
814 TU.ClangTidyProvider = addClangArgs({
"-Wunused",
"-Wno-unused-function"},
815 {
"clang-diagnostic-unused-function"});
816 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
817 TU.ClangTidyProvider = addClangArgs({
"-Wunused-function",
"-Wno-unused"},
818 {
"clang-diagnostic-unused-function"});
819 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
823 TU.ExtraArgs = {
"-Wunused"};
824 TU.ClangTidyProvider = addClangArgs({
"-Wno-unused"}, {});
825 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
827 TU.ExtraArgs = {
"-Wno-unused"};
828 TU.ClangTidyProvider = addClangArgs({
"-Wunused"}, {
"-*, clang-diagnostic-*"});
829 EXPECT_THAT(TU.build().getDiagnostics(), SizeIs(1));
832TEST(DiagnosticTest, ClangTidyCompilerWarningsWithoutChecks) {
834 TU.ClangTidyProvider = [](tidy::ClangTidyOptions &Opts, llvm::StringRef) {
835 Opts.Checks =
"-*,clang-diagnostic-*";
836 Opts.ExtraArgs = {
"-Wunused-function"};
837 Opts.WarningsAsErrors =
"clang-diagnostic-unused-function";
840 diagSeverity(DiagnosticsEngine::Warning));
841 EXPECT_THAT(TU.build().getDiagnostics(), ElementsAre(
Warning));
844 TU.Code =
"static void foo() {} // NOLINT(clang-diagnostic-unused-function)";
845 EXPECT_THAT(TU.build().getDiagnostics(), ElementsAre(
Warning));
848TEST(DiagnosticTest, ClangTidyWarningOptionsBeforeBeginSourceFile) {
849 auto Build = [](
bool Suppress,
bool ExtraArgsBefore) {
852 Modules.
add(std::make_unique<ClangTidyFeatureModule>(
853 [=](tidy::ClangTidyOptions &Opts, llvm::StringRef) {
857 ExtraArgsBefore ? Opts.ExtraArgsBefore : Opts.ExtraArgs;
858 Args = {
"-Wno-incomplete-umbrella"};
861 TU.FeatureModules = &Modules;
862 TU.ExtraArgs = {
"-fmodules",
863 "-fmodule-map-file=" +
testPath(
"module.modulemap")};
864 TU.AdditionalFiles[
"module.modulemap"] =
865 R
"(module M { umbrella "missing" })";
869 auto Inputs = TU.inputs(FS);
870 IgnoringDiagConsumer Diags;
876 EXPECT_EQ(
AST->getPreprocessor().getDiagnostics().getNumWarnings(),
884TEST(DiagnosticTest, ClangTidyRelativeInput) {
885 if (!CLANGD_TIDY_CHECKS)
886 GTEST_SKIP() <<
"Requires clang-tidy checks";
889 TU.Filename =
"project/src/test.cpp";
890 TU.AdditionalFiles[
"project/src/.clang-tidy"] =
891 "Checks: '-*,modernize-use-nullptr'";
894 Modules.
add(std::make_unique<ClangTidyFeatureModule>(
895 [&](tidy::ClangTidyOptions &Opts, llvm::StringRef Filename) {
896 EXPECT_EQ(Filename,
testPath(TU.Filename));
897 Provider(Opts, Filename);
899 TU.FeatureModules = &Modules;
900 auto Inputs = TU.inputs(FS);
901 Inputs.CompileCommand.Directory =
testPath(
"project");
903 for (
const auto *InputFile : {
"src/./test.cpp",
"../other/test.cpp"}) {
904 SCOPED_TRACE(InputFile);
905 IgnoringDiagConsumer Diags;
908 auto Kind = CI->getFrontendOpts().Inputs.front().getKind();
909 CI->getFrontendOpts().Inputs = {FrontendInputFile(InputFile, Kind)};
913 ASSERT_THAT(
AST->getDiagnostics(), SizeIs(1));
914 EXPECT_EQ(
AST->getDiagnostics().front().Name,
"modernize-use-nullptr");
918TEST(DiagnosticTest, LongFixMessages) {
923 int somereallyreallyreallyreallyreallyreallyreallyreallylongidentifier;
924 [[omereallyreallyreallyreallyreallyreallyreallyreallylongidentifier]]= 10;
929 TU.build().getDiagnostics(),
930 ElementsAre(withFix(Fix(
932 "somereallyreallyreallyreallyreallyreallyreallyreallylongidentifier",
933 "change 'omereallyreallyreallyreallyreallyreallyreallyreall…' to "
934 "'somereallyreallyreallyreallyreallyreallyreallyreal…'"))));
944 TU.Code = std::string(Source.code());
945 EXPECT_THAT(TU.build().getDiagnostics(),
947 Fix(Source.range(), "ident",
"change 'ide\\…' to 'ident'"))));
950TEST(DiagnosticTest, NewLineFixMessage) {
953 TU.ExtraArgs = {
"-Wnewline-eof"};
955 TU.build().getDiagnostics(),
956 ElementsAre(withFix((
Fix(Source.range(),
"\n",
"insert '\\n'")))));
959TEST(DiagnosticTest, ClangTidySuppressionCommentTrumpsWarningAsError) {
963 double f = [[8]] / i; // NOLINT
967 TU.ClangTidyProvider =
968 addTidyChecks("bugprone-integer-division",
"bugprone-integer-division");
969 EXPECT_THAT(TU.build().getDiagnostics(), UnorderedElementsAre());
972TEST(DiagnosticTest, ClangTidyNoLiteralDataInMacroToken) {
975 using pthread_t = int;
976 int pthread_kill(pthread_t thread, int sig);
979 return pthread_kill(thread, 0);
983 TU.ClangTidyProvider = addTidyChecks("bugprone-bad-signal-to-kill-thread");
984 EXPECT_THAT(TU.build().getDiagnostics(), UnorderedElementsAre());
987TEST(DiagnosticTest, BadSignalToKillThreadInPreamble) {
990 using pthread_t = int;
991 int pthread_kill(pthread_t thread, int sig);
994 return pthread_kill(thread, 15);
998 TU.HeaderFilename = "signal.h";
999 TU.HeaderCode =
"#define SIGTERM 15";
1000 TU.ClangTidyProvider =
addTidyChecks(
"bugprone-bad-signal-to-kill-thread");
1001 EXPECT_THAT(TU.build().getDiagnostics(),
1002 ifTidyChecks(UnorderedElementsAre(
1003 diagName(
"bugprone-bad-signal-to-kill-thread"))));
1006TEST(DiagnosticTest, ClangTidyMacroToEnumCheck) {
1013 std::vector<TidyProvider> Providers;
1014 Providers.push_back(
1015 addTidyChecks("cppcoreguidelines-macro-to-enum,modernize-macro-to-enum"));
1017 TU.ClangTidyProvider =
combine(std::move(Providers));
1018 EXPECT_THAT(TU.build().getDiagnostics(), UnorderedElementsAre());
1021TEST(DiagnosticTest, ElseAfterReturnRange) {
1026 } [[else]] if (cond == 2) {
1033 TU.ClangTidyProvider = addTidyChecks("llvm-else-after-return");
1034 EXPECT_THAT(TU.build().getDiagnostics(),
1035 ifTidyChecks(ElementsAre(
1036 Diag(Main.range(),
"do not use 'else' after 'return'"))));
1039TEST(DiagnosticTest, ClangTidySelfContainedDiags) {
1048 void InitVariables() {
1049 float $C[[C]]$CFix[[]];
1050 double $D[[D]]$DFix[[]];
1054 TU.ClangTidyProvider =
1055 addTidyChecks("cppcoreguidelines-prefer-member-initializer,"
1056 "cppcoreguidelines-init-variables");
1057 clangd::Fix ExpectedAFix;
1058 ExpectedAFix.Message =
1059 "'A' should be initialized in a member initializer of the constructor";
1060 ExpectedAFix.Edits.push_back(
TextEdit{Main.range(
"Fix"),
" : A(1)"});
1061 ExpectedAFix.Edits.push_back(
TextEdit{Main.range(
"A"),
""});
1066 clangd::Fix ExpectedBFix;
1067 ExpectedBFix.Message =
1068 "'B' should be initialized in a member initializer of the constructor";
1069 ExpectedBFix.Edits.push_back(
TextEdit{Main.range(
"Fix"),
" : B(1)"});
1070 ExpectedBFix.Edits.push_back(
TextEdit{Main.range(
"B"),
""});
1072 clangd::Fix ExpectedCFix;
1073 ExpectedCFix.Message =
"variable 'C' is not initialized";
1074 ExpectedCFix.Edits.push_back(
1075 TextEdit{Main.range(
"MathHeader"),
"#include <math.h>\n\n"});
1076 ExpectedCFix.Edits.push_back(
TextEdit{Main.range(
"CFix"),
" = NAN"});
1080 clangd::Fix ExpectedDFix;
1081 ExpectedDFix.Message =
"variable 'D' is not initialized";
1082 ExpectedDFix.Edits.push_back(
1083 TextEdit{Main.range(
"MathHeader"),
"#include <math.h>\n\n"});
1084 ExpectedDFix.Edits.push_back(
TextEdit{Main.range(
"DFix"),
" = NAN"});
1086 TU.build().getDiagnostics(),
1087 ifTidyChecks(UnorderedElementsAre(
1088 AllOf(
Diag(Main.range(
"A"),
"'A' should be initialized in a member "
1089 "initializer of the constructor"),
1090 withFix(equalToFix(ExpectedAFix))),
1091 AllOf(
Diag(Main.range(
"B"),
"'B' should be initialized in a member "
1092 "initializer of the constructor"),
1093 withFix(equalToFix(ExpectedBFix))),
1094 AllOf(
Diag(Main.range(
"C"),
"variable 'C' is not initialized"),
1095 withFix(equalToFix(ExpectedCFix))),
1096 AllOf(
Diag(Main.range(
"D"),
"variable 'D' is not initialized"),
1097 withFix(equalToFix(ExpectedDFix))))));
1100TEST(DiagnosticTest, ClangTidySelfContainedDiagsFormatting) {
1104 virtual void Reset1() = 0;
1105 virtual void Reset2() = 0;
1107 class A : public Interface {
1108 // This will be marked by clangd to use override instead of virtual
1109 $virtual1[[virtual ]]void $Reset1[[Reset1]]()$override1[[]];
1110 $virtual2[[virtual ]]/**/void $Reset2[[Reset2]]()$override2[[]];
1114 TU.ClangTidyProvider =
1115 addTidyChecks("cppcoreguidelines-explicit-virtual-functions,");
1116 clangd::Fix
const ExpectedFix1{
1117 "prefer using 'override' or (rarely) 'final' "
1118 "instead of 'virtual'",
1119 {
TextEdit{Main.range(
"virtual1"),
""},
1120 TextEdit{Main.range(
"override1"),
" override"}},
1122 clangd::Fix
const ExpectedFix2{
1123 "prefer using 'override' or (rarely) 'final' "
1124 "instead of 'virtual'",
1125 {
TextEdit{Main.range(
"virtual2"),
""},
1126 TextEdit{Main.range(
"override2"),
" override"}},
1130 EXPECT_THAT(TU.build().getDiagnostics(),
1131 ifTidyChecks(UnorderedElementsAre(
1132 AllOf(
Diag(Main.range(
"Reset1"),
1133 "prefer using 'override' or (rarely) 'final' "
1134 "instead of 'virtual'"),
1135 withFix(equalToFix(ExpectedFix1))),
1136 AllOf(
Diag(Main.range(
"Reset2"),
1137 "prefer using 'override' or (rarely) 'final' "
1138 "instead of 'virtual'"),
1139 withFix(equalToFix(ExpectedFix2))))));
1142TEST(DiagnosticsTest, ClangTidyCallingIntoPreprocessor) {
1143 std::string Main = R
"cpp(
1148 std::string Header = R"cpp(
1149 #define EXTERN extern
1153 TU.AdditionalFiles[
"b.h"] = Header;
1154 TU.ClangTidyProvider =
addTidyChecks(
"modernize-use-trailing-return-type");
1159TEST(DiagnosticsTest, Preprocessor) {
1168 int a = [[b]]; // error-ok
1175 ElementsAre(Diag(Test.range(), "use of undeclared identifier 'b'")));
1178TEST(DiagnosticsTest, IgnoreVerify) {
1180 int a; // expected-error {{}}
1182 TU.ExtraArgs.push_back("-Xclang");
1183 TU.ExtraArgs.push_back(
"-verify");
1184 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
1187TEST(DiagnosticTest, IgnoreBEFilelistOptions) {
1189 TU.ExtraArgs.push_back(
"-Xclang");
1190 for (
const auto *DisableOption :
1191 {
"-fsanitize-ignorelist=null",
"-fprofile-list=null",
1192 "-fxray-always-instrument=null",
"-fxray-never-instrument=null",
1193 "-fxray-attr-list=null"}) {
1194 TU.ExtraArgs.push_back(DisableOption);
1195 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
1196 TU.ExtraArgs.pop_back();
1201TEST(DiagnosticsTest, RecursivePreamble) {
1203 #include "foo.h" // error-ok
1206 TU.Filename = "foo.h";
1207 EXPECT_THAT(TU.build().getDiagnostics(),
1208 ElementsAre(diagName(
"pp_including_mainfile_in_preamble")));
1209 EXPECT_THAT(TU.build().getLocalTopLevelDecls(), SizeIs(1));
1213TEST(DiagnosticsTest, RecursivePreamblePragmaOnce) {
1219 TU.Filename = "foo.h";
1220 EXPECT_THAT(TU.build().getDiagnostics(),
1221 Not(Contains(diagName(
"pp_including_mainfile_in_preamble"))));
1222 EXPECT_THAT(TU.build().getLocalTopLevelDecls(), SizeIs(1));
1227TEST(DiagnosticsTest, RecursivePreambleIfndefGuard) {
1231 #include "foo.h" // error-ok
1235 TU.Filename = "foo.h";
1237 EXPECT_THAT(TU.build().getDiagnostics(),
1238 ElementsAre(diagName(
"pp_including_mainfile_in_preamble")));
1239 EXPECT_THAT(TU.build().getLocalTopLevelDecls(), SizeIs(1));
1242TEST(DiagnosticsTest, PreambleWithPragmaAssumeNonnull) {
1244#pragma clang assume_nonnull begin
1246#pragma clang assume_nonnull end
1248 auto AST = TU.build();
1249 EXPECT_THAT(
AST.getDiagnostics(), IsEmpty());
1250 const auto *
X = cast<FunctionDecl>(
findDecl(
AST,
"foo")).getParamDecl(0);
1251 ASSERT_TRUE(
X->getOriginalType()->getNullability() ==
1252 NullabilityKind::NonNull);
1255TEST(DiagnosticsTest, PreamblePragmaDiagnosticPushPop) {
1257#pragma clang diagnostic push
1261#pragma clang diagnostic pop
1263 auto AST = TU.build();
1264 EXPECT_THAT(
AST.getDiagnostics(), IsEmpty());
1267TEST(DiagnosticsTest, PreambleHeaderWithBadPragmaAssumeNonnull) {
1269#pragma clang assume_nonnull begin // error-ok
1273#include "foo.h" // unterminated assume_nonnull should not affect bar.
1276 TU.AdditionalFiles = {{"foo.h", std::string(Header.code())}};
1277 auto AST = TU.build();
1278 EXPECT_THAT(
AST.getDiagnostics(),
1279 ElementsAre(diagName(
"pp_eof_in_assume_nonnull")));
1280 const auto *
X = cast<FunctionDecl>(
findDecl(
AST,
"foo")).getParamDecl(0);
1281 ASSERT_TRUE(
X->getOriginalType()->getNullability() ==
1282 NullabilityKind::NonNull);
1283 const auto *Y = cast<FunctionDecl>(
findDecl(
AST,
"bar")).getParamDecl(0);
1284 ASSERT_FALSE(Y->getOriginalType()->getNullability());
1287TEST(DiagnosticsTest, InsideMacros) {
1290 #define RET(x) return x + 10
1293 RET($foo[[0]]); // error-ok
1300 ElementsAre(Diag(Test.range("foo"),
1301 "cannot initialize return object of type "
1302 "'int *' with an rvalue of type 'int'"),
1303 Diag(Test.range(
"bar"),
1304 "cannot initialize return object of type "
1305 "'int *' with an rvalue of type 'int'")));
1308TEST(DiagnosticsTest, NoFixItInMacro) {
1310 #define Define(name) void name() {}
1312 [[Define]](main) // error-ok
1315 EXPECT_THAT(TU.build().getDiagnostics(),
1316 ElementsAre(AllOf(
Diag(Test.range(),
"'main' must return 'int'"),
1320TEST(DiagnosticsTest, PragmaSystemHeader) {
1321 Annotations Test(
"#pragma clang [[system_header]]\n");
1324 TU.build().getDiagnostics(),
1326 Diag(Test.range(),
"#pragma system_header ignored in main file"))));
1327 TU.Filename =
"TestTU.h";
1328 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
1331TEST(ClangdTest, MSAsm) {
1334 llvm::InitializeAllTargetInfos();
1336 TU.ExtraArgs = {
"-fms-extensions"};
1337 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
1340TEST(DiagnosticsTest, ToLSP) {
1347 D.ID = clang::diag::err_undeclared_var_use;
1349 D.Name =
"undeclared_var_use";
1351 D.Message =
"something terrible happened";
1352 D.Range = {pos(1, 2), pos(3, 4)};
1353 D.InsideMainFile =
true;
1354 D.Severity = DiagnosticsEngine::Error;
1355 D.File =
"foo/bar/main.cpp";
1356 D.AbsFile = std::string(MainFile.file());
1357 D.OpaqueData[
"test"] =
"bar";
1359 clangd::Note NoteInMain;
1360 NoteInMain.Message =
"declared somewhere in the main file";
1361 NoteInMain.Range = {pos(5, 6), pos(7, 8)};
1362 NoteInMain.Severity = DiagnosticsEngine::Remark;
1363 NoteInMain.File =
"../foo/bar/main.cpp";
1364 NoteInMain.InsideMainFile =
true;
1365 NoteInMain.AbsFile = std::string(MainFile.file());
1367 D.Notes.push_back(NoteInMain);
1369 clangd::Note NoteInHeader;
1370 NoteInHeader.Message =
"declared somewhere in the header file";
1371 NoteInHeader.Range = {pos(9, 10), pos(11, 12)};
1372 NoteInHeader.Severity = DiagnosticsEngine::Note;
1373 NoteInHeader.File =
"../foo/baz/header.h";
1374 NoteInHeader.InsideMainFile =
false;
1375 NoteInHeader.AbsFile = std::string(
HeaderFile.file());
1376 D.Notes.push_back(NoteInHeader);
1379 F.Message =
"do something";
1380 D.Fixes.push_back(F);
1383 clangd::Diagnostic MainLSP;
1384 MainLSP.range =
D.Range;
1385 MainLSP.severity =
getSeverity(DiagnosticsEngine::Error);
1386 MainLSP.code =
"undeclared_var_use";
1387 MainLSP.source =
"clang";
1389 R
"(Something terrible happened (fix available)
1391main.cpp:6:7: remark: declared somewhere in the main file
1393../foo/baz/header.h:10:11:
1394note: declared somewhere in the header file)";
1396 MainLSP.data = D.OpaqueData;
1398 clangd::Diagnostic NoteInMainLSP;
1399 NoteInMainLSP.range = NoteInMain.Range;
1400 NoteInMainLSP.severity = getSeverity(DiagnosticsEngine::Remark);
1401 NoteInMainLSP.message = R"(Declared somewhere in the main file
1403main.cpp:2:3: error: something terrible happened)";
1407 std::vector<std::pair<clangd::Diagnostic, std::vector<clangd::Fix>>> LSPDiags;
1409 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix> Fixes) {
1411 {std::move(LSPDiag),
1412 std::vector<clangd::Fix>(Fixes.begin(), Fixes.end())});
1417 ElementsAre(Pair(equalToLSPDiag(MainLSP), ElementsAre(equalToFix(F))),
1418 Pair(equalToLSPDiag(NoteInMainLSP), IsEmpty())));
1419 EXPECT_EQ(LSPDiags[0].first.code,
"undeclared_var_use");
1420 EXPECT_EQ(LSPDiags[0].first.source,
"clang");
1421 EXPECT_EQ(LSPDiags[1].first.code,
"");
1422 EXPECT_EQ(LSPDiags[1].first.source,
"");
1426 Opts.EmitRelatedLocations =
true;
1428 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix> Fixes) {
1430 {std::move(LSPDiag),
1431 std::vector<clangd::Fix>(Fixes.begin(), Fixes.end())});
1433 MainLSP.message =
"Something terrible happened (fix available)";
1435 NoteInMainDRI.
message =
"Declared somewhere in the main file";
1436 NoteInMainDRI.location.range = NoteInMain.Range;
1437 NoteInMainDRI.location.uri = MainFile;
1438 MainLSP.relatedInformation = {NoteInMainDRI};
1440 NoteInHeaderDRI.
message =
"Declared somewhere in the header file";
1441 NoteInHeaderDRI.location.range = NoteInHeader.Range;
1443 MainLSP.relatedInformation = {NoteInMainDRI, NoteInHeaderDRI};
1444 EXPECT_THAT(LSPDiags, ElementsAre(Pair(equalToLSPDiag(MainLSP),
1445 ElementsAre(equalToFix(F)))));
1448struct SymbolWithHeader {
1450 std::string DeclaringFile;
1451 std::string IncludeHeader;
1454std::unique_ptr<SymbolIndex>
1455buildIndexWithSymbol(llvm::ArrayRef<SymbolWithHeader> Syms) {
1457 for (
const auto &S : Syms) {
1460 Sym.CanonicalDeclaration.FileURI = S.DeclaringFile.c_str();
1461 Sym.Definition.FileURI = S.DeclaringFile.c_str();
1468TEST(IncludeFixerTest, IncompleteType) {
1470 TU.ExtraArgs.push_back(
"-std=c++20");
1471 auto Index = buildIndexWithSymbol(
1472 {SymbolWithHeader{
"ns::X",
"unittest:///x.h",
"\"x.h\""}});
1473 TU.ExternalIndex = Index.get();
1475 std::vector<std::pair<llvm::StringRef, llvm::StringRef>> Tests{
1476 {
"incomplete_nested_name_spec",
"[[ns::X::]]Nested n;"},
1477 {
"incomplete_base_class",
"class Y : [[ns::X]] {};"},
1478 {
"incomplete_member_access",
"auto i = x[[->]]f();"},
1479 {
"incomplete_type",
"auto& [[[]]m] = *x;"},
1480 {
"init_incomplete_type",
1481 "struct C { static int f(ns::X&); }; int i = C::f([[{]]});"},
1482 {
"bad_cast_incomplete",
"auto a = [[static_cast]]<ns::X>(0);"},
1483 {
"template_nontype_parm_incomplete",
"template <ns::X [[foo]]> int a;"},
1484 {
"typecheck_decl_incomplete_type",
"ns::X [[var]];"},
1485 {
"typecheck_incomplete_tag",
"auto i = [[(*x)]]->f();"},
1486 {
"typecheck_nonviable_condition_incomplete",
1487 "struct A { operator ns::X(); } a; const ns::X &[[b]] = a;"},
1488 {
"invalid_incomplete_type_use",
"auto var = [[ns::X()]];"},
1489 {
"sizeof_alignof_incomplete_or_sizeless_type",
1490 "auto s = [[sizeof]](ns::X);"},
1491 {
"for_range_incomplete_type",
"void foo() { for (auto i : [[*]]x ) {} }"},
1492 {
"func_def_incomplete_result",
"ns::X [[func]] () {}"},
1493 {
"field_incomplete_or_sizeless",
"class M { ns::X [[member]]; };"},
1494 {
"array_incomplete_or_sizeless_type",
"auto s = [[(ns::X[]){}]];"},
1495 {
"call_incomplete_return",
"ns::X f(); auto fp = &f; auto z = [[fp()]];"},
1496 {
"call_function_incomplete_return",
"ns::X foo(); auto a = [[foo()]];"},
1497 {
"call_incomplete_argument",
"int m(ns::X); int i = m([[*x]]);"},
1498 {
"switch_incomplete_class_type",
"void a() { [[switch]](*x) {} }"},
1499 {
"delete_incomplete_class_type",
"void f() { [[delete]] *x; }"},
1500 {
"-Wdelete-incomplete",
"void f() { [[delete]] x; }"},
1501 {
"dereference_incomplete_type",
1502 R
"cpp(void f() { asm("" : "=r"([[*]]x)::); })cpp"},
1504 for (
auto Case : Tests) {
1506 TU.Code = Main.code().str() +
"\n // error-ok";
1508 TU.build().getDiagnostics(),
1509 ElementsAre(AllOf(diagName(Case.first), hasRange(Main.range()),
1510 withFix(
Fix(
Range{},
"#include \"x.h\"\n",
1511 "Include \"x.h\" for symbol ns::X")))))
1516TEST(IncludeFixerTest, IncompleteEnum) {
1519 Sym.CanonicalDeclaration.FileURI = Sym.Definition.FileURI =
"unittest:///x.h";
1528 TU.ExtraArgs.push_back(
"-std=c++20");
1529 TU.ExtraArgs.push_back(
"-fno-ms-compatibility");
1531 std::vector<std::pair<llvm::StringRef, llvm::StringRef>> Tests{
1532 {
"incomplete_enum",
"enum class X : int; using enum [[X]];"},
1533 {
"underlying_type_of_incomplete_enum",
1534 "[[__underlying_type]](enum X) i;"},
1536 for (
auto Case : Tests) {
1538 TU.Code = Main.code().str() +
"\n // error-ok";
1539 EXPECT_THAT(TU.build().getDiagnostics(),
1540 Contains(AllOf(diagName(Case.first), hasRange(Main.range()),
1541 withFix(
Fix(
Range{},
"#include \"x.h\"\n",
1542 "Include \"x.h\" for symbol X")))))
1547TEST(IncludeFixerTest, NoSuggestIncludeWhenNoDefinitionInHeader) {
1549$insert[[]]namespace ns {
1552class Y : $base[[public ns::X]] {};
1561 Sym.CanonicalDeclaration.FileURI =
"unittest:///x.h";
1562 Sym.Definition.FileURI =
"unittest:///x.cc";
1569 TU.ExternalIndex = Index.get();
1571 EXPECT_THAT(TU.build().getDiagnostics(),
1572 UnorderedElementsAre(
1573 Diag(Test.range(
"base"),
"base class has incomplete type"),
1574 Diag(Test.range(
"access"),
1575 "member access into incomplete type 'ns::X'")));
1578TEST(IncludeFixerTest, Typo) {
1580$insert[[]]namespace ns {
1582 $unqualified1[[X]] x;
1583 // No fix if the unresolved type is used as specifier. (ns::)X::Nested will be
1584 // considered the unresolved type.
1585 $unqualified2[[X]]::Nested n;
1587struct S : $base[[X]] {};
1590 ns::$qualified1[[X]] x; // ns:: is valid.
1591 ns::$qualified2[[X]](); // Error: no member in namespace
1593 ::$global[[Global]] glob;
1595using Type = ns::$template[[Foo]]<int>;
1598 auto Index = buildIndexWithSymbol(
1599 {SymbolWithHeader{
"ns::X",
"unittest:///x.h",
"\"x.h\""},
1600 SymbolWithHeader{
"Global",
"unittest:///global.h",
"\"global.h\""},
1601 SymbolWithHeader{
"ns::Foo",
"unittest:///foo.h",
"\"foo.h\""}});
1602 TU.ExternalIndex = Index.get();
1605 TU.build().getDiagnostics(),
1606 UnorderedElementsAre(
1607 AllOf(
Diag(Test.range(
"unqualified1"),
"unknown type name 'X'"),
1608 diagName(
"unknown_typename"),
1609 withFix(
Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
1610 "Include \"x.h\" for symbol ns::X"))),
1611 Diag(Test.range(
"unqualified2"),
"use of undeclared identifier 'X'"),
1612 AllOf(
Diag(Test.range(
"qualified1"),
1613 "no type named 'X' in namespace 'ns'"),
1614 diagName(
"typename_nested_not_found"),
1615 withFix(
Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
1616 "Include \"x.h\" for symbol ns::X"))),
1617 AllOf(
Diag(Test.range(
"qualified2"),
1618 "no member named 'X' in namespace 'ns'"),
1619 diagName(
"no_member"),
1620 withFix(
Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
1621 "Include \"x.h\" for symbol ns::X"))),
1622 AllOf(
Diag(Test.range(
"global"),
1623 "no type named 'Global' in the global namespace"),
1624 diagName(
"typename_nested_not_found"),
1625 withFix(
Fix(Test.range(
"insert"),
"#include \"global.h\"\n",
1626 "Include \"global.h\" for symbol Global"))),
1627 AllOf(
Diag(Test.range(
"template"),
1628 "no template named 'Foo' in namespace 'ns'"),
1629 diagName(
"no_member_template"),
1630 withFix(
Fix(Test.range(
"insert"),
"#include \"foo.h\"\n",
1631 "Include \"foo.h\" for symbol ns::Foo"))),
1632 AllOf(
Diag(Test.range(
"base"),
"expected class name"),
1633 diagName(
"expected_class_name"),
1634 withFix(
Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
1635 "Include \"x.h\" for symbol ns::X")))));
1638TEST(IncludeFixerTest, TypoInMacro) {
1649 auto Index = buildIndexWithSymbol(
1650 {SymbolWithHeader{
"X",
"unittest:///x.h",
"\"x.h\""},
1651 SymbolWithHeader{
"ns::X",
"unittest:///ns.h",
"\"x.h\""}});
1652 TU.ExternalIndex = Index.get();
1655 TU.ExtraArgs = {
"-fno-ms-compatibility"};
1656 EXPECT_THAT(TU.build().getDiagnostics(), Each(withFix(_)));
1659TEST(IncludeFixerTest, MultipleMatchedSymbols) {
1661$insert[[]]namespace na {
1664 $unqualified[[X]] x;
1670 auto Index = buildIndexWithSymbol(
1671 {SymbolWithHeader{
"na::X",
"unittest:///a.h",
"\"a.h\""},
1672 SymbolWithHeader{
"na::nb::X",
"unittest:///b.h",
"\"b.h\""}});
1673 TU.ExternalIndex = Index.get();
1675 EXPECT_THAT(TU.build().getDiagnostics(),
1676 UnorderedElementsAre(AllOf(
1677 Diag(Test.range(
"unqualified"),
"unknown type name 'X'"),
1678 diagName(
"unknown_typename"),
1679 withFix(
Fix(Test.range(
"insert"),
"#include \"a.h\"\n",
1680 "Include \"a.h\" for symbol na::X"),
1681 Fix(Test.range(
"insert"),
"#include \"b.h\"\n",
1682 "Include \"b.h\" for symbol na::nb::X")))));
1685TEST(IncludeFixerTest, NoCrashMemberAccess) {
1687 struct X { int xyz; };
1688 void g() { X x; x.$[[xy]]; }
1691 auto Index = buildIndexWithSymbol(
1692 SymbolWithHeader{
"na::X",
"unittest:///a.h",
"\"a.h\""});
1693 TU.ExternalIndex = Index.get();
1696 TU.build().getDiagnostics(),
1697 UnorderedElementsAre(
Diag(Test.range(),
"no member named 'xy' in 'X'")));
1700TEST(IncludeFixerTest, NoCrashOnQualifiedConversionFunctionName) {
1703void f() { operator new[](0, operator::align_val_t{}); }
1705 TU.ExtraArgs.push_back("-std=c++17");
1706 auto Index = buildIndexWithSymbol(
1707 SymbolWithHeader{
"std::align_val_t",
"unittest:///new.h",
"<new>"});
1708 TU.ExternalIndex = Index.get();
1710 EXPECT_THAT(TU.build().getDiagnostics(), Not(IsEmpty()));
1713TEST(IncludeFixerTest, FixConversionFunctionTargetType) {
1715$insert[[]]struct Wrapper {
1716 template <typename T>
1717 operator T() const { return T(); }
1722 auto V = W.operator::$target[[Something]]();
1726 auto Index = buildIndexWithSymbol(
1727 SymbolWithHeader{
"Something",
"unittest:///test.h",
"\"test.h\""});
1728 TU.ExternalIndex = Index.get();
1730 EXPECT_THAT(TU.build().getDiagnostics(),
1733 Contains(
Fix(Test.range(
"insert"),
"#include \"test.h\"\n",
1734 "Include \"test.h\" for symbol Something")))));
1737TEST(IncludeFixerTest, UseCachedIndexResults) {
1741$insert[[]]void foo() {
1764 buildIndexWithSymbol(SymbolWithHeader{
"X",
"unittest:///a.h",
"\"a.h\""});
1765 TU.ExternalIndex = Index.get();
1767 auto Parsed = TU.build();
1768 for (
const auto &D : Parsed.getDiagnostics()) {
1769 if (
D.Fixes.size() != 1) {
1770 ADD_FAILURE() <<
"D.Fixes.size() != 1";
1773 EXPECT_EQ(
D.Fixes[0].Message, std::string(
"Include \"a.h\" for symbol X"));
1777TEST(IncludeFixerTest, UnresolvedNameAsSpecifier) {
1779$insert[[]]namespace ns {
1781void g() { ns::$[[scope]]::X_Y(); }
1784 TU.Code = std::string(Test.code());
1786 TU.ExtraArgs.push_back(
"-fno-ms-compatibility");
1787 auto Index = buildIndexWithSymbol(
1788 SymbolWithHeader{
"ns::scope::X_Y",
"unittest:///x.h",
"\"x.h\""});
1789 TU.ExternalIndex = Index.get();
1792 TU.build().getDiagnostics(),
1793 UnorderedElementsAre(
1794 AllOf(
Diag(Test.range(),
"no member named 'scope' in namespace 'ns'"),
1795 diagName(
"no_member"),
1796 withFix(
Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
1797 "Include \"x.h\" for symbol ns::scope::X_Y")))));
1800TEST(IncludeFixerTest, UnresolvedSpecifierWithSemaCorrection) {
1802$insert[[]]namespace clang {
1804 // "clangd::" will be corrected to "clang::" by Sema.
1805 $q1[[clangd]]::$x[[X]] x;
1806 $q2[[clangd]]::$ns[[ns]]::Y y;
1811 TU.Code = std::string(Test.code());
1813 TU.ExtraArgs.push_back(
"-fno-ms-compatibility");
1814 auto Index = buildIndexWithSymbol(
1815 {SymbolWithHeader{
"clang::clangd::X",
"unittest:///x.h",
"\"x.h\""},
1816 SymbolWithHeader{
"clang::clangd::ns::Y",
"unittest:///y.h",
"\"y.h\""}});
1817 TU.ExternalIndex = Index.get();
1820 TU.build().getDiagnostics(),
1821 UnorderedElementsAre(
1822 AllOf(
Diag(Test.range(
"q1"),
"use of undeclared identifier 'clangd'; "
1823 "did you mean 'clang'?"),
1824 diagName(
"undeclared_var_use_suggest"),
1826 Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
1827 "Include \"x.h\" for symbol clang::clangd::X"))),
1828 AllOf(
Diag(Test.range(
"x"),
"no type named 'X' in namespace 'clang'"),
1829 diagName(
"typename_nested_not_found"),
1830 withFix(
Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
1831 "Include \"x.h\" for symbol clang::clangd::X"))),
1833 Diag(Test.range(
"q2"),
"use of undeclared identifier 'clangd'; "
1834 "did you mean 'clang'?"),
1835 diagName(
"undeclared_var_use_suggest"),
1837 Fix(Test.range(
"insert"),
"#include \"y.h\"\n",
1838 "Include \"y.h\" for symbol clang::clangd::ns::Y"))),
1839 AllOf(
Diag(Test.range(
"ns"),
1840 "no member named 'ns' in namespace 'clang'"),
1841 diagName(
"no_member"),
1843 Fix(Test.range(
"insert"),
"#include \"y.h\"\n",
1844 "Include \"y.h\" for symbol clang::clangd::ns::Y")))));
1847TEST(IncludeFixerTest, SpecifiedScopeIsNamespaceAlias) {
1849$insert[[]]namespace a {}
1856 auto Index = buildIndexWithSymbol(
1857 SymbolWithHeader{
"a::X",
"unittest:///x.h",
"\"x.h\""});
1858 TU.ExternalIndex = Index.get();
1860 EXPECT_THAT(TU.build().getDiagnostics(),
1861 UnorderedElementsAre(AllOf(
1862 Diag(Test.range(),
"no type named 'X' in namespace 'a'"),
1863 diagName(
"typename_nested_not_found"),
1864 withFix(
Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
1865 "Include \"x.h\" for symbol a::X")))));
1868TEST(IncludeFixerTest, NoCrashOnTemplateInstantiations) {
1870 template <typename T> struct Templ {
1871 template <typename U>
1872 typename U::type operator=(const U &);
1877 A() { [[a]]; /*error-ok*/ } // crash if we compute scopes lazily.
1882 auto Index = buildIndexWithSymbol({});
1883 TU.ExternalIndex = Index.get();
1886 TU.build().getDiagnostics(),
1887 ElementsAre(
Diag(Test.range(),
"use of undeclared identifier 'a'")));
1890TEST(IncludeFixerTest, HeaderNamedInDiag) {
1892 $insert[[]]int main() {
1897 TU.ExtraArgs = {
"-xc",
"-std=c99",
1898 "-Wno-error=implicit-function-declaration"};
1899 auto Index = buildIndexWithSymbol({});
1900 TU.ExternalIndex = Index.get();
1903 TU.build().getDiagnostics(),
1905 Diag(Test.range(),
"call to undeclared library function 'printf' "
1906 "with type 'int (const char *, ...)'; ISO C99 "
1907 "and later do not support implicit function "
1909 withFix(
Fix(Test.range(
"insert"),
"#include <stdio.h>\n",
1910 "Include <stdio.h> for symbol printf")))));
1912 TU.ExtraArgs = {
"-xc",
"-std=c89"};
1914 TU.build().getDiagnostics(),
1916 Diag(Test.range(),
"implicitly declaring library function 'printf' "
1917 "with type 'int (const char *, ...)'"),
1918 withFix(
Fix(Test.range(
"insert"),
"#include <stdio.h>\n",
1919 "Include <stdio.h> for symbol printf")))));
1922TEST(IncludeFixerTest, CImplicitFunctionDecl) {
1925 TU.Filename =
"test.c";
1926 TU.ExtraArgs = {
"-std=c99",
"-Wno-error=implicit-function-declaration"};
1930 Sym.CanonicalDeclaration.FileURI =
"unittest:///foo.h";
1937 TU.ExternalIndex = Index.get();
1940 TU.build().getDiagnostics(),
1943 "call to undeclared function 'foo'; ISO C99 and later do not "
1944 "support implicit function declarations"),
1945 withFix(
Fix(
Range{},
"#include \"foo.h\"\n",
1946 "Include \"foo.h\" for symbol foo")))));
1948 TU.ExtraArgs = {
"-std=c89",
"-Wall"};
1949 EXPECT_THAT(TU.build().getDiagnostics(),
1951 Diag(Test.range(),
"implicit declaration of function 'foo'"),
1952 withFix(
Fix(
Range{},
"#include \"foo.h\"\n",
1953 "Include \"foo.h\" for symbol foo")))));
1956TEST(DiagsInHeaders, DiagInsideHeader) {
1959 void foo() {})cpp");
1960 Annotations Header("[[no_type_spec]]; // error-ok");
1962 TU.AdditionalFiles = {{
"a.h", std::string(Header.code())}};
1963 EXPECT_THAT(TU.build().getDiagnostics(),
1964 UnorderedElementsAre(AllOf(
1965 Diag(Main.range(),
"in included file: a type specifier is "
1966 "required for all declarations"),
1967 withNote(
Diag(Header.range(),
"error occurred here")))));
1970TEST(DiagsInHeaders, DiagInTransitiveInclude) {
1973 void foo() {})cpp");
1975 TU.AdditionalFiles = {{"a.h",
"#include \"b.h\""},
1976 {
"b.h",
"no_type_spec; // error-ok"}};
1977 EXPECT_THAT(TU.build().getDiagnostics(),
1978 UnorderedElementsAre(
Diag(Main.range(),
1979 "in included file: a type specifier is "
1980 "required for all declarations")));
1983TEST(DiagsInHeaders, DiagInMultipleHeaders) {
1985 #include $a[["a.h"]]
1986 #include $b[["b.h"]]
1987 void foo() {})cpp");
1989 TU.AdditionalFiles = {{"a.h",
"no_type_spec; // error-ok"},
1990 {
"b.h",
"no_type_spec; // error-ok"}};
1991 EXPECT_THAT(TU.build().getDiagnostics(),
1992 UnorderedElementsAre(
1993 Diag(Main.range(
"a"),
"in included file: a type specifier is "
1994 "required for all declarations"),
1995 Diag(Main.range(
"b"),
"in included file: a type specifier is "
1996 "required for all declarations")));
1999TEST(DiagsInHeaders, PreferExpansionLocation) {
2003 void foo() {})cpp");
2005 TU.AdditionalFiles = {
2006 {"a.h",
"#include \"b.h\"\n"},
2007 {
"b.h",
"#ifndef X\n#define X\nno_type_spec; // error-ok\n#endif"}};
2008 EXPECT_THAT(TU.build().getDiagnostics(),
2009 Contains(
Diag(Main.range(),
"in included file: a type specifier "
2010 "is required for all declarations")));
2013TEST(DiagsInHeaders, PreferExpansionLocationMacros) {
2019 void foo() {})cpp");
2021 TU.AdditionalFiles = {
2022 {"a.h",
"#include \"c.h\"\n"},
2023 {
"b.h",
"#include \"c.h\"\n"},
2024 {
"c.h",
"#ifndef X\n#define X\nno_type_spec; // error-ok\n#endif"}};
2025 EXPECT_THAT(TU.build().getDiagnostics(),
2026 UnorderedElementsAre(
Diag(Main.range(),
2027 "in included file: a type specifier is "
2028 "required for all declarations")));
2031TEST(DiagsInHeaders, LimitDiagsOutsideMainFile) {
2035 void foo() {})cpp");
2037 TU.AdditionalFiles = {{"a.h",
"#include \"c.h\"\n"},
2038 {
"b.h",
"#include \"c.h\"\n"},
2042 no_type_spec_0; // error-ok
2054 EXPECT_THAT(TU.build().getDiagnostics(),
2055 UnorderedElementsAre(Diag(Main.range(),
2056 "in included file: a type specifier is "
2057 "required for all declarations")));
2060TEST(DiagsInHeaders, OnlyErrorOrFatal) {
2063 void foo() {})cpp");
2065 [[no_type_spec]]; // error-ok
2068 TU.AdditionalFiles = {{"a.h", std::string(Header.code())}};
2069 EXPECT_THAT(TU.build().getDiagnostics(),
2070 UnorderedElementsAre(AllOf(
2071 Diag(Main.range(),
"in included file: a type specifier is "
2072 "required for all declarations"),
2073 withNote(
Diag(Header.range(),
"error occurred here")))));
2076TEST(DiagsInHeaders, OnlyDefaultErrorOrFatal) {
2078 #include [["a.h"]] // get unused "foo" warning when building preamble.
2081 namespace { void foo() {} }
2082 void func() {foo();} ;)cpp");
2084 TU.AdditionalFiles = {{"a.h", std::string(Header.code())}};
2086 TU.ExtraArgs = {
"-Werror",
"-Wunused"};
2087 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
2090TEST(DiagsInHeaders, FromNonWrittenSources) {
2093 void foo() {})cpp");
2096 int b = [[FOO]]; // error-ok)cpp");
2098 TU.AdditionalFiles = {{"a.h", std::string(Header.code())}};
2099 TU.ExtraArgs = {
"-DFOO=NOOO"};
2100 EXPECT_THAT(TU.build().getDiagnostics(),
2101 UnorderedElementsAre(AllOf(
2103 "in included file: use of undeclared identifier 'NOOO'"),
2104 withNote(
Diag(Header.range(),
"error occurred here")))));
2107TEST(DiagsInHeaders, ErrorFromMacroExpansion) {
2117 TU.AdditionalFiles = {{"a.h", std::string(Header.code())}};
2118 EXPECT_THAT(TU.build().getDiagnostics(),
2119 UnorderedElementsAre(
2120 Diag(Main.range(),
"in included file: use of undeclared "
2121 "identifier 'foo'; did you mean 'fo'?")));
2124TEST(DiagsInHeaders, ErrorFromMacroArgument) {
2134 TU.AdditionalFiles = {{"a.h", std::string(Header.code())}};
2135 EXPECT_THAT(TU.build().getDiagnostics(),
2136 UnorderedElementsAre(
2137 Diag(Main.range(),
"in included file: use of undeclared "
2138 "identifier 'foo'; did you mean 'fo'?")));
2141TEST(IgnoreDiags, FromNonWrittenInclude) {
2143 TU.
ExtraArgs.push_back(
"--include=a.h");
2144 TU.AdditionalFiles = {{
"a.h",
"void main();"}};
2147 EXPECT_THAT(TU.build().getDiagnostics(), UnorderedElementsAre());
2150TEST(ToLSPDiag, RangeIsInMain) {
2153 D.Range = {pos(1, 2), pos(3, 4)};
2154 D.Notes.emplace_back();
2155 Note &N =
D.Notes.back();
2156 N.Range = {pos(2, 3), pos(3, 4)};
2158 D.InsideMainFile =
true;
2159 N.InsideMainFile =
false;
2161 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix>) {
2162 EXPECT_EQ(LSPDiag.range,
D.Range);
2165 D.InsideMainFile =
false;
2166 N.InsideMainFile =
true;
2168 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix>) {
2169 EXPECT_EQ(LSPDiag.range, N.Range);
2173TEST(ParsedASTTest, ModuleSawDiag) {
2176 auto AST = TU.build();
2178 EXPECT_THAT(
AST.getDiagnostics(),
2179 testing::Contains(
Diag(Code.range(), KDiagMsg.str())));
2188 TU.Code =
"#define FOO\n void bar();\n";
2189 auto AST = TU.build();
2190 EXPECT_THAT(
AST.getDiagnostics(), IsEmpty());
2194 TU.Code = Code.code().str();
2195 auto AST = TU.build();
2197 AST.getDiagnostics(),
2198 testing::Contains(
Diag(Code.range(),
"no newline at end of file")));
2202TEST(Diagnostics, Tags) {
2204 TU.
ExtraArgs = {
"-Wunused",
"-Wdeprecated"};
2206 void bar() __attribute__((deprecated));
2209 $deprecated[[bar]]();
2211 TU.Code = Test.code().str();
2212 EXPECT_THAT(TU.build().getDiagnostics(),
2213 UnorderedElementsAre(
2214 AllOf(Diag(Test.range("unused"),
"unused variable 'x'"),
2216 AllOf(
Diag(Test.range(
"deprecated"),
"'bar' is deprecated"),
2220 $typedef[[typedef int INT]];
2222 TU.Code = Test.code();
2223 TU.ClangTidyProvider = addTidyChecks("modernize-use-using");
2225 TU.build().getDiagnostics(),
2226 ifTidyChecks(UnorderedElementsAre(
2227 AllOf(
Diag(Test.range(
"typedef"),
"use 'using' instead of 'typedef'"),
2231TEST(Diagnostics, TidyDiagsArentAffectedFromWerror) {
2234 Annotations Test(R
"cpp($typedef[[typedef int INT]]; // error-ok)cpp");
2235 TU.Code = Test.code().str();
2236 TU.ClangTidyProvider = addTidyChecks("modernize-use-using");
2238 TU.build().getDiagnostics(),
2239 ifTidyChecks(UnorderedElementsAre(
2240 AllOf(
Diag(Test.range(
"typedef"),
"use 'using' instead of 'typedef'"),
2243 diagSeverity(DiagnosticsEngine::Warning)))));
2245 TU.ClangTidyProvider =
2248 TU.build().getDiagnostics(),
2249 ifTidyChecks(UnorderedElementsAre(
2250 AllOf(
Diag(Test.range(
"typedef"),
"use 'using' instead of 'typedef'"),
2252 diagSeverity(DiagnosticsEngine::Error)))));
2255TEST(Diagnostics, DeprecatedDiagsAreHints) {
2257 std::optional<clangd::Diagnostic>
Diag;
2259 D.Range = {pos(1, 2), pos(3, 4)};
2260 D.InsideMainFile =
true;
2264 D.Severity = DiagnosticsEngine::Warning;
2266 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix>) {
2267 Diag = std::move(LSPDiag);
2273 D.Severity = DiagnosticsEngine::Error;
2275 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix>) {
2276 Diag = std::move(LSPDiag);
2283 D.Severity = DiagnosticsEngine::Warning;
2285 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix>) {
2286 Diag = std::move(LSPDiag);
2291TEST(DiagnosticsTest, IncludeCleaner) {
2293$fix[[ $diag[[#include "unused.h"]]
2299 #include <system_header.h>
2306 TU.Code = Test.code().str();
2307 TU.AdditionalFiles["unused.h"] = R
"cpp(
2311 TU.AdditionalFiles["used.h"] = R
"cpp(
2315 TU.AdditionalFiles["ignore.h"] = R
"cpp(
2319 TU.AdditionalFiles["system/system_header.h"] =
"";
2320 TU.ExtraArgs = {
"-isystem" +
testPath(
"system")};
2324 Cfg.Diagnostics.Includes.IgnoreHeader.emplace_back(
2325 [](llvm::StringRef Header) {
return Header.ends_with(
"ignore.h"); });
2327 auto AST = TU.build();
2329 AST.getDiagnostics(),
2331 Diag(Test.range(
"diag"),
2332 "included header unused.h is not used directly"),
2334 withFix(
Fix(Test.range(
"fix"),
"",
"remove #include directive")))));
2335 auto &
Diag =
AST.getDiagnostics().front();
2337 llvm::ValueIs(Not(IsEmpty())));
2338 Cfg.Diagnostics.SuppressAll =
true;
2340 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
2341 Cfg.Diagnostics.SuppressAll =
false;
2342 Cfg.Diagnostics.Suppress = {
"unused-includes"};
2344 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
2347TEST(DiagnosticsTest, FixItFromHeader) {
2348 llvm::StringLiteral Header(R
"cpp(
2350 void foo(int *, int);)cpp");
2355 $diag[[foo]]($fix[[]]x, 1);
2358 TU.Code = Source.code().str();
2359 TU.HeaderCode = Header.str();
2361 TU.build().getDiagnostics(),
2362 UnorderedElementsAre(AllOf(
2363 Diag(Source.range("diag"),
"no matching function for call to 'foo'"),
2364 withFix(
Fix(Source.range(
"fix"),
"&",
2365 "candidate function not viable: no known conversion from "
2366 "'int' to 'int *' for 1st argument; take the address of "
2367 "the argument with &")))));
2370TEST(DiagnosticsTest, UnusedInHeader) {
2373 TU.ExtraArgs.push_back(
"-Wunused-function");
2374 TU.Filename =
"test.c";
2375 EXPECT_THAT(TU.build().getDiagnostics(),
2376 ElementsAre(withID(diag::warn_unused_function)));
2379 TU.Filename =
"test.h";
2380 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
2383TEST(DiagnosticsTest, DontSuppressSubcategories) {
2394 TU.ExtraArgs.push_back("-Wunreachable-code-aggressive");
2395 TU.Code = Source.code().str();
2400 EXPECT_THAT(TU.build().getDiagnostics(),
2401 ElementsAre(diagName(
"-Wunreachable-code-break")));
2405TEST(DiagnosticsTest, TokenMergeFixit) {
2408 return$lparen[[(]]0$rparen[[)]];
2412 SourceManagerForFile SM("TestTU.cpp", Test.code());
2413 DiagnosticsEngine &DE = SM.get().getDiagnostics();
2415 DE.setClient(&DiagConsumer,
false);
2417 LangOptions LangOpts;
2418 DiagConsumer.BeginSourceFile(LangOpts,
nullptr);
2423 SourceLocation LParenBegin = Loc(Test.range(
"lparen").start);
2424 SourceLocation LParenEnd = Loc(Test.range(
"lparen").end);
2425 SourceLocation RParenBegin = Loc(Test.range(
"rparen").start);
2426 SourceLocation RParenEnd = Loc(Test.range(
"rparen").end);
2428 unsigned ID = DE.getCustomDiagID(DiagnosticsEngine::Warning,
2429 "redundant parentheses around expression");
2430 DiagnosticBuilder DB = DE.Report(LParenBegin, ID);
2431 DB.AddFixItHint(FixItHint::CreateRemoval(
2432 CharSourceRange::getCharRange(LParenBegin, LParenEnd)));
2433 DB.AddFixItHint(FixItHint::CreateRemoval(
2434 CharSourceRange::getCharRange(RParenBegin, RParenEnd)));
2436 DiagConsumer.EndSourceFile();
2438 auto Diags = DiagConsumer.take();
2440 ASSERT_EQ(Diags.size(), 1u);
2441 ASSERT_EQ(Diags[0].Fixes.size(), 1u);
2442 const auto &
Fix = Diags[0].Fixes[0];
2445 EXPECT_EQ(
Fix.
Edits[0].range, Test.range(
"lparen"));
2446 EXPECT_EQ(
Fix.
Edits[0].newText,
" ");
2447 EXPECT_EQ(
Fix.
Edits[1].range, Test.range(
"rparen"));
2448 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.
A FeatureModuleSet is a collection of feature modules installed in clangd.
void add(std::unique_ptr< FeatureModule > M)
static std::unique_ptr< SymbolIndex > build(SymbolSlab Symbols, RefSlab Refs, RelationSlab Relations)
Builds an index from slabs. The index takes ownership of the data.
static std::optional< ParsedAST > build(llvm::StringRef Filename, const ParseInputs &Inputs, std::unique_ptr< clang::CompilerInvocation > CI, llvm::ArrayRef< Diag > CompilerInvocationDiags, std::shared_ptr< const PreambleData > Preamble)
Attempts to run Clang and store the parsed AST.
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.
@ Warning
A warning message.
Symbol func(llvm::StringRef Name)
const NamedDecl & findDecl(ParsedAST &AST, llvm::StringRef QName)
Symbol cls(llvm::StringRef Name)
TidyProvider combine(std::vector< TidyProvider > Providers)
std::unique_ptr< CompilerInvocation > buildCompilerInvocation(const ParseInputs &Inputs, clang::DiagnosticConsumer &D, std::vector< std::string > *CC1Args)
Builds compiler invocation that could be used to build AST or preamble.
void toLSPDiags(const Diag &D, const URIForFile &File, const ClangdDiagnosticOptions &Opts, llvm::function_ref< void(clangd::Diagnostic, llvm::ArrayRef< Fix >)> OutFn)
Conversion to LSP diagnostics.
MATCHER_P2(hasFlag, Flag, Path, "")
std::string testPath(PathRef File, llvm::sys::path::Style Style)
static URISchemeRegistry::Add< TestScheme > X(TestScheme::Scheme, "Test schema")
llvm::json::Value toJSON(const FuzzyFindRequest &Request)
TidyProvider addTidyChecks(llvm::StringRef Checks, llvm::StringRef WarningsAsErrors)
Provider the enables a specific set of checks and warnings as errors.
TEST(BackgroundQueueTest, Priority)
Symbol enm(llvm::StringRef Name)
TidyProvider provideClangTidyFiles(ThreadsafeFS &TFS)
Provider that searches for .clang-tidy configuration files in the directory tree.
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.