2402 lines · cpp
1//===-- InlayHintTests.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-exception6//7//===----------------------------------------------------------------------===//8#include "Annotations.h"9#include "Config.h"10#include "InlayHints.h"11#include "Protocol.h"12#include "TestTU.h"13#include "TestWorkspace.h"14#include "XRefs.h"15#include "support/Context.h"16#include "llvm/ADT/StringRef.h"17#include "llvm/Support/ScopedPrinter.h"18#include "llvm/Support/raw_ostream.h"19#include "gmock/gmock.h"20#include "gtest/gtest.h"21#include <optional>22#include <string>23#include <utility>24#include <vector>25 26namespace clang {27namespace clangd {28 29llvm::raw_ostream &operator<<(llvm::raw_ostream &Stream,30 const InlayHint &Hint) {31 return Stream << Hint.joinLabels() << "@" << Hint.range;32}33 34namespace {35 36using ::testing::ElementsAre;37using ::testing::IsEmpty;38 39constexpr InlayHintOptions DefaultOptsForTests{2};40 41std::vector<InlayHint> hintsOfKind(ParsedAST &AST, InlayHintKind Kind,42 InlayHintOptions Opts) {43 std::vector<InlayHint> Result;44 for (auto &Hint : inlayHints(AST, /*RestrictRange=*/std::nullopt, Opts)) {45 if (Hint.kind == Kind)46 Result.push_back(Hint);47 }48 return Result;49}50 51enum HintSide { Left, Right };52 53struct ExpectedHint {54 std::string Label;55 std::string RangeName;56 HintSide Side = Left;57 58 friend llvm::raw_ostream &operator<<(llvm::raw_ostream &Stream,59 const ExpectedHint &Hint) {60 return Stream << Hint.Label << "@$" << Hint.RangeName;61 }62};63 64MATCHER_P2(HintMatcher, Expected, Code, llvm::to_string(Expected)) {65 llvm::StringRef ExpectedView(Expected.Label);66 std::string ResultLabel = arg.joinLabels();67 if (ResultLabel != ExpectedView.trim(" ") ||68 arg.paddingLeft != ExpectedView.starts_with(" ") ||69 arg.paddingRight != ExpectedView.ends_with(" ")) {70 *result_listener << "label is '" << ResultLabel << "'";71 return false;72 }73 if (arg.range != Code.range(Expected.RangeName)) {74 *result_listener << "range is " << llvm::to_string(arg.range) << " but $"75 << Expected.RangeName << " is "76 << llvm::to_string(Code.range(Expected.RangeName));77 return false;78 }79 return true;80}81 82MATCHER_P(labelIs, Label, "") { return arg.joinLabels() == Label; }83 84Config noHintsConfig() {85 Config C;86 C.InlayHints.Parameters = false;87 C.InlayHints.DeducedTypes = false;88 C.InlayHints.Designators = false;89 C.InlayHints.BlockEnd = false;90 C.InlayHints.DefaultArguments = false;91 return C;92}93 94template <typename... ExpectedHints>95void assertHintsWithHeader(InlayHintKind Kind, llvm::StringRef AnnotatedSource,96 llvm::StringRef HeaderContent, InlayHintOptions Opts,97 ExpectedHints... Expected) {98 Annotations Source(AnnotatedSource);99 TestTU TU = TestTU::withCode(Source.code());100 TU.ExtraArgs.push_back("-std=c++23");101 TU.HeaderCode = HeaderContent;102 auto AST = TU.build();103 104 EXPECT_THAT(hintsOfKind(AST, Kind, Opts),105 ElementsAre(HintMatcher(Expected, Source)...));106 // Sneak in a cross-cutting check that hints are disabled by config.107 // We'll hit an assertion failure if addInlayHint still gets called.108 WithContextValue WithCfg(Config::Key, noHintsConfig());109 EXPECT_THAT(inlayHints(AST, std::nullopt, Opts), IsEmpty());110}111 112template <typename... ExpectedHints>113void assertHints(InlayHintKind Kind, llvm::StringRef AnnotatedSource,114 InlayHintOptions Opts, ExpectedHints... Expected) {115 return assertHintsWithHeader(Kind, AnnotatedSource, "", Opts,116 std::move(Expected)...);117}118 119// Hack to allow expression-statements operating on parameter packs in C++14.120template <typename... T> void ignore(T &&...) {}121 122template <typename... ExpectedHints>123void assertParameterHints(llvm::StringRef AnnotatedSource,124 ExpectedHints... Expected) {125 ignore(Expected.Side = Left...);126 assertHints(InlayHintKind::Parameter, AnnotatedSource, DefaultOptsForTests,127 Expected...);128}129 130template <typename... ExpectedHints>131void assertTypeHints(llvm::StringRef AnnotatedSource,132 ExpectedHints... Expected) {133 ignore(Expected.Side = Right...);134 assertHints(InlayHintKind::Type, AnnotatedSource, DefaultOptsForTests,135 Expected...);136}137 138template <typename... ExpectedHints>139void assertDesignatorHints(llvm::StringRef AnnotatedSource,140 ExpectedHints... Expected) {141 Config Cfg;142 Cfg.InlayHints.Designators = true;143 WithContextValue WithCfg(Config::Key, std::move(Cfg));144 assertHints(InlayHintKind::Designator, AnnotatedSource, DefaultOptsForTests,145 Expected...);146}147 148template <typename... ExpectedHints>149void assertBlockEndHintsWithOpts(llvm::StringRef AnnotatedSource,150 InlayHintOptions Opts,151 ExpectedHints... Expected) {152 Config Cfg;153 Cfg.InlayHints.BlockEnd = true;154 WithContextValue WithCfg(Config::Key, std::move(Cfg));155 assertHints(InlayHintKind::BlockEnd, AnnotatedSource, Opts, Expected...);156}157 158template <typename... ExpectedHints>159void assertBlockEndHints(llvm::StringRef AnnotatedSource,160 ExpectedHints... Expected) {161 assertBlockEndHintsWithOpts(AnnotatedSource, DefaultOptsForTests,162 Expected...);163}164 165TEST(ParameterHints, Smoke) {166 assertParameterHints(R"cpp(167 void foo(int param);168 void bar() {169 foo($param[[42]]);170 }171 )cpp",172 ExpectedHint{"param: ", "param"});173}174 175TEST(ParameterHints, NoName) {176 // No hint for anonymous parameter.177 assertParameterHints(R"cpp(178 void foo(int);179 void bar() {180 foo(42);181 }182 )cpp");183}184 185TEST(ParameterHints, NoNameConstReference) {186 // No hint for anonymous const l-value ref parameter.187 assertParameterHints(R"cpp(188 void foo(const int&);189 void bar() {190 foo(42);191 }192 )cpp");193}194 195TEST(ParameterHints, NoNameReference) {196 // Reference hint for anonymous l-value ref parameter.197 assertParameterHints(R"cpp(198 void foo(int&);199 void bar() {200 int i;201 foo($param[[i]]);202 }203 )cpp",204 ExpectedHint{"&: ", "param"});205}206 207TEST(ParameterHints, NoNameRValueReference) {208 // No reference hint for anonymous r-value ref parameter.209 assertParameterHints(R"cpp(210 void foo(int&&);211 void bar() {212 foo(42);213 }214 )cpp");215}216 217TEST(ParameterHints, NoNameVariadicDeclaration) {218 // No hint for anonymous variadic parameter219 assertParameterHints(R"cpp(220 template <typename... Args>221 void foo(Args&& ...);222 void bar() {223 foo(42);224 }225 )cpp");226}227 228TEST(ParameterHints, NoNameVariadicForwarded) {229 // No hint for anonymous variadic parameter230 // This prototype of std::forward is sufficient for clang to recognize it231 assertParameterHints(R"cpp(232 namespace std { template <typename T> T&& forward(T&); }233 void foo(int);234 template <typename... Args>235 void bar(Args&&... args) { return foo(std::forward<Args>(args)...); }236 void baz() {237 bar(42);238 }239 )cpp");240}241 242TEST(ParameterHints, NoNameVariadicPlain) {243 // No hint for anonymous variadic parameter244 assertParameterHints(R"cpp(245 void foo(int);246 template <typename... Args>247 void bar(Args&&... args) { return foo(args...); }248 void baz() {249 bar(42);250 }251 )cpp");252}253 254TEST(ParameterHints, NameInDefinition) {255 // Parameter name picked up from definition if necessary.256 assertParameterHints(R"cpp(257 void foo(int);258 void bar() {259 foo($param[[42]]);260 }261 void foo(int param) {};262 )cpp",263 ExpectedHint{"param: ", "param"});264}265 266TEST(ParameterHints, NamePartiallyInDefinition) {267 // Parameter name picked up from definition if necessary.268 assertParameterHints(R"cpp(269 void foo(int, int b);270 void bar() {271 foo($param1[[42]], $param2[[42]]);272 }273 void foo(int a, int) {};274 )cpp",275 ExpectedHint{"a: ", "param1"},276 ExpectedHint{"b: ", "param2"});277}278 279TEST(ParameterHints, NameInDefinitionVariadic) {280 // Parameter name picked up from definition in a resolved forwarded parameter.281 assertParameterHints(R"cpp(282 void foo(int, int);283 template <typename... Args>284 void bar(Args... args) {285 foo(args...);286 }287 void baz() {288 bar($param1[[42]], $param2[[42]]);289 }290 void foo(int a, int b) {};291 )cpp",292 ExpectedHint{"a: ", "param1"},293 ExpectedHint{"b: ", "param2"});294}295 296TEST(ParameterHints, NameMismatch) {297 // Prefer name from declaration.298 assertParameterHints(R"cpp(299 void foo(int good);300 void bar() {301 foo($good[[42]]);302 }303 void foo(int bad) {};304 )cpp",305 ExpectedHint{"good: ", "good"});306}307 308TEST(ParameterHints, NameConstReference) {309 // Only name hint for const l-value ref parameter.310 assertParameterHints(R"cpp(311 void foo(const int& param);312 void bar() {313 foo($param[[42]]);314 }315 )cpp",316 ExpectedHint{"param: ", "param"});317}318 319TEST(ParameterHints, NameTypeAliasConstReference) {320 // Only name hint for const l-value ref parameter via type alias.321 assertParameterHints(R"cpp(322 using alias = const int&;323 void foo(alias param);324 void bar() {325 int i;326 foo($param[[i]]);327 }328 )cpp",329 ExpectedHint{"param: ", "param"});330}331 332TEST(ParameterHints, NameReference) {333 // Reference and name hint for l-value ref parameter.334 assertParameterHints(R"cpp(335 void foo(int& param);336 void bar() {337 int i;338 foo($param[[i]]);339 }340 )cpp",341 ExpectedHint{"¶m: ", "param"});342}343 344TEST(ParameterHints, NameTypeAliasReference) {345 // Reference and name hint for l-value ref parameter via type alias.346 assertParameterHints(R"cpp(347 using alias = int&;348 void foo(alias param);349 void bar() {350 int i;351 foo($param[[i]]);352 }353 )cpp",354 ExpectedHint{"¶m: ", "param"});355}356 357TEST(ParameterHints, NameRValueReference) {358 // Only name hint for r-value ref parameter.359 assertParameterHints(R"cpp(360 void foo(int&& param);361 void bar() {362 foo($param[[42]]);363 }364 )cpp",365 ExpectedHint{"param: ", "param"});366}367 368TEST(ParameterHints, VariadicForwardedConstructor) {369 // Name hint for variadic parameter using std::forward in a constructor call370 // This prototype of std::forward is sufficient for clang to recognize it371 assertParameterHints(R"cpp(372 namespace std { template <typename T> T&& forward(T&); }373 struct S { S(int a); };374 template <typename T, typename... Args>375 T bar(Args&&... args) { return T{std::forward<Args>(args)...}; }376 void baz() {377 int b;378 bar<S>($param[[b]]);379 }380 )cpp",381 ExpectedHint{"a: ", "param"});382}383 384TEST(ParameterHints, VariadicPlainConstructor) {385 // Name hint for variadic parameter in a constructor call386 assertParameterHints(R"cpp(387 struct S { S(int a); };388 template <typename T, typename... Args>389 T bar(Args&&... args) { return T{args...}; }390 void baz() {391 int b;392 bar<S>($param[[b]]);393 }394 )cpp",395 ExpectedHint{"a: ", "param"});396}397 398TEST(ParameterHints, VariadicForwardedNewConstructor) {399 // Name hint for variadic parameter using std::forward in a new expression400 // This prototype of std::forward is sufficient for clang to recognize it401 assertParameterHints(R"cpp(402 namespace std { template <typename T> T&& forward(T&); }403 struct S { S(int a); };404 template <typename T, typename... Args>405 T* bar(Args&&... args) { return new T{std::forward<Args>(args)...}; }406 void baz() {407 int b;408 bar<S>($param[[b]]);409 }410 )cpp",411 ExpectedHint{"a: ", "param"});412}413 414TEST(ParameterHints, VariadicPlainNewConstructor) {415 // Name hint for variadic parameter in a new expression416 assertParameterHints(R"cpp(417 struct S { S(int a); };418 template <typename T, typename... Args>419 T* bar(Args&&... args) { return new T{args...}; }420 void baz() {421 int b;422 bar<S>($param[[b]]);423 }424 )cpp",425 ExpectedHint{"a: ", "param"});426}427 428TEST(ParameterHints, VariadicForwarded) {429 // Name for variadic parameter using std::forward430 // This prototype of std::forward is sufficient for clang to recognize it431 assertParameterHints(R"cpp(432 namespace std { template <typename T> T&& forward(T&); }433 void foo(int a);434 template <typename... Args>435 void bar(Args&&... args) { return foo(std::forward<Args>(args)...); }436 void baz() {437 int b;438 bar($param[[b]]);439 }440 )cpp",441 ExpectedHint{"a: ", "param"});442}443 444TEST(ParameterHints, VariadicPlain) {445 // Name hint for variadic parameter446 assertParameterHints(R"cpp(447 void foo(int a);448 template <typename... Args>449 void bar(Args&&... args) { return foo(args...); }450 void baz() {451 bar($param[[42]]);452 }453 )cpp",454 ExpectedHint{"a: ", "param"});455}456 457TEST(ParameterHints, VariadicPlainWithPackFirst) {458 // Name hint for variadic parameter when the parameter pack is not the last459 // template parameter460 assertParameterHints(R"cpp(461 void foo(int a);462 template <typename... Args, typename Arg>463 void bar(Arg, Args&&... args) { return foo(args...); }464 void baz() {465 bar(1, $param[[42]]);466 }467 )cpp",468 ExpectedHint{"a: ", "param"});469}470 471TEST(ParameterHints, VariadicSplitTwolevel) {472 // Name for variadic parameter that involves both head and tail parameters to473 // deal with.474 // This prototype of std::forward is sufficient for clang to recognize it475 assertParameterHints(R"cpp(476 namespace std { template <typename T> T&& forward(T&); }477 void baz(int, int b, double);478 template <typename... Args>479 void foo(int a, Args&&... args) {480 return baz(1, std::forward<Args>(args)..., 1.0);481 }482 template <typename... Args>483 void bar(Args&&... args) { return foo(std::forward<Args>(args)...); }484 void bazz() {485 bar($param1[[32]], $param2[[42]]);486 }487 )cpp",488 ExpectedHint{"a: ", "param1"},489 ExpectedHint{"b: ", "param2"});490}491 492TEST(ParameterHints, VariadicNameFromSpecialization) {493 // We don't try to resolve forwarding parameters if the function call uses a494 // specialization.495 assertParameterHints(R"cpp(496 void foo(int a);497 template <typename... Args>498 void bar(Args... args) {499 foo(args...);500 }501 template <>502 void bar<int>(int b);503 void baz() {504 bar($param[[42]]);505 }506 )cpp",507 ExpectedHint{"b: ", "param"});508}509 510TEST(ParameterHints, VariadicNameFromSpecializationRecursive) {511 // We don't try to resolve forwarding parameters inside a forwarding function512 // call if that function call uses a specialization.513 assertParameterHints(R"cpp(514 void foo2(int a);515 template <typename... Args>516 void foo(Args... args) {517 foo2(args...);518 }519 template <typename... Args>520 void bar(Args... args) {521 foo(args...);522 }523 template <>524 void foo<int>(int b);525 void baz() {526 bar($param[[42]]);527 }528 )cpp",529 ExpectedHint{"b: ", "param"});530}531 532TEST(ParameterHints, VariadicOverloaded) {533 // Name for variadic parameter for an overloaded function with unique number534 // of parameters.535 // This prototype of std::forward is sufficient for clang to recognize it536 assertParameterHints(537 R"cpp(538 namespace std { template <typename T> T&& forward(T&); }539 void baz(int b, int c);540 void baz(int bb, int cc, int dd);541 template <typename... Args>542 void foo(int a, Args&&... args) {543 return baz(std::forward<Args>(args)...);544 }545 template <typename... Args>546 void bar(Args&&... args) { return foo(std::forward<Args>(args)...); }547 void bazz() {548 bar($param1[[32]], $param2[[42]], $param3[[52]]);549 bar($param4[[1]], $param5[[2]], $param6[[3]], $param7[[4]]);550 }551 )cpp",552 ExpectedHint{"a: ", "param1"}, ExpectedHint{"b: ", "param2"},553 ExpectedHint{"c: ", "param3"}, ExpectedHint{"a: ", "param4"},554 ExpectedHint{"bb: ", "param5"}, ExpectedHint{"cc: ", "param6"},555 ExpectedHint{"dd: ", "param7"});556}557 558TEST(ParameterHints, VariadicRecursive) {559 // make_tuple-like recursive variadic call560 assertParameterHints(561 R"cpp(562 void foo();563 564 template <typename Head, typename... Tail>565 void foo(Head head, Tail... tail) {566 foo(tail...);567 }568 569 template <typename... Args>570 void bar(Args... args) {571 foo(args...);572 }573 574 int main() {575 bar(1, 2, 3);576 }577 )cpp");578}579 580TEST(ParameterHints, VariadicVarargs) {581 // variadic call involving varargs (to make sure we don't crash)582 assertParameterHints(R"cpp(583 void foo(int fixed, ...);584 template <typename... Args>585 void bar(Args&&... args) {586 foo(args...);587 }588 589 void baz() {590 bar($fixed[[41]], 42, 43);591 }592 )cpp");593}594 595TEST(ParameterHints, VariadicTwolevelUnresolved) {596 // the same setting as VariadicVarargs, only with parameter pack597 assertParameterHints(R"cpp(598 template <typename... Args>599 void foo(int fixed, Args&& ... args);600 template <typename... Args>601 void bar(Args&&... args) {602 foo(args...);603 }604 605 void baz() {606 bar($fixed[[41]], 42, 43);607 }608 )cpp",609 ExpectedHint{"fixed: ", "fixed"});610}611 612TEST(ParameterHints, VariadicTwoCalls) {613 // only the first call using the parameter pack should be picked up614 assertParameterHints(615 R"cpp(616 void f1(int a, int b);617 void f2(int c, int d);618 619 bool cond;620 621 template <typename... Args>622 void foo(Args... args) {623 if (cond) {624 f1(args...);625 } else {626 f2(args...);627 }628 }629 630 int main() {631 foo($param1[[1]], $param2[[2]]);632 }633 )cpp",634 ExpectedHint{"a: ", "param1"}, ExpectedHint{"b: ", "param2"});635}636 637TEST(ParameterHints, VariadicInfinite) {638 // infinite recursion should not break clangd639 assertParameterHints(640 R"cpp(641 template <typename... Args>642 void foo(Args...);643 644 template <typename... Args>645 void bar(Args... args) {646 foo(args...);647 }648 649 template <typename... Args>650 void foo(Args... args) {651 bar(args...);652 }653 654 int main() {655 foo(1, 2);656 }657 )cpp");658}659 660TEST(ParameterHints, VariadicDuplicatePack) {661 // edge cases with multiple adjacent packs should work662 assertParameterHints(663 R"cpp(664 void foo(int a, int b, int c, int);665 666 template <typename... Args>667 void bar(int, Args... args, int d) {668 foo(args..., d);669 }670 671 template <typename... Args>672 void baz(Args... args, Args... args2) {673 bar<Args..., int>(1, args..., args2...);674 }675 676 int main() {677 baz<int, int>($p1[[1]], $p2[[2]], $p3[[3]], $p4[[4]]);678 }679 )cpp",680 ExpectedHint{"a: ", "p1"}, ExpectedHint{"b: ", "p2"},681 ExpectedHint{"c: ", "p3"}, ExpectedHint{"d: ", "p4"});682}683 684TEST(ParameterHints, VariadicEmplace) {685 // emplace-like calls should forward constructor parameters686 // This prototype of std::forward is sufficient for clang to recognize it687 assertParameterHints(688 R"cpp(689 namespace std { template <typename T> T&& forward(T&); }690 using size_t = decltype(sizeof(0));691 void *operator new(size_t, void *);692 struct S {693 S(int A);694 S(int B, int C);695 };696 struct alloc {697 template <typename T>698 T* allocate();699 template <typename T, typename... Args>700 void construct(T* ptr, Args&&... args) {701 ::new ((void*)ptr) T{std::forward<Args>(args)...};702 }703 };704 template <typename T>705 struct container {706 template <typename... Args>707 void emplace(Args&&... args) {708 alloc a;709 auto ptr = a.template allocate<T>();710 a.construct(ptr, std::forward<Args>(args)...);711 }712 };713 void foo() {714 container<S> c;715 c.emplace($param1[[1]]);716 c.emplace($param2[[2]], $param3[[3]]);717 }718 )cpp",719 ExpectedHint{"A: ", "param1"}, ExpectedHint{"B: ", "param2"},720 ExpectedHint{"C: ", "param3"});721}722 723TEST(ParameterHints, VariadicReferenceHint) {724 assertParameterHints(R"cpp(725 void foo(int&);726 template <typename... Args>727 void bar(Args... args) { return foo(args...); }728 void baz() {729 int a;730 bar(a);731 bar(1);732 }733 )cpp");734}735 736TEST(ParameterHints, VariadicReferenceHintForwardingRef) {737 assertParameterHints(R"cpp(738 void foo(int&);739 template <typename... Args>740 void bar(Args&&... args) { return foo(args...); }741 void baz() {742 int a;743 bar($param[[a]]);744 bar(1);745 }746 )cpp",747 ExpectedHint{"&: ", "param"});748}749 750TEST(ParameterHints, VariadicReferenceHintForwardingRefStdForward) {751 assertParameterHints(R"cpp(752 namespace std { template <typename T> T&& forward(T&); }753 void foo(int&);754 template <typename... Args>755 void bar(Args&&... args) { return foo(std::forward<Args>(args)...); }756 void baz() {757 int a;758 bar($param[[a]]);759 }760 )cpp",761 ExpectedHint{"&: ", "param"});762}763 764TEST(ParameterHints, VariadicNoReferenceHintForwardingRefStdForward) {765 assertParameterHints(R"cpp(766 namespace std { template <typename T> T&& forward(T&); }767 void foo(int);768 template <typename... Args>769 void bar(Args&&... args) { return foo(std::forward<Args>(args)...); }770 void baz() {771 int a;772 bar(a);773 bar(1);774 }775 )cpp");776}777 778TEST(ParameterHints, VariadicNoReferenceHintUnresolvedForward) {779 assertParameterHints(R"cpp(780 template <typename... Args>781 void foo(Args&&... args);782 void bar() {783 int a;784 foo(a);785 }786 )cpp");787}788 789TEST(ParameterHints, MatchingNameVariadicForwarded) {790 // No name hint for variadic parameter with matching name791 // This prototype of std::forward is sufficient for clang to recognize it792 assertParameterHints(R"cpp(793 namespace std { template <typename T> T&& forward(T&); }794 void foo(int a);795 template <typename... Args>796 void bar(Args&&... args) { return foo(std::forward<Args>(args)...); }797 void baz() {798 int a;799 bar(a);800 }801 )cpp");802}803 804TEST(ParameterHints, MatchingNameVariadicPlain) {805 // No name hint for variadic parameter with matching name806 assertParameterHints(R"cpp(807 void foo(int a);808 template <typename... Args>809 void bar(Args&&... args) { return foo(args...); }810 void baz() {811 int a;812 bar(a);813 }814 )cpp");815}816 817TEST(ParameterHints, Operator) {818 // No hint for operator call with operator syntax.819 assertParameterHints(R"cpp(820 struct S {};821 void operator+(S lhs, S rhs);822 void bar() {823 S a, b;824 a + b;825 }826 )cpp");827}828 829TEST(ParameterHints, FunctionCallOperator) {830 assertParameterHints(R"cpp(831 struct W {832 void operator()(int x);833 };834 struct S : W {835 using W::operator();836 static void operator()(int x, int y);837 };838 void bar() {839 auto l1 = [](int x) {};840 auto l2 = [](int x) static {};841 842 S s;843 s($1[[1]]);844 s.operator()($2[[1]]);845 s.operator()($3[[1]], $4[[2]]);846 S::operator()($5[[1]], $6[[2]]);847 848 l1($7[[1]]);849 l1.operator()($8[[1]]);850 l2($9[[1]]);851 l2.operator()($10[[1]]);852 853 void (*ptr)(int a, int b) = &S::operator();854 ptr($11[[1]], $12[[2]]);855 }856 )cpp",857 ExpectedHint{"x: ", "1"}, ExpectedHint{"x: ", "2"},858 ExpectedHint{"x: ", "3"}, ExpectedHint{"y: ", "4"},859 ExpectedHint{"x: ", "5"}, ExpectedHint{"y: ", "6"},860 ExpectedHint{"x: ", "7"}, ExpectedHint{"x: ", "8"},861 ExpectedHint{"x: ", "9"}, ExpectedHint{"x: ", "10"},862 ExpectedHint{"a: ", "11"}, ExpectedHint{"b: ", "12"});863}864 865TEST(ParameterHints, DeducingThis) {866 assertParameterHints(R"cpp(867 struct S {868 template <typename This>869 auto operator()(this This &&Self, int Param) {870 return 42;871 }872 873 auto function(this auto &Self, int Param) {874 return Param;875 }876 };877 void work() {878 S s;879 s($1[[42]]);880 s.function($2[[42]]);881 S()($3[[42]]);882 auto lambda = [](this auto &Self, char C) -> void {883 return Self(C);884 };885 lambda($4[['A']]);886 }887 )cpp",888 ExpectedHint{"Param: ", "1"},889 ExpectedHint{"Param: ", "2"},890 ExpectedHint{"Param: ", "3"}, ExpectedHint{"C: ", "4"});891}892 893TEST(ParameterHints, Macros) {894 // Handling of macros depends on where the call's argument list comes from.895 896 // If it comes from a macro definition, there's nothing to hint897 // at the invocation site.898 assertParameterHints(R"cpp(899 void foo(int param);900 #define ExpandsToCall() foo(42)901 void bar() {902 ExpandsToCall();903 }904 )cpp");905 906 // The argument expression being a macro invocation shouldn't interfere907 // with hinting.908 assertParameterHints(R"cpp(909 #define PI 3.14910 void foo(double param);911 void bar() {912 foo($param[[PI]]);913 }914 )cpp",915 ExpectedHint{"param: ", "param"});916 917 // If the whole argument list comes from a macro parameter, hint it.918 assertParameterHints(R"cpp(919 void abort();920 #define ASSERT(expr) if (!expr) abort()921 int foo(int param);922 void bar() {923 ASSERT(foo($param[[42]]) == 0);924 }925 )cpp",926 ExpectedHint{"param: ", "param"});927 928 // If the macro expands to multiple arguments, don't hint it.929 assertParameterHints(R"cpp(930 void foo(double x, double y);931 #define CONSTANTS 3.14, 2.72932 void bar() {933 foo(CONSTANTS);934 }935 )cpp");936}937 938TEST(ParameterHints, ConstructorParens) {939 assertParameterHints(R"cpp(940 struct S {941 S(int param);942 };943 void bar() {944 S obj($param[[42]]);945 }946 )cpp",947 ExpectedHint{"param: ", "param"});948}949 950TEST(ParameterHints, ConstructorBraces) {951 assertParameterHints(R"cpp(952 struct S {953 S(int param);954 };955 void bar() {956 S obj{$param[[42]]};957 }958 )cpp",959 ExpectedHint{"param: ", "param"});960}961 962TEST(ParameterHints, ConstructorStdInitList) {963 // Do not show hints for std::initializer_list constructors.964 assertParameterHints(R"cpp(965 namespace std {966 template <typename E> class initializer_list { const E *a, *b; };967 }968 struct S {969 S(std::initializer_list<int> param);970 };971 void bar() {972 S obj{42, 43};973 }974 )cpp");975}976 977TEST(ParameterHints, MemberInit) {978 assertParameterHints(R"cpp(979 struct S {980 S(int param);981 };982 struct T {983 S member;984 T() : member($param[[42]]) {}985 };986 )cpp",987 ExpectedHint{"param: ", "param"});988}989 990TEST(ParameterHints, ImplicitConstructor) {991 assertParameterHints(R"cpp(992 struct S {993 S(int param);994 };995 void bar(S);996 S foo() {997 // Do not show hint for implicit constructor call in argument.998 bar(42);999 // Do not show hint for implicit constructor call in return.1000 return 42;1001 }1002 )cpp");1003}1004 1005TEST(ParameterHints, FunctionPointer) {1006 assertParameterHints(1007 R"cpp(1008 void (*f1)(int param);1009 void (__stdcall *f2)(int param);1010 using f3_t = void(*)(int param);1011 f3_t f3;1012 using f4_t = void(__stdcall *)(int param);1013 f4_t f4;1014 __attribute__((noreturn)) f4_t f5;1015 void bar() {1016 f1($f1[[42]]);1017 f2($f2[[42]]);1018 f3($f3[[42]]);1019 f4($f4[[42]]);1020 // This one runs into an edge case in clang's type model1021 // and we can't extract the parameter name. But at least1022 // we shouldn't crash.1023 f5(42);1024 }1025 )cpp",1026 ExpectedHint{"param: ", "f1"}, ExpectedHint{"param: ", "f2"},1027 ExpectedHint{"param: ", "f3"}, ExpectedHint{"param: ", "f4"});1028}1029 1030TEST(ParameterHints, ArgMatchesParam) {1031 assertParameterHints(R"cpp(1032 void foo(int param);1033 struct S {1034 static const int param = 42;1035 };1036 void bar() {1037 int param = 42;1038 // Do not show redundant "param: param".1039 foo(param);1040 // But show it if the argument is qualified.1041 foo($param[[S::param]]);1042 }1043 struct A {1044 int param;1045 void bar() {1046 // Do not show "param: param" for member-expr.1047 foo(param);1048 }1049 };1050 )cpp",1051 ExpectedHint{"param: ", "param"});1052}1053 1054TEST(ParameterHints, ArgMatchesParamReference) {1055 assertParameterHints(R"cpp(1056 void foo(int& param);1057 void foo2(const int& param);1058 void bar() {1059 int param;1060 // show reference hint on mutable reference1061 foo($param[[param]]);1062 // but not on const reference1063 foo2(param);1064 }1065 )cpp",1066 ExpectedHint{"&: ", "param"});1067}1068 1069TEST(ParameterHints, LeadingUnderscore) {1070 assertParameterHints(R"cpp(1071 void foo(int p1, int _p2, int __p3);1072 void bar() {1073 foo($p1[[41]], $p2[[42]], $p3[[43]]);1074 }1075 )cpp",1076 ExpectedHint{"p1: ", "p1"}, ExpectedHint{"p2: ", "p2"},1077 ExpectedHint{"p3: ", "p3"});1078}1079 1080TEST(ParameterHints, DependentCalls) {1081 assertParameterHints(R"cpp(1082 template <typename T>1083 void nonmember(T par1);1084 1085 template <typename T>1086 struct A {1087 void member(T par2);1088 static void static_member(T par3);1089 };1090 1091 void overload(int anInt);1092 void overload(double aDouble);1093 1094 template <typename T>1095 struct S {1096 void bar(A<T> a, T t) {1097 nonmember($par1[[t]]);1098 a.member($par2[[t]]);1099 A<T>::static_member($par3[[t]]);1100 // We don't want to arbitrarily pick between1101 // "anInt" or "aDouble", so just show no hint.1102 overload(T{});1103 }1104 };1105 )cpp",1106 ExpectedHint{"par1: ", "par1"},1107 ExpectedHint{"par2: ", "par2"},1108 ExpectedHint{"par3: ", "par3"});1109}1110 1111TEST(ParameterHints, VariadicFunction) {1112 assertParameterHints(R"cpp(1113 template <typename... T>1114 void foo(int fixed, T... variadic);1115 1116 void bar() {1117 foo($fixed[[41]], 42, 43);1118 }1119 )cpp",1120 ExpectedHint{"fixed: ", "fixed"});1121}1122 1123TEST(ParameterHints, VarargsFunction) {1124 assertParameterHints(R"cpp(1125 void foo(int fixed, ...);1126 1127 void bar() {1128 foo($fixed[[41]], 42, 43);1129 }1130 )cpp",1131 ExpectedHint{"fixed: ", "fixed"});1132}1133 1134TEST(ParameterHints, CopyOrMoveConstructor) {1135 // Do not show hint for parameter of copy or move constructor.1136 assertParameterHints(R"cpp(1137 struct S {1138 S();1139 S(const S& other);1140 S(S&& other);1141 };1142 void bar() {1143 S a;1144 S b(a); // copy1145 S c(S()); // move1146 }1147 )cpp");1148}1149 1150TEST(ParameterHints, AggregateInit) {1151 // FIXME: This is not implemented yet, but it would be a natural1152 // extension to show member names as hints here.1153 assertParameterHints(R"cpp(1154 struct Point {1155 int x;1156 int y;1157 };1158 void bar() {1159 Point p{41, 42};1160 }1161 )cpp");1162}1163 1164TEST(ParameterHints, UserDefinedLiteral) {1165 // Do not hint call to user-defined literal operator.1166 assertParameterHints(R"cpp(1167 long double operator"" _w(long double param);1168 void bar() {1169 1.2_w;1170 }1171 )cpp");1172}1173 1174TEST(ParameterHints, ParamNameComment) {1175 // Do not hint an argument which already has a comment1176 // with the parameter name preceding it.1177 assertParameterHints(R"cpp(1178 void foo(int param);1179 void bar() {1180 foo(/*param*/42);1181 foo( /* param = */ 42);1182#define X 421183#define Y X1184#define Z(...) Y1185 foo(/*param=*/Z(a));1186 foo($macro[[Z(a)]]);1187 foo(/* the answer */$param[[42]]);1188 }1189 )cpp",1190 ExpectedHint{"param: ", "macro"},1191 ExpectedHint{"param: ", "param"});1192}1193 1194TEST(ParameterHints, SetterFunctions) {1195 assertParameterHints(R"cpp(1196 struct S {1197 void setParent(S* parent);1198 void set_parent(S* parent);1199 void setTimeout(int timeoutMillis);1200 void setTimeoutMillis(int timeout_millis);1201 };1202 void bar() {1203 S s;1204 // Parameter name matches setter name - omit hint.1205 s.setParent(nullptr);1206 // Support snake_case1207 s.set_parent(nullptr);1208 // Parameter name may contain extra info - show hint.1209 s.setTimeout($timeoutMillis[[120]]);1210 // FIXME: Ideally we'd want to omit this.1211 s.setTimeoutMillis($timeout_millis[[120]]);1212 }1213 )cpp",1214 ExpectedHint{"timeoutMillis: ", "timeoutMillis"},1215 ExpectedHint{"timeout_millis: ", "timeout_millis"});1216}1217 1218TEST(ParameterHints, BuiltinFunctions) {1219 // This prototype of std::forward is sufficient for clang to recognize it1220 assertParameterHints(R"cpp(1221 namespace std { template <typename T> T&& forward(T&); }1222 void foo() {1223 int i;1224 std::forward(i);1225 }1226 )cpp");1227}1228 1229TEST(ParameterHints, IncludeAtNonGlobalScope) {1230 Annotations FooInc(R"cpp(1231 void bar() { foo(42); }1232 )cpp");1233 Annotations FooCC(R"cpp(1234 struct S {1235 void foo(int param);1236 #include "foo.inc"1237 };1238 )cpp");1239 1240 TestWorkspace Workspace;1241 Workspace.addSource("foo.inc", FooInc.code());1242 Workspace.addMainFile("foo.cc", FooCC.code());1243 1244 auto AST = Workspace.openFile("foo.cc");1245 ASSERT_TRUE(bool(AST));1246 1247 // Ensure the hint for the call in foo.inc is NOT materialized in foo.cc.1248 EXPECT_EQ(1249 hintsOfKind(*AST, InlayHintKind::Parameter, DefaultOptsForTests).size(),1250 0u);1251}1252 1253TEST(TypeHints, Smoke) {1254 assertTypeHints(R"cpp(1255 auto $waldo[[waldo]] = 42;1256 )cpp",1257 ExpectedHint{": int", "waldo"});1258}1259 1260TEST(TypeHints, Decorations) {1261 assertTypeHints(R"cpp(1262 int x = 42;1263 auto* $var1[[var1]] = &x;1264 auto&& $var2[[var2]] = x;1265 const auto& $var3[[var3]] = x;1266 )cpp",1267 ExpectedHint{": int *", "var1"},1268 ExpectedHint{": int &", "var2"},1269 ExpectedHint{": const int &", "var3"});1270}1271 1272TEST(TypeHints, DecltypeAuto) {1273 assertTypeHints(R"cpp(1274 int x = 42;1275 int& y = x;1276 decltype(auto) $z[[z]] = y;1277 )cpp",1278 ExpectedHint{": int &", "z"});1279}1280 1281TEST(TypeHints, NoQualifiers) {1282 assertTypeHints(R"cpp(1283 namespace A {1284 namespace B {1285 struct S1 {};1286 S1 foo();1287 auto $x[[x]] = foo();1288 1289 struct S2 {1290 template <typename T>1291 struct Inner {};1292 };1293 S2::Inner<int> bar();1294 auto $y[[y]] = bar();1295 }1296 }1297 )cpp",1298 ExpectedHint{": S1", "x"}, ExpectedHint{": Inner<int>", "y"});1299}1300 1301TEST(TypeHints, Lambda) {1302 // Do not print something overly verbose like the lambda's location.1303 // Show hints for init-captures (but not regular captures).1304 assertTypeHints(R"cpp(1305 void f() {1306 int cap = 42;1307 auto $L[[L]] = [cap, $init[[init]] = 1 + 1](int a$ret[[)]] { 1308 return a + cap + init; 1309 };1310 }1311 )cpp",1312 ExpectedHint{": (lambda)", "L"},1313 ExpectedHint{": int", "init"}, ExpectedHint{"-> int", "ret"});1314 1315 // Lambda return hint shown even if no param list.1316 // (The digraph :> is just a ] that doesn't conflict with the annotations).1317 assertTypeHints("auto $L[[x]] = <:$ret[[:>]]{return 42;};",1318 ExpectedHint{": (lambda)", "L"},1319 ExpectedHint{"-> int", "ret"});1320}1321 1322// Structured bindings tests.1323// Note, we hint the individual bindings, not the aggregate.1324 1325TEST(TypeHints, StructuredBindings_PublicStruct) {1326 assertTypeHints(R"cpp(1327 // Struct with public fields.1328 struct Point {1329 int x;1330 int y;1331 };1332 Point foo();1333 auto [$x[[x]], $y[[y]]] = foo();1334 )cpp",1335 ExpectedHint{": int", "x"}, ExpectedHint{": int", "y"});1336}1337 1338TEST(TypeHints, StructuredBindings_Array) {1339 assertTypeHints(R"cpp(1340 int arr[2];1341 auto [$x[[x]], $y[[y]]] = arr;1342 )cpp",1343 ExpectedHint{": int", "x"}, ExpectedHint{": int", "y"});1344}1345 1346TEST(TypeHints, StructuredBindings_TupleLike) {1347 assertTypeHints(R"cpp(1348 // Tuple-like type.1349 struct IntPair {1350 int a;1351 int b;1352 };1353 namespace std {1354 template <typename T>1355 struct tuple_size {};1356 template <>1357 struct tuple_size<IntPair> {1358 constexpr static unsigned value = 2;1359 };1360 template <unsigned I, typename T>1361 struct tuple_element {};1362 template <unsigned I>1363 struct tuple_element<I, IntPair> {1364 using type = int;1365 };1366 }1367 template <unsigned I>1368 int get(const IntPair& p) {1369 if constexpr (I == 0) {1370 return p.a;1371 } else if constexpr (I == 1) {1372 return p.b;1373 }1374 }1375 IntPair bar();1376 auto [$x[[x]], $y[[y]]] = bar();1377 )cpp",1378 ExpectedHint{": int", "x"}, ExpectedHint{": int", "y"});1379}1380 1381TEST(TypeHints, StructuredBindings_NoInitializer) {1382 assertTypeHints(R"cpp(1383 // No initializer (ill-formed).1384 // Do not show useless "NULL TYPE" hint. 1385 auto [x, y]; /*error-ok*/1386 )cpp");1387}1388 1389TEST(TypeHints, InvalidType) {1390 assertTypeHints(R"cpp(1391 auto x = (unknown_type)42; /*error-ok*/1392 auto *y = (unknown_ptr)nullptr;1393 )cpp");1394}1395 1396TEST(TypeHints, ReturnTypeDeduction) {1397 assertTypeHints(1398 R"cpp(1399 auto f1(int x$ret1a[[)]]; // Hint forward declaration too1400 auto f1(int x$ret1b[[)]] { return x + 1; }1401 1402 // Include pointer operators in hint1403 int s;1404 auto& f2($ret2[[)]] { return s; }1405 1406 // Do not hint `auto` for trailing return type.1407 auto f3() -> int;1408 1409 // Do not hint when a trailing return type is specified.1410 auto f4() -> auto* { return "foo"; }1411 1412 auto f5($noreturn[[)]] {}1413 1414 // `auto` conversion operator1415 struct A {1416 operator auto($retConv[[)]] { return 42; }1417 };1418 1419 // FIXME: Dependent types do not work yet.1420 template <typename T>1421 struct S {1422 auto method() { return T(); }1423 };1424 )cpp",1425 ExpectedHint{"-> int", "ret1a"}, ExpectedHint{"-> int", "ret1b"},1426 ExpectedHint{"-> int &", "ret2"}, ExpectedHint{"-> void", "noreturn"},1427 ExpectedHint{"-> int", "retConv"});1428}1429 1430TEST(TypeHints, DependentType) {1431 assertTypeHints(R"cpp(1432 template <typename T>1433 void foo(T arg) {1434 // The hint would just be "auto" and we can't do any better.1435 auto var1 = arg.method();1436 // FIXME: It would be nice to show "T" as the hint.1437 auto $var2[[var2]] = arg;1438 }1439 1440 template <typename T>1441 void bar(T arg) {1442 auto [a, b] = arg;1443 }1444 )cpp",1445 ExpectedHint{": T", "var2"});1446}1447 1448TEST(TypeHints, LongTypeName) {1449 assertTypeHints(R"cpp(1450 template <typename, typename, typename>1451 struct A {};1452 struct MultipleWords {};1453 A<MultipleWords, MultipleWords, MultipleWords> foo();1454 // Omit type hint past a certain length (currently 32)1455 auto var = foo();1456 )cpp");1457 1458 Config Cfg;1459 Cfg.InlayHints.TypeNameLimit = 0;1460 WithContextValue WithCfg(Config::Key, std::move(Cfg));1461 1462 assertTypeHints(1463 R"cpp(1464 template <typename, typename, typename>1465 struct A {};1466 struct MultipleWords {};1467 A<MultipleWords, MultipleWords, MultipleWords> foo();1468 // Should have type hint with TypeNameLimit = 01469 auto $var[[var]] = foo();1470 )cpp",1471 ExpectedHint{": A<MultipleWords, MultipleWords, MultipleWords>", "var"});1472}1473 1474TEST(TypeHints, DefaultTemplateArgs) {1475 assertTypeHints(R"cpp(1476 template <typename, typename = int>1477 struct A {};1478 A<float> foo();1479 auto $var[[var]] = foo();1480 A<float> bar[1];1481 auto [$binding[[value]]] = bar;1482 )cpp",1483 ExpectedHint{": A<float>", "var"},1484 ExpectedHint{": A<float>", "binding"});1485}1486 1487TEST(DefaultArguments, Smoke) {1488 Config Cfg;1489 Cfg.InlayHints.Parameters =1490 true; // To test interplay of parameters and default parameters1491 Cfg.InlayHints.DeducedTypes = false;1492 Cfg.InlayHints.Designators = false;1493 Cfg.InlayHints.BlockEnd = false;1494 1495 Cfg.InlayHints.DefaultArguments = true;1496 WithContextValue WithCfg(Config::Key, std::move(Cfg));1497 1498 const auto *Code = R"cpp(1499 int foo(int A = 4) { return A; }1500 int bar(int A, int B = 1, bool C = foo($default1[[)]]) { return A; }1501 int A = bar($explicit[[2]]$default2[[)]];1502 1503 void baz(int = 5) { if (false) baz($unnamed[[)]]; };1504 )cpp";1505 1506 assertHints(InlayHintKind::DefaultArgument, Code, DefaultOptsForTests,1507 ExpectedHint{"A: 4", "default1", Left},1508 ExpectedHint{", B: 1, C: foo()", "default2", Left},1509 ExpectedHint{"5", "unnamed", Left});1510 1511 assertHints(InlayHintKind::Parameter, Code, DefaultOptsForTests,1512 ExpectedHint{"A: ", "explicit", Left});1513}1514 1515TEST(DefaultArguments, WithoutParameterNames) {1516 Config Cfg;1517 Cfg.InlayHints.Parameters = false; // To test just default args this time1518 Cfg.InlayHints.DeducedTypes = false;1519 Cfg.InlayHints.Designators = false;1520 Cfg.InlayHints.BlockEnd = false;1521 1522 Cfg.InlayHints.DefaultArguments = true;1523 WithContextValue WithCfg(Config::Key, std::move(Cfg));1524 1525 const auto *Code = R"cpp(1526 struct Baz {1527 Baz(float a = 3 //1528 + 2);1529 };1530 struct Foo {1531 Foo(int, Baz baz = //1532 Baz{$abbreviated[[}]]1533 1534 //1535 ) {}1536 };1537 1538 int main() {1539 Foo foo1(1$paren[[)]];1540 Foo foo2{2$brace1[[}]];1541 Foo foo3 = {3$brace2[[}]];1542 auto foo4 = Foo{4$brace3[[}]];1543 }1544 )cpp";1545 1546 assertHints(InlayHintKind::DefaultArgument, Code, DefaultOptsForTests,1547 ExpectedHint{"...", "abbreviated", Left},1548 ExpectedHint{", Baz{}", "paren", Left},1549 ExpectedHint{", Baz{}", "brace1", Left},1550 ExpectedHint{", Baz{}", "brace2", Left},1551 ExpectedHint{", Baz{}", "brace3", Left});1552 1553 assertHints(InlayHintKind::Parameter, Code, DefaultOptsForTests);1554}1555 1556TEST(TypeHints, Deduplication) {1557 assertTypeHints(R"cpp(1558 template <typename T>1559 void foo() {1560 auto $var[[var]] = 42;1561 }1562 template void foo<int>();1563 template void foo<float>();1564 )cpp",1565 ExpectedHint{": int", "var"});1566}1567 1568TEST(TypeHints, SinglyInstantiatedTemplate) {1569 assertTypeHints(R"cpp(1570 auto $lambda[[x]] = [](auto *$param[[y]], auto) { return 42; };1571 int m = x("foo", 3);1572 )cpp",1573 ExpectedHint{": (lambda)", "lambda"},1574 ExpectedHint{": const char *", "param"});1575 1576 // No hint for packs, or auto params following packs1577 assertTypeHints(R"cpp(1578 int x(auto $a[[a]], auto... b, auto c) { return 42; }1579 int m = x<void*, char, float>(nullptr, 'c', 2.0, 2);1580 )cpp",1581 ExpectedHint{": void *", "a"});1582}1583 1584TEST(TypeHints, Aliased) {1585 // Check that we don't crash for functions without a FunctionTypeLoc.1586 // https://github.com/clangd/clangd/issues/11401587 TestTU TU = TestTU::withCode("void foo(void){} extern typeof(foo) foo;");1588 TU.ExtraArgs.push_back("-xc");1589 auto AST = TU.build();1590 1591 EXPECT_THAT(hintsOfKind(AST, InlayHintKind::Type, DefaultOptsForTests),1592 IsEmpty());1593}1594 1595TEST(TypeHints, CallingConvention) {1596 // Check that we don't crash for lambdas with an annotation1597 // https://github.com/clangd/clangd/issues/22231598 Annotations Source(R"cpp(1599 void test() {1600 []($lambda[[)]]__cdecl {};1601 }1602 )cpp");1603 TestTU TU = TestTU::withCode(Source.code());1604 TU.ExtraArgs.push_back("--target=x86_64-w64-mingw32");1605 TU.PredefineMacros = true; // for the __cdecl1606 auto AST = TU.build();1607 1608 EXPECT_THAT(1609 hintsOfKind(AST, InlayHintKind::Type, DefaultOptsForTests),1610 ElementsAre(HintMatcher(ExpectedHint{"-> void", "lambda"}, Source)));1611}1612 1613TEST(TypeHints, Decltype) {1614 assertTypeHints(R"cpp(1615 $a[[decltype(0)]] a;1616 $b[[decltype(a)]] b;1617 const $c[[decltype(0)]] &c = b;1618 1619 // Don't show for dependent type1620 template <class T>1621 constexpr decltype(T{}) d;1622 1623 $e[[decltype(0)]] e();1624 auto f() -> $f[[decltype(0)]];1625 1626 template <class, class> struct Foo;1627 using G = Foo<$g[[decltype(0)]], float>;1628 1629 auto $h[[h]] = $i[[decltype(0)]]{};1630 1631 // No crash1632 /* error-ok */1633 auto $j[[s]];1634 )cpp",1635 ExpectedHint{": int", "a"}, ExpectedHint{": int", "b"},1636 ExpectedHint{": int", "c"}, ExpectedHint{": int", "e"},1637 ExpectedHint{": int", "f"}, ExpectedHint{": int", "g"},1638 ExpectedHint{": int", "h"}, ExpectedHint{": int", "i"});1639}1640 1641TEST(TypeHints, SubstTemplateParameterAliases) {1642 llvm::StringRef Header = R"cpp(1643 template <class T> struct allocator {};1644 1645 template <class T, class A>1646 struct vector_base {1647 using pointer = T*;1648 };1649 1650 template <class T, class A>1651 struct internal_iterator_type_template_we_dont_expect {};1652 1653 struct my_iterator {};1654 1655 template <class T, class A = allocator<T>>1656 struct vector : vector_base<T, A> {1657 using base = vector_base<T, A>;1658 typedef T value_type;1659 typedef base::pointer pointer;1660 using allocator_type = A;1661 using size_type = int;1662 using iterator = internal_iterator_type_template_we_dont_expect<T, A>;1663 using non_template_iterator = my_iterator;1664 1665 value_type& operator[](int index) { return elements[index]; }1666 const value_type& at(int index) const { return elements[index]; }1667 pointer data() { return &elements[0]; }1668 allocator_type get_allocator() { return A(); }1669 size_type size() const { return 10; }1670 iterator begin() { return iterator(); }1671 non_template_iterator end() { return non_template_iterator(); }1672 1673 T elements[10];1674 };1675 )cpp";1676 1677 llvm::StringRef VectorIntPtr = R"cpp(1678 vector<int *> array;1679 auto $no_modifier[[x]] = array[3];1680 auto* $ptr_modifier[[ptr]] = &array[3];1681 auto& $ref_modifier[[ref]] = array[3];1682 auto& $at[[immutable]] = array.at(3);1683 1684 auto $data[[data]] = array.data();1685 auto $allocator[[alloc]] = array.get_allocator();1686 auto $size[[size]] = array.size();1687 auto $begin[[begin]] = array.begin();1688 auto $end[[end]] = array.end();1689 )cpp";1690 1691 assertHintsWithHeader(1692 InlayHintKind::Type, VectorIntPtr, Header, DefaultOptsForTests,1693 ExpectedHint{": int *", "no_modifier"},1694 ExpectedHint{": int **", "ptr_modifier"},1695 ExpectedHint{": int *&", "ref_modifier"},1696 ExpectedHint{": int *const &", "at"}, ExpectedHint{": int **", "data"},1697 ExpectedHint{": allocator<int *>", "allocator"},1698 ExpectedHint{": size_type", "size"}, ExpectedHint{": iterator", "begin"},1699 ExpectedHint{": non_template_iterator", "end"});1700 1701 llvm::StringRef VectorInt = R"cpp(1702 vector<int> array;1703 auto $no_modifier[[by_value]] = array[3];1704 auto* $ptr_modifier[[ptr]] = &array[3];1705 auto& $ref_modifier[[ref]] = array[3];1706 auto& $at[[immutable]] = array.at(3);1707 1708 auto $data[[data]] = array.data();1709 auto $allocator[[alloc]] = array.get_allocator();1710 auto $size[[size]] = array.size();1711 auto $begin[[begin]] = array.begin();1712 auto $end[[end]] = array.end();1713 )cpp";1714 1715 assertHintsWithHeader(1716 InlayHintKind::Type, VectorInt, Header, DefaultOptsForTests,1717 ExpectedHint{": int", "no_modifier"},1718 ExpectedHint{": int *", "ptr_modifier"},1719 ExpectedHint{": int &", "ref_modifier"},1720 ExpectedHint{": const int &", "at"}, ExpectedHint{": int *", "data"},1721 ExpectedHint{": allocator<int>", "allocator"},1722 ExpectedHint{": size_type", "size"}, ExpectedHint{": iterator", "begin"},1723 ExpectedHint{": non_template_iterator", "end"});1724 1725 llvm::StringRef TypeAlias = R"cpp(1726 // If the type alias is not of substituted template parameter type,1727 // do not show desugared type.1728 using VeryLongLongTypeName = my_iterator;1729 using Short = VeryLongLongTypeName;1730 1731 auto $short_name[[my_value]] = Short();1732 1733 // Same applies with templates.1734 template <typename T, typename A>1735 using basic_static_vector = vector<T, A>;1736 template <typename T>1737 using static_vector = basic_static_vector<T, allocator<T>>;1738 1739 auto $vector_name[[vec]] = static_vector<int>();1740 )cpp";1741 1742 assertHintsWithHeader(InlayHintKind::Type, TypeAlias, Header,1743 DefaultOptsForTests,1744 ExpectedHint{": Short", "short_name"},1745 ExpectedHint{": static_vector<int>", "vector_name"});1746}1747 1748TEST(DesignatorHints, Basic) {1749 assertDesignatorHints(R"cpp(1750 struct S { int x, y, z; };1751 S s {$x[[1]], $y[[2+2]]};1752 1753 int x[] = {$0[[0]], $1[[1]]};1754 )cpp",1755 ExpectedHint{".x=", "x"}, ExpectedHint{".y=", "y"},1756 ExpectedHint{"[0]=", "0"}, ExpectedHint{"[1]=", "1"});1757}1758 1759TEST(DesignatorHints, Nested) {1760 assertDesignatorHints(R"cpp(1761 struct Inner { int x, y; };1762 struct Outer { Inner a, b; };1763 Outer o{ $a[[{ $x[[1]], $y[[2]] }]], $bx[[3]] };1764 )cpp",1765 ExpectedHint{".a=", "a"}, ExpectedHint{".x=", "x"},1766 ExpectedHint{".y=", "y"}, ExpectedHint{".b.x=", "bx"});1767}1768 1769TEST(DesignatorHints, AnonymousRecord) {1770 assertDesignatorHints(R"cpp(1771 struct S {1772 union {1773 struct {1774 struct {1775 int y;1776 };1777 } x;1778 };1779 };1780 S s{$xy[[42]]};1781 )cpp",1782 ExpectedHint{".x.y=", "xy"});1783}1784 1785TEST(DesignatorHints, Suppression) {1786 assertDesignatorHints(R"cpp(1787 struct Point { int a, b, c, d, e, f, g, h; };1788 Point p{/*a=*/1, .c=2, /* .d = */3, $e[[4]]};1789 )cpp",1790 ExpectedHint{".e=", "e"});1791}1792 1793TEST(DesignatorHints, StdArray) {1794 // Designators for std::array should be [0] rather than .__elements[0].1795 // While technically correct, the designator is useless and horrible to read.1796 assertDesignatorHints(R"cpp(1797 template <typename T, int N> struct Array { T __elements[N]; };1798 Array<int, 2> x = {$0[[0]], $1[[1]]};1799 )cpp",1800 ExpectedHint{"[0]=", "0"}, ExpectedHint{"[1]=", "1"});1801}1802 1803TEST(DesignatorHints, OnlyAggregateInit) {1804 assertDesignatorHints(R"cpp(1805 struct Copyable { int x; } c;1806 Copyable d{c};1807 1808 struct Constructible { Constructible(int x); };1809 Constructible x{42};1810 )cpp" /*no designator hints expected (but param hints!)*/);1811}1812 1813TEST(DesignatorHints, NoCrash) {1814 assertDesignatorHints(R"cpp(1815 /*error-ok*/1816 struct A {};1817 struct Foo {int a; int b;};1818 void test() {1819 Foo f{A(), $b[[1]]};1820 }1821 )cpp",1822 ExpectedHint{".b=", "b"});1823}1824 1825TEST(InlayHints, RestrictRange) {1826 Annotations Code(R"cpp(1827 auto a = false;1828 [[auto b = 1;1829 auto c = '2';]]1830 auto d = 3.f;1831 )cpp");1832 auto AST = TestTU::withCode(Code.code()).build();1833 EXPECT_THAT(inlayHints(AST, Code.range()),1834 ElementsAre(labelIs(": int"), labelIs(": char")));1835}1836 1837TEST(ParameterHints, PseudoObjectExpr) {1838 Annotations Code(R"cpp(1839 struct S {1840 __declspec(property(get=GetX, put=PutX)) int x[];1841 int GetX(int y, int z) { return 42 + y; }1842 void PutX(int) { }1843 1844 // This is a PseudoObjectExpression whose syntactic form is a binary1845 // operator.1846 void Work(int y) { x = y; } // Not `x = y: y`.1847 };1848 1849 int printf(const char *Format, ...);1850 1851 int main() {1852 S s;1853 __builtin_dump_struct(&s, printf); // Not `Format: __builtin_dump_struct()`1854 printf($Param[["Hello, %d"]], 42); // Normal calls are not affected.1855 // This builds a PseudoObjectExpr, but here it's useful for showing the1856 // arguments from the semantic form.1857 return s.x[ $one[[1]] ][ $two[[2]] ]; // `x[y: 1][z: 2]`1858 }1859 )cpp");1860 auto TU = TestTU::withCode(Code.code());1861 TU.ExtraArgs.push_back("-fms-extensions");1862 auto AST = TU.build();1863 EXPECT_THAT(inlayHints(AST, std::nullopt),1864 ElementsAre(HintMatcher(ExpectedHint{"Format: ", "Param"}, Code),1865 HintMatcher(ExpectedHint{"y: ", "one"}, Code),1866 HintMatcher(ExpectedHint{"z: ", "two"}, Code)));1867}1868 1869TEST(ParameterHints, ArgPacksAndConstructors) {1870 assertParameterHints(1871 R"cpp(1872 struct Foo{ Foo(); Foo(int x); };1873 void foo(Foo a, int b);1874 template <typename... Args>1875 void bar(Args... args) {1876 foo(args...);1877 }1878 template <typename... Args>1879 void baz(Args... args) { foo($param1[[Foo{args...}]], $param2[[1]]); }1880 1881 template <typename... Args>1882 void bax(Args... args) { foo($param3[[{args...}]], args...); }1883 1884 void foo() {1885 bar($param4[[Foo{}]], $param5[[42]]);1886 bar($param6[[42]], $param7[[42]]);1887 baz($param8[[42]]);1888 bax($param9[[42]]);1889 }1890 )cpp",1891 ExpectedHint{"a: ", "param1"}, ExpectedHint{"b: ", "param2"},1892 ExpectedHint{"a: ", "param3"}, ExpectedHint{"a: ", "param4"},1893 ExpectedHint{"b: ", "param5"}, ExpectedHint{"a: ", "param6"},1894 ExpectedHint{"b: ", "param7"}, ExpectedHint{"x: ", "param8"},1895 ExpectedHint{"b: ", "param9"});1896}1897 1898TEST(ParameterHints, DoesntExpandAllArgs) {1899 assertParameterHints(1900 R"cpp(1901 void foo(int x, int y);1902 int id(int a, int b, int c);1903 template <typename... Args>1904 void bar(Args... args) {1905 foo(id($param1[[args]], $param2[[1]], $param3[[args]])...);1906 }1907 void foo() {1908 bar(1, 2); // FIXME: We could have `bar(a: 1, a: 2)` here.1909 }1910 )cpp",1911 ExpectedHint{"a: ", "param1"}, ExpectedHint{"b: ", "param2"},1912 ExpectedHint{"c: ", "param3"});1913}1914 1915TEST(BlockEndHints, Functions) {1916 assertBlockEndHints(R"cpp(1917 int foo() {1918 return 41;1919 $foo[[}]]1920 1921 template<int X> 1922 int bar() { 1923 // No hint for lambda for now1924 auto f = []() { 1925 return X; 1926 };1927 return f(); 1928 $bar[[}]]1929 1930 // No hint because this isn't a definition1931 int buz();1932 1933 struct S{};1934 bool operator==(S, S) {1935 return true;1936 $opEqual[[}]]1937 )cpp",1938 ExpectedHint{" // foo", "foo"},1939 ExpectedHint{" // bar", "bar"},1940 ExpectedHint{" // operator==", "opEqual"});1941}1942 1943TEST(BlockEndHints, Methods) {1944 assertBlockEndHints(R"cpp(1945 struct Test {1946 // No hint because there's no function body1947 Test() = default;1948 1949 ~Test() {1950 $dtor[[}]]1951 1952 void method1() {1953 $method1[[}]]1954 1955 // No hint because this isn't a definition1956 void method2();1957 1958 template <typename T>1959 void method3() {1960 $method3[[}]]1961 1962 // No hint because this isn't a definition1963 template <typename T>1964 void method4();1965 1966 Test operator+(int) const {1967 return *this;1968 $opIdentity[[}]]1969 1970 operator bool() const {1971 return true;1972 $opBool[[}]]1973 1974 // No hint because there's no function body1975 operator int() const = delete;1976 } x;1977 1978 void Test::method2() {1979 $method2[[}]]1980 1981 template <typename T>1982 void Test::method4() {1983 $method4[[}]]1984 )cpp",1985 ExpectedHint{" // ~Test", "dtor"},1986 ExpectedHint{" // method1", "method1"},1987 ExpectedHint{" // method3", "method3"},1988 ExpectedHint{" // operator+", "opIdentity"},1989 ExpectedHint{" // operator bool", "opBool"},1990 ExpectedHint{" // Test::method2", "method2"},1991 ExpectedHint{" // Test::method4", "method4"});1992}1993 1994TEST(BlockEndHints, Namespaces) {1995 assertBlockEndHints(1996 R"cpp(1997 namespace {1998 void foo();1999 $anon[[}]]2000 2001 namespace ns {2002 void bar();2003 $ns[[}]]2004 )cpp",2005 ExpectedHint{" // namespace", "anon"},2006 ExpectedHint{" // namespace ns", "ns"});2007}2008 2009TEST(BlockEndHints, Types) {2010 assertBlockEndHints(2011 R"cpp(2012 struct S {2013 $S[[};]]2014 2015 class C {2016 $C[[};]]2017 2018 union U {2019 $U[[};]]2020 2021 enum E1 {2022 $E1[[};]]2023 2024 enum class E2 {2025 $E2[[};]]2026 )cpp",2027 ExpectedHint{" // struct S", "S"}, ExpectedHint{" // class C", "C"},2028 ExpectedHint{" // union U", "U"}, ExpectedHint{" // enum E1", "E1"},2029 ExpectedHint{" // enum class E2", "E2"});2030}2031 2032TEST(BlockEndHints, If) {2033 assertBlockEndHints(2034 R"cpp(2035 void foo(bool cond) {2036 void* ptr;2037 if (cond)2038 ;2039 2040 if (cond) {2041 $simple[[}]]2042 2043 if (cond) {2044 } else {2045 $ifelse[[}]]2046 2047 if (cond) {2048 } else if (!cond) {2049 $elseif[[}]]2050 2051 if (cond) {2052 } else {2053 if (!cond) {2054 $inner[[}]]2055 $outer[[}]]2056 2057 if (auto X = cond) {2058 $init[[}]]2059 2060 if (int i = 0; i > 10) {2061 $init_cond[[}]]2062 2063 if (ptr != nullptr) {2064 $null_check[[}]]2065 } // suppress2066 )cpp",2067 ExpectedHint{" // if cond", "simple"},2068 ExpectedHint{" // if cond", "ifelse"}, ExpectedHint{" // if", "elseif"},2069 ExpectedHint{" // if !cond", "inner"},2070 ExpectedHint{" // if cond", "outer"}, ExpectedHint{" // if X", "init"},2071 ExpectedHint{" // if i > 10", "init_cond"},2072 ExpectedHint{" // if ptr != nullptr", "null_check"});2073}2074 2075TEST(BlockEndHints, Loops) {2076 assertBlockEndHints(2077 R"cpp(2078 void foo() {2079 while (true)2080 ;2081 2082 while (true) {2083 $while[[}]]2084 2085 do {2086 } while (true);2087 2088 for (;true;) {2089 $forcond[[}]]2090 2091 for (int I = 0; I < 10; ++I) {2092 $forvar[[}]]2093 2094 int Vs[] = {1,2,3};2095 for (auto V : Vs) {2096 $foreach[[}]]2097 } // suppress2098 )cpp",2099 ExpectedHint{" // while true", "while"},2100 ExpectedHint{" // for true", "forcond"},2101 ExpectedHint{" // for I", "forvar"},2102 ExpectedHint{" // for V", "foreach"});2103}2104 2105TEST(BlockEndHints, Switch) {2106 assertBlockEndHints(2107 R"cpp(2108 void foo(int I) {2109 switch (I) {2110 case 0: break;2111 $switch[[}]]2112 } // suppress2113 )cpp",2114 ExpectedHint{" // switch I", "switch"});2115}2116 2117TEST(BlockEndHints, PrintLiterals) {2118 assertBlockEndHints(2119 R"cpp(2120 void foo() {2121 while ("foo") {2122 $string[[}]]2123 2124 while ("foo but this time it is very long") {2125 $string_long[[}]]2126 2127 while (true) {2128 $boolean[[}]]2129 2130 while (1) {2131 $integer[[}]]2132 2133 while (1.5) {2134 $float[[}]]2135 } // suppress2136 )cpp",2137 ExpectedHint{" // while \"foo\"", "string"},2138 ExpectedHint{" // while \"foo but...\"", "string_long"},2139 ExpectedHint{" // while true", "boolean"},2140 ExpectedHint{" // while 1", "integer"},2141 ExpectedHint{" // while 1.5", "float"});2142}2143 2144TEST(BlockEndHints, PrintRefs) {2145 assertBlockEndHints(2146 R"cpp(2147 namespace ns {2148 int Var;2149 int func1();2150 int func2(int, int);2151 struct S {2152 int Field;2153 int method1() const;2154 int method2(int, int) const;2155 }; // suppress2156 } // suppress2157 void foo() {2158 int int_a {};2159 while (ns::Var) {2160 $var[[}]]2161 2162 while (ns::func1()) {2163 $func1[[}]]2164 2165 while (ns::func2(int_a, int_a)) {2166 $func2[[}]]2167 2168 while (ns::S{}.Field) {2169 $field[[}]]2170 2171 while (ns::S{}.method1()) {2172 $method1[[}]]2173 2174 while (ns::S{}.method2(int_a, int_a)) {2175 $method2[[}]]2176 } // suppress2177 )cpp",2178 ExpectedHint{" // while Var", "var"},2179 ExpectedHint{" // while func1()", "func1"},2180 ExpectedHint{" // while func2(...)", "func2"},2181 ExpectedHint{" // while Field", "field"},2182 ExpectedHint{" // while method1()", "method1"},2183 ExpectedHint{" // while method2(...)", "method2"});2184}2185 2186TEST(BlockEndHints, PrintConversions) {2187 assertBlockEndHints(2188 R"cpp(2189 struct S {2190 S(int);2191 S(int, int);2192 explicit operator bool();2193 }; // suppress2194 void foo(int I) {2195 while (float(I)) {2196 $convert_primitive[[}]]2197 2198 while (S(I)) {2199 $convert_class[[}]]2200 2201 while (S(I, I)) {2202 $construct_class[[}]]2203 } // suppress2204 )cpp",2205 ExpectedHint{" // while float", "convert_primitive"},2206 ExpectedHint{" // while S", "convert_class"},2207 ExpectedHint{" // while S", "construct_class"});2208}2209 2210TEST(BlockEndHints, PrintOperators) {2211 std::string AnnotatedCode = R"cpp(2212 void foo(Integer I) {2213 while(++I){2214 $preinc[[}]]2215 2216 while(I++){2217 $postinc[[}]]2218 2219 while(+(I + I)){2220 $unary_complex[[}]]2221 2222 while(I < 0){2223 $compare[[}]]2224 2225 while((I + I) < I){2226 $lhs_complex[[}]]2227 2228 while(I < (I + I)){2229 $rhs_complex[[}]]2230 2231 while((I + I) < (I + I)){2232 $binary_complex[[}]]2233 } // suppress2234 )cpp";2235 2236 // We can't store shared expectations in a vector, assertHints uses varargs.2237 auto AssertExpectedHints = [&](llvm::StringRef Code) {2238 assertBlockEndHints(Code, ExpectedHint{" // while ++I", "preinc"},2239 ExpectedHint{" // while I++", "postinc"},2240 ExpectedHint{" // while", "unary_complex"},2241 ExpectedHint{" // while I < 0", "compare"},2242 ExpectedHint{" // while ... < I", "lhs_complex"},2243 ExpectedHint{" // while I < ...", "rhs_complex"},2244 ExpectedHint{" // while", "binary_complex"});2245 };2246 2247 // First with built-in operators.2248 AssertExpectedHints("using Integer = int;" + AnnotatedCode);2249 // And now with overloading!2250 AssertExpectedHints(R"cpp(2251 struct Integer {2252 explicit operator bool();2253 Integer operator++();2254 Integer operator++(int);2255 Integer operator+(Integer);2256 Integer operator+();2257 bool operator<(Integer);2258 bool operator<(int);2259 }; // suppress2260 )cpp" + AnnotatedCode);2261}2262 2263TEST(BlockEndHints, TrailingSemicolon) {2264 assertBlockEndHints(R"cpp(2265 // The hint is placed after the trailing ';'2266 struct S1 {2267 $S1[[} ;]] 2268 2269 // The hint is always placed in the same line with the closing '}'.2270 // So in this case where ';' is missing, it is attached to '}'.2271 struct S2 {2272 $S2[[}]]2273 2274 ;2275 2276 // No hint because only one trailing ';' is allowed2277 struct S3 {2278 };;2279 2280 // No hint because trailing ';' is only allowed for class/struct/union/enum2281 void foo() {2282 };2283 2284 // Rare case, but yes we'll have a hint here.2285 struct {2286 int x;2287 $anon[[}]]2288 2289 s2;2290 )cpp",2291 ExpectedHint{" // struct S1", "S1"},2292 ExpectedHint{" // struct S2", "S2"},2293 ExpectedHint{" // struct", "anon"});2294}2295 2296TEST(BlockEndHints, TrailingText) {2297 assertBlockEndHints(R"cpp(2298 struct S1 {2299 $S1[[} ;]]2300 2301 // No hint for S2 because of the trailing comment2302 struct S2 {2303 }; /* Put anything here */2304 2305 struct S3 {2306 // No hint for S4 because of the trailing source code2307 struct S4 {2308 };$S3[[};]]2309 2310 // No hint for ns because of the trailing comment2311 namespace ns {2312 } // namespace ns2313 )cpp",2314 ExpectedHint{" // struct S1", "S1"},2315 ExpectedHint{" // struct S3", "S3"});2316}2317 2318TEST(BlockEndHints, Macro) {2319 assertBlockEndHints(R"cpp(2320 #define DECL_STRUCT(NAME) struct NAME {2321 #define RBRACE }2322 2323 DECL_STRUCT(S1)2324 $S1[[};]]2325 2326 // No hint because we require a '}'2327 DECL_STRUCT(S2)2328 RBRACE;2329 )cpp",2330 ExpectedHint{" // struct S1", "S1"});2331}2332 2333TEST(BlockEndHints, PointerToMemberFunction) {2334 // Do not crash trying to summarize `a->*p`.2335 assertBlockEndHints(R"cpp(2336 class A {};2337 using Predicate = bool(A::*)();2338 void foo(A* a, Predicate p) {2339 if ((a->*p)()) {2340 $ptrmem[[}]]2341 } // suppress2342 )cpp",2343 ExpectedHint{" // if ()", "ptrmem"});2344}2345 2346TEST(BlockEndHints, MinLineLimit) {2347 InlayHintOptions Opts;2348 Opts.HintMinLineLimit = 10;2349 2350 // namespace ns below is exactly 10 lines2351 assertBlockEndHintsWithOpts(2352 R"cpp(2353 namespace ns {2354 int Var;2355 int func1();2356 int func2(int, int);2357 struct S {2358 int Field;2359 int method1() const;2360 int method2(int, int) const;2361 };2362 $namespace[[}]]2363 void foo() {2364 int int_a {};2365 while (ns::Var) {2366 }2367 2368 while (ns::func1()) {2369 }2370 2371 while (ns::func2(int_a, int_a)) {2372 }2373 2374 while (ns::S{}.Field) {2375 }2376 2377 while (ns::S{}.method1()) {2378 }2379 2380 while (ns::S{}.method2(int_a, int_a)) {2381 }2382 $foo[[}]]2383 )cpp",2384 Opts, ExpectedHint{" // namespace ns", "namespace"},2385 ExpectedHint{" // foo", "foo"});2386}2387 2388// FIXME: Low-hanging fruit where we could omit a type hint:2389// - auto x = TypeName(...);2390// - auto x = (TypeName) (...);2391// - auto x = static_cast<TypeName>(...); // and other built-in casts2392 2393// Annoyances for which a heuristic is not obvious:2394// - auto x = llvm::dyn_cast<LongTypeName>(y); // and similar2395// - stdlib algos return unwieldy __normal_iterator<X*, ...> type2396// (For this one, perhaps we should omit type hints that start2397// with a double underscore.)2398 2399} // namespace2400} // namespace clangd2401} // namespace clang2402