clang-tools 23.0.0git
DiagnosticsTests.cpp
Go to the documentation of this file.
1//===--- DiagnosticsTests.cpp ------------------------------------*- C++-*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "../clang-tidy/ClangTidyOptions.h"
10#include "Annotations.h"
11#include "Config.h"
12#include "Diagnostics.h"
13#include "Feature.h"
14#include "FeatureModule.h"
15#include "ParsedAST.h"
16#include "Protocol.h"
17#include "TestFS.h"
18#include "TestIndex.h"
19#include "TestTU.h"
20#include "TidyProvider.h"
21#include "index/MemIndex.h"
22#include "index/Ref.h"
23#include "index/Relation.h"
24#include "index/Symbol.h"
25#include "support/Context.h"
26#include "support/Path.h"
27#include "clang/AST/Decl.h"
28#include "clang/Basic/Diagnostic.h"
29#include "clang/Basic/DiagnosticSema.h"
30#include "clang/Basic/LLVM.h"
31#include "clang/Basic/Specifiers.h"
32#include "llvm/ADT/ArrayRef.h"
33#include "llvm/ADT/StringRef.h"
34#include "llvm/Support/JSON.h"
35#include "llvm/Support/ScopedPrinter.h"
36#include "llvm/Support/TargetSelect.h"
37#include "llvm/Testing/Support/SupportHelpers.h"
38#include "gmock/gmock.h"
39#include "gtest/gtest.h"
40#include <cstddef>
41#include <memory>
42#include <optional>
43#include <string>
44#include <utility>
45#include <vector>
46
47namespace clang {
48namespace clangd {
49namespace {
50
51using ::testing::_;
52using ::testing::AllOf;
53using ::testing::Contains;
54using ::testing::Each;
55using ::testing::ElementsAre;
56using ::testing::Field;
57using ::testing::IsEmpty;
58using ::testing::Not;
59using ::testing::Pair;
60using ::testing::SizeIs;
61using ::testing::UnorderedElementsAre;
62
63::testing::Matcher<const Diag &> withFix(::testing::Matcher<Fix> FixMatcher) {
64 return Field(&Diag::Fixes, ElementsAre(FixMatcher));
65}
66
67::testing::Matcher<const Diag &> withFix(::testing::Matcher<Fix> FixMatcher1,
68 ::testing::Matcher<Fix> FixMatcher2) {
69 return Field(&Diag::Fixes, UnorderedElementsAre(FixMatcher1, FixMatcher2));
70}
71
72::testing::Matcher<const Diag &> withID(unsigned ID) {
73 return Field(&Diag::ID, ID);
74}
75::testing::Matcher<const Diag &>
76withNote(::testing::Matcher<Note> NoteMatcher) {
77 return Field(&Diag::Notes, ElementsAre(NoteMatcher));
78}
79
80::testing::Matcher<const Diag &>
81withNote(::testing::Matcher<Note> NoteMatcher1,
82 ::testing::Matcher<Note> NoteMatcher2) {
83 return Field(&Diag::Notes, UnorderedElementsAre(NoteMatcher1, NoteMatcher2));
84}
85
86::testing::Matcher<const Diag &>
87withTag(::testing::Matcher<DiagnosticTag> TagMatcher) {
88 return Field(&Diag::Tags, Contains(TagMatcher));
89}
90
91MATCHER_P(hasRange, Range, "") { return arg.Range == Range; }
92
93MATCHER_P2(Diag, Range, Message,
94 "Diag at " + llvm::to_string(Range) + " = [" + Message + "]") {
95 return arg.Range == Range && arg.Message == Message;
96}
97
98MATCHER_P3(Fix, Range, Replacement, Message,
99 "Fix " + llvm::to_string(Range) + " => " +
100 ::testing::PrintToString(Replacement) + " = [" + Message + "]") {
101 return arg.Message == Message && arg.Edits.size() == 1 &&
102 arg.Edits[0].range == Range && arg.Edits[0].newText == Replacement;
103}
104
105MATCHER_P(fixMessage, Message, "") { return arg.Message == Message; }
106
107MATCHER_P(equalToLSPDiag, LSPDiag,
108 "LSP diagnostic " + llvm::to_string(LSPDiag)) {
109 if (toJSON(arg) != toJSON(LSPDiag)) {
110 *result_listener << llvm::formatv("expected:\n{0:2}\ngot\n{1:2}",
111 toJSON(LSPDiag), toJSON(arg))
112 .str();
113 return false;
114 }
115 return true;
116}
117
118MATCHER_P(diagSource, S, "") { return arg.Source == S; }
119MATCHER_P(diagName, N, "") { return arg.Name == N; }
120MATCHER_P(diagSeverity, S, "") { return arg.Severity == S; }
121
122MATCHER_P(equalToFix, Fix, "LSP fix " + llvm::to_string(Fix)) {
123 if (arg.Message != Fix.Message)
124 return false;
125 if (arg.Edits.size() != Fix.Edits.size())
126 return false;
127 for (std::size_t I = 0; I < arg.Edits.size(); ++I) {
128 if (arg.Edits[I].range != Fix.Edits[I].range ||
129 arg.Edits[I].newText != Fix.Edits[I].newText)
130 return false;
131 }
132 return true;
133}
134
135// Helper function to make tests shorter.
136Position pos(int Line, int Character) {
137 Position Res;
138 Res.line = Line;
139 Res.character = Character;
140 return Res;
141}
142
143// Normally returns the provided diagnostics matcher.
144// If clang-tidy checks are not linked in, returns a matcher for no diagnostics!
145// This is intended for tests where the diagnostics come from clang-tidy checks.
146// We don't #ifdef each individual test as it's intrusive and we want to ensure
147// that as much of the test is still compiled an run as possible.
148::testing::Matcher<std::vector<clangd::Diag>>
149ifTidyChecks(::testing::Matcher<std::vector<clangd::Diag>> M) {
150 if (!CLANGD_TIDY_CHECKS)
151 return IsEmpty();
152 return M;
153}
154
155TEST(DiagnosticsTest, DiagnosticRanges) {
156 // Check we report correct ranges, including various edge-cases.
157 Annotations Test(R"cpp(
158 // error-ok
159 #define ID(X) X
160 namespace test{};
161 void $decl[[foo]]();
162 int main() {
163 struct Container { int* begin(); int* end(); } *container;
164 for (auto i : $insertstar[[]]$range[[container]]) {
165 }
166
167 $typo[[go\
168o]]();
169 foo()$semicolon[[]]//with comments
170 $unk[[unknown]]();
171 double $type[[bar]] = "foo";
172 struct Foo { int x; }; Foo a;
173 a.$nomember[[y]];
174 test::$nomembernamespace[[test]];
175 $macro[[ID($macroarg[[fod]])]]();
176 }
177 )cpp");
178 auto TU = TestTU::withCode(Test.code());
179 EXPECT_THAT(
180 TU.build().getDiagnostics(),
181 ElementsAre(
182 // Make sure the whole token is highlighted.
183 AllOf(Diag(Test.range("range"),
184 "invalid range expression of type 'struct Container *'; "
185 "did you mean to dereference it with '*'?"),
186 withFix(Fix(Test.range("insertstar"), "*", "insert '*'"))),
187 // This range spans lines.
188 AllOf(Diag(Test.range("typo"),
189 "use of undeclared identifier 'goo'; did you mean 'foo'?"),
190 diagSource(Diag::Clang), diagName("undeclared_var_use_suggest"),
191 withFix(
192 Fix(Test.range("typo"), "foo", "change 'go\\…' to 'foo'")),
193 // This is a pretty normal range.
194 withNote(Diag(Test.range("decl"), "'foo' declared here"))),
195 // This range is zero-width and insertion. Therefore make sure we are
196 // not expanding it into other tokens. Since we are not going to
197 // replace those.
198 AllOf(Diag(Test.range("semicolon"), "expected ';' after expression"),
199 withFix(Fix(Test.range("semicolon"), ";", "insert ';'"))),
200 // This range isn't provided by clang, we expand to the token.
201 Diag(Test.range("unk"), "use of undeclared identifier 'unknown'"),
202 Diag(Test.range("type"),
203 "cannot initialize a variable of type 'double' with an lvalue "
204 "of type 'const char[4]'"),
205 Diag(Test.range("nomember"), "no member named 'y' in 'Foo'"),
206 Diag(Test.range("nomembernamespace"),
207 "no member named 'test' in namespace 'test'"),
208 AllOf(Diag(Test.range("macro"),
209 "use of undeclared identifier 'fod'; did you mean 'foo'?"),
210 withFix(Fix(Test.range("macroarg"), "foo",
211 "change 'fod' to 'foo'")))));
212}
213
214// Verify that the -Wswitch case-not-covered diagnostic range covers the
215// whole expression. This is important because the "populate-switch" tweak
216// fires for the full expression range (see tweaks/PopulateSwitchTests.cpp).
217// The quickfix flow only works end-to-end if the tweak can be triggered on
218// the diagnostic's range.
219TEST(DiagnosticsTest, WSwitch) {
220 Annotations Test(R"cpp(
221 enum A { X };
222 struct B { A a; };
223 void foo(B b) {
224 switch ([[b.a]]) {}
225 }
226 )cpp");
227 auto TU = TestTU::withCode(Test.code());
228 TU.ExtraArgs = {"-Wswitch"};
229 EXPECT_THAT(TU.build().getDiagnostics(),
230 ElementsAre(Diag(Test.range(),
231 "enumeration value 'X' not handled in switch")));
232}
233
234TEST(DiagnosticsTest, FlagsMatter) {
235 Annotations Test("[[void]] main() {} // error-ok");
236 auto TU = TestTU::withCode(Test.code());
237 EXPECT_THAT(TU.build().getDiagnostics(),
238 ElementsAre(AllOf(Diag(Test.range(), "'main' must return 'int'"),
239 withFix(Fix(Test.range(), "int",
240 "change 'void' to 'int'")))));
241 // Same code built as C gets different diagnostics.
242 TU.Filename = "Plain.c";
243 EXPECT_THAT(
244 TU.build().getDiagnostics(),
245 ElementsAre(AllOf(
246 Diag(Test.range(), "return type of 'main' is not 'int'"),
247 withFix(Fix(Test.range(), "int", "change return type to 'int'")))));
248}
249
250TEST(DiagnosticsTest, DiagnosticPreamble) {
251 Annotations Test(R"cpp(
252 #include $[["not-found.h"]] // error-ok
253 )cpp");
254
255 auto TU = TestTU::withCode(Test.code());
256 EXPECT_THAT(TU.build().getDiagnostics(),
257 ElementsAre(::testing::AllOf(
258 Diag(Test.range(), "'not-found.h' file not found"),
259 diagSource(Diag::Clang), diagName("pp_file_not_found"))));
260}
261
262TEST(DiagnosticsTest, DeduplicatedClangTidyDiagnostics) {
263 Annotations Test(R"cpp(
264 float foo = [[0.1f]];
265 )cpp");
266 auto TU = TestTU::withCode(Test.code());
267 // Enable alias clang-tidy checks, these checks emit the same diagnostics
268 // (except the check name).
269 TU.ClangTidyProvider = addTidyChecks("readability-uppercase-literal-suffix,"
270 "cert-dcl16-c");
271 // Verify that we filter out the duplicated diagnostic message.
272 EXPECT_THAT(
273 TU.build().getDiagnostics(),
274 ifTidyChecks(UnorderedElementsAre(::testing::AllOf(
275 Diag(Test.range(),
276 "floating point literal has suffix 'f', which is not uppercase"),
277 diagSource(Diag::ClangTidy)))));
278
279 Test = Annotations(R"cpp(
280 template<typename T>
281 void func(T) {
282 float f = [[0.3f]];
283 }
284 void k() {
285 func(123);
286 func(2.0);
287 }
288 )cpp");
289 TU.Code = std::string(Test.code());
290 // The check doesn't handle template instantiations which ends up emitting
291 // duplicated messages, verify that we deduplicate them.
292 EXPECT_THAT(
293 TU.build().getDiagnostics(),
294 ifTidyChecks(UnorderedElementsAre(::testing::AllOf(
295 Diag(Test.range(),
296 "floating point literal has suffix 'f', which is not uppercase"),
297 diagSource(Diag::ClangTidy)))));
298}
299
300TEST(DiagnosticsTest, ClangTidy) {
301 Annotations Test(R"cpp(
302 #include $deprecated[["assert.h"]]
303
304 #define $macrodef[[SQUARE]](X) (X)*(X)
305 int $main[[main]]() {
306 int y = 4;
307 return SQUARE($macroarg[[++]]y);
308 return $doubled[[sizeof(sizeof(int))]];
309 }
310
311 // misc-no-recursion uses a custom traversal from the TUDecl
312 void foo();
313 void $bar[[bar]]() {
314 foo();
315 }
316 void $foo[[foo]]() {
317 bar();
318 }
319 )cpp");
320 auto TU = TestTU::withCode(Test.code());
321 TU.AdditionalFiles["system/assert.h"] = ""; // Suppress "not found" error.
322 TU.ClangTidyProvider = addTidyChecks("bugprone-sizeof-expression,"
323 "bugprone-macro-repeated-side-effects,"
324 "modernize-deprecated-headers,"
325 "modernize-use-trailing-return-type,"
326 "misc-no-recursion");
327 TU.ExtraArgs.push_back("-isystem" + testPath("system"));
328 TU.ExtraArgs.push_back("-Wno-unsequenced");
329 EXPECT_THAT(
330 TU.build().getDiagnostics(),
331 ifTidyChecks(UnorderedElementsAre(
332 AllOf(Diag(Test.range("deprecated"),
333 "inclusion of deprecated C++ header 'assert.h'; consider "
334 "using 'cassert' instead"),
335 diagSource(Diag::ClangTidy),
336 diagName("modernize-deprecated-headers"),
337 withFix(Fix(Test.range("deprecated"), "<cassert>",
338 "change '\"assert.h\"' to '<cassert>'"))),
339 Diag(Test.range("doubled"),
340 "suspicious usage of 'sizeof(sizeof(...))'"),
341 AllOf(Diag(Test.range("macroarg"),
342 "side effects in the 1st macro argument 'X' are "
343 "repeated in "
344 "macro expansion"),
345 diagSource(Diag::ClangTidy),
346 diagName("bugprone-macro-repeated-side-effects"),
347 withNote(Diag(Test.range("macrodef"),
348 "macro 'SQUARE' defined here"))),
349 AllOf(Diag(Test.range("main"),
350 "use a trailing return type for this function"),
351 diagSource(Diag::ClangTidy),
352 diagName("modernize-use-trailing-return-type"),
353 // Verify there's no "[check-name]" suffix in the message.
354 withFix(fixMessage(
355 "use a trailing return type for this function"))),
356 Diag(Test.range("foo"),
357 "function 'foo' is within a recursive call chain"),
358 Diag(Test.range("bar"),
359 "function 'bar' is within a recursive call chain"))));
360}
361
362TEST(DiagnosticsTest, ClangTidyEOF) {
363 // clang-format off
364 Annotations Test(R"cpp(
365 [[#]]include <b.h>
366 #include "a.h")cpp");
367 // clang-format on
368 auto TU = TestTU::withCode(Test.code());
369 TU.ExtraArgs = {"-isystem."};
370 TU.AdditionalFiles["a.h"] = TU.AdditionalFiles["b.h"] = "";
371 TU.ClangTidyProvider = addTidyChecks("llvm-include-order");
372 EXPECT_THAT(
373 TU.build().getDiagnostics(),
374 ifTidyChecks(Contains(
375 AllOf(Diag(Test.range(), "#includes are not sorted properly"),
376 diagSource(Diag::ClangTidy), diagName("llvm-include-order")))));
377}
378
379TEST(DiagnosticTest, TemplatesInHeaders) {
380 // Diagnostics from templates defined in headers are placed at the expansion.
381 Annotations Main(R"cpp(
382 Derived<int> [[y]]; // error-ok
383 )cpp");
384 Annotations Header(R"cpp(
385 template <typename T>
386 struct Derived : [[T]] {};
387 )cpp");
388 TestTU TU = TestTU::withCode(Main.code());
389 TU.HeaderCode = Header.code().str();
390 EXPECT_THAT(
391 TU.build().getDiagnostics(),
392 ElementsAre(AllOf(
393 Diag(Main.range(), "in template: base specifier must name a class"),
394 withNote(Diag(Header.range(), "error occurred here"),
395 Diag(Main.range(), "in instantiation of template class "
396 "'Derived<int>' requested here")))));
397}
398
399TEST(DiagnosticTest, MakeUnique) {
400 // We usually miss diagnostics from header functions as we don't parse them.
401 // std::make_unique is an exception.
402 Annotations Main(R"cpp(
403 struct S { S(char*); };
404 auto x = std::[[make_unique]]<S>(42); // error-ok
405 )cpp");
406 TestTU TU = TestTU::withCode(Main.code());
407 TU.HeaderCode = R"cpp(
408 namespace std {
409 // These mocks aren't quite right - we omit unique_ptr for simplicity.
410 // forward is included to show its body is not needed to get the diagnostic.
411 template <typename T> T&& forward(T& t);
412 template <typename T, typename... A> T* make_unique(A&&... args) {
413 return new T(std::forward<A>(args)...);
414 }
415 }
416 )cpp";
417 EXPECT_THAT(TU.build().getDiagnostics(),
418 UnorderedElementsAre(
419 Diag(Main.range(),
420 "in template: "
421 "no matching constructor for initialization of 'S'")));
422}
423
424TEST(DiagnosticTest, CoroutineInHeader) {
425 StringRef CoroutineH = R"cpp(
426namespace std {
427template <class Ret, typename... T>
428struct coroutine_traits { using promise_type = typename Ret::promise_type; };
429
430template <class Promise = void>
431struct coroutine_handle {
432 static coroutine_handle from_address(void *) noexcept;
433 static coroutine_handle from_promise(Promise &promise);
434 constexpr void* address() const noexcept;
435};
436template <>
437struct coroutine_handle<void> {
438 template <class PromiseType>
439 coroutine_handle(coroutine_handle<PromiseType>) noexcept;
440 static coroutine_handle from_address(void *);
441 constexpr void* address() const noexcept;
442};
443
444struct awaitable {
445 bool await_ready() noexcept { return false; }
446 void await_suspend(coroutine_handle<>) noexcept {}
447 void await_resume() noexcept {}
448};
449} // namespace std
450 )cpp";
451
452 StringRef Header = R"cpp(
453#include "coroutine.h"
454template <typename T> struct [[clang::coro_return_type]] Gen {
455 struct promise_type {
456 Gen<T> get_return_object() {
457 return {};
458 }
459 std::awaitable initial_suspend();
460 std::awaitable final_suspend() noexcept;
461 void unhandled_exception();
462 void return_value(T t);
463 };
464};
465
466Gen<int> foo_coro(int b) { co_return b; }
467 )cpp";
468 Annotations Main(R"cpp(
469// error-ok
470#include "header.hpp"
471Gen<int> $[[bar_coro]](int b) { return foo_coro(b); }
472 )cpp");
473 TestTU TU = TestTU::withCode(Main.code());
474 TU.AdditionalFiles["coroutine.h"] = std::string(CoroutineH);
475 TU.AdditionalFiles["header.hpp"] = std::string(Header);
476 TU.ExtraArgs.push_back("--std=c++20");
477 EXPECT_THAT(TU.build().getDiagnostics(), ElementsAre(hasRange(Main.range())));
478}
479
480TEST(DiagnosticTest, MakeShared) {
481 // We usually miss diagnostics from header functions as we don't parse them.
482 // std::make_shared is only parsed when --parse-forwarding-functions is set
483 Annotations Main(R"cpp(
484 struct S { S(char*); };
485 auto x = std::[[make_shared]]<S>(42); // error-ok
486 )cpp");
487 TestTU TU = TestTU::withCode(Main.code());
488 TU.HeaderCode = R"cpp(
489 namespace std {
490 // These mocks aren't quite right - we omit shared_ptr for simplicity.
491 // forward is included to show its body is not needed to get the diagnostic.
492 template <typename T> T&& forward(T& t);
493 template <typename T, typename... A> T* make_shared(A&&... args) {
494 return new T(std::forward<A>(args)...);
495 }
496 }
497 )cpp";
498 TU.ParseOpts.PreambleParseForwardingFunctions = true;
499 EXPECT_THAT(TU.build().getDiagnostics(),
500 UnorderedElementsAre(
501 Diag(Main.range(),
502 "in template: "
503 "no matching constructor for initialization of 'S'")));
504}
505
506TEST(DiagnosticTest, NoMultipleDiagnosticInFlight) {
507 Annotations Main(R"cpp(
508 template <typename T> struct Foo {
509 T *begin();
510 T *end();
511 };
512 struct LabelInfo {
513 int a;
514 bool b;
515 };
516
517 void f() {
518 Foo<LabelInfo> label_info_map;
519 [[for]] (auto it = label_info_map.begin(); it != label_info_map.end(); ++it) {
520 auto S = *it;
521 }
522 }
523 )cpp");
524 TestTU TU = TestTU::withCode(Main.code());
525 TU.ClangTidyProvider = addTidyChecks("modernize-loop-convert");
526 EXPECT_THAT(
527 TU.build().getDiagnostics(),
528 ifTidyChecks(UnorderedElementsAre(::testing::AllOf(
529 Diag(Main.range(), "use range-based for loop instead"),
530 diagSource(Diag::ClangTidy), diagName("modernize-loop-convert")))));
531}
532
533TEST(DiagnosticTest, RespectsDiagnosticConfig) {
534 Annotations Main(R"cpp(
535 // error-ok
536 void x() {
537 [[unknown]]();
538 $ret[[return]] 42;
539 }
540 )cpp");
541 auto TU = TestTU::withCode(Main.code());
542 EXPECT_THAT(
543 TU.build().getDiagnostics(),
544 ElementsAre(Diag(Main.range(), "use of undeclared identifier 'unknown'"),
545 Diag(Main.range("ret"),
546 "void function 'x' should not return a value")));
547 Config Cfg;
548 Cfg.Diagnostics.Suppress.insert("return-mismatch");
549 WithContextValue WithCfg(Config::Key, std::move(Cfg));
550 EXPECT_THAT(TU.build().getDiagnostics(),
551 ElementsAre(Diag(Main.range(),
552 "use of undeclared identifier 'unknown'")));
553}
554
555TEST(DiagnosticTest, RespectsDiagnosticConfigInHeader) {
556 Annotations Header(R"cpp(
557 int x = "42"; // error-ok
558 )cpp");
559 Annotations Main(R"cpp(
560 #include "header.hpp"
561 )cpp");
562 auto TU = TestTU::withCode(Main.code());
563 TU.AdditionalFiles["header.hpp"] = std::string(Header.code());
564 Config Cfg;
565 Cfg.Diagnostics.Suppress.insert("init_conversion_failed");
566 WithContextValue WithCfg(Config::Key, std::move(Cfg));
567 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
568}
569
570TEST(DiagnosticTest, ClangTidySuppressionComment) {
571 Annotations Main(R"cpp(
572 int main() {
573 int i = 3;
574 double d = 8 / i; // NOLINT
575 // NOLINTNEXTLINE
576 double e = 8 / i;
577 #define BAD 8 / i
578 double f = BAD; // NOLINT
579 double g = [[8]] / i;
580 #define BAD2 BAD
581 double h = BAD2; // NOLINT
582 // NOLINTBEGIN
583 double x = BAD2;
584 double y = BAD2;
585 // NOLINTEND
586
587 // verify no crashes on unmatched nolints.
588 // NOLINTBEGIN
589 }
590 )cpp");
591 TestTU TU = TestTU::withCode(Main.code());
592 TU.ClangTidyProvider = addTidyChecks("bugprone-integer-division");
593 EXPECT_THAT(
594 TU.build().getDiagnostics(),
595 ifTidyChecks(UnorderedElementsAre(::testing::AllOf(
596 Diag(Main.range(), "result of integer division used in a floating "
597 "point context; possible loss of precision"),
598 diagSource(Diag::ClangTidy),
599 diagName("bugprone-integer-division")))));
600}
601
602TEST(DiagnosticTest, ClangTidySystemMacro) {
603 Annotations Main(R"cpp(
604 #include "user.h"
605 #include "system.h"
606 int i = 3;
607 double x = $inline[[8]] / i;
608 double y = $user[[DIVIDE_USER]](i);
609 double z = DIVIDE_SYS(i);
610 )cpp");
611
612 auto TU = TestTU::withCode(Main.code());
613 TU.AdditionalFiles["user.h"] = R"cpp(
614 #define DIVIDE_USER(Y) 8/Y
615 )cpp";
616 TU.AdditionalFiles["system.h"] = R"cpp(
617 #pragma clang system_header
618 #define DIVIDE_SYS(Y) 8/Y
619 )cpp";
620
621 TU.ClangTidyProvider = addTidyChecks("bugprone-integer-division");
622 std::string BadDivision = "result of integer division used in a floating "
623 "point context; possible loss of precision";
624
625 // Expect to see warning from user macros, but not system macros.
626 // This matches clang-tidy --system-headers=0 (the default).
627 EXPECT_THAT(TU.build().getDiagnostics(),
628 ifTidyChecks(
629 UnorderedElementsAre(Diag(Main.range("inline"), BadDivision),
630 Diag(Main.range("user"), BadDivision))));
631}
632
633TEST(DiagnosticTest, ClangTidyWarningAsError) {
634 Annotations Main(R"cpp(
635 int main() {
636 int i = 3;
637 double f = [[8]] / i; // error-ok
638 }
639 )cpp");
640 TestTU TU = TestTU::withCode(Main.code());
641 TU.ClangTidyProvider =
642 addTidyChecks("bugprone-integer-division", "bugprone-integer-division");
643 EXPECT_THAT(
644 TU.build().getDiagnostics(),
645 ifTidyChecks(UnorderedElementsAre(::testing::AllOf(
646 Diag(Main.range(), "result of integer division used in a floating "
647 "point context; possible loss of precision"),
648 diagSource(Diag::ClangTidy), diagName("bugprone-integer-division"),
649 diagSeverity(DiagnosticsEngine::Error)))));
650}
651
652TidyProvider addClangArgs(std::vector<llvm::StringRef> ExtraArgs,
653 llvm::StringRef Checks) {
654 return [ExtraArgs = std::move(ExtraArgs), Checks = Checks.str()](
655 tidy::ClangTidyOptions &Opts, llvm::StringRef) {
656 if (!Opts.ExtraArgs)
657 Opts.ExtraArgs.emplace();
658 for (llvm::StringRef Arg : ExtraArgs)
659 Opts.ExtraArgs->emplace_back(Arg);
660 if (!Checks.empty())
661 Opts.Checks = Checks;
662 };
663}
664
665TEST(DiagnosticTest, ClangTidyEnablesClangWarning) {
666 Annotations Main(R"cpp( // error-ok
667 static void [[foo]]() {}
668 )cpp");
669 TestTU TU = TestTU::withCode(Main.code());
670 // This is always emitted as a clang warning, not a clang-tidy diagnostic.
671 auto UnusedFooWarning =
672 AllOf(Diag(Main.range(), "unused function 'foo'"),
673 diagName("-Wunused-function"), diagSource(Diag::Clang),
674 diagSeverity(DiagnosticsEngine::Warning));
675
676 // Check the -Wunused warning isn't initially on.
677 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
678
679 // We enable warnings based on clang-tidy extra args, if the matching
680 // clang-diagnostic- is there.
681 TU.ClangTidyProvider =
682 addClangArgs({"-Wunused"}, "clang-diagnostic-unused-function");
683 EXPECT_THAT(TU.build().getDiagnostics(), ElementsAre(UnusedFooWarning));
684
685 // clang-diagnostic-* is acceptable
686 TU.ClangTidyProvider = addClangArgs({"-Wunused"}, "clang-diagnostic-*");
687 EXPECT_THAT(TU.build().getDiagnostics(), ElementsAre(UnusedFooWarning));
688 // And plain * (may turn on other checks too).
689 TU.ClangTidyProvider = addClangArgs({"-Wunused"}, "*");
690 EXPECT_THAT(TU.build().getDiagnostics(), Contains(UnusedFooWarning));
691 // And we can explicitly exclude a category too.
692 TU.ClangTidyProvider = addClangArgs(
693 {"-Wunused"}, "clang-diagnostic-*,-clang-diagnostic-unused-function");
694 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
695
696 // Without the exact check specified, the warnings are not enabled.
697 TU.ClangTidyProvider = addClangArgs({"-Wunused"}, "clang-diagnostic-unused");
698 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
699
700 // We don't respect other args.
701 TU.ClangTidyProvider = addClangArgs({"-Wunused", "-Dfoo=bar"},
702 "clang-diagnostic-unused-function");
703 EXPECT_THAT(TU.build().getDiagnostics(), ElementsAre(UnusedFooWarning))
704 << "Not unused function 'bar'!";
705
706 // -Werror doesn't apply to warnings enabled by clang-tidy extra args.
707 TU.ExtraArgs = {"-Werror"};
708 TU.ClangTidyProvider =
709 addClangArgs({"-Wunused"}, "clang-diagnostic-unused-function");
710 EXPECT_THAT(TU.build().getDiagnostics(),
711 ElementsAre(diagSeverity(DiagnosticsEngine::Warning)));
712
713 // But clang-tidy extra args won't *downgrade* errors to warnings either.
714 TU.ExtraArgs = {"-Wunused", "-Werror"};
715 TU.ClangTidyProvider =
716 addClangArgs({"-Wunused"}, "clang-diagnostic-unused-function");
717 EXPECT_THAT(TU.build().getDiagnostics(),
718 ElementsAre(diagSeverity(DiagnosticsEngine::Error)));
719
720 // FIXME: we're erroneously downgrading the whole group, this should be Error.
721 TU.ExtraArgs = {"-Wunused-function", "-Werror"};
722 TU.ClangTidyProvider =
723 addClangArgs({"-Wunused"}, "clang-diagnostic-unused-label");
724 EXPECT_THAT(TU.build().getDiagnostics(),
725 ElementsAre(diagSeverity(DiagnosticsEngine::Warning)));
726
727 // This looks silly, but it's the typical result if a warning is enabled by a
728 // high-level .clang-tidy file and disabled by a low-level one.
729 TU.ExtraArgs = {};
730 TU.ClangTidyProvider = addClangArgs({"-Wunused", "-Wno-unused"},
731 "clang-diagnostic-unused-function");
732 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
733
734 // Overriding only works in the proper order.
735 TU.ClangTidyProvider =
736 addClangArgs({"-Wunused"}, {"clang-diagnostic-unused-function"});
737 EXPECT_THAT(TU.build().getDiagnostics(), SizeIs(1));
738
739 // More specific vs less-specific: match clang behavior
740 TU.ClangTidyProvider = addClangArgs({"-Wunused", "-Wno-unused-function"},
741 {"clang-diagnostic-unused-function"});
742 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
743 TU.ClangTidyProvider = addClangArgs({"-Wunused-function", "-Wno-unused"},
744 {"clang-diagnostic-unused-function"});
745 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
746
747 // We do allow clang-tidy config to disable warnings from the compile
748 // command. It's unclear this is ideal, but it's hard to avoid.
749 TU.ExtraArgs = {"-Wunused"};
750 TU.ClangTidyProvider = addClangArgs({"-Wno-unused"}, {});
751 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
752
753 TU.ExtraArgs = {"-Wno-unused"};
754 TU.ClangTidyProvider = addClangArgs({"-Wunused"}, {"-*, clang-diagnostic-*"});
755 EXPECT_THAT(TU.build().getDiagnostics(), SizeIs(1));
756}
757
758TEST(DiagnosticTest, LongFixMessages) {
759 // We limit the size of printed code.
760 Annotations Source(R"cpp(
761 int main() {
762 // error-ok
763 int somereallyreallyreallyreallyreallyreallyreallyreallylongidentifier;
764 [[omereallyreallyreallyreallyreallyreallyreallyreallylongidentifier]]= 10;
765 }
766 )cpp");
767 TestTU TU = TestTU::withCode(Source.code());
768 EXPECT_THAT(
769 TU.build().getDiagnostics(),
770 ElementsAre(withFix(Fix(
771 Source.range(),
772 "somereallyreallyreallyreallyreallyreallyreallyreallylongidentifier",
773 "change 'omereallyreallyreallyreallyreallyreallyreallyreall…' to "
774 "'somereallyreallyreallyreallyreallyreallyreallyreal…'"))));
775 // Only show changes up to a first newline.
776 Source = Annotations(R"cpp(
777 // error-ok
778 int main() {
779 int ident;
780 [[ide\
781n]] = 10; // error-ok
782 }
783 )cpp");
784 TU.Code = std::string(Source.code());
785 EXPECT_THAT(TU.build().getDiagnostics(),
786 ElementsAre(withFix(
787 Fix(Source.range(), "ident", "change 'ide\\…' to 'ident'"))));
788}
789
790TEST(DiagnosticTest, NewLineFixMessage) {
791 Annotations Source("int a;[[]]");
792 TestTU TU = TestTU::withCode(Source.code());
793 TU.ExtraArgs = {"-Wnewline-eof"};
794 EXPECT_THAT(
795 TU.build().getDiagnostics(),
796 ElementsAre(withFix((Fix(Source.range(), "\n", "insert '\\n'")))));
797}
798
799TEST(DiagnosticTest, ClangTidySuppressionCommentTrumpsWarningAsError) {
800 Annotations Main(R"cpp(
801 int main() {
802 int i = 3;
803 double f = [[8]] / i; // NOLINT
804 }
805 )cpp");
806 TestTU TU = TestTU::withCode(Main.code());
807 TU.ClangTidyProvider =
808 addTidyChecks("bugprone-integer-division", "bugprone-integer-division");
809 EXPECT_THAT(TU.build().getDiagnostics(), UnorderedElementsAre());
810}
811
812TEST(DiagnosticTest, ClangTidyNoLiteralDataInMacroToken) {
813 Annotations Main(R"cpp(
814 #define SIGTERM 15
815 using pthread_t = int;
816 int pthread_kill(pthread_t thread, int sig);
817 int func() {
818 pthread_t thread;
819 return pthread_kill(thread, 0);
820 }
821 )cpp");
822 TestTU TU = TestTU::withCode(Main.code());
823 TU.ClangTidyProvider = addTidyChecks("bugprone-bad-signal-to-kill-thread");
824 EXPECT_THAT(TU.build().getDiagnostics(), UnorderedElementsAre()); // no-crash
825}
826
827TEST(DiagnosticTest, BadSignalToKillThreadInPreamble) {
828 Annotations Main(R"cpp(
829 #include "signal.h"
830 using pthread_t = int;
831 int pthread_kill(pthread_t thread, int sig);
832 int func() {
833 pthread_t thread;
834 return pthread_kill(thread, 15);
835 }
836 )cpp");
837 TestTU TU = TestTU::withCode(Main.code());
838 TU.HeaderFilename = "signal.h";
839 TU.HeaderCode = "#define SIGTERM 15";
840 TU.ClangTidyProvider = addTidyChecks("bugprone-bad-signal-to-kill-thread");
841 EXPECT_THAT(TU.build().getDiagnostics(),
842 ifTidyChecks(UnorderedElementsAre(
843 diagName("bugprone-bad-signal-to-kill-thread"))));
844}
845
846TEST(DiagnosticTest, ClangTidyMacroToEnumCheck) {
847 Annotations Main(R"cpp(
848 #if 1
849 auto foo();
850 #endif
851 )cpp");
852 TestTU TU = TestTU::withCode(Main.code());
853 std::vector<TidyProvider> Providers;
854 Providers.push_back(
855 addTidyChecks("cppcoreguidelines-macro-to-enum,modernize-macro-to-enum"));
856 Providers.push_back(disableUnusableChecks());
857 TU.ClangTidyProvider = combine(std::move(Providers));
858 EXPECT_THAT(TU.build().getDiagnostics(), UnorderedElementsAre()); // no-crash
859}
860
861TEST(DiagnosticTest, ElseAfterReturnRange) {
862 Annotations Main(R"cpp(
863 int foo(int cond) {
864 if (cond == 1) {
865 return 42;
866 } [[else]] if (cond == 2) {
867 return 43;
868 }
869 return 44;
870 }
871 )cpp");
872 TestTU TU = TestTU::withCode(Main.code());
873 TU.ClangTidyProvider = addTidyChecks("llvm-else-after-return");
874 EXPECT_THAT(TU.build().getDiagnostics(),
875 ifTidyChecks(ElementsAre(
876 Diag(Main.range(), "do not use 'else' after 'return'"))));
877}
878
879TEST(DiagnosticTest, ClangTidySelfContainedDiags) {
880 Annotations Main(R"cpp($MathHeader[[]]
881 struct Foo{
882 int A, B;
883 Foo()$Fix[[]] {
884 $A[[A = 1;]]
885 $B[[B = 1;]]
886 }
887 };
888 void InitVariables() {
889 float $C[[C]]$CFix[[]];
890 double $D[[D]]$DFix[[]];
891 }
892 )cpp");
893 TestTU TU = TestTU::withCode(Main.code());
894 TU.ClangTidyProvider =
895 addTidyChecks("cppcoreguidelines-prefer-member-initializer,"
896 "cppcoreguidelines-init-variables");
897 clangd::Fix ExpectedAFix;
898 ExpectedAFix.Message =
899 "'A' should be initialized in a member initializer of the constructor";
900 ExpectedAFix.Edits.push_back(TextEdit{Main.range("Fix"), " : A(1)"});
901 ExpectedAFix.Edits.push_back(TextEdit{Main.range("A"), ""});
902
903 // When invoking clang-tidy normally, this code would produce `, B(1)` as the
904 // fix the `B` member, as it would think its already included the ` : ` from
905 // the previous `A` fix.
906 clangd::Fix ExpectedBFix;
907 ExpectedBFix.Message =
908 "'B' should be initialized in a member initializer of the constructor";
909 ExpectedBFix.Edits.push_back(TextEdit{Main.range("Fix"), " : B(1)"});
910 ExpectedBFix.Edits.push_back(TextEdit{Main.range("B"), ""});
911
912 clangd::Fix ExpectedCFix;
913 ExpectedCFix.Message = "variable 'C' is not initialized";
914 ExpectedCFix.Edits.push_back(TextEdit{Main.range("CFix"), " = NAN"});
915 ExpectedCFix.Edits.push_back(
916 TextEdit{Main.range("MathHeader"), "#include <math.h>\n\n"});
917
918 // Again in clang-tidy only the include directive would be emitted for the
919 // first warning. However we need the include attaching for both warnings.
920 clangd::Fix ExpectedDFix;
921 ExpectedDFix.Message = "variable 'D' is not initialized";
922 ExpectedDFix.Edits.push_back(TextEdit{Main.range("DFix"), " = NAN"});
923 ExpectedDFix.Edits.push_back(
924 TextEdit{Main.range("MathHeader"), "#include <math.h>\n\n"});
925 EXPECT_THAT(
926 TU.build().getDiagnostics(),
927 ifTidyChecks(UnorderedElementsAre(
928 AllOf(Diag(Main.range("A"), "'A' should be initialized in a member "
929 "initializer of the constructor"),
930 withFix(equalToFix(ExpectedAFix))),
931 AllOf(Diag(Main.range("B"), "'B' should be initialized in a member "
932 "initializer of the constructor"),
933 withFix(equalToFix(ExpectedBFix))),
934 AllOf(Diag(Main.range("C"), "variable 'C' is not initialized"),
935 withFix(equalToFix(ExpectedCFix))),
936 AllOf(Diag(Main.range("D"), "variable 'D' is not initialized"),
937 withFix(equalToFix(ExpectedDFix))))));
938}
939
940TEST(DiagnosticTest, ClangTidySelfContainedDiagsFormatting) {
941 Annotations Main(R"cpp(
942 class Interface {
943 public:
944 virtual void Reset1() = 0;
945 virtual void Reset2() = 0;
946 };
947 class A : public Interface {
948 // This will be marked by clangd to use override instead of virtual
949 $virtual1[[virtual ]]void $Reset1[[Reset1]]()$override1[[]];
950 $virtual2[[virtual ]]/**/void $Reset2[[Reset2]]()$override2[[]];
951 };
952 )cpp");
953 TestTU TU = TestTU::withCode(Main.code());
954 TU.ClangTidyProvider =
955 addTidyChecks("cppcoreguidelines-explicit-virtual-functions,");
956 clangd::Fix const ExpectedFix1{
957 "prefer using 'override' or (rarely) 'final' "
958 "instead of 'virtual'",
959 {TextEdit{Main.range("override1"), " override"},
960 TextEdit{Main.range("virtual1"), ""}},
961 {}};
962 clangd::Fix const ExpectedFix2{
963 "prefer using 'override' or (rarely) 'final' "
964 "instead of 'virtual'",
965 {TextEdit{Main.range("override2"), " override"},
966 TextEdit{Main.range("virtual2"), ""}},
967 {}};
968 // Note that in the Fix we expect the "virtual" keyword and the following
969 // whitespace to be deleted
970 EXPECT_THAT(TU.build().getDiagnostics(),
971 ifTidyChecks(UnorderedElementsAre(
972 AllOf(Diag(Main.range("Reset1"),
973 "prefer using 'override' or (rarely) 'final' "
974 "instead of 'virtual'"),
975 withFix(equalToFix(ExpectedFix1))),
976 AllOf(Diag(Main.range("Reset2"),
977 "prefer using 'override' or (rarely) 'final' "
978 "instead of 'virtual'"),
979 withFix(equalToFix(ExpectedFix2))))));
980}
981
982TEST(DiagnosticsTest, ClangTidyCallingIntoPreprocessor) {
983 std::string Main = R"cpp(
984 extern "C" {
985 #include "b.h"
986 }
987 )cpp";
988 std::string Header = R"cpp(
989 #define EXTERN extern
990 EXTERN int waldo();
991 )cpp";
992 auto TU = TestTU::withCode(Main);
993 TU.AdditionalFiles["b.h"] = Header;
994 TU.ClangTidyProvider = addTidyChecks("modernize-use-trailing-return-type");
995 // Check that no assertion failures occur during the build
996 TU.build();
997}
998
999TEST(DiagnosticsTest, Preprocessor) {
1000 // This looks like a preamble, but there's an #else in the middle!
1001 // Check that:
1002 // - the #else doesn't generate diagnostics (we had this bug)
1003 // - we get diagnostics from the taken branch
1004 // - we get no diagnostics from the not taken branch
1005 Annotations Test(R"cpp(
1006 #ifndef FOO
1007 #define FOO
1008 int a = [[b]]; // error-ok
1009 #else
1010 int x = y;
1011 #endif
1012 )cpp");
1013 EXPECT_THAT(
1014 TestTU::withCode(Test.code()).build().getDiagnostics(),
1015 ElementsAre(Diag(Test.range(), "use of undeclared identifier 'b'")));
1016}
1017
1018TEST(DiagnosticsTest, IgnoreVerify) {
1019 auto TU = TestTU::withCode(R"cpp(
1020 int a; // expected-error {{}}
1021 )cpp");
1022 TU.ExtraArgs.push_back("-Xclang");
1023 TU.ExtraArgs.push_back("-verify");
1024 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
1025}
1026
1027TEST(DiagnosticTest, IgnoreBEFilelistOptions) {
1028 auto TU = TestTU::withCode("");
1029 TU.ExtraArgs.push_back("-Xclang");
1030 for (const auto *DisableOption :
1031 {"-fsanitize-ignorelist=null", "-fprofile-list=null",
1032 "-fxray-always-instrument=null", "-fxray-never-instrument=null",
1033 "-fxray-attr-list=null"}) {
1034 TU.ExtraArgs.push_back(DisableOption);
1035 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
1036 TU.ExtraArgs.pop_back();
1037 }
1038}
1039
1040// Recursive main-file include is diagnosed, and doesn't crash.
1041TEST(DiagnosticsTest, RecursivePreamble) {
1042 auto TU = TestTU::withCode(R"cpp(
1043 #include "foo.h" // error-ok
1044 int symbol;
1045 )cpp");
1046 TU.Filename = "foo.h";
1047 EXPECT_THAT(TU.build().getDiagnostics(),
1048 ElementsAre(diagName("pp_including_mainfile_in_preamble")));
1049 EXPECT_THAT(TU.build().getLocalTopLevelDecls(), SizeIs(1));
1050}
1051
1052// Recursive main-file include with #pragma once guard is OK.
1053TEST(DiagnosticsTest, RecursivePreamblePragmaOnce) {
1054 auto TU = TestTU::withCode(R"cpp(
1055 #pragma once
1056 #include "foo.h"
1057 int symbol;
1058 )cpp");
1059 TU.Filename = "foo.h";
1060 EXPECT_THAT(TU.build().getDiagnostics(),
1061 Not(Contains(diagName("pp_including_mainfile_in_preamble"))));
1062 EXPECT_THAT(TU.build().getLocalTopLevelDecls(), SizeIs(1));
1063}
1064
1065// Recursive main-file include with #ifndef guard should be OK.
1066// However, it's not yet recognized (incomplete at end of preamble).
1067TEST(DiagnosticsTest, RecursivePreambleIfndefGuard) {
1068 auto TU = TestTU::withCode(R"cpp(
1069 #ifndef FOO
1070 #define FOO
1071 #include "foo.h" // error-ok
1072 int symbol;
1073 #endif
1074 )cpp");
1075 TU.Filename = "foo.h";
1076 // FIXME: should be no errors here.
1077 EXPECT_THAT(TU.build().getDiagnostics(),
1078 ElementsAre(diagName("pp_including_mainfile_in_preamble")));
1079 EXPECT_THAT(TU.build().getLocalTopLevelDecls(), SizeIs(1));
1080}
1081
1082TEST(DiagnosticsTest, PreambleWithPragmaAssumeNonnull) {
1083 auto TU = TestTU::withCode(R"cpp(
1084#pragma clang assume_nonnull begin
1085void foo(int *x);
1086#pragma clang assume_nonnull end
1087)cpp");
1088 auto AST = TU.build();
1089 EXPECT_THAT(AST.getDiagnostics(), IsEmpty());
1090 const auto *X = cast<FunctionDecl>(findDecl(AST, "foo")).getParamDecl(0);
1091 ASSERT_TRUE(X->getOriginalType()->getNullability() ==
1092 NullabilityKind::NonNull);
1093}
1094
1095TEST(DiagnosticsTest, PreamblePragmaDiagnosticPushPop) {
1096 auto TU = TestTU::withCode(R"cpp(
1097#pragma clang diagnostic push
1098int main() {
1099 return 0;
1100}
1101#pragma clang diagnostic pop
1102)cpp");
1103 auto AST = TU.build();
1104 EXPECT_THAT(AST.getDiagnostics(), IsEmpty());
1105}
1106
1107TEST(DiagnosticsTest, PreambleHeaderWithBadPragmaAssumeNonnull) {
1108 Annotations Header(R"cpp(
1109#pragma clang assume_nonnull begin // error-ok
1110void foo(int *X);
1111)cpp");
1112 auto TU = TestTU::withCode(R"cpp(
1113#include "foo.h" // unterminated assume_nonnull should not affect bar.
1114void bar(int *Y);
1115)cpp");
1116 TU.AdditionalFiles = {{"foo.h", std::string(Header.code())}};
1117 auto AST = TU.build();
1118 EXPECT_THAT(AST.getDiagnostics(),
1119 ElementsAre(diagName("pp_eof_in_assume_nonnull")));
1120 const auto *X = cast<FunctionDecl>(findDecl(AST, "foo")).getParamDecl(0);
1121 ASSERT_TRUE(X->getOriginalType()->getNullability() ==
1122 NullabilityKind::NonNull);
1123 const auto *Y = cast<FunctionDecl>(findDecl(AST, "bar")).getParamDecl(0);
1124 ASSERT_FALSE(Y->getOriginalType()->getNullability());
1125}
1126
1127TEST(DiagnosticsTest, InsideMacros) {
1128 Annotations Test(R"cpp(
1129 #define TEN 10
1130 #define RET(x) return x + 10
1131
1132 int* foo() {
1133 RET($foo[[0]]); // error-ok
1134 }
1135 int* bar() {
1136 return $bar[[TEN]];
1137 }
1138 )cpp");
1139 EXPECT_THAT(TestTU::withCode(Test.code()).build().getDiagnostics(),
1140 ElementsAre(Diag(Test.range("foo"),
1141 "cannot initialize return object of type "
1142 "'int *' with an rvalue of type 'int'"),
1143 Diag(Test.range("bar"),
1144 "cannot initialize return object of type "
1145 "'int *' with an rvalue of type 'int'")));
1146}
1147
1148TEST(DiagnosticsTest, NoFixItInMacro) {
1149 Annotations Test(R"cpp(
1150 #define Define(name) void name() {}
1151
1152 [[Define]](main) // error-ok
1153 )cpp");
1154 auto TU = TestTU::withCode(Test.code());
1155 EXPECT_THAT(TU.build().getDiagnostics(),
1156 ElementsAre(AllOf(Diag(Test.range(), "'main' must return 'int'"),
1157 Not(withFix(_)))));
1158}
1159
1160TEST(DiagnosticsTest, PragmaSystemHeader) {
1161 Annotations Test("#pragma clang [[system_header]]\n");
1162 auto TU = TestTU::withCode(Test.code());
1163 EXPECT_THAT(
1164 TU.build().getDiagnostics(),
1165 ElementsAre(AllOf(
1166 Diag(Test.range(), "#pragma system_header ignored in main file"))));
1167 TU.Filename = "TestTU.h";
1168 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
1169}
1170
1171TEST(ClangdTest, MSAsm) {
1172 // Parsing MS assembly tries to use the target MCAsmInfo, which we don't link.
1173 // We used to crash here. Now clang emits a diagnostic, which we filter out.
1174 llvm::InitializeAllTargetInfos(); // As in ClangdMain
1175 auto TU = TestTU::withCode("void fn() { __asm { cmp cl,64 } }");
1176 TU.ExtraArgs = {"-fms-extensions"};
1177 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
1178}
1179
1180TEST(DiagnosticsTest, ToLSP) {
1181 URIForFile MainFile =
1182 URIForFile::canonicalize(testPath("foo/bar/main.cpp"), "");
1184 URIForFile::canonicalize(testPath("foo/bar/header.h"), "");
1185
1186 clangd::Diag D;
1187 D.ID = clang::diag::err_undeclared_var_use;
1189 D.Name = "undeclared_var_use";
1190 D.Source = clangd::Diag::Clang;
1191 D.Message = "something terrible happened";
1192 D.Range = {pos(1, 2), pos(3, 4)};
1193 D.InsideMainFile = true;
1194 D.Severity = DiagnosticsEngine::Error;
1195 D.File = "foo/bar/main.cpp";
1196 D.AbsFile = std::string(MainFile.file());
1197 D.OpaqueData["test"] = "bar";
1198
1199 clangd::Note NoteInMain;
1200 NoteInMain.Message = "declared somewhere in the main file";
1201 NoteInMain.Range = {pos(5, 6), pos(7, 8)};
1202 NoteInMain.Severity = DiagnosticsEngine::Remark;
1203 NoteInMain.File = "../foo/bar/main.cpp";
1204 NoteInMain.InsideMainFile = true;
1205 NoteInMain.AbsFile = std::string(MainFile.file());
1206
1207 D.Notes.push_back(NoteInMain);
1208
1209 clangd::Note NoteInHeader;
1210 NoteInHeader.Message = "declared somewhere in the header file";
1211 NoteInHeader.Range = {pos(9, 10), pos(11, 12)};
1212 NoteInHeader.Severity = DiagnosticsEngine::Note;
1213 NoteInHeader.File = "../foo/baz/header.h";
1214 NoteInHeader.InsideMainFile = false;
1215 NoteInHeader.AbsFile = std::string(HeaderFile.file());
1216 D.Notes.push_back(NoteInHeader);
1217
1218 clangd::Fix F;
1219 F.Message = "do something";
1220 D.Fixes.push_back(F);
1221
1222 // Diagnostics should turn into these:
1223 clangd::Diagnostic MainLSP;
1224 MainLSP.range = D.Range;
1225 MainLSP.severity = getSeverity(DiagnosticsEngine::Error);
1226 MainLSP.code = "undeclared_var_use";
1227 MainLSP.source = "clang";
1228 MainLSP.message =
1229 R"(Something terrible happened (fix available)
1230
1231main.cpp:6:7: remark: declared somewhere in the main file
1232
1233../foo/baz/header.h:10:11:
1234note: declared somewhere in the header file)";
1235 MainLSP.tags = {DiagnosticTag::Unnecessary};
1236 MainLSP.data = D.OpaqueData;
1237
1238 clangd::Diagnostic NoteInMainLSP;
1239 NoteInMainLSP.range = NoteInMain.Range;
1240 NoteInMainLSP.severity = getSeverity(DiagnosticsEngine::Remark);
1241 NoteInMainLSP.message = R"(Declared somewhere in the main file
1242
1243main.cpp:2:3: error: something terrible happened)";
1244
1246 // Transform diagnostics and check the results.
1247 std::vector<std::pair<clangd::Diagnostic, std::vector<clangd::Fix>>> LSPDiags;
1248 toLSPDiags(D, MainFile, Opts,
1249 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix> Fixes) {
1250 LSPDiags.push_back(
1251 {std::move(LSPDiag),
1252 std::vector<clangd::Fix>(Fixes.begin(), Fixes.end())});
1253 });
1254
1255 EXPECT_THAT(
1256 LSPDiags,
1257 ElementsAre(Pair(equalToLSPDiag(MainLSP), ElementsAre(equalToFix(F))),
1258 Pair(equalToLSPDiag(NoteInMainLSP), IsEmpty())));
1259 EXPECT_EQ(LSPDiags[0].first.code, "undeclared_var_use");
1260 EXPECT_EQ(LSPDiags[0].first.source, "clang");
1261 EXPECT_EQ(LSPDiags[1].first.code, "");
1262 EXPECT_EQ(LSPDiags[1].first.source, "");
1263
1264 // Same thing, but don't flatten notes into the main list.
1265 LSPDiags.clear();
1266 Opts.EmitRelatedLocations = true;
1267 toLSPDiags(D, MainFile, Opts,
1268 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix> Fixes) {
1269 LSPDiags.push_back(
1270 {std::move(LSPDiag),
1271 std::vector<clangd::Fix>(Fixes.begin(), Fixes.end())});
1272 });
1273 MainLSP.message = "Something terrible happened (fix available)";
1274 DiagnosticRelatedInformation NoteInMainDRI;
1275 NoteInMainDRI.message = "Declared somewhere in the main file";
1276 NoteInMainDRI.location.range = NoteInMain.Range;
1277 NoteInMainDRI.location.uri = MainFile;
1278 MainLSP.relatedInformation = {NoteInMainDRI};
1279 DiagnosticRelatedInformation NoteInHeaderDRI;
1280 NoteInHeaderDRI.message = "Declared somewhere in the header file";
1281 NoteInHeaderDRI.location.range = NoteInHeader.Range;
1282 NoteInHeaderDRI.location.uri = HeaderFile;
1283 MainLSP.relatedInformation = {NoteInMainDRI, NoteInHeaderDRI};
1284 EXPECT_THAT(LSPDiags, ElementsAre(Pair(equalToLSPDiag(MainLSP),
1285 ElementsAre(equalToFix(F)))));
1286}
1287
1288struct SymbolWithHeader {
1289 std::string QName;
1290 std::string DeclaringFile;
1291 std::string IncludeHeader;
1292};
1293
1294std::unique_ptr<SymbolIndex>
1295buildIndexWithSymbol(llvm::ArrayRef<SymbolWithHeader> Syms) {
1297 for (const auto &S : Syms) {
1298 Symbol Sym = cls(S.QName);
1300 Sym.CanonicalDeclaration.FileURI = S.DeclaringFile.c_str();
1301 Sym.Definition.FileURI = S.DeclaringFile.c_str();
1302 Sym.IncludeHeaders.emplace_back(S.IncludeHeader, 1, Symbol::Include);
1303 Slab.insert(Sym);
1304 }
1305 return MemIndex::build(std::move(Slab).build(), RefSlab(), RelationSlab());
1306}
1307
1308TEST(IncludeFixerTest, IncompleteType) {
1309 auto TU = TestTU::withHeaderCode("namespace ns { class X; } ns::X *x;");
1310 TU.ExtraArgs.push_back("-std=c++20");
1311 auto Index = buildIndexWithSymbol(
1312 {SymbolWithHeader{"ns::X", "unittest:///x.h", "\"x.h\""}});
1313 TU.ExternalIndex = Index.get();
1314
1315 std::vector<std::pair<llvm::StringRef, llvm::StringRef>> Tests{
1316 {"incomplete_nested_name_spec", "[[ns::X::]]Nested n;"},
1317 {"incomplete_base_class", "class Y : [[ns::X]] {};"},
1318 {"incomplete_member_access", "auto i = x[[->]]f();"},
1319 {"incomplete_type", "auto& [[[]]m] = *x;"},
1320 {"init_incomplete_type",
1321 "struct C { static int f(ns::X&); }; int i = C::f([[{]]});"},
1322 {"bad_cast_incomplete", "auto a = [[static_cast]]<ns::X>(0);"},
1323 {"template_nontype_parm_incomplete", "template <ns::X [[foo]]> int a;"},
1324 {"typecheck_decl_incomplete_type", "ns::X [[var]];"},
1325 {"typecheck_incomplete_tag", "auto i = [[(*x)]]->f();"},
1326 {"typecheck_nonviable_condition_incomplete",
1327 "struct A { operator ns::X(); } a; const ns::X &[[b]] = a;"},
1328 {"invalid_incomplete_type_use", "auto var = [[ns::X()]];"},
1329 {"sizeof_alignof_incomplete_or_sizeless_type",
1330 "auto s = [[sizeof]](ns::X);"},
1331 {"for_range_incomplete_type", "void foo() { for (auto i : [[*]]x ) {} }"},
1332 {"func_def_incomplete_result", "ns::X [[func]] () {}"},
1333 {"field_incomplete_or_sizeless", "class M { ns::X [[member]]; };"},
1334 {"array_incomplete_or_sizeless_type", "auto s = [[(ns::X[]){}]];"},
1335 {"call_incomplete_return", "ns::X f(); auto fp = &f; auto z = [[fp()]];"},
1336 {"call_function_incomplete_return", "ns::X foo(); auto a = [[foo()]];"},
1337 {"call_incomplete_argument", "int m(ns::X); int i = m([[*x]]);"},
1338 {"switch_incomplete_class_type", "void a() { [[switch]](*x) {} }"},
1339 {"delete_incomplete_class_type", "void f() { [[delete]] *x; }"},
1340 {"-Wdelete-incomplete", "void f() { [[delete]] x; }"},
1341 {"dereference_incomplete_type",
1342 R"cpp(void f() { asm("" : "=r"([[*]]x)::); })cpp"},
1343 };
1344 for (auto Case : Tests) {
1345 Annotations Main(Case.second);
1346 TU.Code = Main.code().str() + "\n // error-ok";
1347 EXPECT_THAT(
1348 TU.build().getDiagnostics(),
1349 ElementsAre(AllOf(diagName(Case.first), hasRange(Main.range()),
1350 withFix(Fix(Range{}, "#include \"x.h\"\n",
1351 "Include \"x.h\" for symbol ns::X")))))
1352 << Case.second;
1353 }
1354}
1355
1356TEST(IncludeFixerTest, IncompleteEnum) {
1357 Symbol Sym = enm("X");
1359 Sym.CanonicalDeclaration.FileURI = Sym.Definition.FileURI = "unittest:///x.h";
1360 Sym.IncludeHeaders.emplace_back("\"x.h\"", 1, Symbol::Include);
1362 Slab.insert(Sym);
1363 auto Index =
1364 MemIndex::build(std::move(Slab).build(), RefSlab(), RelationSlab());
1365
1366 TestTU TU;
1367 TU.ExternalIndex = Index.get();
1368 TU.ExtraArgs.push_back("-std=c++20");
1369 TU.ExtraArgs.push_back("-fno-ms-compatibility"); // else incomplete enum is OK
1370
1371 std::vector<std::pair<llvm::StringRef, llvm::StringRef>> Tests{
1372 {"incomplete_enum", "enum class X : int; using enum [[X]];"},
1373 {"underlying_type_of_incomplete_enum",
1374 "[[__underlying_type]](enum X) i;"},
1375 };
1376 for (auto Case : Tests) {
1377 Annotations Main(Case.second);
1378 TU.Code = Main.code().str() + "\n // error-ok";
1379 EXPECT_THAT(TU.build().getDiagnostics(),
1380 Contains(AllOf(diagName(Case.first), hasRange(Main.range()),
1381 withFix(Fix(Range{}, "#include \"x.h\"\n",
1382 "Include \"x.h\" for symbol X")))))
1383 << Case.second;
1384 }
1385}
1386
1387TEST(IncludeFixerTest, NoSuggestIncludeWhenNoDefinitionInHeader) {
1388 Annotations Test(R"cpp(// error-ok
1389$insert[[]]namespace ns {
1390 class X;
1391}
1392class Y : $base[[public ns::X]] {};
1393int main() {
1394 ns::X *x;
1395 x$access[[->]]f();
1396}
1397 )cpp");
1398 auto TU = TestTU::withCode(Test.code());
1399 Symbol Sym = cls("ns::X");
1401 Sym.CanonicalDeclaration.FileURI = "unittest:///x.h";
1402 Sym.Definition.FileURI = "unittest:///x.cc";
1403 Sym.IncludeHeaders.emplace_back("\"x.h\"", 1, Symbol::Include);
1404
1406 Slab.insert(Sym);
1407 auto Index =
1408 MemIndex::build(std::move(Slab).build(), RefSlab(), RelationSlab());
1409 TU.ExternalIndex = Index.get();
1410
1411 EXPECT_THAT(TU.build().getDiagnostics(),
1412 UnorderedElementsAre(
1413 Diag(Test.range("base"), "base class has incomplete type"),
1414 Diag(Test.range("access"),
1415 "member access into incomplete type 'ns::X'")));
1416}
1417
1418TEST(IncludeFixerTest, Typo) {
1419 Annotations Test(R"cpp(// error-ok
1420$insert[[]]namespace ns {
1421void foo() {
1422 $unqualified1[[X]] x;
1423 // No fix if the unresolved type is used as specifier. (ns::)X::Nested will be
1424 // considered the unresolved type.
1425 $unqualified2[[X]]::Nested n;
1426}
1427struct S : $base[[X]] {};
1428}
1429void bar() {
1430 ns::$qualified1[[X]] x; // ns:: is valid.
1431 ns::$qualified2[[X]](); // Error: no member in namespace
1432
1433 ::$global[[Global]] glob;
1434}
1435using Type = ns::$template[[Foo]]<int>;
1436 )cpp");
1437 auto TU = TestTU::withCode(Test.code());
1438 auto Index = buildIndexWithSymbol(
1439 {SymbolWithHeader{"ns::X", "unittest:///x.h", "\"x.h\""},
1440 SymbolWithHeader{"Global", "unittest:///global.h", "\"global.h\""},
1441 SymbolWithHeader{"ns::Foo", "unittest:///foo.h", "\"foo.h\""}});
1442 TU.ExternalIndex = Index.get();
1443
1444 EXPECT_THAT(
1445 TU.build().getDiagnostics(),
1446 UnorderedElementsAre(
1447 AllOf(Diag(Test.range("unqualified1"), "unknown type name 'X'"),
1448 diagName("unknown_typename"),
1449 withFix(Fix(Test.range("insert"), "#include \"x.h\"\n",
1450 "Include \"x.h\" for symbol ns::X"))),
1451 Diag(Test.range("unqualified2"), "use of undeclared identifier 'X'"),
1452 AllOf(Diag(Test.range("qualified1"),
1453 "no type named 'X' in namespace 'ns'"),
1454 diagName("typename_nested_not_found"),
1455 withFix(Fix(Test.range("insert"), "#include \"x.h\"\n",
1456 "Include \"x.h\" for symbol ns::X"))),
1457 AllOf(Diag(Test.range("qualified2"),
1458 "no member named 'X' in namespace 'ns'"),
1459 diagName("no_member"),
1460 withFix(Fix(Test.range("insert"), "#include \"x.h\"\n",
1461 "Include \"x.h\" for symbol ns::X"))),
1462 AllOf(Diag(Test.range("global"),
1463 "no type named 'Global' in the global namespace"),
1464 diagName("typename_nested_not_found"),
1465 withFix(Fix(Test.range("insert"), "#include \"global.h\"\n",
1466 "Include \"global.h\" for symbol Global"))),
1467 AllOf(Diag(Test.range("template"),
1468 "no template named 'Foo' in namespace 'ns'"),
1469 diagName("no_member_template"),
1470 withFix(Fix(Test.range("insert"), "#include \"foo.h\"\n",
1471 "Include \"foo.h\" for symbol ns::Foo"))),
1472 AllOf(Diag(Test.range("base"), "expected class name"),
1473 diagName("expected_class_name"),
1474 withFix(Fix(Test.range("insert"), "#include \"x.h\"\n",
1475 "Include \"x.h\" for symbol ns::X")))));
1476}
1477
1478TEST(IncludeFixerTest, TypoInMacro) {
1479 auto TU = TestTU::withCode(R"cpp(// error-ok
1480#define ID(T) T
1481X a1;
1482ID(X a2);
1483ns::X a3;
1484ID(ns::X a4);
1485namespace ns{};
1486ns::X a5;
1487ID(ns::X a6);
1488)cpp");
1489 auto Index = buildIndexWithSymbol(
1490 {SymbolWithHeader{"X", "unittest:///x.h", "\"x.h\""},
1491 SymbolWithHeader{"ns::X", "unittest:///ns.h", "\"x.h\""}});
1492 TU.ExternalIndex = Index.get();
1493 // FIXME: -fms-compatibility (which is default on windows) breaks the
1494 // ns::X cases when the namespace is undeclared. Find out why!
1495 TU.ExtraArgs = {"-fno-ms-compatibility"};
1496 EXPECT_THAT(TU.build().getDiagnostics(), Each(withFix(_)));
1497}
1498
1499TEST(IncludeFixerTest, MultipleMatchedSymbols) {
1500 Annotations Test(R"cpp(// error-ok
1501$insert[[]]namespace na {
1502namespace nb {
1503void foo() {
1504 $unqualified[[X]] x;
1505}
1506}
1507}
1508 )cpp");
1509 auto TU = TestTU::withCode(Test.code());
1510 auto Index = buildIndexWithSymbol(
1511 {SymbolWithHeader{"na::X", "unittest:///a.h", "\"a.h\""},
1512 SymbolWithHeader{"na::nb::X", "unittest:///b.h", "\"b.h\""}});
1513 TU.ExternalIndex = Index.get();
1514
1515 EXPECT_THAT(TU.build().getDiagnostics(),
1516 UnorderedElementsAre(AllOf(
1517 Diag(Test.range("unqualified"), "unknown type name 'X'"),
1518 diagName("unknown_typename"),
1519 withFix(Fix(Test.range("insert"), "#include \"a.h\"\n",
1520 "Include \"a.h\" for symbol na::X"),
1521 Fix(Test.range("insert"), "#include \"b.h\"\n",
1522 "Include \"b.h\" for symbol na::nb::X")))));
1523}
1524
1525TEST(IncludeFixerTest, NoCrashMemberAccess) {
1526 Annotations Test(R"cpp(// error-ok
1527 struct X { int xyz; };
1528 void g() { X x; x.$[[xy]]; }
1529 )cpp");
1530 auto TU = TestTU::withCode(Test.code());
1531 auto Index = buildIndexWithSymbol(
1532 SymbolWithHeader{"na::X", "unittest:///a.h", "\"a.h\""});
1533 TU.ExternalIndex = Index.get();
1534
1535 EXPECT_THAT(
1536 TU.build().getDiagnostics(),
1537 UnorderedElementsAre(Diag(Test.range(), "no member named 'xy' in 'X'")));
1538}
1539
1540TEST(IncludeFixerTest, UseCachedIndexResults) {
1541 // As index results for the identical request are cached, more than 5 fixes
1542 // are generated.
1543 Annotations Test(R"cpp(// error-ok
1544$insert[[]]void foo() {
1545 $x1[[X]] x;
1546 $x2[[X]] x;
1547 $x3[[X]] x;
1548 $x4[[X]] x;
1549 $x5[[X]] x;
1550 $x6[[X]] x;
1551 $x7[[X]] x;
1552}
1553
1554class X;
1555void bar(X *x) {
1556 x$a1[[->]]f();
1557 x$a2[[->]]f();
1558 x$a3[[->]]f();
1559 x$a4[[->]]f();
1560 x$a5[[->]]f();
1561 x$a6[[->]]f();
1562 x$a7[[->]]f();
1563}
1564 )cpp");
1565 auto TU = TestTU::withCode(Test.code());
1566 auto Index =
1567 buildIndexWithSymbol(SymbolWithHeader{"X", "unittest:///a.h", "\"a.h\""});
1568 TU.ExternalIndex = Index.get();
1569
1570 auto Parsed = TU.build();
1571 for (const auto &D : Parsed.getDiagnostics()) {
1572 if (D.Fixes.size() != 1) {
1573 ADD_FAILURE() << "D.Fixes.size() != 1";
1574 continue;
1575 }
1576 EXPECT_EQ(D.Fixes[0].Message, std::string("Include \"a.h\" for symbol X"));
1577 }
1578}
1579
1580TEST(IncludeFixerTest, UnresolvedNameAsSpecifier) {
1581 Annotations Test(R"cpp(// error-ok
1582$insert[[]]namespace ns {
1583}
1584void g() { ns::$[[scope]]::X_Y(); }
1585 )cpp");
1586 TestTU TU;
1587 TU.Code = std::string(Test.code());
1588 // FIXME: Figure out why this is needed and remove it, PR43662.
1589 TU.ExtraArgs.push_back("-fno-ms-compatibility");
1590 auto Index = buildIndexWithSymbol(
1591 SymbolWithHeader{"ns::scope::X_Y", "unittest:///x.h", "\"x.h\""});
1592 TU.ExternalIndex = Index.get();
1593
1594 EXPECT_THAT(
1595 TU.build().getDiagnostics(),
1596 UnorderedElementsAre(
1597 AllOf(Diag(Test.range(), "no member named 'scope' in namespace 'ns'"),
1598 diagName("no_member"),
1599 withFix(Fix(Test.range("insert"), "#include \"x.h\"\n",
1600 "Include \"x.h\" for symbol ns::scope::X_Y")))));
1601}
1602
1603TEST(IncludeFixerTest, UnresolvedSpecifierWithSemaCorrection) {
1604 Annotations Test(R"cpp(// error-ok
1605$insert[[]]namespace clang {
1606void f() {
1607 // "clangd::" will be corrected to "clang::" by Sema.
1608 $q1[[clangd]]::$x[[X]] x;
1609 $q2[[clangd]]::$ns[[ns]]::Y y;
1610}
1611}
1612 )cpp");
1613 TestTU TU;
1614 TU.Code = std::string(Test.code());
1615 // FIXME: Figure out why this is needed and remove it, PR43662.
1616 TU.ExtraArgs.push_back("-fno-ms-compatibility");
1617 auto Index = buildIndexWithSymbol(
1618 {SymbolWithHeader{"clang::clangd::X", "unittest:///x.h", "\"x.h\""},
1619 SymbolWithHeader{"clang::clangd::ns::Y", "unittest:///y.h", "\"y.h\""}});
1620 TU.ExternalIndex = Index.get();
1621
1622 EXPECT_THAT(
1623 TU.build().getDiagnostics(),
1624 UnorderedElementsAre(
1625 AllOf(Diag(Test.range("q1"), "use of undeclared identifier 'clangd'; "
1626 "did you mean 'clang'?"),
1627 diagName("undeclared_var_use_suggest"),
1628 withFix(_, // change clangd to clang
1629 Fix(Test.range("insert"), "#include \"x.h\"\n",
1630 "Include \"x.h\" for symbol clang::clangd::X"))),
1631 AllOf(Diag(Test.range("x"), "no type named 'X' in namespace 'clang'"),
1632 diagName("typename_nested_not_found"),
1633 withFix(Fix(Test.range("insert"), "#include \"x.h\"\n",
1634 "Include \"x.h\" for symbol clang::clangd::X"))),
1635 AllOf(
1636 Diag(Test.range("q2"), "use of undeclared identifier 'clangd'; "
1637 "did you mean 'clang'?"),
1638 diagName("undeclared_var_use_suggest"),
1639 withFix(_, // change clangd to clang
1640 Fix(Test.range("insert"), "#include \"y.h\"\n",
1641 "Include \"y.h\" for symbol clang::clangd::ns::Y"))),
1642 AllOf(Diag(Test.range("ns"),
1643 "no member named 'ns' in namespace 'clang'"),
1644 diagName("no_member"),
1645 withFix(
1646 Fix(Test.range("insert"), "#include \"y.h\"\n",
1647 "Include \"y.h\" for symbol clang::clangd::ns::Y")))));
1648}
1649
1650TEST(IncludeFixerTest, SpecifiedScopeIsNamespaceAlias) {
1651 Annotations Test(R"cpp(// error-ok
1652$insert[[]]namespace a {}
1653namespace b = a;
1654namespace c {
1655 b::$[[X]] x;
1656}
1657 )cpp");
1658 auto TU = TestTU::withCode(Test.code());
1659 auto Index = buildIndexWithSymbol(
1660 SymbolWithHeader{"a::X", "unittest:///x.h", "\"x.h\""});
1661 TU.ExternalIndex = Index.get();
1662
1663 EXPECT_THAT(TU.build().getDiagnostics(),
1664 UnorderedElementsAre(AllOf(
1665 Diag(Test.range(), "no type named 'X' in namespace 'a'"),
1666 diagName("typename_nested_not_found"),
1667 withFix(Fix(Test.range("insert"), "#include \"x.h\"\n",
1668 "Include \"x.h\" for symbol a::X")))));
1669}
1670
1671TEST(IncludeFixerTest, NoCrashOnTemplateInstantiations) {
1672 Annotations Test(R"cpp(
1673 template <typename T> struct Templ {
1674 template <typename U>
1675 typename U::type operator=(const U &);
1676 };
1677
1678 struct A {
1679 Templ<char> s;
1680 A() { [[a]]; /*error-ok*/ } // crash if we compute scopes lazily.
1681 };
1682 )cpp");
1683
1684 auto TU = TestTU::withCode(Test.code());
1685 auto Index = buildIndexWithSymbol({});
1686 TU.ExternalIndex = Index.get();
1687
1688 EXPECT_THAT(
1689 TU.build().getDiagnostics(),
1690 ElementsAre(Diag(Test.range(), "use of undeclared identifier 'a'")));
1691}
1692
1693TEST(IncludeFixerTest, HeaderNamedInDiag) {
1694 Annotations Test(R"cpp(
1695 $insert[[]]int main() {
1696 [[printf]]("");
1697 }
1698 )cpp");
1699 auto TU = TestTU::withCode(Test.code());
1700 TU.ExtraArgs = {"-xc", "-std=c99",
1701 "-Wno-error=implicit-function-declaration"};
1702 auto Index = buildIndexWithSymbol({});
1703 TU.ExternalIndex = Index.get();
1704
1705 EXPECT_THAT(
1706 TU.build().getDiagnostics(),
1707 ElementsAre(AllOf(
1708 Diag(Test.range(), "call to undeclared library function 'printf' "
1709 "with type 'int (const char *, ...)'; ISO C99 "
1710 "and later do not support implicit function "
1711 "declarations"),
1712 withFix(Fix(Test.range("insert"), "#include <stdio.h>\n",
1713 "Include <stdio.h> for symbol printf")))));
1714
1715 TU.ExtraArgs = {"-xc", "-std=c89"};
1716 EXPECT_THAT(
1717 TU.build().getDiagnostics(),
1718 ElementsAre(AllOf(
1719 Diag(Test.range(), "implicitly declaring library function 'printf' "
1720 "with type 'int (const char *, ...)'"),
1721 withFix(Fix(Test.range("insert"), "#include <stdio.h>\n",
1722 "Include <stdio.h> for symbol printf")))));
1723}
1724
1725TEST(IncludeFixerTest, CImplicitFunctionDecl) {
1726 Annotations Test("void x() { [[foo]](); }");
1727 auto TU = TestTU::withCode(Test.code());
1728 TU.Filename = "test.c";
1729 TU.ExtraArgs = {"-std=c99", "-Wno-error=implicit-function-declaration"};
1730
1731 Symbol Sym = func("foo");
1733 Sym.CanonicalDeclaration.FileURI = "unittest:///foo.h";
1734 Sym.IncludeHeaders.emplace_back("\"foo.h\"", 1, Symbol::Include);
1735
1737 Slab.insert(Sym);
1738 auto Index =
1739 MemIndex::build(std::move(Slab).build(), RefSlab(), RelationSlab());
1740 TU.ExternalIndex = Index.get();
1741
1742 EXPECT_THAT(
1743 TU.build().getDiagnostics(),
1744 ElementsAre(AllOf(
1745 Diag(Test.range(),
1746 "call to undeclared function 'foo'; ISO C99 and later do not "
1747 "support implicit function declarations"),
1748 withFix(Fix(Range{}, "#include \"foo.h\"\n",
1749 "Include \"foo.h\" for symbol foo")))));
1750
1751 TU.ExtraArgs = {"-std=c89", "-Wall"};
1752 EXPECT_THAT(TU.build().getDiagnostics(),
1753 ElementsAre(AllOf(
1754 Diag(Test.range(), "implicit declaration of function 'foo'"),
1755 withFix(Fix(Range{}, "#include \"foo.h\"\n",
1756 "Include \"foo.h\" for symbol foo")))));
1757}
1758
1759TEST(DiagsInHeaders, DiagInsideHeader) {
1760 Annotations Main(R"cpp(
1761 #include [["a.h"]]
1762 void foo() {})cpp");
1763 Annotations Header("[[no_type_spec]]; // error-ok");
1764 TestTU TU = TestTU::withCode(Main.code());
1765 TU.AdditionalFiles = {{"a.h", std::string(Header.code())}};
1766 EXPECT_THAT(TU.build().getDiagnostics(),
1767 UnorderedElementsAre(AllOf(
1768 Diag(Main.range(), "in included file: a type specifier is "
1769 "required for all declarations"),
1770 withNote(Diag(Header.range(), "error occurred here")))));
1771}
1772
1773TEST(DiagsInHeaders, DiagInTransitiveInclude) {
1774 Annotations Main(R"cpp(
1775 #include [["a.h"]]
1776 void foo() {})cpp");
1777 TestTU TU = TestTU::withCode(Main.code());
1778 TU.AdditionalFiles = {{"a.h", "#include \"b.h\""},
1779 {"b.h", "no_type_spec; // error-ok"}};
1780 EXPECT_THAT(TU.build().getDiagnostics(),
1781 UnorderedElementsAre(Diag(Main.range(),
1782 "in included file: a type specifier is "
1783 "required for all declarations")));
1784}
1785
1786TEST(DiagsInHeaders, DiagInMultipleHeaders) {
1787 Annotations Main(R"cpp(
1788 #include $a[["a.h"]]
1789 #include $b[["b.h"]]
1790 void foo() {})cpp");
1791 TestTU TU = TestTU::withCode(Main.code());
1792 TU.AdditionalFiles = {{"a.h", "no_type_spec; // error-ok"},
1793 {"b.h", "no_type_spec; // error-ok"}};
1794 EXPECT_THAT(TU.build().getDiagnostics(),
1795 UnorderedElementsAre(
1796 Diag(Main.range("a"), "in included file: a type specifier is "
1797 "required for all declarations"),
1798 Diag(Main.range("b"), "in included file: a type specifier is "
1799 "required for all declarations")));
1800}
1801
1802TEST(DiagsInHeaders, PreferExpansionLocation) {
1803 Annotations Main(R"cpp(
1804 #include [["a.h"]]
1805 #include "b.h"
1806 void foo() {})cpp");
1807 TestTU TU = TestTU::withCode(Main.code());
1808 TU.AdditionalFiles = {
1809 {"a.h", "#include \"b.h\"\n"},
1810 {"b.h", "#ifndef X\n#define X\nno_type_spec; // error-ok\n#endif"}};
1811 EXPECT_THAT(TU.build().getDiagnostics(),
1812 Contains(Diag(Main.range(), "in included file: a type specifier "
1813 "is required for all declarations")));
1814}
1815
1816TEST(DiagsInHeaders, PreferExpansionLocationMacros) {
1817 Annotations Main(R"cpp(
1818 #define X
1819 #include "a.h"
1820 #undef X
1821 #include [["b.h"]]
1822 void foo() {})cpp");
1823 TestTU TU = TestTU::withCode(Main.code());
1824 TU.AdditionalFiles = {
1825 {"a.h", "#include \"c.h\"\n"},
1826 {"b.h", "#include \"c.h\"\n"},
1827 {"c.h", "#ifndef X\n#define X\nno_type_spec; // error-ok\n#endif"}};
1828 EXPECT_THAT(TU.build().getDiagnostics(),
1829 UnorderedElementsAre(Diag(Main.range(),
1830 "in included file: a type specifier is "
1831 "required for all declarations")));
1832}
1833
1834TEST(DiagsInHeaders, LimitDiagsOutsideMainFile) {
1835 Annotations Main(R"cpp(
1836 #include [["a.h"]]
1837 #include "b.h"
1838 void foo() {})cpp");
1839 TestTU TU = TestTU::withCode(Main.code());
1840 TU.AdditionalFiles = {{"a.h", "#include \"c.h\"\n"},
1841 {"b.h", "#include \"c.h\"\n"},
1842 {"c.h", R"cpp(
1843 #ifndef X
1844 #define X
1845 no_type_spec_0; // error-ok
1846 no_type_spec_1;
1847 no_type_spec_2;
1848 no_type_spec_3;
1849 no_type_spec_4;
1850 no_type_spec_5;
1851 no_type_spec_6;
1852 no_type_spec_7;
1853 no_type_spec_8;
1854 no_type_spec_9;
1855 no_type_spec_10;
1856 #endif)cpp"}};
1857 EXPECT_THAT(TU.build().getDiagnostics(),
1858 UnorderedElementsAre(Diag(Main.range(),
1859 "in included file: a type specifier is "
1860 "required for all declarations")));
1861}
1862
1863TEST(DiagsInHeaders, OnlyErrorOrFatal) {
1864 Annotations Main(R"cpp(
1865 #include [["a.h"]]
1866 void foo() {})cpp");
1867 Annotations Header(R"cpp(
1868 [[no_type_spec]]; // error-ok
1869 int x = 5/0;)cpp");
1870 TestTU TU = TestTU::withCode(Main.code());
1871 TU.AdditionalFiles = {{"a.h", std::string(Header.code())}};
1872 EXPECT_THAT(TU.build().getDiagnostics(),
1873 UnorderedElementsAre(AllOf(
1874 Diag(Main.range(), "in included file: a type specifier is "
1875 "required for all declarations"),
1876 withNote(Diag(Header.range(), "error occurred here")))));
1877}
1878
1879TEST(DiagsInHeaders, OnlyDefaultErrorOrFatal) {
1880 Annotations Main(R"cpp(
1881 #include [["a.h"]] // get unused "foo" warning when building preamble.
1882 )cpp");
1883 Annotations Header(R"cpp(
1884 namespace { void foo() {} }
1885 void func() {foo();} ;)cpp");
1886 TestTU TU = TestTU::withCode(Main.code());
1887 TU.AdditionalFiles = {{"a.h", std::string(Header.code())}};
1888 // promote warnings to errors.
1889 TU.ExtraArgs = {"-Werror", "-Wunused"};
1890 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
1891}
1892
1893TEST(DiagsInHeaders, FromNonWrittenSources) {
1894 Annotations Main(R"cpp(
1895 #include [["a.h"]]
1896 void foo() {})cpp");
1897 Annotations Header(R"cpp(
1898 int x = 5/0;
1899 int b = [[FOO]]; // error-ok)cpp");
1900 TestTU TU = TestTU::withCode(Main.code());
1901 TU.AdditionalFiles = {{"a.h", std::string(Header.code())}};
1902 TU.ExtraArgs = {"-DFOO=NOOO"};
1903 EXPECT_THAT(TU.build().getDiagnostics(),
1904 UnorderedElementsAre(AllOf(
1905 Diag(Main.range(),
1906 "in included file: use of undeclared identifier 'NOOO'"),
1907 withNote(Diag(Header.range(), "error occurred here")))));
1908}
1909
1910TEST(DiagsInHeaders, ErrorFromMacroExpansion) {
1911 Annotations Main(R"cpp(
1912 void bar() {
1913 int fo; // error-ok
1914 #include [["a.h"]]
1915 })cpp");
1916 Annotations Header(R"cpp(
1917 #define X foo
1918 X;)cpp");
1919 TestTU TU = TestTU::withCode(Main.code());
1920 TU.AdditionalFiles = {{"a.h", std::string(Header.code())}};
1921 EXPECT_THAT(TU.build().getDiagnostics(),
1922 UnorderedElementsAre(
1923 Diag(Main.range(), "in included file: use of undeclared "
1924 "identifier 'foo'; did you mean 'fo'?")));
1925}
1926
1927TEST(DiagsInHeaders, ErrorFromMacroArgument) {
1928 Annotations Main(R"cpp(
1929 void bar() {
1930 int fo; // error-ok
1931 #include [["a.h"]]
1932 })cpp");
1933 Annotations Header(R"cpp(
1934 #define X(arg) arg
1935 X(foo);)cpp");
1936 TestTU TU = TestTU::withCode(Main.code());
1937 TU.AdditionalFiles = {{"a.h", std::string(Header.code())}};
1938 EXPECT_THAT(TU.build().getDiagnostics(),
1939 UnorderedElementsAre(
1940 Diag(Main.range(), "in included file: use of undeclared "
1941 "identifier 'foo'; did you mean 'fo'?")));
1942}
1943
1944TEST(IgnoreDiags, FromNonWrittenInclude) {
1945 TestTU TU;
1946 TU.ExtraArgs.push_back("--include=a.h");
1947 TU.AdditionalFiles = {{"a.h", "void main();"}};
1948 // The diagnostic "main must return int" is from the header, we don't attempt
1949 // to render it in the main file as there is no written location there.
1950 EXPECT_THAT(TU.build().getDiagnostics(), UnorderedElementsAre());
1951}
1952
1953TEST(ToLSPDiag, RangeIsInMain) {
1955 clangd::Diag D;
1956 D.Range = {pos(1, 2), pos(3, 4)};
1957 D.Notes.emplace_back();
1958 Note &N = D.Notes.back();
1959 N.Range = {pos(2, 3), pos(3, 4)};
1960
1961 D.InsideMainFile = true;
1962 N.InsideMainFile = false;
1963 toLSPDiags(D, {}, Opts,
1964 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix>) {
1965 EXPECT_EQ(LSPDiag.range, D.Range);
1966 });
1967
1968 D.InsideMainFile = false;
1969 N.InsideMainFile = true;
1970 toLSPDiags(D, {}, Opts,
1971 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix>) {
1972 EXPECT_EQ(LSPDiag.range, N.Range);
1973 });
1974}
1975
1976TEST(ParsedASTTest, ModuleSawDiag) {
1977 TestTU TU;
1978
1979 auto AST = TU.build();
1980 #if 0
1981 EXPECT_THAT(AST.getDiagnostics(),
1982 testing::Contains(Diag(Code.range(), KDiagMsg.str())));
1983 #endif
1984}
1985
1986TEST(Preamble, EndsOnNonEmptyLine) {
1987 TestTU TU;
1988 TU.ExtraArgs = {"-Wnewline-eof"};
1989
1990 {
1991 TU.Code = "#define FOO\n void bar();\n";
1992 auto AST = TU.build();
1993 EXPECT_THAT(AST.getDiagnostics(), IsEmpty());
1994 }
1995 {
1996 Annotations Code("#define FOO[[]]");
1997 TU.Code = Code.code().str();
1998 auto AST = TU.build();
1999 EXPECT_THAT(
2000 AST.getDiagnostics(),
2001 testing::Contains(Diag(Code.range(), "no newline at end of file")));
2002 }
2003}
2004
2005TEST(Diagnostics, Tags) {
2006 TestTU TU;
2007 TU.ExtraArgs = {"-Wunused", "-Wdeprecated"};
2008 Annotations Test(R"cpp(
2009 void bar() __attribute__((deprecated));
2010 void foo() {
2011 int $unused[[x]];
2012 $deprecated[[bar]]();
2013 })cpp");
2014 TU.Code = Test.code().str();
2015 EXPECT_THAT(TU.build().getDiagnostics(),
2016 UnorderedElementsAre(
2017 AllOf(Diag(Test.range("unused"), "unused variable 'x'"),
2019 AllOf(Diag(Test.range("deprecated"), "'bar' is deprecated"),
2020 withTag(DiagnosticTag::Deprecated))));
2021
2022 Test = Annotations(R"cpp(
2023 $typedef[[typedef int INT]];
2024 )cpp");
2025 TU.Code = Test.code();
2026 TU.ClangTidyProvider = addTidyChecks("modernize-use-using");
2027 EXPECT_THAT(
2028 TU.build().getDiagnostics(),
2029 ifTidyChecks(UnorderedElementsAre(
2030 AllOf(Diag(Test.range("typedef"), "use 'using' instead of 'typedef'"),
2031 withTag(DiagnosticTag::Deprecated)))));
2032}
2033
2034TEST(Diagnostics, TidyDiagsArentAffectedFromWerror) {
2035 TestTU TU;
2036 TU.ExtraArgs = {"-Werror"};
2037 Annotations Test(R"cpp($typedef[[typedef int INT]]; // error-ok)cpp");
2038 TU.Code = Test.code().str();
2039 TU.ClangTidyProvider = addTidyChecks("modernize-use-using");
2040 EXPECT_THAT(
2041 TU.build().getDiagnostics(),
2042 ifTidyChecks(UnorderedElementsAre(
2043 AllOf(Diag(Test.range("typedef"), "use 'using' instead of 'typedef'"),
2044 // Make sure severity for clang-tidy finding isn't bumped to
2045 // error due to Werror in compile flags.
2046 diagSeverity(DiagnosticsEngine::Warning)))));
2047
2048 TU.ClangTidyProvider =
2049 addTidyChecks("modernize-use-using", /*WarningsAsErrors=*/"modernize-*");
2050 EXPECT_THAT(
2051 TU.build().getDiagnostics(),
2052 ifTidyChecks(UnorderedElementsAre(
2053 AllOf(Diag(Test.range("typedef"), "use 'using' instead of 'typedef'"),
2054 // Unless bumped explicitly with WarnAsError.
2055 diagSeverity(DiagnosticsEngine::Error)))));
2056}
2057
2058TEST(Diagnostics, DeprecatedDiagsAreHints) {
2060 std::optional<clangd::Diagnostic> Diag;
2061 clangd::Diag D;
2062 D.Range = {pos(1, 2), pos(3, 4)};
2063 D.InsideMainFile = true;
2064
2065 // Downgrade warnings with deprecated tags to remark.
2066 D.Tags = {Deprecated};
2067 D.Severity = DiagnosticsEngine::Warning;
2068 toLSPDiags(D, {}, Opts,
2069 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix>) {
2070 Diag = std::move(LSPDiag);
2071 });
2072 EXPECT_EQ(Diag->severity, getSeverity(DiagnosticsEngine::Remark));
2073 Diag.reset();
2074
2075 // Preserve errors.
2076 D.Severity = DiagnosticsEngine::Error;
2077 toLSPDiags(D, {}, Opts,
2078 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix>) {
2079 Diag = std::move(LSPDiag);
2080 });
2081 EXPECT_EQ(Diag->severity, getSeverity(DiagnosticsEngine::Error));
2082 Diag.reset();
2083
2084 // No-op without tag.
2085 D.Tags = {};
2086 D.Severity = DiagnosticsEngine::Warning;
2087 toLSPDiags(D, {}, Opts,
2088 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix>) {
2089 Diag = std::move(LSPDiag);
2090 });
2091 EXPECT_EQ(Diag->severity, getSeverity(DiagnosticsEngine::Warning));
2092}
2093
2094TEST(DiagnosticsTest, IncludeCleaner) {
2095 Annotations Test(R"cpp(
2096$fix[[ $diag[[#include "unused.h"]]
2097]]
2098 #include "used.h"
2099
2100 #include "ignore.h"
2101
2102 #include <system_header.h>
2103
2104 void foo() {
2105 used();
2106 }
2107 )cpp");
2108 TestTU TU;
2109 TU.Code = Test.code().str();
2110 TU.AdditionalFiles["unused.h"] = R"cpp(
2111 #pragma once
2112 void unused() {}
2113 )cpp";
2114 TU.AdditionalFiles["used.h"] = R"cpp(
2115 #pragma once
2116 void used() {}
2117 )cpp";
2118 TU.AdditionalFiles["ignore.h"] = R"cpp(
2119 #pragma once
2120 void ignore() {}
2121 )cpp";
2122 TU.AdditionalFiles["system/system_header.h"] = "";
2123 TU.ExtraArgs = {"-isystem" + testPath("system")};
2124 Config Cfg;
2126 // Set filtering.
2127 Cfg.Diagnostics.Includes.IgnoreHeader.emplace_back(
2128 [](llvm::StringRef Header) { return Header.ends_with("ignore.h"); });
2129 WithContextValue WithCfg(Config::Key, std::move(Cfg));
2130 auto AST = TU.build();
2131 EXPECT_THAT(
2132 AST.getDiagnostics(),
2133 Contains(AllOf(
2134 Diag(Test.range("diag"),
2135 "included header unused.h is not used directly"),
2136 withTag(DiagnosticTag::Unnecessary), diagSource(Diag::Clangd),
2137 withFix(Fix(Test.range("fix"), "", "remove #include directive")))));
2138 auto &Diag = AST.getDiagnostics().front();
2140 llvm::ValueIs(Not(IsEmpty())));
2141 Cfg.Diagnostics.SuppressAll = true;
2142 WithContextValue SuppressAllWithCfg(Config::Key, std::move(Cfg));
2143 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
2144 Cfg.Diagnostics.SuppressAll = false;
2145 Cfg.Diagnostics.Suppress = {"unused-includes"};
2146 WithContextValue SuppressFilterWithCfg(Config::Key, std::move(Cfg));
2147 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
2148}
2149
2150TEST(DiagnosticsTest, FixItFromHeader) {
2151 llvm::StringLiteral Header(R"cpp(
2152 void foo(int *);
2153 void foo(int *, int);)cpp");
2154 Annotations Source(R"cpp(
2155 /*error-ok*/
2156 void bar() {
2157 int x;
2158 $diag[[foo]]($fix[[]]x, 1);
2159 })cpp");
2160 TestTU TU;
2161 TU.Code = Source.code().str();
2162 TU.HeaderCode = Header.str();
2163 EXPECT_THAT(
2164 TU.build().getDiagnostics(),
2165 UnorderedElementsAre(AllOf(
2166 Diag(Source.range("diag"), "no matching function for call to 'foo'"),
2167 withFix(Fix(Source.range("fix"), "&",
2168 "candidate function not viable: no known conversion from "
2169 "'int' to 'int *' for 1st argument; take the address of "
2170 "the argument with &")))));
2171}
2172
2173TEST(DiagnosticsTest, UnusedInHeader) {
2174 // Clang diagnoses unused static inline functions outside headers.
2175 auto TU = TestTU::withCode("static inline void foo(void) {}");
2176 TU.ExtraArgs.push_back("-Wunused-function");
2177 TU.Filename = "test.c";
2178 EXPECT_THAT(TU.build().getDiagnostics(),
2179 ElementsAre(withID(diag::warn_unused_function)));
2180 // Sema should recognize a *.h file open in clangd as a header.
2181 // https://github.com/clangd/vscode-clangd/issues/360
2182 TU.Filename = "test.h";
2183 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
2184}
2185
2186TEST(DiagnosticsTest, DontSuppressSubcategories) {
2187 Annotations Source(R"cpp(
2188 /*error-ok*/
2189 void bar(int x) {
2190 switch(x) {
2191 default:
2192 break;
2193 break;
2194 }
2195 })cpp");
2196 TestTU TU;
2197 TU.ExtraArgs.push_back("-Wunreachable-code-aggressive");
2198 TU.Code = Source.code().str();
2199 Config Cfg;
2200 // This shouldn't suppress subcategory unreachable-break.
2201 Cfg.Diagnostics.Suppress = {"unreachable-code"};
2202 WithContextValue SuppressFilterWithCfg(Config::Key, std::move(Cfg));
2203 EXPECT_THAT(TU.build().getDiagnostics(),
2204 ElementsAre(diagName("-Wunreachable-code-break")));
2205}
2206
2207} // namespace
2208} // namespace clangd
2209} // namespace clang
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.
Definition Annotations.h:23
static std::unique_ptr< SymbolIndex > build(SymbolSlab Symbols, RefSlab Refs, RelationSlab Relations)
Builds an index from slabs. The index takes ownership of the data.
Definition MemIndex.cpp:18
An efficient structure of storing large set of symbol references in memory.
Definition Ref.h:111
SymbolSlab::Builder is a mutable container that can 'freeze' to SymbolSlab.
Definition Symbol.h:224
void insert(const Symbol &S)
Adds a symbol, overwriting any existing one with the same ID.
Definition Symbol.cpp:52
WithContextValue extends Context::current() with a single value.
Definition Context.h:200
FIXME: Skip testing on windows temporarily due to the different escaping code mode.
Definition AST.cpp:44
llvm::unique_function< void(tidy::ClangTidyOptions &, llvm::StringRef) const > TidyProvider
A factory to modify a tidy::ClangTidyOptions.
Symbol func(llvm::StringRef Name)
Definition TestIndex.cpp:62
const NamedDecl & findDecl(ParsedAST &AST, llvm::StringRef QName)
Definition TestTU.cpp:220
Symbol cls(llvm::StringRef Name)
Definition TestIndex.cpp:66
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, "")
MATCHER_P(named, N, "")
std::string testPath(PathRef File, llvm::sys::path::Style Style)
Definition TestFS.cpp:94
static URISchemeRegistry::Add< TestScheme > X(TestScheme::Scheme, "Test schema")
llvm::json::Value toJSON(const FuzzyFindRequest &Request)
Definition Index.cpp:45
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)
Definition TestIndex.cpp:70
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.
Definition Protocol.h:936
@ Unnecessary
Unused or unnecessary code.
Definition Protocol.h:932
std::optional< std::string > getDiagnosticDocURI(Diag::DiagSource Source, unsigned ID, llvm::StringRef Name)
Returns a URI providing more information about a particular diagnostic.
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Settings that express user/project preferences and control clangd behavior.
Definition Config.h:45
static clangd::Key< Config > Key
Context key which can be used to set the current Config.
Definition Config.h:49
@ Strict
Diagnose missing and unused includes.
Definition Config.h:99
struct clang::clangd::Config::@343034053122374337352226322054223376344037116252 Diagnostics
Controls warnings and errors when parsing code.
llvm::StringSet Suppress
Definition Config.h:106
IncludesPolicy UnusedIncludes
Definition Config.h:116
A top-level diagnostic that may have Notes and Fixes.
Definition Diagnostics.h:98
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 related message and source code location for a diagnostic.
Definition Protocol.h:919
std::string message
The message of this related diagnostic information.
Definition Protocol.h:923
Represents a single fix-it that editor can apply to fix the error.
Definition Diagnostics.h:81
std::string Message
Message for the fix-it.
Definition Diagnostics.h:83
llvm::SmallVector< TextEdit, 1 > Edits
TextEdits from clang's fix-its. Must be non-empty.
Definition Diagnostics.h:85
Represents a header file to be include'd.
Definition Headers.h:42
Represents a note for the diagnostic.
Definition Diagnostics.h:95
int line
Line position in a document (zero-based).
Definition Protocol.h:159
The class presents a C++ symbol, e.g.
Definition Symbol.h:39
@ IndexedForCodeCompletion
Whether or not this symbol is meant to be used for the code completion.
Definition Symbol.h:141
@ Include
#include "header.h"
Definition Symbol.h:93
std::vector< std::string > ExtraArgs
Definition TestTU.h:60
std::string Code
Definition TestTU.h:49
static TestTU withHeaderCode(llvm::StringRef HeaderCode)
Definition TestTU.h:42
static TestTU withCode(llvm::StringRef Code)
Definition TestTU.h:36
const SymbolIndex * ExternalIndex
Definition TestTU.h:67
static URIForFile canonicalize(llvm::StringRef AbsPath, llvm::StringRef TUPath)
Canonicalizes AbsPath via URI.
Definition Protocol.cpp:46