9#include "../clang-tidy/ClangTidyOptions.h"
27#include "clang/AST/Decl.h"
28#include "clang/Basic/Diagnostic.h"
29#include "clang/Basic/DiagnosticSema.h"
30#include "clang/Basic/LLVM.h"
31#include "clang/Basic/Specifiers.h"
32#include "llvm/ADT/ArrayRef.h"
33#include "llvm/ADT/StringRef.h"
34#include "llvm/Support/JSON.h"
35#include "llvm/Support/ScopedPrinter.h"
36#include "llvm/Support/TargetSelect.h"
37#include "llvm/Testing/Support/SupportHelpers.h"
38#include "gmock/gmock.h"
39#include "gtest/gtest.h"
52using ::testing::AllOf;
53using ::testing::Contains;
55using ::testing::ElementsAre;
56using ::testing::Field;
57using ::testing::IsEmpty;
60using ::testing::SizeIs;
61using ::testing::UnorderedElementsAre;
63::testing::Matcher<const Diag &> withFix(::testing::Matcher<Fix> FixMatcher) {
67::testing::Matcher<const Diag &> withFix(::testing::Matcher<Fix> FixMatcher1,
68 ::testing::Matcher<Fix> FixMatcher2) {
72::testing::Matcher<const Diag &> withID(
unsigned ID) {
75::testing::Matcher<const Diag &>
76withNote(::testing::Matcher<Note> NoteMatcher) {
80::testing::Matcher<const Diag &>
81withNote(::testing::Matcher<Note> NoteMatcher1,
82 ::testing::Matcher<Note> NoteMatcher2) {
83 return Field(&
Diag::Notes, UnorderedElementsAre(NoteMatcher1, NoteMatcher2));
86::testing::Matcher<const Diag &>
87withTag(::testing::Matcher<DiagnosticTag> TagMatcher) {
94 "Diag at " + llvm::to_string(
Range) +
" = [" + Message +
"]") {
95 return arg.Range ==
Range && arg.Message == Message;
98MATCHER_P3(
Fix,
Range, Replacement, Message,
99 "Fix " + llvm::to_string(
Range) +
" => " +
100 ::testing::PrintToString(Replacement) +
" = [" + Message +
"]") {
101 return arg.Message == Message && arg.Edits.size() == 1 &&
102 arg.Edits[0].range ==
Range && arg.Edits[0].newText == Replacement;
105MATCHER_P(fixMessage, Message,
"") {
return arg.Message == Message; }
108 "LSP diagnostic " + llvm::to_string(LSPDiag)) {
110 *result_listener << llvm::formatv(
"expected:\n{0:2}\ngot\n{1:2}",
118MATCHER_P(diagSource, S,
"") {
return arg.Source == S; }
119MATCHER_P(diagName, N,
"") {
return arg.Name == N; }
120MATCHER_P(diagSeverity, S,
"") {
return arg.Severity == S; }
125 if (arg.Edits.size() !=
Fix.
Edits.size())
127 for (std::size_t I = 0; I < arg.Edits.size(); ++I) {
128 if (arg.Edits[I].range !=
Fix.
Edits[I].range ||
129 arg.Edits[I].newText !=
Fix.
Edits[I].newText)
136Position pos(
int Line,
int Character) {
148::testing::Matcher<std::vector<clangd::Diag>>
149ifTidyChecks(::testing::Matcher<std::vector<clangd::Diag>> M) {
150 if (!CLANGD_TIDY_CHECKS)
155TEST(DiagnosticsTest, DiagnosticRanges) {
163 struct Container { int* begin(); int* end(); } *container;
164 for (auto i : $insertstar[[]]$range[[container]]) {
169 foo()$semicolon[[]]//with comments
171 double $type[[bar]] = "foo";
172 struct Foo { int x; }; Foo a;
174 test::$nomembernamespace[[test]];
175 $macro[[ID($macroarg[[fod]])]]();
180 TU.build().getDiagnostics(),
183 AllOf(
Diag(Test.range(
"range"),
184 "invalid range expression of type 'struct Container *'; "
185 "did you mean to dereference it with '*'?"),
186 withFix(
Fix(Test.range(
"insertstar"),
"*",
"insert '*'"))),
188 AllOf(
Diag(Test.range(
"typo"),
189 "use of undeclared identifier 'goo'; did you mean 'foo'?"),
190 diagSource(
Diag::Clang), diagName(
"undeclared_var_use_suggest"),
192 Fix(Test.range(
"typo"),
"foo",
"change 'go\\…' to 'foo'")),
194 withNote(
Diag(Test.range(
"decl"),
"'foo' declared here"))),
198 AllOf(
Diag(Test.range(
"semicolon"),
"expected ';' after expression"),
199 withFix(
Fix(Test.range(
"semicolon"),
";",
"insert ';'"))),
201 Diag(Test.range(
"unk"),
"use of undeclared identifier 'unknown'"),
202 Diag(Test.range(
"type"),
203 "cannot initialize a variable of type 'double' with an lvalue "
204 "of type 'const char[4]'"),
205 Diag(Test.range(
"nomember"),
"no member named 'y' in 'Foo'"),
206 Diag(Test.range(
"nomembernamespace"),
207 "no member named 'test' in namespace 'test'"),
208 AllOf(
Diag(Test.range(
"macro"),
209 "use of undeclared identifier 'fod'; did you mean 'foo'?"),
210 withFix(
Fix(Test.range(
"macroarg"),
"foo",
211 "change 'fod' to 'foo'")))));
219TEST(DiagnosticsTest, WSwitch) {
228 TU.ExtraArgs = {
"-Wswitch"};
229 EXPECT_THAT(TU.build().getDiagnostics(),
230 ElementsAre(
Diag(Test.range(),
231 "enumeration value 'X' not handled in switch")));
234TEST(DiagnosticsTest, FlagsMatter) {
235 Annotations Test(
"[[void]] main() {} // error-ok");
237 EXPECT_THAT(TU.build().getDiagnostics(),
238 ElementsAre(AllOf(
Diag(Test.range(),
"'main' must return 'int'"),
239 withFix(
Fix(Test.range(),
"int",
240 "change 'void' to 'int'")))));
242 TU.Filename =
"Plain.c";
244 TU.build().getDiagnostics(),
246 Diag(Test.range(),
"return type of 'main' is not 'int'"),
247 withFix(
Fix(Test.range(),
"int",
"change return type to 'int'")))));
250TEST(DiagnosticsTest, DiagnosticPreamble) {
252 #include $[["not-found.h"]] // error-ok
256 EXPECT_THAT(TU.build().getDiagnostics(),
257 ElementsAre(::testing::AllOf(
258 Diag(Test.range(),
"'not-found.h' file not found"),
259 diagSource(
Diag::Clang), diagName(
"pp_file_not_found"))));
262TEST(DiagnosticsTest, DeduplicatedClangTidyDiagnostics) {
264 float foo = [[0.1f]];
269 TU.ClangTidyProvider =
addTidyChecks(
"readability-uppercase-literal-suffix,"
273 TU.build().getDiagnostics(),
274 ifTidyChecks(UnorderedElementsAre(::testing::AllOf(
276 "floating point literal has suffix 'f', which is not uppercase"),
289 TU.Code = std::string(Test.code());
293 TU.build().getDiagnostics(),
294 ifTidyChecks(UnorderedElementsAre(::testing::AllOf(
296 "floating point literal has suffix 'f', which is not uppercase"),
300TEST(DiagnosticsTest, ClangTidy) {
302 #include $deprecated[["assert.h"]]
304 #define $macrodef[[SQUARE]](X) (X)*(X)
305 int $main[[main]]() {
307 return SQUARE($macroarg[[++]]y);
308 return $doubled[[sizeof(sizeof(int))]];
311 // misc-no-recursion uses a custom traversal from the TUDecl
321 TU.AdditionalFiles[
"system/assert.h"] =
"";
322 TU.ClangTidyProvider =
addTidyChecks(
"bugprone-sizeof-expression,"
323 "bugprone-macro-repeated-side-effects,"
324 "modernize-deprecated-headers,"
325 "modernize-use-trailing-return-type,"
326 "misc-no-recursion");
327 TU.ExtraArgs.push_back(
"-isystem" +
testPath(
"system"));
328 TU.ExtraArgs.push_back(
"-Wno-unsequenced");
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"),
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 "
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"),
352 diagName(
"modernize-use-trailing-return-type"),
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"))));
362TEST(DiagnosticsTest, ClangTidyRedundantParenthesesFix) {
365 return$lparen[[(]]0$rparen[[)]];
369 TU.ClangTidyProvider =
addTidyChecks(
"readability-redundant-parentheses");
371 clangd::Fix ExpectedFix;
372 ExpectedFix.Message =
"redundant parentheses around expression";
373 ExpectedFix.Edits.push_back(
TextEdit{Test.range(
"lparen"),
" "});
374 ExpectedFix.Edits.push_back(
TextEdit{Test.range(
"rparen"),
""});
377 TU.build().getDiagnostics(),
378 ifTidyChecks(ElementsAre(AllOf(
379 Diag(Test.range(
"lparen"),
"redundant parentheses around expression"),
381 diagName(
"readability-redundant-parentheses"),
382 withFix(equalToFix(ExpectedFix))))));
385TEST(DiagnosticsTest, ClangTidyEOF) {
389 #include "a.h")cpp");
392 TU.ExtraArgs = {
"-isystem."};
393 TU.AdditionalFiles[
"a.h"] = TU.AdditionalFiles[
"b.h"] =
"";
396 TU.build().getDiagnostics(),
397 ifTidyChecks(Contains(
398 AllOf(
Diag(Test.range(),
"#includes are not sorted properly"),
402TEST(DiagnosticTest, TemplatesInHeaders) {
405 Derived<int> [[y]]; // error-ok
408 template <typename T>
409 struct Derived : [[T]] {};
412 TU.HeaderCode = Header.code().str();
414 TU.build().getDiagnostics(),
416 Diag(Main.range(), "in template: base specifier must name a class"),
417 withNote(
Diag(Header.range(),
"error occurred here"),
418 Diag(Main.range(),
"in instantiation of template class "
419 "'Derived<int>' requested here")))));
422TEST(DiagnosticTest, MakeUnique) {
426 struct S { S(char*); };
427 auto x = std::[[make_unique]]<S>(42); // error-ok
430 TU.HeaderCode = R"cpp(
432 // These mocks aren't quite right - we omit unique_ptr for simplicity.
433 // forward is included to show its body is not needed to get the diagnostic.
434 template <typename T> T&& forward(T& t);
435 template <typename T, typename... A> T* make_unique(A&&... args) {
436 return new T(std::forward<A>(args)...);
440 EXPECT_THAT(TU.build().getDiagnostics(),
441 UnorderedElementsAre(
444 "no matching constructor for initialization of 'S'")));
447TEST(DiagnosticTest, CoroutineInHeader) {
448 StringRef CoroutineH = R
"cpp(
450template <class Ret, typename... T>
451struct coroutine_traits { using promise_type = typename Ret::promise_type; };
453template <class Promise = void>
454struct coroutine_handle {
455 static coroutine_handle from_address(void *) noexcept;
456 static coroutine_handle from_promise(Promise &promise);
457 constexpr void* address() const noexcept;
460struct coroutine_handle<void> {
461 template <class PromiseType>
462 coroutine_handle(coroutine_handle<PromiseType>) noexcept;
463 static coroutine_handle from_address(void *);
464 constexpr void* address() const noexcept;
468 bool await_ready() noexcept { return false; }
469 void await_suspend(coroutine_handle<>) noexcept {}
470 void await_resume() noexcept {}
475 StringRef Header = R"cpp(
476#include "coroutine.h"
477template <typename T> struct [[clang::coro_return_type]] Gen {
478 struct promise_type {
479 Gen<T> get_return_object() {
482 std::awaitable initial_suspend();
483 std::awaitable final_suspend() noexcept;
484 void unhandled_exception();
485 void return_value(T t);
489Gen<int> foo_coro(int b) { co_return b; }
494Gen<int> $[[bar_coro]](int b) { return foo_coro(b); }
497 TU.AdditionalFiles["coroutine.h"] = std::string(CoroutineH);
498 TU.AdditionalFiles[
"header.hpp"] = std::string(Header);
499 TU.ExtraArgs.push_back(
"--std=c++20");
500 EXPECT_THAT(TU.build().getDiagnostics(), ElementsAre(hasRange(Main.range())));
503TEST(DiagnosticTest, MakeShared) {
507 struct S { S(char*); };
508 auto x = std::[[make_shared]]<S>(42); // error-ok
511 TU.HeaderCode = R"cpp(
513 // These mocks aren't quite right - we omit shared_ptr for simplicity.
514 // forward is included to show its body is not needed to get the diagnostic.
515 template <typename T> T&& forward(T& t);
516 template <typename T, typename... A> T* make_shared(A&&... args) {
517 return new T(std::forward<A>(args)...);
521 TU.ParseOpts.PreambleParseForwardingFunctions = true;
522 EXPECT_THAT(TU.build().getDiagnostics(),
523 UnorderedElementsAre(
526 "no matching constructor for initialization of 'S'")));
529TEST(DiagnosticTest, NoMultipleDiagnosticInFlight) {
531 template <typename T> struct Foo {
541 Foo<LabelInfo> label_info_map;
542 [[for]] (auto it = label_info_map.begin(); it != label_info_map.end(); ++it) {
548 TU.ClangTidyProvider = addTidyChecks("modernize-loop-convert");
550 TU.build().getDiagnostics(),
551 ifTidyChecks(UnorderedElementsAre(::testing::AllOf(
552 Diag(Main.range(),
"use range-based for loop instead"),
556TEST(DiagnosticTest, RespectsDiagnosticConfig) {
566 TU.build().getDiagnostics(),
567 ElementsAre(
Diag(Main.range(),
"use of undeclared identifier 'unknown'"),
568 Diag(Main.range(
"ret"),
569 "void function 'x' should not return a value")));
573 EXPECT_THAT(TU.build().getDiagnostics(),
574 ElementsAre(
Diag(Main.range(),
575 "use of undeclared identifier 'unknown'")));
578TEST(DiagnosticTest, RespectsDiagnosticConfigInHeader) {
580 int x = "42"; // error-ok
583 #include "header.hpp"
586 TU.AdditionalFiles[
"header.hpp"] = std::string(Header.code());
590 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
593TEST(DiagnosticTest, ClangTidySuppressionComment) {
597 double d = 8 / i; // NOLINT
601 double f = BAD; // NOLINT
602 double g = [[8]] / i;
604 double h = BAD2; // NOLINT
610 // verify no crashes on unmatched nolints.
615 TU.ClangTidyProvider = addTidyChecks("bugprone-integer-division");
617 TU.build().getDiagnostics(),
618 ifTidyChecks(UnorderedElementsAre(::testing::AllOf(
619 Diag(Main.range(),
"result of integer division used in a floating "
620 "point context; possible loss of precision"),
622 diagName(
"bugprone-integer-division")))));
625TEST(DiagnosticTest, ClangTidySystemMacro) {
630 double x = $inline[[8]] / i;
631 double y = $user[[DIVIDE_USER]](i);
632 double z = DIVIDE_SYS(i);
636 TU.AdditionalFiles[
"user.h"] = R
"cpp(
637 #define DIVIDE_USER(Y) 8/Y
639 TU.AdditionalFiles["system.h"] = R
"cpp(
640 #pragma clang system_header
641 #define DIVIDE_SYS(Y) 8/Y
644 TU.ClangTidyProvider = addTidyChecks("bugprone-integer-division");
645 std::string BadDivision =
"result of integer division used in a floating "
646 "point context; possible loss of precision";
650 EXPECT_THAT(TU.build().getDiagnostics(),
652 UnorderedElementsAre(
Diag(Main.range(
"inline"), BadDivision),
653 Diag(Main.range(
"user"), BadDivision))));
656TEST(DiagnosticTest, ClangTidyWarningAsError) {
660 double f = [[8]] / i; // error-ok
664 TU.ClangTidyProvider =
665 addTidyChecks("bugprone-integer-division",
"bugprone-integer-division");
667 TU.build().getDiagnostics(),
668 ifTidyChecks(UnorderedElementsAre(::testing::AllOf(
669 Diag(Main.range(),
"result of integer division used in a floating "
670 "point context; possible loss of precision"),
672 diagSeverity(DiagnosticsEngine::Error)))));
675TidyProvider addClangArgs(std::vector<llvm::StringRef> ExtraArgs,
677 return [ExtraArgs = std::move(ExtraArgs),
Checks =
Checks.str()](
678 tidy::ClangTidyOptions &Opts, llvm::StringRef) {
680 Opts.ExtraArgs.emplace();
681 for (llvm::StringRef Arg : ExtraArgs)
682 Opts.ExtraArgs->emplace_back(Arg);
688TEST(DiagnosticTest, ClangTidyEnablesClangWarning) {
690 static void [[foo]]() {}
694 auto UnusedFooWarning =
695 AllOf(
Diag(Main.range(),
"unused function 'foo'"),
696 diagName(
"-Wunused-function"), diagSource(
Diag::Clang),
697 diagSeverity(DiagnosticsEngine::Warning));
700 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
704 TU.ClangTidyProvider =
705 addClangArgs({
"-Wunused"},
"clang-diagnostic-unused-function");
706 EXPECT_THAT(TU.build().getDiagnostics(), ElementsAre(UnusedFooWarning));
709 TU.ClangTidyProvider = addClangArgs({
"-Wunused"},
"clang-diagnostic-*");
710 EXPECT_THAT(TU.build().getDiagnostics(), ElementsAre(UnusedFooWarning));
712 TU.ClangTidyProvider = addClangArgs({
"-Wunused"},
"*");
713 EXPECT_THAT(TU.build().getDiagnostics(), Contains(UnusedFooWarning));
715 TU.ClangTidyProvider = addClangArgs(
716 {
"-Wunused"},
"clang-diagnostic-*,-clang-diagnostic-unused-function");
717 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
720 TU.ClangTidyProvider = addClangArgs({
"-Wunused"},
"clang-diagnostic-unused");
721 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
724 TU.ClangTidyProvider = addClangArgs({
"-Wunused",
"-Dfoo=bar"},
725 "clang-diagnostic-unused-function");
726 EXPECT_THAT(TU.build().getDiagnostics(), ElementsAre(UnusedFooWarning))
727 <<
"Not unused function 'bar'!";
730 TU.ExtraArgs = {
"-Werror"};
731 TU.ClangTidyProvider =
732 addClangArgs({
"-Wunused"},
"clang-diagnostic-unused-function");
733 EXPECT_THAT(TU.build().getDiagnostics(),
734 ElementsAre(diagSeverity(DiagnosticsEngine::Warning)));
737 TU.ExtraArgs = {
"-Wunused",
"-Werror"};
738 TU.ClangTidyProvider =
739 addClangArgs({
"-Wunused"},
"clang-diagnostic-unused-function");
740 EXPECT_THAT(TU.build().getDiagnostics(),
741 ElementsAre(diagSeverity(DiagnosticsEngine::Error)));
744 TU.ExtraArgs = {
"-Wunused-function",
"-Werror"};
745 TU.ClangTidyProvider =
746 addClangArgs({
"-Wunused"},
"clang-diagnostic-unused-label");
747 EXPECT_THAT(TU.build().getDiagnostics(),
748 ElementsAre(diagSeverity(DiagnosticsEngine::Warning)));
753 TU.ClangTidyProvider = addClangArgs({
"-Wunused",
"-Wno-unused"},
754 "clang-diagnostic-unused-function");
755 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
758 TU.ClangTidyProvider =
759 addClangArgs({
"-Wunused"}, {
"clang-diagnostic-unused-function"});
760 EXPECT_THAT(TU.build().getDiagnostics(), SizeIs(1));
763 TU.ClangTidyProvider = addClangArgs({
"-Wunused",
"-Wno-unused-function"},
764 {
"clang-diagnostic-unused-function"});
765 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
766 TU.ClangTidyProvider = addClangArgs({
"-Wunused-function",
"-Wno-unused"},
767 {
"clang-diagnostic-unused-function"});
768 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
772 TU.ExtraArgs = {
"-Wunused"};
773 TU.ClangTidyProvider = addClangArgs({
"-Wno-unused"}, {});
774 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
776 TU.ExtraArgs = {
"-Wno-unused"};
777 TU.ClangTidyProvider = addClangArgs({
"-Wunused"}, {
"-*, clang-diagnostic-*"});
778 EXPECT_THAT(TU.build().getDiagnostics(), SizeIs(1));
781TEST(DiagnosticTest, LongFixMessages) {
786 int somereallyreallyreallyreallyreallyreallyreallyreallylongidentifier;
787 [[omereallyreallyreallyreallyreallyreallyreallyreallylongidentifier]]= 10;
792 TU.build().getDiagnostics(),
793 ElementsAre(withFix(Fix(
795 "somereallyreallyreallyreallyreallyreallyreallyreallylongidentifier",
796 "change 'omereallyreallyreallyreallyreallyreallyreallyreall…' to "
797 "'somereallyreallyreallyreallyreallyreallyreallyreal…'"))));
807 TU.Code = std::string(Source.code());
808 EXPECT_THAT(TU.build().getDiagnostics(),
810 Fix(Source.range(), "ident",
"change 'ide\\…' to 'ident'"))));
813TEST(DiagnosticTest, NewLineFixMessage) {
816 TU.ExtraArgs = {
"-Wnewline-eof"};
818 TU.build().getDiagnostics(),
819 ElementsAre(withFix((
Fix(Source.range(),
"\n",
"insert '\\n'")))));
822TEST(DiagnosticTest, ClangTidySuppressionCommentTrumpsWarningAsError) {
826 double f = [[8]] / i; // NOLINT
830 TU.ClangTidyProvider =
831 addTidyChecks("bugprone-integer-division",
"bugprone-integer-division");
832 EXPECT_THAT(TU.build().getDiagnostics(), UnorderedElementsAre());
835TEST(DiagnosticTest, ClangTidyNoLiteralDataInMacroToken) {
838 using pthread_t = int;
839 int pthread_kill(pthread_t thread, int sig);
842 return pthread_kill(thread, 0);
846 TU.ClangTidyProvider = addTidyChecks("bugprone-bad-signal-to-kill-thread");
847 EXPECT_THAT(TU.build().getDiagnostics(), UnorderedElementsAre());
850TEST(DiagnosticTest, BadSignalToKillThreadInPreamble) {
853 using pthread_t = int;
854 int pthread_kill(pthread_t thread, int sig);
857 return pthread_kill(thread, 15);
861 TU.HeaderFilename = "signal.h";
862 TU.HeaderCode =
"#define SIGTERM 15";
863 TU.ClangTidyProvider =
addTidyChecks(
"bugprone-bad-signal-to-kill-thread");
864 EXPECT_THAT(TU.build().getDiagnostics(),
865 ifTidyChecks(UnorderedElementsAre(
866 diagName(
"bugprone-bad-signal-to-kill-thread"))));
869TEST(DiagnosticTest, ClangTidyMacroToEnumCheck) {
876 std::vector<TidyProvider> Providers;
878 addTidyChecks("cppcoreguidelines-macro-to-enum,modernize-macro-to-enum"));
880 TU.ClangTidyProvider =
combine(std::move(Providers));
881 EXPECT_THAT(TU.build().getDiagnostics(), UnorderedElementsAre());
884TEST(DiagnosticTest, ElseAfterReturnRange) {
889 } [[else]] if (cond == 2) {
896 TU.ClangTidyProvider = addTidyChecks("llvm-else-after-return");
897 EXPECT_THAT(TU.build().getDiagnostics(),
898 ifTidyChecks(ElementsAre(
899 Diag(Main.range(),
"do not use 'else' after 'return'"))));
902TEST(DiagnosticTest, ClangTidySelfContainedDiags) {
911 void InitVariables() {
912 float $C[[C]]$CFix[[]];
913 double $D[[D]]$DFix[[]];
917 TU.ClangTidyProvider =
919 "cppcoreguidelines-init-variables");
920 clangd::Fix ExpectedAFix;
921 ExpectedAFix.Message =
922 "'A' should be initialized in a member initializer of the constructor";
923 ExpectedAFix.Edits.push_back(
TextEdit{Main.range(
"Fix"),
" : A(1)"});
924 ExpectedAFix.Edits.push_back(
TextEdit{Main.range(
"A"),
""});
929 clangd::Fix ExpectedBFix;
930 ExpectedBFix.Message =
931 "'B' should be initialized in a member initializer of the constructor";
932 ExpectedBFix.Edits.push_back(
TextEdit{Main.range(
"Fix"),
" : B(1)"});
933 ExpectedBFix.Edits.push_back(
TextEdit{Main.range(
"B"),
""});
935 clangd::Fix ExpectedCFix;
936 ExpectedCFix.Message =
"variable 'C' is not initialized";
937 ExpectedCFix.Edits.push_back(
TextEdit{Main.range(
"CFix"),
" = NAN"});
938 ExpectedCFix.Edits.push_back(
939 TextEdit{Main.range(
"MathHeader"),
"#include <math.h>\n\n"});
943 clangd::Fix ExpectedDFix;
944 ExpectedDFix.Message =
"variable 'D' is not initialized";
945 ExpectedDFix.Edits.push_back(
TextEdit{Main.range(
"DFix"),
" = NAN"});
946 ExpectedDFix.Edits.push_back(
947 TextEdit{Main.range(
"MathHeader"),
"#include <math.h>\n\n"});
949 TU.build().getDiagnostics(),
950 ifTidyChecks(UnorderedElementsAre(
951 AllOf(
Diag(Main.range(
"A"),
"'A' should be initialized in a member "
952 "initializer of the constructor"),
953 withFix(equalToFix(ExpectedAFix))),
954 AllOf(
Diag(Main.range(
"B"),
"'B' should be initialized in a member "
955 "initializer of the constructor"),
956 withFix(equalToFix(ExpectedBFix))),
957 AllOf(
Diag(Main.range(
"C"),
"variable 'C' is not initialized"),
958 withFix(equalToFix(ExpectedCFix))),
959 AllOf(
Diag(Main.range(
"D"),
"variable 'D' is not initialized"),
960 withFix(equalToFix(ExpectedDFix))))));
963TEST(DiagnosticTest, ClangTidySelfContainedDiagsFormatting) {
967 virtual void Reset1() = 0;
968 virtual void Reset2() = 0;
970 class A : public Interface {
971 // This will be marked by clangd to use override instead of virtual
972 $virtual1[[virtual ]]void $Reset1[[Reset1]]()$override1[[]];
973 $virtual2[[virtual ]]/**/void $Reset2[[Reset2]]()$override2[[]];
977 TU.ClangTidyProvider =
978 addTidyChecks("cppcoreguidelines-explicit-virtual-functions,");
979 clangd::Fix
const ExpectedFix1{
980 "prefer using 'override' or (rarely) 'final' "
981 "instead of 'virtual'",
982 {
TextEdit{Main.range(
"override1"),
" override"},
983 TextEdit{Main.range(
"virtual1"),
""}},
985 clangd::Fix
const ExpectedFix2{
986 "prefer using 'override' or (rarely) 'final' "
987 "instead of 'virtual'",
988 {
TextEdit{Main.range(
"override2"),
" override"},
989 TextEdit{Main.range(
"virtual2"),
""}},
993 EXPECT_THAT(TU.build().getDiagnostics(),
994 ifTidyChecks(UnorderedElementsAre(
995 AllOf(
Diag(Main.range(
"Reset1"),
996 "prefer using 'override' or (rarely) 'final' "
997 "instead of 'virtual'"),
998 withFix(equalToFix(ExpectedFix1))),
999 AllOf(
Diag(Main.range(
"Reset2"),
1000 "prefer using 'override' or (rarely) 'final' "
1001 "instead of 'virtual'"),
1002 withFix(equalToFix(ExpectedFix2))))));
1005TEST(DiagnosticsTest, ClangTidyCallingIntoPreprocessor) {
1006 std::string Main = R
"cpp(
1011 std::string Header = R"cpp(
1012 #define EXTERN extern
1016 TU.AdditionalFiles[
"b.h"] = Header;
1017 TU.ClangTidyProvider =
addTidyChecks(
"modernize-use-trailing-return-type");
1022TEST(DiagnosticsTest, Preprocessor) {
1031 int a = [[b]]; // error-ok
1038 ElementsAre(Diag(Test.range(), "use of undeclared identifier 'b'")));
1041TEST(DiagnosticsTest, IgnoreVerify) {
1043 int a; // expected-error {{}}
1045 TU.ExtraArgs.push_back("-Xclang");
1046 TU.ExtraArgs.push_back(
"-verify");
1047 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
1050TEST(DiagnosticTest, IgnoreBEFilelistOptions) {
1052 TU.ExtraArgs.push_back(
"-Xclang");
1053 for (
const auto *DisableOption :
1054 {
"-fsanitize-ignorelist=null",
"-fprofile-list=null",
1055 "-fxray-always-instrument=null",
"-fxray-never-instrument=null",
1056 "-fxray-attr-list=null"}) {
1057 TU.ExtraArgs.push_back(DisableOption);
1058 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
1059 TU.ExtraArgs.pop_back();
1064TEST(DiagnosticsTest, RecursivePreamble) {
1066 #include "foo.h" // error-ok
1069 TU.Filename = "foo.h";
1070 EXPECT_THAT(TU.build().getDiagnostics(),
1071 ElementsAre(diagName(
"pp_including_mainfile_in_preamble")));
1072 EXPECT_THAT(TU.build().getLocalTopLevelDecls(), SizeIs(1));
1076TEST(DiagnosticsTest, RecursivePreamblePragmaOnce) {
1082 TU.Filename = "foo.h";
1083 EXPECT_THAT(TU.build().getDiagnostics(),
1084 Not(Contains(diagName(
"pp_including_mainfile_in_preamble"))));
1085 EXPECT_THAT(TU.build().getLocalTopLevelDecls(), SizeIs(1));
1090TEST(DiagnosticsTest, RecursivePreambleIfndefGuard) {
1094 #include "foo.h" // error-ok
1098 TU.Filename = "foo.h";
1100 EXPECT_THAT(TU.build().getDiagnostics(),
1101 ElementsAre(diagName(
"pp_including_mainfile_in_preamble")));
1102 EXPECT_THAT(TU.build().getLocalTopLevelDecls(), SizeIs(1));
1105TEST(DiagnosticsTest, PreambleWithPragmaAssumeNonnull) {
1107#pragma clang assume_nonnull begin
1109#pragma clang assume_nonnull end
1111 auto AST = TU.build();
1112 EXPECT_THAT(
AST.getDiagnostics(), IsEmpty());
1113 const auto *
X = cast<FunctionDecl>(
findDecl(
AST,
"foo")).getParamDecl(0);
1114 ASSERT_TRUE(
X->getOriginalType()->getNullability() ==
1115 NullabilityKind::NonNull);
1118TEST(DiagnosticsTest, PreamblePragmaDiagnosticPushPop) {
1120#pragma clang diagnostic push
1124#pragma clang diagnostic pop
1126 auto AST = TU.build();
1127 EXPECT_THAT(
AST.getDiagnostics(), IsEmpty());
1130TEST(DiagnosticsTest, PreambleHeaderWithBadPragmaAssumeNonnull) {
1132#pragma clang assume_nonnull begin // error-ok
1136#include "foo.h" // unterminated assume_nonnull should not affect bar.
1139 TU.AdditionalFiles = {{"foo.h", std::string(Header.code())}};
1140 auto AST = TU.build();
1141 EXPECT_THAT(
AST.getDiagnostics(),
1142 ElementsAre(diagName(
"pp_eof_in_assume_nonnull")));
1143 const auto *
X = cast<FunctionDecl>(
findDecl(
AST,
"foo")).getParamDecl(0);
1144 ASSERT_TRUE(
X->getOriginalType()->getNullability() ==
1145 NullabilityKind::NonNull);
1146 const auto *Y = cast<FunctionDecl>(
findDecl(
AST,
"bar")).getParamDecl(0);
1147 ASSERT_FALSE(Y->getOriginalType()->getNullability());
1150TEST(DiagnosticsTest, InsideMacros) {
1153 #define RET(x) return x + 10
1156 RET($foo[[0]]); // error-ok
1163 ElementsAre(Diag(Test.range("foo"),
1164 "cannot initialize return object of type "
1165 "'int *' with an rvalue of type 'int'"),
1166 Diag(Test.range(
"bar"),
1167 "cannot initialize return object of type "
1168 "'int *' with an rvalue of type 'int'")));
1171TEST(DiagnosticsTest, NoFixItInMacro) {
1173 #define Define(name) void name() {}
1175 [[Define]](main) // error-ok
1178 EXPECT_THAT(TU.build().getDiagnostics(),
1179 ElementsAre(AllOf(
Diag(Test.range(),
"'main' must return 'int'"),
1183TEST(DiagnosticsTest, PragmaSystemHeader) {
1184 Annotations Test(
"#pragma clang [[system_header]]\n");
1187 TU.build().getDiagnostics(),
1189 Diag(Test.range(),
"#pragma system_header ignored in main file"))));
1190 TU.Filename =
"TestTU.h";
1191 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
1194TEST(ClangdTest, MSAsm) {
1197 llvm::InitializeAllTargetInfos();
1199 TU.ExtraArgs = {
"-fms-extensions"};
1200 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
1203TEST(DiagnosticsTest, ToLSP) {
1210 D.ID = clang::diag::err_undeclared_var_use;
1212 D.Name =
"undeclared_var_use";
1214 D.Message =
"something terrible happened";
1215 D.Range = {pos(1, 2), pos(3, 4)};
1216 D.InsideMainFile =
true;
1217 D.Severity = DiagnosticsEngine::Error;
1218 D.File =
"foo/bar/main.cpp";
1219 D.AbsFile = std::string(MainFile.file());
1220 D.OpaqueData[
"test"] =
"bar";
1222 clangd::Note NoteInMain;
1223 NoteInMain.Message =
"declared somewhere in the main file";
1224 NoteInMain.Range = {pos(5, 6), pos(7, 8)};
1225 NoteInMain.Severity = DiagnosticsEngine::Remark;
1226 NoteInMain.File =
"../foo/bar/main.cpp";
1227 NoteInMain.InsideMainFile =
true;
1228 NoteInMain.AbsFile = std::string(MainFile.file());
1230 D.Notes.push_back(NoteInMain);
1232 clangd::Note NoteInHeader;
1233 NoteInHeader.Message =
"declared somewhere in the header file";
1234 NoteInHeader.Range = {pos(9, 10), pos(11, 12)};
1235 NoteInHeader.Severity = DiagnosticsEngine::Note;
1236 NoteInHeader.File =
"../foo/baz/header.h";
1237 NoteInHeader.InsideMainFile =
false;
1238 NoteInHeader.AbsFile = std::string(
HeaderFile.file());
1239 D.Notes.push_back(NoteInHeader);
1242 F.Message =
"do something";
1243 D.Fixes.push_back(F);
1246 clangd::Diagnostic MainLSP;
1247 MainLSP.range =
D.Range;
1248 MainLSP.severity =
getSeverity(DiagnosticsEngine::Error);
1249 MainLSP.code =
"undeclared_var_use";
1250 MainLSP.source =
"clang";
1252 R
"(Something terrible happened (fix available)
1254main.cpp:6:7: remark: declared somewhere in the main file
1256../foo/baz/header.h:10:11:
1257note: declared somewhere in the header file)";
1259 MainLSP.data = D.OpaqueData;
1261 clangd::Diagnostic NoteInMainLSP;
1262 NoteInMainLSP.range = NoteInMain.Range;
1263 NoteInMainLSP.severity = getSeverity(DiagnosticsEngine::Remark);
1264 NoteInMainLSP.message = R"(Declared somewhere in the main file
1266main.cpp:2:3: error: something terrible happened)";
1270 std::vector<std::pair<clangd::Diagnostic, std::vector<clangd::Fix>>> LSPDiags;
1272 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix> Fixes) {
1274 {std::move(LSPDiag),
1275 std::vector<clangd::Fix>(Fixes.begin(), Fixes.end())});
1280 ElementsAre(Pair(equalToLSPDiag(MainLSP), ElementsAre(equalToFix(F))),
1281 Pair(equalToLSPDiag(NoteInMainLSP), IsEmpty())));
1282 EXPECT_EQ(LSPDiags[0].first.code,
"undeclared_var_use");
1283 EXPECT_EQ(LSPDiags[0].first.source,
"clang");
1284 EXPECT_EQ(LSPDiags[1].first.code,
"");
1285 EXPECT_EQ(LSPDiags[1].first.source,
"");
1289 Opts.EmitRelatedLocations =
true;
1291 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix> Fixes) {
1293 {std::move(LSPDiag),
1294 std::vector<clangd::Fix>(Fixes.begin(), Fixes.end())});
1296 MainLSP.message =
"Something terrible happened (fix available)";
1298 NoteInMainDRI.
message =
"Declared somewhere in the main file";
1299 NoteInMainDRI.location.range = NoteInMain.Range;
1300 NoteInMainDRI.location.uri = MainFile;
1301 MainLSP.relatedInformation = {NoteInMainDRI};
1303 NoteInHeaderDRI.
message =
"Declared somewhere in the header file";
1304 NoteInHeaderDRI.location.range = NoteInHeader.Range;
1306 MainLSP.relatedInformation = {NoteInMainDRI, NoteInHeaderDRI};
1307 EXPECT_THAT(LSPDiags, ElementsAre(Pair(equalToLSPDiag(MainLSP),
1308 ElementsAre(equalToFix(F)))));
1311struct SymbolWithHeader {
1313 std::string DeclaringFile;
1314 std::string IncludeHeader;
1317std::unique_ptr<SymbolIndex>
1318buildIndexWithSymbol(llvm::ArrayRef<SymbolWithHeader> Syms) {
1320 for (
const auto &S : Syms) {
1323 Sym.CanonicalDeclaration.FileURI = S.DeclaringFile.c_str();
1324 Sym.Definition.FileURI = S.DeclaringFile.c_str();
1331TEST(IncludeFixerTest, IncompleteType) {
1333 TU.ExtraArgs.push_back(
"-std=c++20");
1334 auto Index = buildIndexWithSymbol(
1335 {SymbolWithHeader{
"ns::X",
"unittest:///x.h",
"\"x.h\""}});
1336 TU.ExternalIndex = Index.get();
1338 std::vector<std::pair<llvm::StringRef, llvm::StringRef>> Tests{
1339 {
"incomplete_nested_name_spec",
"[[ns::X::]]Nested n;"},
1340 {
"incomplete_base_class",
"class Y : [[ns::X]] {};"},
1341 {
"incomplete_member_access",
"auto i = x[[->]]f();"},
1342 {
"incomplete_type",
"auto& [[[]]m] = *x;"},
1343 {
"init_incomplete_type",
1344 "struct C { static int f(ns::X&); }; int i = C::f([[{]]});"},
1345 {
"bad_cast_incomplete",
"auto a = [[static_cast]]<ns::X>(0);"},
1346 {
"template_nontype_parm_incomplete",
"template <ns::X [[foo]]> int a;"},
1347 {
"typecheck_decl_incomplete_type",
"ns::X [[var]];"},
1348 {
"typecheck_incomplete_tag",
"auto i = [[(*x)]]->f();"},
1349 {
"typecheck_nonviable_condition_incomplete",
1350 "struct A { operator ns::X(); } a; const ns::X &[[b]] = a;"},
1351 {
"invalid_incomplete_type_use",
"auto var = [[ns::X()]];"},
1352 {
"sizeof_alignof_incomplete_or_sizeless_type",
1353 "auto s = [[sizeof]](ns::X);"},
1354 {
"for_range_incomplete_type",
"void foo() { for (auto i : [[*]]x ) {} }"},
1355 {
"func_def_incomplete_result",
"ns::X [[func]] () {}"},
1356 {
"field_incomplete_or_sizeless",
"class M { ns::X [[member]]; };"},
1357 {
"array_incomplete_or_sizeless_type",
"auto s = [[(ns::X[]){}]];"},
1358 {
"call_incomplete_return",
"ns::X f(); auto fp = &f; auto z = [[fp()]];"},
1359 {
"call_function_incomplete_return",
"ns::X foo(); auto a = [[foo()]];"},
1360 {
"call_incomplete_argument",
"int m(ns::X); int i = m([[*x]]);"},
1361 {
"switch_incomplete_class_type",
"void a() { [[switch]](*x) {} }"},
1362 {
"delete_incomplete_class_type",
"void f() { [[delete]] *x; }"},
1363 {
"-Wdelete-incomplete",
"void f() { [[delete]] x; }"},
1364 {
"dereference_incomplete_type",
1365 R
"cpp(void f() { asm("" : "=r"([[*]]x)::); })cpp"},
1367 for (
auto Case : Tests) {
1369 TU.Code = Main.code().str() +
"\n // error-ok";
1371 TU.build().getDiagnostics(),
1372 ElementsAre(AllOf(diagName(Case.first), hasRange(Main.range()),
1373 withFix(
Fix(
Range{},
"#include \"x.h\"\n",
1374 "Include \"x.h\" for symbol ns::X")))))
1379TEST(IncludeFixerTest, IncompleteEnum) {
1382 Sym.CanonicalDeclaration.FileURI = Sym.Definition.FileURI =
"unittest:///x.h";
1391 TU.ExtraArgs.push_back(
"-std=c++20");
1392 TU.ExtraArgs.push_back(
"-fno-ms-compatibility");
1394 std::vector<std::pair<llvm::StringRef, llvm::StringRef>> Tests{
1395 {
"incomplete_enum",
"enum class X : int; using enum [[X]];"},
1396 {
"underlying_type_of_incomplete_enum",
1397 "[[__underlying_type]](enum X) i;"},
1399 for (
auto Case : Tests) {
1401 TU.Code = Main.code().str() +
"\n // error-ok";
1402 EXPECT_THAT(TU.build().getDiagnostics(),
1403 Contains(AllOf(diagName(Case.first), hasRange(Main.range()),
1404 withFix(
Fix(
Range{},
"#include \"x.h\"\n",
1405 "Include \"x.h\" for symbol X")))))
1410TEST(IncludeFixerTest, NoSuggestIncludeWhenNoDefinitionInHeader) {
1412$insert[[]]namespace ns {
1415class Y : $base[[public ns::X]] {};
1424 Sym.CanonicalDeclaration.FileURI =
"unittest:///x.h";
1425 Sym.Definition.FileURI =
"unittest:///x.cc";
1432 TU.ExternalIndex = Index.get();
1434 EXPECT_THAT(TU.build().getDiagnostics(),
1435 UnorderedElementsAre(
1436 Diag(Test.range(
"base"),
"base class has incomplete type"),
1437 Diag(Test.range(
"access"),
1438 "member access into incomplete type 'ns::X'")));
1441TEST(IncludeFixerTest, Typo) {
1443$insert[[]]namespace ns {
1445 $unqualified1[[X]] x;
1446 // No fix if the unresolved type is used as specifier. (ns::)X::Nested will be
1447 // considered the unresolved type.
1448 $unqualified2[[X]]::Nested n;
1450struct S : $base[[X]] {};
1453 ns::$qualified1[[X]] x; // ns:: is valid.
1454 ns::$qualified2[[X]](); // Error: no member in namespace
1456 ::$global[[Global]] glob;
1458using Type = ns::$template[[Foo]]<int>;
1461 auto Index = buildIndexWithSymbol(
1462 {SymbolWithHeader{
"ns::X",
"unittest:///x.h",
"\"x.h\""},
1463 SymbolWithHeader{
"Global",
"unittest:///global.h",
"\"global.h\""},
1464 SymbolWithHeader{
"ns::Foo",
"unittest:///foo.h",
"\"foo.h\""}});
1465 TU.ExternalIndex = Index.get();
1468 TU.build().getDiagnostics(),
1469 UnorderedElementsAre(
1470 AllOf(
Diag(Test.range(
"unqualified1"),
"unknown type name 'X'"),
1471 diagName(
"unknown_typename"),
1472 withFix(
Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
1473 "Include \"x.h\" for symbol ns::X"))),
1474 Diag(Test.range(
"unqualified2"),
"use of undeclared identifier 'X'"),
1475 AllOf(
Diag(Test.range(
"qualified1"),
1476 "no type named 'X' in namespace 'ns'"),
1477 diagName(
"typename_nested_not_found"),
1478 withFix(
Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
1479 "Include \"x.h\" for symbol ns::X"))),
1480 AllOf(
Diag(Test.range(
"qualified2"),
1481 "no member named 'X' in namespace 'ns'"),
1482 diagName(
"no_member"),
1483 withFix(
Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
1484 "Include \"x.h\" for symbol ns::X"))),
1485 AllOf(
Diag(Test.range(
"global"),
1486 "no type named 'Global' in the global namespace"),
1487 diagName(
"typename_nested_not_found"),
1488 withFix(
Fix(Test.range(
"insert"),
"#include \"global.h\"\n",
1489 "Include \"global.h\" for symbol Global"))),
1490 AllOf(
Diag(Test.range(
"template"),
1491 "no template named 'Foo' in namespace 'ns'"),
1492 diagName(
"no_member_template"),
1493 withFix(
Fix(Test.range(
"insert"),
"#include \"foo.h\"\n",
1494 "Include \"foo.h\" for symbol ns::Foo"))),
1495 AllOf(
Diag(Test.range(
"base"),
"expected class name"),
1496 diagName(
"expected_class_name"),
1497 withFix(
Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
1498 "Include \"x.h\" for symbol ns::X")))));
1501TEST(IncludeFixerTest, TypoInMacro) {
1512 auto Index = buildIndexWithSymbol(
1513 {SymbolWithHeader{
"X",
"unittest:///x.h",
"\"x.h\""},
1514 SymbolWithHeader{
"ns::X",
"unittest:///ns.h",
"\"x.h\""}});
1515 TU.ExternalIndex = Index.get();
1518 TU.ExtraArgs = {
"-fno-ms-compatibility"};
1519 EXPECT_THAT(TU.build().getDiagnostics(), Each(withFix(_)));
1522TEST(IncludeFixerTest, MultipleMatchedSymbols) {
1524$insert[[]]namespace na {
1527 $unqualified[[X]] x;
1533 auto Index = buildIndexWithSymbol(
1534 {SymbolWithHeader{
"na::X",
"unittest:///a.h",
"\"a.h\""},
1535 SymbolWithHeader{
"na::nb::X",
"unittest:///b.h",
"\"b.h\""}});
1536 TU.ExternalIndex = Index.get();
1538 EXPECT_THAT(TU.build().getDiagnostics(),
1539 UnorderedElementsAre(AllOf(
1540 Diag(Test.range(
"unqualified"),
"unknown type name 'X'"),
1541 diagName(
"unknown_typename"),
1542 withFix(
Fix(Test.range(
"insert"),
"#include \"a.h\"\n",
1543 "Include \"a.h\" for symbol na::X"),
1544 Fix(Test.range(
"insert"),
"#include \"b.h\"\n",
1545 "Include \"b.h\" for symbol na::nb::X")))));
1548TEST(IncludeFixerTest, NoCrashMemberAccess) {
1550 struct X { int xyz; };
1551 void g() { X x; x.$[[xy]]; }
1554 auto Index = buildIndexWithSymbol(
1555 SymbolWithHeader{
"na::X",
"unittest:///a.h",
"\"a.h\""});
1556 TU.ExternalIndex = Index.get();
1559 TU.build().getDiagnostics(),
1560 UnorderedElementsAre(
Diag(Test.range(),
"no member named 'xy' in 'X'")));
1563TEST(IncludeFixerTest, UseCachedIndexResults) {
1567$insert[[]]void foo() {
1590 buildIndexWithSymbol(SymbolWithHeader{
"X",
"unittest:///a.h",
"\"a.h\""});
1591 TU.ExternalIndex = Index.get();
1593 auto Parsed = TU.build();
1594 for (
const auto &D : Parsed.getDiagnostics()) {
1595 if (
D.Fixes.size() != 1) {
1596 ADD_FAILURE() <<
"D.Fixes.size() != 1";
1599 EXPECT_EQ(
D.Fixes[0].Message, std::string(
"Include \"a.h\" for symbol X"));
1603TEST(IncludeFixerTest, UnresolvedNameAsSpecifier) {
1605$insert[[]]namespace ns {
1607void g() { ns::$[[scope]]::X_Y(); }
1610 TU.Code = std::string(Test.code());
1612 TU.ExtraArgs.push_back(
"-fno-ms-compatibility");
1613 auto Index = buildIndexWithSymbol(
1614 SymbolWithHeader{
"ns::scope::X_Y",
"unittest:///x.h",
"\"x.h\""});
1615 TU.ExternalIndex = Index.get();
1618 TU.build().getDiagnostics(),
1619 UnorderedElementsAre(
1620 AllOf(
Diag(Test.range(),
"no member named 'scope' in namespace 'ns'"),
1621 diagName(
"no_member"),
1622 withFix(
Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
1623 "Include \"x.h\" for symbol ns::scope::X_Y")))));
1626TEST(IncludeFixerTest, UnresolvedSpecifierWithSemaCorrection) {
1628$insert[[]]namespace clang {
1630 // "clangd::" will be corrected to "clang::" by Sema.
1631 $q1[[clangd]]::$x[[X]] x;
1632 $q2[[clangd]]::$ns[[ns]]::Y y;
1637 TU.Code = std::string(Test.code());
1639 TU.ExtraArgs.push_back(
"-fno-ms-compatibility");
1640 auto Index = buildIndexWithSymbol(
1641 {SymbolWithHeader{
"clang::clangd::X",
"unittest:///x.h",
"\"x.h\""},
1642 SymbolWithHeader{
"clang::clangd::ns::Y",
"unittest:///y.h",
"\"y.h\""}});
1643 TU.ExternalIndex = Index.get();
1646 TU.build().getDiagnostics(),
1647 UnorderedElementsAre(
1648 AllOf(
Diag(Test.range(
"q1"),
"use of undeclared identifier 'clangd'; "
1649 "did you mean 'clang'?"),
1650 diagName(
"undeclared_var_use_suggest"),
1652 Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
1653 "Include \"x.h\" for symbol clang::clangd::X"))),
1654 AllOf(
Diag(Test.range(
"x"),
"no type named 'X' in namespace 'clang'"),
1655 diagName(
"typename_nested_not_found"),
1656 withFix(
Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
1657 "Include \"x.h\" for symbol clang::clangd::X"))),
1659 Diag(Test.range(
"q2"),
"use of undeclared identifier 'clangd'; "
1660 "did you mean 'clang'?"),
1661 diagName(
"undeclared_var_use_suggest"),
1663 Fix(Test.range(
"insert"),
"#include \"y.h\"\n",
1664 "Include \"y.h\" for symbol clang::clangd::ns::Y"))),
1665 AllOf(
Diag(Test.range(
"ns"),
1666 "no member named 'ns' in namespace 'clang'"),
1667 diagName(
"no_member"),
1669 Fix(Test.range(
"insert"),
"#include \"y.h\"\n",
1670 "Include \"y.h\" for symbol clang::clangd::ns::Y")))));
1673TEST(IncludeFixerTest, SpecifiedScopeIsNamespaceAlias) {
1675$insert[[]]namespace a {}
1682 auto Index = buildIndexWithSymbol(
1683 SymbolWithHeader{
"a::X",
"unittest:///x.h",
"\"x.h\""});
1684 TU.ExternalIndex = Index.get();
1686 EXPECT_THAT(TU.build().getDiagnostics(),
1687 UnorderedElementsAre(AllOf(
1688 Diag(Test.range(),
"no type named 'X' in namespace 'a'"),
1689 diagName(
"typename_nested_not_found"),
1690 withFix(
Fix(Test.range(
"insert"),
"#include \"x.h\"\n",
1691 "Include \"x.h\" for symbol a::X")))));
1694TEST(IncludeFixerTest, NoCrashOnTemplateInstantiations) {
1696 template <typename T> struct Templ {
1697 template <typename U>
1698 typename U::type operator=(const U &);
1703 A() { [[a]]; /*error-ok*/ } // crash if we compute scopes lazily.
1708 auto Index = buildIndexWithSymbol({});
1709 TU.ExternalIndex = Index.get();
1712 TU.build().getDiagnostics(),
1713 ElementsAre(
Diag(Test.range(),
"use of undeclared identifier 'a'")));
1716TEST(IncludeFixerTest, HeaderNamedInDiag) {
1718 $insert[[]]int main() {
1723 TU.ExtraArgs = {
"-xc",
"-std=c99",
1724 "-Wno-error=implicit-function-declaration"};
1725 auto Index = buildIndexWithSymbol({});
1726 TU.ExternalIndex = Index.get();
1729 TU.build().getDiagnostics(),
1731 Diag(Test.range(),
"call to undeclared library function 'printf' "
1732 "with type 'int (const char *, ...)'; ISO C99 "
1733 "and later do not support implicit function "
1735 withFix(
Fix(Test.range(
"insert"),
"#include <stdio.h>\n",
1736 "Include <stdio.h> for symbol printf")))));
1738 TU.ExtraArgs = {
"-xc",
"-std=c89"};
1740 TU.build().getDiagnostics(),
1742 Diag(Test.range(),
"implicitly declaring library function 'printf' "
1743 "with type 'int (const char *, ...)'"),
1744 withFix(
Fix(Test.range(
"insert"),
"#include <stdio.h>\n",
1745 "Include <stdio.h> for symbol printf")))));
1748TEST(IncludeFixerTest, CImplicitFunctionDecl) {
1751 TU.Filename =
"test.c";
1752 TU.ExtraArgs = {
"-std=c99",
"-Wno-error=implicit-function-declaration"};
1756 Sym.CanonicalDeclaration.FileURI =
"unittest:///foo.h";
1763 TU.ExternalIndex = Index.get();
1766 TU.build().getDiagnostics(),
1769 "call to undeclared function 'foo'; ISO C99 and later do not "
1770 "support implicit function declarations"),
1771 withFix(
Fix(
Range{},
"#include \"foo.h\"\n",
1772 "Include \"foo.h\" for symbol foo")))));
1774 TU.ExtraArgs = {
"-std=c89",
"-Wall"};
1775 EXPECT_THAT(TU.build().getDiagnostics(),
1777 Diag(Test.range(),
"implicit declaration of function 'foo'"),
1778 withFix(
Fix(
Range{},
"#include \"foo.h\"\n",
1779 "Include \"foo.h\" for symbol foo")))));
1782TEST(DiagsInHeaders, DiagInsideHeader) {
1785 void foo() {})cpp");
1786 Annotations Header("[[no_type_spec]]; // error-ok");
1788 TU.AdditionalFiles = {{
"a.h", std::string(Header.code())}};
1789 EXPECT_THAT(TU.build().getDiagnostics(),
1790 UnorderedElementsAre(AllOf(
1791 Diag(Main.range(),
"in included file: a type specifier is "
1792 "required for all declarations"),
1793 withNote(
Diag(Header.range(),
"error occurred here")))));
1796TEST(DiagsInHeaders, DiagInTransitiveInclude) {
1799 void foo() {})cpp");
1801 TU.AdditionalFiles = {{"a.h",
"#include \"b.h\""},
1802 {
"b.h",
"no_type_spec; // error-ok"}};
1803 EXPECT_THAT(TU.build().getDiagnostics(),
1804 UnorderedElementsAre(
Diag(Main.range(),
1805 "in included file: a type specifier is "
1806 "required for all declarations")));
1809TEST(DiagsInHeaders, DiagInMultipleHeaders) {
1811 #include $a[["a.h"]]
1812 #include $b[["b.h"]]
1813 void foo() {})cpp");
1815 TU.AdditionalFiles = {{"a.h",
"no_type_spec; // error-ok"},
1816 {
"b.h",
"no_type_spec; // error-ok"}};
1817 EXPECT_THAT(TU.build().getDiagnostics(),
1818 UnorderedElementsAre(
1819 Diag(Main.range(
"a"),
"in included file: a type specifier is "
1820 "required for all declarations"),
1821 Diag(Main.range(
"b"),
"in included file: a type specifier is "
1822 "required for all declarations")));
1825TEST(DiagsInHeaders, PreferExpansionLocation) {
1829 void foo() {})cpp");
1831 TU.AdditionalFiles = {
1832 {"a.h",
"#include \"b.h\"\n"},
1833 {
"b.h",
"#ifndef X\n#define X\nno_type_spec; // error-ok\n#endif"}};
1834 EXPECT_THAT(TU.build().getDiagnostics(),
1835 Contains(
Diag(Main.range(),
"in included file: a type specifier "
1836 "is required for all declarations")));
1839TEST(DiagsInHeaders, PreferExpansionLocationMacros) {
1845 void foo() {})cpp");
1847 TU.AdditionalFiles = {
1848 {"a.h",
"#include \"c.h\"\n"},
1849 {
"b.h",
"#include \"c.h\"\n"},
1850 {
"c.h",
"#ifndef X\n#define X\nno_type_spec; // error-ok\n#endif"}};
1851 EXPECT_THAT(TU.build().getDiagnostics(),
1852 UnorderedElementsAre(
Diag(Main.range(),
1853 "in included file: a type specifier is "
1854 "required for all declarations")));
1857TEST(DiagsInHeaders, LimitDiagsOutsideMainFile) {
1861 void foo() {})cpp");
1863 TU.AdditionalFiles = {{"a.h",
"#include \"c.h\"\n"},
1864 {
"b.h",
"#include \"c.h\"\n"},
1868 no_type_spec_0; // error-ok
1880 EXPECT_THAT(TU.build().getDiagnostics(),
1881 UnorderedElementsAre(Diag(Main.range(),
1882 "in included file: a type specifier is "
1883 "required for all declarations")));
1886TEST(DiagsInHeaders, OnlyErrorOrFatal) {
1889 void foo() {})cpp");
1891 [[no_type_spec]]; // error-ok
1894 TU.AdditionalFiles = {{"a.h", std::string(Header.code())}};
1895 EXPECT_THAT(TU.build().getDiagnostics(),
1896 UnorderedElementsAre(AllOf(
1897 Diag(Main.range(),
"in included file: a type specifier is "
1898 "required for all declarations"),
1899 withNote(
Diag(Header.range(),
"error occurred here")))));
1902TEST(DiagsInHeaders, OnlyDefaultErrorOrFatal) {
1904 #include [["a.h"]] // get unused "foo" warning when building preamble.
1907 namespace { void foo() {} }
1908 void func() {foo();} ;)cpp");
1910 TU.AdditionalFiles = {{"a.h", std::string(Header.code())}};
1912 TU.ExtraArgs = {
"-Werror",
"-Wunused"};
1913 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
1916TEST(DiagsInHeaders, FromNonWrittenSources) {
1919 void foo() {})cpp");
1922 int b = [[FOO]]; // error-ok)cpp");
1924 TU.AdditionalFiles = {{"a.h", std::string(Header.code())}};
1925 TU.ExtraArgs = {
"-DFOO=NOOO"};
1926 EXPECT_THAT(TU.build().getDiagnostics(),
1927 UnorderedElementsAre(AllOf(
1929 "in included file: use of undeclared identifier 'NOOO'"),
1930 withNote(
Diag(Header.range(),
"error occurred here")))));
1933TEST(DiagsInHeaders, ErrorFromMacroExpansion) {
1943 TU.AdditionalFiles = {{"a.h", std::string(Header.code())}};
1944 EXPECT_THAT(TU.build().getDiagnostics(),
1945 UnorderedElementsAre(
1946 Diag(Main.range(),
"in included file: use of undeclared "
1947 "identifier 'foo'; did you mean 'fo'?")));
1950TEST(DiagsInHeaders, ErrorFromMacroArgument) {
1960 TU.AdditionalFiles = {{"a.h", std::string(Header.code())}};
1961 EXPECT_THAT(TU.build().getDiagnostics(),
1962 UnorderedElementsAre(
1963 Diag(Main.range(),
"in included file: use of undeclared "
1964 "identifier 'foo'; did you mean 'fo'?")));
1967TEST(IgnoreDiags, FromNonWrittenInclude) {
1969 TU.
ExtraArgs.push_back(
"--include=a.h");
1970 TU.AdditionalFiles = {{
"a.h",
"void main();"}};
1973 EXPECT_THAT(TU.build().getDiagnostics(), UnorderedElementsAre());
1976TEST(ToLSPDiag, RangeIsInMain) {
1979 D.Range = {pos(1, 2), pos(3, 4)};
1980 D.Notes.emplace_back();
1981 Note &N =
D.Notes.back();
1982 N.Range = {pos(2, 3), pos(3, 4)};
1984 D.InsideMainFile =
true;
1985 N.InsideMainFile =
false;
1987 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix>) {
1988 EXPECT_EQ(LSPDiag.range,
D.Range);
1991 D.InsideMainFile =
false;
1992 N.InsideMainFile =
true;
1994 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix>) {
1995 EXPECT_EQ(LSPDiag.range, N.Range);
1999TEST(ParsedASTTest, ModuleSawDiag) {
2002 auto AST = TU.build();
2004 EXPECT_THAT(
AST.getDiagnostics(),
2005 testing::Contains(
Diag(Code.range(), KDiagMsg.str())));
2014 TU.Code =
"#define FOO\n void bar();\n";
2015 auto AST = TU.build();
2016 EXPECT_THAT(
AST.getDiagnostics(), IsEmpty());
2020 TU.Code = Code.code().str();
2021 auto AST = TU.build();
2023 AST.getDiagnostics(),
2024 testing::Contains(
Diag(Code.range(),
"no newline at end of file")));
2028TEST(Diagnostics, Tags) {
2030 TU.
ExtraArgs = {
"-Wunused",
"-Wdeprecated"};
2032 void bar() __attribute__((deprecated));
2035 $deprecated[[bar]]();
2037 TU.Code = Test.code().str();
2038 EXPECT_THAT(TU.build().getDiagnostics(),
2039 UnorderedElementsAre(
2040 AllOf(Diag(Test.range("unused"),
"unused variable 'x'"),
2042 AllOf(
Diag(Test.range(
"deprecated"),
"'bar' is deprecated"),
2046 $typedef[[typedef int INT]];
2048 TU.Code = Test.code();
2049 TU.ClangTidyProvider = addTidyChecks("modernize-use-using");
2051 TU.build().getDiagnostics(),
2052 ifTidyChecks(UnorderedElementsAre(
2053 AllOf(
Diag(Test.range(
"typedef"),
"use 'using' instead of 'typedef'"),
2057TEST(Diagnostics, TidyDiagsArentAffectedFromWerror) {
2060 Annotations Test(R
"cpp($typedef[[typedef int INT]]; // error-ok)cpp");
2061 TU.Code = Test.code().str();
2062 TU.ClangTidyProvider = addTidyChecks("modernize-use-using");
2064 TU.build().getDiagnostics(),
2065 ifTidyChecks(UnorderedElementsAre(
2066 AllOf(
Diag(Test.range(
"typedef"),
"use 'using' instead of 'typedef'"),
2069 diagSeverity(DiagnosticsEngine::Warning)))));
2071 TU.ClangTidyProvider =
2074 TU.build().getDiagnostics(),
2075 ifTidyChecks(UnorderedElementsAre(
2076 AllOf(
Diag(Test.range(
"typedef"),
"use 'using' instead of 'typedef'"),
2078 diagSeverity(DiagnosticsEngine::Error)))));
2081TEST(Diagnostics, DeprecatedDiagsAreHints) {
2083 std::optional<clangd::Diagnostic>
Diag;
2085 D.Range = {pos(1, 2), pos(3, 4)};
2086 D.InsideMainFile =
true;
2090 D.Severity = DiagnosticsEngine::Warning;
2092 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix>) {
2093 Diag = std::move(LSPDiag);
2099 D.Severity = DiagnosticsEngine::Error;
2101 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix>) {
2102 Diag = std::move(LSPDiag);
2109 D.Severity = DiagnosticsEngine::Warning;
2111 [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix>) {
2112 Diag = std::move(LSPDiag);
2117TEST(DiagnosticsTest, IncludeCleaner) {
2119$fix[[ $diag[[#include "unused.h"]]
2125 #include <system_header.h>
2132 TU.Code = Test.code().str();
2133 TU.AdditionalFiles["unused.h"] = R
"cpp(
2137 TU.AdditionalFiles["used.h"] = R
"cpp(
2141 TU.AdditionalFiles["ignore.h"] = R
"cpp(
2145 TU.AdditionalFiles["system/system_header.h"] =
"";
2146 TU.ExtraArgs = {
"-isystem" +
testPath(
"system")};
2150 Cfg.Diagnostics.Includes.IgnoreHeader.emplace_back(
2151 [](llvm::StringRef Header) {
return Header.ends_with(
"ignore.h"); });
2153 auto AST = TU.build();
2155 AST.getDiagnostics(),
2157 Diag(Test.range(
"diag"),
2158 "included header unused.h is not used directly"),
2160 withFix(
Fix(Test.range(
"fix"),
"",
"remove #include directive")))));
2161 auto &
Diag =
AST.getDiagnostics().front();
2163 llvm::ValueIs(Not(IsEmpty())));
2164 Cfg.Diagnostics.SuppressAll =
true;
2166 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
2167 Cfg.Diagnostics.SuppressAll =
false;
2168 Cfg.Diagnostics.Suppress = {
"unused-includes"};
2170 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
2173TEST(DiagnosticsTest, FixItFromHeader) {
2174 llvm::StringLiteral Header(R
"cpp(
2176 void foo(int *, int);)cpp");
2181 $diag[[foo]]($fix[[]]x, 1);
2184 TU.Code = Source.code().str();
2185 TU.HeaderCode = Header.str();
2187 TU.build().getDiagnostics(),
2188 UnorderedElementsAre(AllOf(
2189 Diag(Source.range("diag"),
"no matching function for call to 'foo'"),
2190 withFix(
Fix(Source.range(
"fix"),
"&",
2191 "candidate function not viable: no known conversion from "
2192 "'int' to 'int *' for 1st argument; take the address of "
2193 "the argument with &")))));
2196TEST(DiagnosticsTest, UnusedInHeader) {
2199 TU.ExtraArgs.push_back(
"-Wunused-function");
2200 TU.Filename =
"test.c";
2201 EXPECT_THAT(TU.build().getDiagnostics(),
2202 ElementsAre(withID(diag::warn_unused_function)));
2205 TU.Filename =
"test.h";
2206 EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
2209TEST(DiagnosticsTest, DontSuppressSubcategories) {
2220 TU.ExtraArgs.push_back("-Wunreachable-code-aggressive");
2221 TU.Code = Source.code().str();
2226 EXPECT_THAT(TU.build().getDiagnostics(),
2227 ElementsAre(diagName(
"-Wunreachable-code-break")));
static cl::opt< bool > Fix("fix", desc(R"(
Apply suggested fixes. Without -fix-errors
clang-tidy will bail out if any compilation
errors were found.
)"), cl::init(false), cl::cat(ClangTidyCategory))
static cl::opt< std::string > Checks("checks", desc(R"(
Comma-separated list of globs with optional '-'
prefix. Globs are processed in order of
appearance in the list. Globs without '-'
prefix add checks with matching names to the
set, globs with the '-' prefix remove checks
with matching names from the set of enabled
checks. This option's value is appended to the
value of the 'Checks' option in .clang-tidy
file, if any.
)"), cl::init(""), cl::cat(ClangTidyCategory))
Same as llvm::Annotations, but adjusts functions to LSP-specific types for positions and ranges.
static std::unique_ptr< SymbolIndex > build(SymbolSlab Symbols, RefSlab Refs, RelationSlab Relations)
Builds an index from slabs. The index takes ownership of the data.
An efficient structure of storing large set of symbol references in memory.
SymbolSlab::Builder is a mutable container that can 'freeze' to SymbolSlab.
void insert(const Symbol &S)
Adds a symbol, overwriting any existing one with the same ID.
WithContextValue extends Context::current() with a single value.
FIXME: Skip testing on windows temporarily due to the different escaping code mode.
llvm::unique_function< void(tidy::ClangTidyOptions &, llvm::StringRef) const > TidyProvider
A factory to modify a tidy::ClangTidyOptions.
Symbol func(llvm::StringRef Name)
const NamedDecl & findDecl(ParsedAST &AST, llvm::StringRef QName)
Symbol cls(llvm::StringRef Name)
TidyProvider combine(std::vector< TidyProvider > Providers)
void toLSPDiags(const Diag &D, const URIForFile &File, const ClangdDiagnosticOptions &Opts, llvm::function_ref< void(clangd::Diagnostic, llvm::ArrayRef< Fix >)> OutFn)
Conversion to LSP diagnostics.
MATCHER_P2(hasFlag, Flag, Path, "")
std::string testPath(PathRef File, llvm::sys::path::Style Style)
static URISchemeRegistry::Add< TestScheme > X(TestScheme::Scheme, "Test schema")
llvm::json::Value toJSON(const FuzzyFindRequest &Request)
TidyProvider addTidyChecks(llvm::StringRef Checks, llvm::StringRef WarningsAsErrors)
Provider the enables a specific set of checks and warnings as errors.
TEST(BackgroundQueueTest, Priority)
Symbol enm(llvm::StringRef Name)
TidyProvider disableUnusableChecks(llvm::ArrayRef< std::string > ExtraBadChecks)
Provider that will disable checks known to not work with clangd.
int getSeverity(DiagnosticsEngine::Level L)
Convert from clang diagnostic level to LSP severity.
@ Deprecated
Deprecated or obsolete code.
@ Unnecessary
Unused or unnecessary code.
std::optional< std::string > getDiagnosticDocURI(Diag::DiagSource Source, unsigned ID, llvm::StringRef Name)
Returns a URI providing more information about a particular diagnostic.
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Settings that express user/project preferences and control clangd behavior.
static clangd::Key< Config > Key
Context key which can be used to set the current Config.
@ Strict
Diagnose missing and unused includes.
struct clang::clangd::Config::@343034053122374337352226322054223376344037116252 Diagnostics
Controls warnings and errors when parsing code.
IncludesPolicy UnusedIncludes
A top-level diagnostic that may have Notes and Fixes.
std::vector< Fix > Fixes
Alternative fixes for this diagnostic, one should be chosen.
llvm::SmallVector< DiagnosticTag, 1 > Tags
enum clang::clangd::Diag::DiagSource Source
std::vector< Note > Notes
Elaborate on the problem, usually pointing to a related piece of code.
Represents a single fix-it that editor can apply to fix the error.
std::string Message
Message for the fix-it.
llvm::SmallVector< TextEdit, 1 > Edits
TextEdits from clang's fix-its. Must be non-empty.
Represents a note for the diagnostic.
int line
Line position in a document (zero-based).
The class presents a C++ symbol, e.g.
@ IndexedForCodeCompletion
Whether or not this symbol is meant to be used for the code completion.
@ Include
#include "header.h"
std::vector< std::string > ExtraArgs
static TestTU withHeaderCode(llvm::StringRef HeaderCode)
static TestTU withCode(llvm::StringRef Code)
const SymbolIndex * ExternalIndex
static URIForFile canonicalize(llvm::StringRef AbsPath, llvm::StringRef TUPath)
Canonicalizes AbsPath via URI.