1cbc9c4eaSNathan Ridge //===-- InlayHintTests.cpp -------------------------------*- C++ -*-------===// 2cbc9c4eaSNathan Ridge // 3cbc9c4eaSNathan Ridge // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4cbc9c4eaSNathan Ridge // See https://llvm.org/LICENSE.txt for license information. 5cbc9c4eaSNathan Ridge // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6cbc9c4eaSNathan Ridge // 7cbc9c4eaSNathan Ridge //===----------------------------------------------------------------------===// 8cbc9c4eaSNathan Ridge #include "Annotations.h" 916fd5c27SSam McCall #include "Config.h" 10cbc9c4eaSNathan Ridge #include "InlayHints.h" 11cbc9c4eaSNathan Ridge #include "Protocol.h" 12cbc9c4eaSNathan Ridge #include "TestTU.h" 1347d9d55cSNathan Ridge #include "TestWorkspace.h" 14cbc9c4eaSNathan Ridge #include "XRefs.h" 1516fd5c27SSam McCall #include "support/Context.h" 163137ca80SKadir Cetinkaya #include "llvm/ADT/StringRef.h" 17ce944327SSam McCall #include "llvm/Support/ScopedPrinter.h" 182eb16991STor Shepherd #include "llvm/Support/raw_ostream.h" 19cbc9c4eaSNathan Ridge #include "gmock/gmock.h" 20cbc9c4eaSNathan Ridge #include "gtest/gtest.h" 212eb16991STor Shepherd #include <optional> 22ee032bccSSam McCall #include <string> 232eb16991STor Shepherd #include <utility> 24ee032bccSSam McCall #include <vector> 25cbc9c4eaSNathan Ridge 26cbc9c4eaSNathan Ridge namespace clang { 27cbc9c4eaSNathan Ridge namespace clangd { 280be2657cSNathan Ridge 29ce944327SSam McCall llvm::raw_ostream &operator<<(llvm::raw_ostream &Stream, 30ce944327SSam McCall const InlayHint &Hint) { 31c6a65e4bSYounan Zhang return Stream << Hint.joinLabels() << "@" << Hint.range; 320be2657cSNathan Ridge } 330be2657cSNathan Ridge 34cbc9c4eaSNathan Ridge namespace { 35cbc9c4eaSNathan Ridge 367c19fdd5SSam McCall using ::testing::ElementsAre; 3716fd5c27SSam McCall using ::testing::IsEmpty; 38cbc9c4eaSNathan Ridge 390be2657cSNathan Ridge std::vector<InlayHint> hintsOfKind(ParsedAST &AST, InlayHintKind Kind) { 40cbc9c4eaSNathan Ridge std::vector<InlayHint> Result; 41649ef338SKazu Hirata for (auto &Hint : inlayHints(AST, /*RestrictRange=*/std::nullopt)) { 420be2657cSNathan Ridge if (Hint.kind == Kind) 43cbc9c4eaSNathan Ridge Result.push_back(Hint); 44cbc9c4eaSNathan Ridge } 45cbc9c4eaSNathan Ridge return Result; 46cbc9c4eaSNathan Ridge } 47cbc9c4eaSNathan Ridge 487c19fdd5SSam McCall enum HintSide { Left, Right }; 497c19fdd5SSam McCall 50cbc9c4eaSNathan Ridge struct ExpectedHint { 51cbc9c4eaSNathan Ridge std::string Label; 52cbc9c4eaSNathan Ridge std::string RangeName; 537c19fdd5SSam McCall HintSide Side = Left; 540be2657cSNathan Ridge 55ce944327SSam McCall friend llvm::raw_ostream &operator<<(llvm::raw_ostream &Stream, 560be2657cSNathan Ridge const ExpectedHint &Hint) { 57ce944327SSam McCall return Stream << Hint.Label << "@$" << Hint.RangeName; 580be2657cSNathan Ridge } 59cbc9c4eaSNathan Ridge }; 60cbc9c4eaSNathan Ridge 61ce944327SSam McCall MATCHER_P2(HintMatcher, Expected, Code, llvm::to_string(Expected)) { 623137ca80SKadir Cetinkaya llvm::StringRef ExpectedView(Expected.Label); 63c6a65e4bSYounan Zhang std::string ResultLabel = arg.joinLabels(); 64c6a65e4bSYounan Zhang if (ResultLabel != ExpectedView.trim(" ") || 65d5953e3eSKazu Hirata arg.paddingLeft != ExpectedView.starts_with(" ") || 66d5953e3eSKazu Hirata arg.paddingRight != ExpectedView.ends_with(" ")) { 67c6a65e4bSYounan Zhang *result_listener << "label is '" << ResultLabel << "'"; 68ce944327SSam McCall return false; 69ce944327SSam McCall } 70ce944327SSam McCall if (arg.range != Code.range(Expected.RangeName)) { 712da5c578SSam McCall *result_listener << "range is " << llvm::to_string(arg.range) << " but $" 72ce944327SSam McCall << Expected.RangeName << " is " 73ce944327SSam McCall << llvm::to_string(Code.range(Expected.RangeName)); 74ce944327SSam McCall return false; 75ce944327SSam McCall } 76ce944327SSam McCall return true; 77cbc9c4eaSNathan Ridge } 78cbc9c4eaSNathan Ridge 79c6a65e4bSYounan Zhang MATCHER_P(labelIs, Label, "") { return arg.joinLabels() == Label; } 807c19fdd5SSam McCall 8116fd5c27SSam McCall Config noHintsConfig() { 8216fd5c27SSam McCall Config C; 8316fd5c27SSam McCall C.InlayHints.Parameters = false; 8416fd5c27SSam McCall C.InlayHints.DeducedTypes = false; 85ce944327SSam McCall C.InlayHints.Designators = false; 869e6a342fSdaiyousei-qz C.InlayHints.BlockEnd = false; 872eb16991STor Shepherd C.InlayHints.DefaultArguments = false; 8816fd5c27SSam McCall return C; 8916fd5c27SSam McCall } 9016fd5c27SSam McCall 91cbc9c4eaSNathan Ridge template <typename... ExpectedHints> 92b1193c13SYounan Zhang void assertHintsWithHeader(InlayHintKind Kind, llvm::StringRef AnnotatedSource, 93b1193c13SYounan Zhang llvm::StringRef HeaderContent, 94cbc9c4eaSNathan Ridge ExpectedHints... Expected) { 95cbc9c4eaSNathan Ridge Annotations Source(AnnotatedSource); 96cbc9c4eaSNathan Ridge TestTU TU = TestTU::withCode(Source.code()); 97cbd6ac61SYounan Zhang TU.ExtraArgs.push_back("-std=c++23"); 98b1193c13SYounan Zhang TU.HeaderCode = HeaderContent; 99cbc9c4eaSNathan Ridge auto AST = TU.build(); 100cbc9c4eaSNathan Ridge 1010be2657cSNathan Ridge EXPECT_THAT(hintsOfKind(AST, Kind), 1027c19fdd5SSam McCall ElementsAre(HintMatcher(Expected, Source)...)); 10316fd5c27SSam McCall // Sneak in a cross-cutting check that hints are disabled by config. 10416fd5c27SSam McCall // We'll hit an assertion failure if addInlayHint still gets called. 10516fd5c27SSam McCall WithContextValue WithCfg(Config::Key, noHintsConfig()); 106649ef338SKazu Hirata EXPECT_THAT(inlayHints(AST, std::nullopt), IsEmpty()); 107cbc9c4eaSNathan Ridge } 108cbc9c4eaSNathan Ridge 109b1193c13SYounan Zhang template <typename... ExpectedHints> 110b1193c13SYounan Zhang void assertHints(InlayHintKind Kind, llvm::StringRef AnnotatedSource, 111b1193c13SYounan Zhang ExpectedHints... Expected) { 112b1193c13SYounan Zhang return assertHintsWithHeader(Kind, AnnotatedSource, "", 113b1193c13SYounan Zhang std::move(Expected)...); 114b1193c13SYounan Zhang } 115b1193c13SYounan Zhang 1167c19fdd5SSam McCall // Hack to allow expression-statements operating on parameter packs in C++14. 1177c19fdd5SSam McCall template <typename... T> void ignore(T &&...) {} 1187c19fdd5SSam McCall 1190be2657cSNathan Ridge template <typename... ExpectedHints> 1200be2657cSNathan Ridge void assertParameterHints(llvm::StringRef AnnotatedSource, 1210be2657cSNathan Ridge ExpectedHints... Expected) { 1227c19fdd5SSam McCall ignore(Expected.Side = Left...); 1233137ca80SKadir Cetinkaya assertHints(InlayHintKind::Parameter, AnnotatedSource, Expected...); 1240be2657cSNathan Ridge } 1250be2657cSNathan Ridge 1260be2657cSNathan Ridge template <typename... ExpectedHints> 1270be2657cSNathan Ridge void assertTypeHints(llvm::StringRef AnnotatedSource, 1280be2657cSNathan Ridge ExpectedHints... Expected) { 1297c19fdd5SSam McCall ignore(Expected.Side = Right...); 1303137ca80SKadir Cetinkaya assertHints(InlayHintKind::Type, AnnotatedSource, Expected...); 1310be2657cSNathan Ridge } 1320be2657cSNathan Ridge 133ce944327SSam McCall template <typename... ExpectedHints> 134ce944327SSam McCall void assertDesignatorHints(llvm::StringRef AnnotatedSource, 135ce944327SSam McCall ExpectedHints... Expected) { 136ce944327SSam McCall Config Cfg; 137ce944327SSam McCall Cfg.InlayHints.Designators = true; 138ce944327SSam McCall WithContextValue WithCfg(Config::Key, std::move(Cfg)); 1393137ca80SKadir Cetinkaya assertHints(InlayHintKind::Designator, AnnotatedSource, Expected...); 140ce944327SSam McCall } 141ce944327SSam McCall 1429e6a342fSdaiyousei-qz template <typename... ExpectedHints> 1439e6a342fSdaiyousei-qz void assertBlockEndHints(llvm::StringRef AnnotatedSource, 1449e6a342fSdaiyousei-qz ExpectedHints... Expected) { 1459e6a342fSdaiyousei-qz Config Cfg; 1469e6a342fSdaiyousei-qz Cfg.InlayHints.BlockEnd = true; 1479e6a342fSdaiyousei-qz WithContextValue WithCfg(Config::Key, std::move(Cfg)); 1489e6a342fSdaiyousei-qz assertHints(InlayHintKind::BlockEnd, AnnotatedSource, Expected...); 1499e6a342fSdaiyousei-qz } 1509e6a342fSdaiyousei-qz 151cbc9c4eaSNathan Ridge TEST(ParameterHints, Smoke) { 152cbc9c4eaSNathan Ridge assertParameterHints(R"cpp( 153cbc9c4eaSNathan Ridge void foo(int param); 154cbc9c4eaSNathan Ridge void bar() { 155cbc9c4eaSNathan Ridge foo($param[[42]]); 156cbc9c4eaSNathan Ridge } 157cbc9c4eaSNathan Ridge )cpp", 158cbc9c4eaSNathan Ridge ExpectedHint{"param: ", "param"}); 159cbc9c4eaSNathan Ridge } 160cbc9c4eaSNathan Ridge 161cbc9c4eaSNathan Ridge TEST(ParameterHints, NoName) { 162cbc9c4eaSNathan Ridge // No hint for anonymous parameter. 163cbc9c4eaSNathan Ridge assertParameterHints(R"cpp( 164cbc9c4eaSNathan Ridge void foo(int); 165cbc9c4eaSNathan Ridge void bar() { 166cbc9c4eaSNathan Ridge foo(42); 167cbc9c4eaSNathan Ridge } 168cbc9c4eaSNathan Ridge )cpp"); 169cbc9c4eaSNathan Ridge } 170cbc9c4eaSNathan Ridge 171043e9650STobias Ribizel TEST(ParameterHints, NoNameConstReference) { 172043e9650STobias Ribizel // No hint for anonymous const l-value ref parameter. 173043e9650STobias Ribizel assertParameterHints(R"cpp( 174043e9650STobias Ribizel void foo(const int&); 175043e9650STobias Ribizel void bar() { 176043e9650STobias Ribizel foo(42); 177043e9650STobias Ribizel } 178043e9650STobias Ribizel )cpp"); 179043e9650STobias Ribizel } 180043e9650STobias Ribizel 181043e9650STobias Ribizel TEST(ParameterHints, NoNameReference) { 182043e9650STobias Ribizel // Reference hint for anonymous l-value ref parameter. 183043e9650STobias Ribizel assertParameterHints(R"cpp( 184043e9650STobias Ribizel void foo(int&); 185043e9650STobias Ribizel void bar() { 186043e9650STobias Ribizel int i; 187043e9650STobias Ribizel foo($param[[i]]); 188043e9650STobias Ribizel } 189043e9650STobias Ribizel )cpp", 190043e9650STobias Ribizel ExpectedHint{"&: ", "param"}); 191043e9650STobias Ribizel } 192043e9650STobias Ribizel 193043e9650STobias Ribizel TEST(ParameterHints, NoNameRValueReference) { 194043e9650STobias Ribizel // No reference hint for anonymous r-value ref parameter. 195043e9650STobias Ribizel assertParameterHints(R"cpp( 196043e9650STobias Ribizel void foo(int&&); 197043e9650STobias Ribizel void bar() { 198043e9650STobias Ribizel foo(42); 199043e9650STobias Ribizel } 200043e9650STobias Ribizel )cpp"); 201043e9650STobias Ribizel } 202043e9650STobias Ribizel 203a638648fSTobias Ribizel TEST(ParameterHints, NoNameVariadicDeclaration) { 204a638648fSTobias Ribizel // No hint for anonymous variadic parameter 205a638648fSTobias Ribizel assertParameterHints(R"cpp( 206a638648fSTobias Ribizel template <typename... Args> 207a638648fSTobias Ribizel void foo(Args&& ...); 208a638648fSTobias Ribizel void bar() { 209a638648fSTobias Ribizel foo(42); 210a638648fSTobias Ribizel } 211a638648fSTobias Ribizel )cpp"); 212a638648fSTobias Ribizel } 213a638648fSTobias Ribizel 214a638648fSTobias Ribizel TEST(ParameterHints, NoNameVariadicForwarded) { 215a638648fSTobias Ribizel // No hint for anonymous variadic parameter 216a638648fSTobias Ribizel // This prototype of std::forward is sufficient for clang to recognize it 217a638648fSTobias Ribizel assertParameterHints(R"cpp( 218a638648fSTobias Ribizel namespace std { template <typename T> T&& forward(T&); } 219a638648fSTobias Ribizel void foo(int); 220a638648fSTobias Ribizel template <typename... Args> 221a638648fSTobias Ribizel void bar(Args&&... args) { return foo(std::forward<Args>(args)...); } 222a638648fSTobias Ribizel void baz() { 223a638648fSTobias Ribizel bar(42); 224a638648fSTobias Ribizel } 225a638648fSTobias Ribizel )cpp"); 226a638648fSTobias Ribizel } 227a638648fSTobias Ribizel 228a638648fSTobias Ribizel TEST(ParameterHints, NoNameVariadicPlain) { 229a638648fSTobias Ribizel // No hint for anonymous variadic parameter 230a638648fSTobias Ribizel assertParameterHints(R"cpp( 231a638648fSTobias Ribizel void foo(int); 232a638648fSTobias Ribizel template <typename... Args> 233a638648fSTobias Ribizel void bar(Args&&... args) { return foo(args...); } 234a638648fSTobias Ribizel void baz() { 235a638648fSTobias Ribizel bar(42); 236a638648fSTobias Ribizel } 237a638648fSTobias Ribizel )cpp"); 238a638648fSTobias Ribizel } 239a638648fSTobias Ribizel 240cbc9c4eaSNathan Ridge TEST(ParameterHints, NameInDefinition) { 241cbc9c4eaSNathan Ridge // Parameter name picked up from definition if necessary. 242cbc9c4eaSNathan Ridge assertParameterHints(R"cpp( 243cbc9c4eaSNathan Ridge void foo(int); 244cbc9c4eaSNathan Ridge void bar() { 245cbc9c4eaSNathan Ridge foo($param[[42]]); 246cbc9c4eaSNathan Ridge } 247cbc9c4eaSNathan Ridge void foo(int param) {}; 248cbc9c4eaSNathan Ridge )cpp", 249cbc9c4eaSNathan Ridge ExpectedHint{"param: ", "param"}); 250cbc9c4eaSNathan Ridge } 251cbc9c4eaSNathan Ridge 252a638648fSTobias Ribizel TEST(ParameterHints, NamePartiallyInDefinition) { 253a638648fSTobias Ribizel // Parameter name picked up from definition if necessary. 254a638648fSTobias Ribizel assertParameterHints(R"cpp( 255a638648fSTobias Ribizel void foo(int, int b); 256a638648fSTobias Ribizel void bar() { 257a638648fSTobias Ribizel foo($param1[[42]], $param2[[42]]); 258a638648fSTobias Ribizel } 259a638648fSTobias Ribizel void foo(int a, int) {}; 260a638648fSTobias Ribizel )cpp", 261a638648fSTobias Ribizel ExpectedHint{"a: ", "param1"}, 262a638648fSTobias Ribizel ExpectedHint{"b: ", "param2"}); 263a638648fSTobias Ribizel } 264a638648fSTobias Ribizel 265a638648fSTobias Ribizel TEST(ParameterHints, NameInDefinitionVariadic) { 266a638648fSTobias Ribizel // Parameter name picked up from definition in a resolved forwarded parameter. 267a638648fSTobias Ribizel assertParameterHints(R"cpp( 268a638648fSTobias Ribizel void foo(int, int); 269a638648fSTobias Ribizel template <typename... Args> 270a638648fSTobias Ribizel void bar(Args... args) { 271a638648fSTobias Ribizel foo(args...); 272a638648fSTobias Ribizel } 273a638648fSTobias Ribizel void baz() { 274a638648fSTobias Ribizel bar($param1[[42]], $param2[[42]]); 275a638648fSTobias Ribizel } 276a638648fSTobias Ribizel void foo(int a, int b) {}; 277a638648fSTobias Ribizel )cpp", 278a638648fSTobias Ribizel ExpectedHint{"a: ", "param1"}, 279a638648fSTobias Ribizel ExpectedHint{"b: ", "param2"}); 280a638648fSTobias Ribizel } 281a638648fSTobias Ribizel 282cbc9c4eaSNathan Ridge TEST(ParameterHints, NameMismatch) { 283cbc9c4eaSNathan Ridge // Prefer name from declaration. 284cbc9c4eaSNathan Ridge assertParameterHints(R"cpp( 285cbc9c4eaSNathan Ridge void foo(int good); 286cbc9c4eaSNathan Ridge void bar() { 287cbc9c4eaSNathan Ridge foo($good[[42]]); 288cbc9c4eaSNathan Ridge } 289cbc9c4eaSNathan Ridge void foo(int bad) {}; 290cbc9c4eaSNathan Ridge )cpp", 291cbc9c4eaSNathan Ridge ExpectedHint{"good: ", "good"}); 292cbc9c4eaSNathan Ridge } 293cbc9c4eaSNathan Ridge 294043e9650STobias Ribizel TEST(ParameterHints, NameConstReference) { 295043e9650STobias Ribizel // Only name hint for const l-value ref parameter. 296043e9650STobias Ribizel assertParameterHints(R"cpp( 297043e9650STobias Ribizel void foo(const int& param); 298043e9650STobias Ribizel void bar() { 299043e9650STobias Ribizel foo($param[[42]]); 300043e9650STobias Ribizel } 301043e9650STobias Ribizel )cpp", 302043e9650STobias Ribizel ExpectedHint{"param: ", "param"}); 303043e9650STobias Ribizel } 304043e9650STobias Ribizel 305043e9650STobias Ribizel TEST(ParameterHints, NameTypeAliasConstReference) { 306043e9650STobias Ribizel // Only name hint for const l-value ref parameter via type alias. 307043e9650STobias Ribizel assertParameterHints(R"cpp( 308043e9650STobias Ribizel using alias = const int&; 309043e9650STobias Ribizel void foo(alias param); 310043e9650STobias Ribizel void bar() { 311043e9650STobias Ribizel int i; 312043e9650STobias Ribizel foo($param[[i]]); 313043e9650STobias Ribizel } 314043e9650STobias Ribizel )cpp", 315043e9650STobias Ribizel ExpectedHint{"param: ", "param"}); 316043e9650STobias Ribizel } 317043e9650STobias Ribizel 318043e9650STobias Ribizel TEST(ParameterHints, NameReference) { 319043e9650STobias Ribizel // Reference and name hint for l-value ref parameter. 320043e9650STobias Ribizel assertParameterHints(R"cpp( 321043e9650STobias Ribizel void foo(int& param); 322043e9650STobias Ribizel void bar() { 323043e9650STobias Ribizel int i; 324043e9650STobias Ribizel foo($param[[i]]); 325043e9650STobias Ribizel } 326043e9650STobias Ribizel )cpp", 327043e9650STobias Ribizel ExpectedHint{"¶m: ", "param"}); 328043e9650STobias Ribizel } 329043e9650STobias Ribizel 330043e9650STobias Ribizel TEST(ParameterHints, NameTypeAliasReference) { 331043e9650STobias Ribizel // Reference and name hint for l-value ref parameter via type alias. 332043e9650STobias Ribizel assertParameterHints(R"cpp( 333043e9650STobias Ribizel using alias = int&; 334043e9650STobias Ribizel void foo(alias param); 335043e9650STobias Ribizel void bar() { 336043e9650STobias Ribizel int i; 337043e9650STobias Ribizel foo($param[[i]]); 338043e9650STobias Ribizel } 339043e9650STobias Ribizel )cpp", 340043e9650STobias Ribizel ExpectedHint{"¶m: ", "param"}); 341043e9650STobias Ribizel } 342043e9650STobias Ribizel 343043e9650STobias Ribizel TEST(ParameterHints, NameRValueReference) { 344043e9650STobias Ribizel // Only name hint for r-value ref parameter. 345043e9650STobias Ribizel assertParameterHints(R"cpp( 346043e9650STobias Ribizel void foo(int&& param); 347043e9650STobias Ribizel void bar() { 348043e9650STobias Ribizel foo($param[[42]]); 349043e9650STobias Ribizel } 350043e9650STobias Ribizel )cpp", 351043e9650STobias Ribizel ExpectedHint{"param: ", "param"}); 352043e9650STobias Ribizel } 353043e9650STobias Ribizel 354a638648fSTobias Ribizel TEST(ParameterHints, VariadicForwardedConstructor) { 355a638648fSTobias Ribizel // Name hint for variadic parameter using std::forward in a constructor call 356a638648fSTobias Ribizel // This prototype of std::forward is sufficient for clang to recognize it 357a638648fSTobias Ribizel assertParameterHints(R"cpp( 358a638648fSTobias Ribizel namespace std { template <typename T> T&& forward(T&); } 359a638648fSTobias Ribizel struct S { S(int a); }; 360a638648fSTobias Ribizel template <typename T, typename... Args> 361a638648fSTobias Ribizel T bar(Args&&... args) { return T{std::forward<Args>(args)...}; } 362a638648fSTobias Ribizel void baz() { 363a638648fSTobias Ribizel int b; 364a638648fSTobias Ribizel bar<S>($param[[b]]); 365a638648fSTobias Ribizel } 366a638648fSTobias Ribizel )cpp", 367a638648fSTobias Ribizel ExpectedHint{"a: ", "param"}); 368a638648fSTobias Ribizel } 369a638648fSTobias Ribizel 370a638648fSTobias Ribizel TEST(ParameterHints, VariadicPlainConstructor) { 371a638648fSTobias Ribizel // Name hint for variadic parameter in a constructor call 372a638648fSTobias Ribizel assertParameterHints(R"cpp( 373a638648fSTobias Ribizel struct S { S(int a); }; 374a638648fSTobias Ribizel template <typename T, typename... Args> 375a638648fSTobias Ribizel T bar(Args&&... args) { return T{args...}; } 376a638648fSTobias Ribizel void baz() { 377a638648fSTobias Ribizel int b; 378a638648fSTobias Ribizel bar<S>($param[[b]]); 379a638648fSTobias Ribizel } 380a638648fSTobias Ribizel )cpp", 381a638648fSTobias Ribizel ExpectedHint{"a: ", "param"}); 382a638648fSTobias Ribizel } 383a638648fSTobias Ribizel 384a638648fSTobias Ribizel TEST(ParameterHints, VariadicForwardedNewConstructor) { 385a638648fSTobias Ribizel // Name hint for variadic parameter using std::forward in a new expression 386a638648fSTobias Ribizel // This prototype of std::forward is sufficient for clang to recognize it 387a638648fSTobias Ribizel assertParameterHints(R"cpp( 388a638648fSTobias Ribizel namespace std { template <typename T> T&& forward(T&); } 389a638648fSTobias Ribizel struct S { S(int a); }; 390a638648fSTobias Ribizel template <typename T, typename... Args> 391a638648fSTobias Ribizel T* bar(Args&&... args) { return new T{std::forward<Args>(args)...}; } 392a638648fSTobias Ribizel void baz() { 393a638648fSTobias Ribizel int b; 394a638648fSTobias Ribizel bar<S>($param[[b]]); 395a638648fSTobias Ribizel } 396a638648fSTobias Ribizel )cpp", 397a638648fSTobias Ribizel ExpectedHint{"a: ", "param"}); 398a638648fSTobias Ribizel } 399a638648fSTobias Ribizel 400a638648fSTobias Ribizel TEST(ParameterHints, VariadicPlainNewConstructor) { 401a638648fSTobias Ribizel // Name hint for variadic parameter in a new expression 402a638648fSTobias Ribizel assertParameterHints(R"cpp( 403a638648fSTobias Ribizel struct S { S(int a); }; 404a638648fSTobias Ribizel template <typename T, typename... Args> 405a638648fSTobias Ribizel T* bar(Args&&... args) { return new T{args...}; } 406a638648fSTobias Ribizel void baz() { 407a638648fSTobias Ribizel int b; 408a638648fSTobias Ribizel bar<S>($param[[b]]); 409a638648fSTobias Ribizel } 410a638648fSTobias Ribizel )cpp", 411a638648fSTobias Ribizel ExpectedHint{"a: ", "param"}); 412a638648fSTobias Ribizel } 413a638648fSTobias Ribizel 414a638648fSTobias Ribizel TEST(ParameterHints, VariadicForwarded) { 415a638648fSTobias Ribizel // Name for variadic parameter using std::forward 416a638648fSTobias Ribizel // This prototype of std::forward is sufficient for clang to recognize it 417a638648fSTobias Ribizel assertParameterHints(R"cpp( 418a638648fSTobias Ribizel namespace std { template <typename T> T&& forward(T&); } 419a638648fSTobias Ribizel void foo(int a); 420a638648fSTobias Ribizel template <typename... Args> 421a638648fSTobias Ribizel void bar(Args&&... args) { return foo(std::forward<Args>(args)...); } 422a638648fSTobias Ribizel void baz() { 423a638648fSTobias Ribizel int b; 424a638648fSTobias Ribizel bar($param[[b]]); 425a638648fSTobias Ribizel } 426a638648fSTobias Ribizel )cpp", 427a638648fSTobias Ribizel ExpectedHint{"a: ", "param"}); 428a638648fSTobias Ribizel } 429a638648fSTobias Ribizel 430a638648fSTobias Ribizel TEST(ParameterHints, VariadicPlain) { 431a638648fSTobias Ribizel // Name hint for variadic parameter 432a638648fSTobias Ribizel assertParameterHints(R"cpp( 433a638648fSTobias Ribizel void foo(int a); 434a638648fSTobias Ribizel template <typename... Args> 435a638648fSTobias Ribizel void bar(Args&&... args) { return foo(args...); } 436a638648fSTobias Ribizel void baz() { 437a638648fSTobias Ribizel bar($param[[42]]); 438a638648fSTobias Ribizel } 439a638648fSTobias Ribizel )cpp", 440a638648fSTobias Ribizel ExpectedHint{"a: ", "param"}); 441a638648fSTobias Ribizel } 442a638648fSTobias Ribizel 443a638648fSTobias Ribizel TEST(ParameterHints, VariadicPlainWithPackFirst) { 444a638648fSTobias Ribizel // Name hint for variadic parameter when the parameter pack is not the last 445a638648fSTobias Ribizel // template parameter 446a638648fSTobias Ribizel assertParameterHints(R"cpp( 447a638648fSTobias Ribizel void foo(int a); 448a638648fSTobias Ribizel template <typename... Args, typename Arg> 449a638648fSTobias Ribizel void bar(Arg, Args&&... args) { return foo(args...); } 450a638648fSTobias Ribizel void baz() { 451a638648fSTobias Ribizel bar(1, $param[[42]]); 452a638648fSTobias Ribizel } 453a638648fSTobias Ribizel )cpp", 454a638648fSTobias Ribizel ExpectedHint{"a: ", "param"}); 455a638648fSTobias Ribizel } 456a638648fSTobias Ribizel 457a638648fSTobias Ribizel TEST(ParameterHints, VariadicSplitTwolevel) { 458a638648fSTobias Ribizel // Name for variadic parameter that involves both head and tail parameters to 459a638648fSTobias Ribizel // deal with. 460a638648fSTobias Ribizel // This prototype of std::forward is sufficient for clang to recognize it 461a638648fSTobias Ribizel assertParameterHints(R"cpp( 462a638648fSTobias Ribizel namespace std { template <typename T> T&& forward(T&); } 463a638648fSTobias Ribizel void baz(int, int b, double); 464a638648fSTobias Ribizel template <typename... Args> 465a638648fSTobias Ribizel void foo(int a, Args&&... args) { 466a638648fSTobias Ribizel return baz(1, std::forward<Args>(args)..., 1.0); 467a638648fSTobias Ribizel } 468a638648fSTobias Ribizel template <typename... Args> 469a638648fSTobias Ribizel void bar(Args&&... args) { return foo(std::forward<Args>(args)...); } 470a638648fSTobias Ribizel void bazz() { 471a638648fSTobias Ribizel bar($param1[[32]], $param2[[42]]); 472a638648fSTobias Ribizel } 473a638648fSTobias Ribizel )cpp", 474a638648fSTobias Ribizel ExpectedHint{"a: ", "param1"}, 475a638648fSTobias Ribizel ExpectedHint{"b: ", "param2"}); 476a638648fSTobias Ribizel } 477a638648fSTobias Ribizel 478a638648fSTobias Ribizel TEST(ParameterHints, VariadicNameFromSpecialization) { 479a638648fSTobias Ribizel // We don't try to resolve forwarding parameters if the function call uses a 480a638648fSTobias Ribizel // specialization. 481a638648fSTobias Ribizel assertParameterHints(R"cpp( 482a638648fSTobias Ribizel void foo(int a); 483a638648fSTobias Ribizel template <typename... Args> 484a638648fSTobias Ribizel void bar(Args... args) { 485a638648fSTobias Ribizel foo(args...); 486a638648fSTobias Ribizel } 487a638648fSTobias Ribizel template <> 488a638648fSTobias Ribizel void bar<int>(int b); 489a638648fSTobias Ribizel void baz() { 490a638648fSTobias Ribizel bar($param[[42]]); 491a638648fSTobias Ribizel } 492a638648fSTobias Ribizel )cpp", 493a638648fSTobias Ribizel ExpectedHint{"b: ", "param"}); 494a638648fSTobias Ribizel } 495a638648fSTobias Ribizel 496a638648fSTobias Ribizel TEST(ParameterHints, VariadicNameFromSpecializationRecursive) { 497a638648fSTobias Ribizel // We don't try to resolve forwarding parameters inside a forwarding function 498a638648fSTobias Ribizel // call if that function call uses a specialization. 499a638648fSTobias Ribizel assertParameterHints(R"cpp( 500a638648fSTobias Ribizel void foo2(int a); 501a638648fSTobias Ribizel template <typename... Args> 502a638648fSTobias Ribizel void foo(Args... args) { 503a638648fSTobias Ribizel foo2(args...); 504a638648fSTobias Ribizel } 505a638648fSTobias Ribizel template <typename... Args> 506a638648fSTobias Ribizel void bar(Args... args) { 507a638648fSTobias Ribizel foo(args...); 508a638648fSTobias Ribizel } 509a638648fSTobias Ribizel template <> 510a638648fSTobias Ribizel void foo<int>(int b); 511a638648fSTobias Ribizel void baz() { 512a638648fSTobias Ribizel bar($param[[42]]); 513a638648fSTobias Ribizel } 514a638648fSTobias Ribizel )cpp", 515a638648fSTobias Ribizel ExpectedHint{"b: ", "param"}); 516a638648fSTobias Ribizel } 517a638648fSTobias Ribizel 518a638648fSTobias Ribizel TEST(ParameterHints, VariadicOverloaded) { 519a638648fSTobias Ribizel // Name for variadic parameter for an overloaded function with unique number 520a638648fSTobias Ribizel // of parameters. 521a638648fSTobias Ribizel // This prototype of std::forward is sufficient for clang to recognize it 522a638648fSTobias Ribizel assertParameterHints( 523a638648fSTobias Ribizel R"cpp( 524a638648fSTobias Ribizel namespace std { template <typename T> T&& forward(T&); } 525a638648fSTobias Ribizel void baz(int b, int c); 526a638648fSTobias Ribizel void baz(int bb, int cc, int dd); 527a638648fSTobias Ribizel template <typename... Args> 528a638648fSTobias Ribizel void foo(int a, Args&&... args) { 529a638648fSTobias Ribizel return baz(std::forward<Args>(args)...); 530a638648fSTobias Ribizel } 531a638648fSTobias Ribizel template <typename... Args> 532a638648fSTobias Ribizel void bar(Args&&... args) { return foo(std::forward<Args>(args)...); } 533a638648fSTobias Ribizel void bazz() { 534a638648fSTobias Ribizel bar($param1[[32]], $param2[[42]], $param3[[52]]); 535a638648fSTobias Ribizel bar($param4[[1]], $param5[[2]], $param6[[3]], $param7[[4]]); 536a638648fSTobias Ribizel } 537a638648fSTobias Ribizel )cpp", 538a638648fSTobias Ribizel ExpectedHint{"a: ", "param1"}, ExpectedHint{"b: ", "param2"}, 539a638648fSTobias Ribizel ExpectedHint{"c: ", "param3"}, ExpectedHint{"a: ", "param4"}, 540a638648fSTobias Ribizel ExpectedHint{"bb: ", "param5"}, ExpectedHint{"cc: ", "param6"}, 541a638648fSTobias Ribizel ExpectedHint{"dd: ", "param7"}); 542a638648fSTobias Ribizel } 543a638648fSTobias Ribizel 544a638648fSTobias Ribizel TEST(ParameterHints, VariadicRecursive) { 545a638648fSTobias Ribizel // make_tuple-like recursive variadic call 546a638648fSTobias Ribizel assertParameterHints( 547a638648fSTobias Ribizel R"cpp( 548a638648fSTobias Ribizel void foo(); 549a638648fSTobias Ribizel 550a638648fSTobias Ribizel template <typename Head, typename... Tail> 551a638648fSTobias Ribizel void foo(Head head, Tail... tail) { 552a638648fSTobias Ribizel foo(tail...); 553a638648fSTobias Ribizel } 554a638648fSTobias Ribizel 555a638648fSTobias Ribizel template <typename... Args> 556a638648fSTobias Ribizel void bar(Args... args) { 557a638648fSTobias Ribizel foo(args...); 558a638648fSTobias Ribizel } 559a638648fSTobias Ribizel 560a638648fSTobias Ribizel int main() { 561a638648fSTobias Ribizel bar(1, 2, 3); 562a638648fSTobias Ribizel } 563a638648fSTobias Ribizel )cpp"); 564a638648fSTobias Ribizel } 565a638648fSTobias Ribizel 566a638648fSTobias Ribizel TEST(ParameterHints, VariadicVarargs) { 567a638648fSTobias Ribizel // variadic call involving varargs (to make sure we don't crash) 568a638648fSTobias Ribizel assertParameterHints(R"cpp( 569a638648fSTobias Ribizel void foo(int fixed, ...); 570a638648fSTobias Ribizel template <typename... Args> 571a638648fSTobias Ribizel void bar(Args&&... args) { 572a638648fSTobias Ribizel foo(args...); 573a638648fSTobias Ribizel } 574a638648fSTobias Ribizel 575a638648fSTobias Ribizel void baz() { 576a638648fSTobias Ribizel bar($fixed[[41]], 42, 43); 577a638648fSTobias Ribizel } 578a638648fSTobias Ribizel )cpp"); 579a638648fSTobias Ribizel } 580a638648fSTobias Ribizel 581a638648fSTobias Ribizel TEST(ParameterHints, VariadicTwolevelUnresolved) { 582a638648fSTobias Ribizel // the same setting as VariadicVarargs, only with parameter pack 583a638648fSTobias Ribizel assertParameterHints(R"cpp( 584a638648fSTobias Ribizel template <typename... Args> 585a638648fSTobias Ribizel void foo(int fixed, Args&& ... args); 586a638648fSTobias Ribizel template <typename... Args> 587a638648fSTobias Ribizel void bar(Args&&... args) { 588a638648fSTobias Ribizel foo(args...); 589a638648fSTobias Ribizel } 590a638648fSTobias Ribizel 591a638648fSTobias Ribizel void baz() { 592a638648fSTobias Ribizel bar($fixed[[41]], 42, 43); 593a638648fSTobias Ribizel } 594a638648fSTobias Ribizel )cpp", 595a638648fSTobias Ribizel ExpectedHint{"fixed: ", "fixed"}); 596a638648fSTobias Ribizel } 597a638648fSTobias Ribizel 598a638648fSTobias Ribizel TEST(ParameterHints, VariadicTwoCalls) { 599a638648fSTobias Ribizel // only the first call using the parameter pack should be picked up 600a638648fSTobias Ribizel assertParameterHints( 601a638648fSTobias Ribizel R"cpp( 602a638648fSTobias Ribizel void f1(int a, int b); 603a638648fSTobias Ribizel void f2(int c, int d); 604a638648fSTobias Ribizel 605a638648fSTobias Ribizel bool cond; 606a638648fSTobias Ribizel 607a638648fSTobias Ribizel template <typename... Args> 608a638648fSTobias Ribizel void foo(Args... args) { 609a638648fSTobias Ribizel if (cond) { 610a638648fSTobias Ribizel f1(args...); 611a638648fSTobias Ribizel } else { 612a638648fSTobias Ribizel f2(args...); 613a638648fSTobias Ribizel } 614a638648fSTobias Ribizel } 615a638648fSTobias Ribizel 616a638648fSTobias Ribizel int main() { 617a638648fSTobias Ribizel foo($param1[[1]], $param2[[2]]); 618a638648fSTobias Ribizel } 619a638648fSTobias Ribizel )cpp", 620a638648fSTobias Ribizel ExpectedHint{"a: ", "param1"}, ExpectedHint{"b: ", "param2"}); 621a638648fSTobias Ribizel } 622a638648fSTobias Ribizel 623a638648fSTobias Ribizel TEST(ParameterHints, VariadicInfinite) { 624a638648fSTobias Ribizel // infinite recursion should not break clangd 625a638648fSTobias Ribizel assertParameterHints( 626a638648fSTobias Ribizel R"cpp( 627a638648fSTobias Ribizel template <typename... Args> 628a638648fSTobias Ribizel void foo(Args...); 629a638648fSTobias Ribizel 630a638648fSTobias Ribizel template <typename... Args> 631a638648fSTobias Ribizel void bar(Args... args) { 632a638648fSTobias Ribizel foo(args...); 633a638648fSTobias Ribizel } 634a638648fSTobias Ribizel 635a638648fSTobias Ribizel template <typename... Args> 636a638648fSTobias Ribizel void foo(Args... args) { 637a638648fSTobias Ribizel bar(args...); 638a638648fSTobias Ribizel } 639a638648fSTobias Ribizel 640a638648fSTobias Ribizel int main() { 641a638648fSTobias Ribizel foo(1, 2); 642a638648fSTobias Ribizel } 643a638648fSTobias Ribizel )cpp"); 644a638648fSTobias Ribizel } 645a638648fSTobias Ribizel 646a638648fSTobias Ribizel TEST(ParameterHints, VariadicDuplicatePack) { 647a638648fSTobias Ribizel // edge cases with multiple adjacent packs should work 648a638648fSTobias Ribizel assertParameterHints( 649a638648fSTobias Ribizel R"cpp( 650a638648fSTobias Ribizel void foo(int a, int b, int c, int); 651a638648fSTobias Ribizel 652a638648fSTobias Ribizel template <typename... Args> 653a638648fSTobias Ribizel void bar(int, Args... args, int d) { 654a638648fSTobias Ribizel foo(args..., d); 655a638648fSTobias Ribizel } 656a638648fSTobias Ribizel 657a638648fSTobias Ribizel template <typename... Args> 658a638648fSTobias Ribizel void baz(Args... args, Args... args2) { 659a638648fSTobias Ribizel bar<Args..., int>(1, args..., args2...); 660a638648fSTobias Ribizel } 661a638648fSTobias Ribizel 662a638648fSTobias Ribizel int main() { 663a638648fSTobias Ribizel baz<int, int>($p1[[1]], $p2[[2]], $p3[[3]], $p4[[4]]); 664a638648fSTobias Ribizel } 665a638648fSTobias Ribizel )cpp", 666a638648fSTobias Ribizel ExpectedHint{"a: ", "p1"}, ExpectedHint{"b: ", "p2"}, 667a638648fSTobias Ribizel ExpectedHint{"c: ", "p3"}, ExpectedHint{"d: ", "p4"}); 668a638648fSTobias Ribizel } 669a638648fSTobias Ribizel 670a638648fSTobias Ribizel TEST(ParameterHints, VariadicEmplace) { 671a638648fSTobias Ribizel // emplace-like calls should forward constructor parameters 672a638648fSTobias Ribizel // This prototype of std::forward is sufficient for clang to recognize it 673a638648fSTobias Ribizel assertParameterHints( 674a638648fSTobias Ribizel R"cpp( 675a638648fSTobias Ribizel namespace std { template <typename T> T&& forward(T&); } 676a638648fSTobias Ribizel using size_t = decltype(sizeof(0)); 677a638648fSTobias Ribizel void *operator new(size_t, void *); 678a638648fSTobias Ribizel struct S { 679a638648fSTobias Ribizel S(int A); 680a638648fSTobias Ribizel S(int B, int C); 681a638648fSTobias Ribizel }; 682a638648fSTobias Ribizel struct alloc { 683a638648fSTobias Ribizel template <typename T> 684a638648fSTobias Ribizel T* allocate(); 685a638648fSTobias Ribizel template <typename T, typename... Args> 686a638648fSTobias Ribizel void construct(T* ptr, Args&&... args) { 687a638648fSTobias Ribizel ::new ((void*)ptr) T{std::forward<Args>(args)...}; 688a638648fSTobias Ribizel } 689a638648fSTobias Ribizel }; 690a638648fSTobias Ribizel template <typename T> 691a638648fSTobias Ribizel struct container { 692a638648fSTobias Ribizel template <typename... Args> 693a638648fSTobias Ribizel void emplace(Args&&... args) { 694a638648fSTobias Ribizel alloc a; 695a638648fSTobias Ribizel auto ptr = a.template allocate<T>(); 696a638648fSTobias Ribizel a.construct(ptr, std::forward<Args>(args)...); 697a638648fSTobias Ribizel } 698a638648fSTobias Ribizel }; 699a638648fSTobias Ribizel void foo() { 700a638648fSTobias Ribizel container<S> c; 701a638648fSTobias Ribizel c.emplace($param1[[1]]); 702a638648fSTobias Ribizel c.emplace($param2[[2]], $param3[[3]]); 703a638648fSTobias Ribizel } 704a638648fSTobias Ribizel )cpp", 705a638648fSTobias Ribizel ExpectedHint{"A: ", "param1"}, ExpectedHint{"B: ", "param2"}, 706a638648fSTobias Ribizel ExpectedHint{"C: ", "param3"}); 707a638648fSTobias Ribizel } 708a638648fSTobias Ribizel 709a638648fSTobias Ribizel TEST(ParameterHints, VariadicReferenceHint) { 710a638648fSTobias Ribizel assertParameterHints(R"cpp( 711a638648fSTobias Ribizel void foo(int&); 712a638648fSTobias Ribizel template <typename... Args> 713a638648fSTobias Ribizel void bar(Args... args) { return foo(args...); } 714a638648fSTobias Ribizel void baz() { 715a638648fSTobias Ribizel int a; 716a638648fSTobias Ribizel bar(a); 717a638648fSTobias Ribizel bar(1); 718a638648fSTobias Ribizel } 719a638648fSTobias Ribizel )cpp"); 720a638648fSTobias Ribizel } 721a638648fSTobias Ribizel 722a638648fSTobias Ribizel TEST(ParameterHints, VariadicReferenceHintForwardingRef) { 723a638648fSTobias Ribizel assertParameterHints(R"cpp( 724a638648fSTobias Ribizel void foo(int&); 725a638648fSTobias Ribizel template <typename... Args> 726a638648fSTobias Ribizel void bar(Args&&... args) { return foo(args...); } 727a638648fSTobias Ribizel void baz() { 728a638648fSTobias Ribizel int a; 729a638648fSTobias Ribizel bar($param[[a]]); 730a638648fSTobias Ribizel bar(1); 731a638648fSTobias Ribizel } 732a638648fSTobias Ribizel )cpp", 733a638648fSTobias Ribizel ExpectedHint{"&: ", "param"}); 734a638648fSTobias Ribizel } 735a638648fSTobias Ribizel 736a638648fSTobias Ribizel TEST(ParameterHints, VariadicReferenceHintForwardingRefStdForward) { 737a638648fSTobias Ribizel assertParameterHints(R"cpp( 738a638648fSTobias Ribizel namespace std { template <typename T> T&& forward(T&); } 739a638648fSTobias Ribizel void foo(int&); 740a638648fSTobias Ribizel template <typename... Args> 741a638648fSTobias Ribizel void bar(Args&&... args) { return foo(std::forward<Args>(args)...); } 742a638648fSTobias Ribizel void baz() { 743a638648fSTobias Ribizel int a; 744a638648fSTobias Ribizel bar($param[[a]]); 745a638648fSTobias Ribizel } 746a638648fSTobias Ribizel )cpp", 747a638648fSTobias Ribizel ExpectedHint{"&: ", "param"}); 748a638648fSTobias Ribizel } 749a638648fSTobias Ribizel 750a638648fSTobias Ribizel TEST(ParameterHints, VariadicNoReferenceHintForwardingRefStdForward) { 751a638648fSTobias Ribizel assertParameterHints(R"cpp( 752a638648fSTobias Ribizel namespace std { template <typename T> T&& forward(T&); } 753a638648fSTobias Ribizel void foo(int); 754a638648fSTobias Ribizel template <typename... Args> 755a638648fSTobias Ribizel void bar(Args&&... args) { return foo(std::forward<Args>(args)...); } 756a638648fSTobias Ribizel void baz() { 757a638648fSTobias Ribizel int a; 758a638648fSTobias Ribizel bar(a); 759a638648fSTobias Ribizel bar(1); 760a638648fSTobias Ribizel } 761a638648fSTobias Ribizel )cpp"); 762a638648fSTobias Ribizel } 763a638648fSTobias Ribizel 764a638648fSTobias Ribizel TEST(ParameterHints, VariadicNoReferenceHintUnresolvedForward) { 765a638648fSTobias Ribizel assertParameterHints(R"cpp( 766a638648fSTobias Ribizel template <typename... Args> 767a638648fSTobias Ribizel void foo(Args&&... args); 768a638648fSTobias Ribizel void bar() { 769a638648fSTobias Ribizel int a; 770a638648fSTobias Ribizel foo(a); 771a638648fSTobias Ribizel } 772a638648fSTobias Ribizel )cpp"); 773a638648fSTobias Ribizel } 774a638648fSTobias Ribizel 775a638648fSTobias Ribizel TEST(ParameterHints, MatchingNameVariadicForwarded) { 776a638648fSTobias Ribizel // No name hint for variadic parameter with matching name 777a638648fSTobias Ribizel // This prototype of std::forward is sufficient for clang to recognize it 778a638648fSTobias Ribizel assertParameterHints(R"cpp( 779a638648fSTobias Ribizel namespace std { template <typename T> T&& forward(T&); } 780a638648fSTobias Ribizel void foo(int a); 781a638648fSTobias Ribizel template <typename... Args> 782a638648fSTobias Ribizel void bar(Args&&... args) { return foo(std::forward<Args>(args)...); } 783a638648fSTobias Ribizel void baz() { 784a638648fSTobias Ribizel int a; 785a638648fSTobias Ribizel bar(a); 786a638648fSTobias Ribizel } 787a638648fSTobias Ribizel )cpp"); 788a638648fSTobias Ribizel } 789a638648fSTobias Ribizel 790a638648fSTobias Ribizel TEST(ParameterHints, MatchingNameVariadicPlain) { 791a638648fSTobias Ribizel // No name hint for variadic parameter with matching name 792a638648fSTobias Ribizel assertParameterHints(R"cpp( 793a638648fSTobias Ribizel void foo(int a); 794a638648fSTobias Ribizel template <typename... Args> 795a638648fSTobias Ribizel void bar(Args&&... args) { return foo(args...); } 796a638648fSTobias Ribizel void baz() { 797a638648fSTobias Ribizel int a; 798a638648fSTobias Ribizel bar(a); 799a638648fSTobias Ribizel } 800a638648fSTobias Ribizel )cpp"); 801a638648fSTobias Ribizel } 802a638648fSTobias Ribizel 803cbc9c4eaSNathan Ridge TEST(ParameterHints, Operator) { 804cbc9c4eaSNathan Ridge // No hint for operator call with operator syntax. 805cbc9c4eaSNathan Ridge assertParameterHints(R"cpp( 806cbc9c4eaSNathan Ridge struct S {}; 807cbc9c4eaSNathan Ridge void operator+(S lhs, S rhs); 808cbc9c4eaSNathan Ridge void bar() { 809cbc9c4eaSNathan Ridge S a, b; 810cbc9c4eaSNathan Ridge a + b; 811cbc9c4eaSNathan Ridge } 812cbc9c4eaSNathan Ridge )cpp"); 813cbc9c4eaSNathan Ridge } 814cbc9c4eaSNathan Ridge 815cbd6ac61SYounan Zhang TEST(ParameterHints, FunctionCallOperator) { 816cbd6ac61SYounan Zhang assertParameterHints(R"cpp( 817cbd6ac61SYounan Zhang struct W { 818cbd6ac61SYounan Zhang void operator()(int x); 819cbd6ac61SYounan Zhang }; 820cbd6ac61SYounan Zhang struct S : W { 821cbd6ac61SYounan Zhang using W::operator(); 822cbd6ac61SYounan Zhang static void operator()(int x, int y); 823cbd6ac61SYounan Zhang }; 824cbd6ac61SYounan Zhang void bar() { 825cbd6ac61SYounan Zhang auto l1 = [](int x) {}; 826cbd6ac61SYounan Zhang auto l2 = [](int x) static {}; 827cbd6ac61SYounan Zhang 828cbd6ac61SYounan Zhang S s; 829cbd6ac61SYounan Zhang s($1[[1]]); 830cbd6ac61SYounan Zhang s.operator()($2[[1]]); 831cbd6ac61SYounan Zhang s.operator()($3[[1]], $4[[2]]); 832cbd6ac61SYounan Zhang S::operator()($5[[1]], $6[[2]]); 833cbd6ac61SYounan Zhang 834cbd6ac61SYounan Zhang l1($7[[1]]); 835cbd6ac61SYounan Zhang l1.operator()($8[[1]]); 836cbd6ac61SYounan Zhang l2($9[[1]]); 837cbd6ac61SYounan Zhang l2.operator()($10[[1]]); 838cbd6ac61SYounan Zhang 839cbd6ac61SYounan Zhang void (*ptr)(int a, int b) = &S::operator(); 840cbd6ac61SYounan Zhang ptr($11[[1]], $12[[2]]); 841cbd6ac61SYounan Zhang } 842cbd6ac61SYounan Zhang )cpp", 843cbd6ac61SYounan Zhang ExpectedHint{"x: ", "1"}, ExpectedHint{"x: ", "2"}, 844cbd6ac61SYounan Zhang ExpectedHint{"x: ", "3"}, ExpectedHint{"y: ", "4"}, 845cbd6ac61SYounan Zhang ExpectedHint{"x: ", "5"}, ExpectedHint{"y: ", "6"}, 846cbd6ac61SYounan Zhang ExpectedHint{"x: ", "7"}, ExpectedHint{"x: ", "8"}, 847cbd6ac61SYounan Zhang ExpectedHint{"x: ", "9"}, ExpectedHint{"x: ", "10"}, 848cbd6ac61SYounan Zhang ExpectedHint{"a: ", "11"}, ExpectedHint{"b: ", "12"}); 849cbd6ac61SYounan Zhang } 850cbd6ac61SYounan Zhang 8514a540ceeSYounan Zhang TEST(ParameterHints, DeducingThis) { 8524a540ceeSYounan Zhang assertParameterHints(R"cpp( 8534a540ceeSYounan Zhang struct S { 8544a540ceeSYounan Zhang template <typename This> 8554a540ceeSYounan Zhang auto operator()(this This &&Self, int Param) { 8564a540ceeSYounan Zhang return 42; 8574a540ceeSYounan Zhang } 8584a540ceeSYounan Zhang 8594a540ceeSYounan Zhang auto function(this auto &Self, int Param) { 8604a540ceeSYounan Zhang return Param; 8614a540ceeSYounan Zhang } 8624a540ceeSYounan Zhang }; 8634a540ceeSYounan Zhang void work() { 8644a540ceeSYounan Zhang S s; 8654a540ceeSYounan Zhang s($1[[42]]); 8664a540ceeSYounan Zhang s.function($2[[42]]); 8674a540ceeSYounan Zhang S()($3[[42]]); 8684a540ceeSYounan Zhang auto lambda = [](this auto &Self, char C) -> void { 8694a540ceeSYounan Zhang return Self(C); 8704a540ceeSYounan Zhang }; 8714a540ceeSYounan Zhang lambda($4[['A']]); 8724a540ceeSYounan Zhang } 8734a540ceeSYounan Zhang )cpp", 8744a540ceeSYounan Zhang ExpectedHint{"Param: ", "1"}, 8754a540ceeSYounan Zhang ExpectedHint{"Param: ", "2"}, 8764a540ceeSYounan Zhang ExpectedHint{"Param: ", "3"}, ExpectedHint{"C: ", "4"}); 8774a540ceeSYounan Zhang } 8784a540ceeSYounan Zhang 879cbc9c4eaSNathan Ridge TEST(ParameterHints, Macros) { 880cbc9c4eaSNathan Ridge // Handling of macros depends on where the call's argument list comes from. 881cbc9c4eaSNathan Ridge 882cbc9c4eaSNathan Ridge // If it comes from a macro definition, there's nothing to hint 883cbc9c4eaSNathan Ridge // at the invocation site. 884cbc9c4eaSNathan Ridge assertParameterHints(R"cpp( 885cbc9c4eaSNathan Ridge void foo(int param); 886cbc9c4eaSNathan Ridge #define ExpandsToCall() foo(42) 887cbc9c4eaSNathan Ridge void bar() { 888cbc9c4eaSNathan Ridge ExpandsToCall(); 889cbc9c4eaSNathan Ridge } 890cbc9c4eaSNathan Ridge )cpp"); 891cbc9c4eaSNathan Ridge 892cbc9c4eaSNathan Ridge // The argument expression being a macro invocation shouldn't interfere 893cbc9c4eaSNathan Ridge // with hinting. 894cbc9c4eaSNathan Ridge assertParameterHints(R"cpp( 895cbc9c4eaSNathan Ridge #define PI 3.14 896cbc9c4eaSNathan Ridge void foo(double param); 897cbc9c4eaSNathan Ridge void bar() { 898cbc9c4eaSNathan Ridge foo($param[[PI]]); 899cbc9c4eaSNathan Ridge } 900cbc9c4eaSNathan Ridge )cpp", 901cbc9c4eaSNathan Ridge ExpectedHint{"param: ", "param"}); 902cbc9c4eaSNathan Ridge 903cbc9c4eaSNathan Ridge // If the whole argument list comes from a macro parameter, hint it. 904cbc9c4eaSNathan Ridge assertParameterHints(R"cpp( 905cbc9c4eaSNathan Ridge void abort(); 906cbc9c4eaSNathan Ridge #define ASSERT(expr) if (!expr) abort() 907cbc9c4eaSNathan Ridge int foo(int param); 908cbc9c4eaSNathan Ridge void bar() { 909cbc9c4eaSNathan Ridge ASSERT(foo($param[[42]]) == 0); 910cbc9c4eaSNathan Ridge } 911cbc9c4eaSNathan Ridge )cpp", 912cbc9c4eaSNathan Ridge ExpectedHint{"param: ", "param"}); 913924974a3SSam McCall 914924974a3SSam McCall // If the macro expands to multiple arguments, don't hint it. 915924974a3SSam McCall assertParameterHints(R"cpp( 916924974a3SSam McCall void foo(double x, double y); 917924974a3SSam McCall #define CONSTANTS 3.14, 2.72 918924974a3SSam McCall void bar() { 919924974a3SSam McCall foo(CONSTANTS); 920924974a3SSam McCall } 921924974a3SSam McCall )cpp"); 922cbc9c4eaSNathan Ridge } 923cbc9c4eaSNathan Ridge 924cbc9c4eaSNathan Ridge TEST(ParameterHints, ConstructorParens) { 925cbc9c4eaSNathan Ridge assertParameterHints(R"cpp( 926cbc9c4eaSNathan Ridge struct S { 927cbc9c4eaSNathan Ridge S(int param); 928cbc9c4eaSNathan Ridge }; 929cbc9c4eaSNathan Ridge void bar() { 930cbc9c4eaSNathan Ridge S obj($param[[42]]); 931cbc9c4eaSNathan Ridge } 932cbc9c4eaSNathan Ridge )cpp", 933cbc9c4eaSNathan Ridge ExpectedHint{"param: ", "param"}); 934cbc9c4eaSNathan Ridge } 935cbc9c4eaSNathan Ridge 936cbc9c4eaSNathan Ridge TEST(ParameterHints, ConstructorBraces) { 937cbc9c4eaSNathan Ridge assertParameterHints(R"cpp( 938cbc9c4eaSNathan Ridge struct S { 939cbc9c4eaSNathan Ridge S(int param); 940cbc9c4eaSNathan Ridge }; 941cbc9c4eaSNathan Ridge void bar() { 942cbc9c4eaSNathan Ridge S obj{$param[[42]]}; 943cbc9c4eaSNathan Ridge } 944cbc9c4eaSNathan Ridge )cpp", 945cbc9c4eaSNathan Ridge ExpectedHint{"param: ", "param"}); 946cbc9c4eaSNathan Ridge } 947cbc9c4eaSNathan Ridge 948cbc9c4eaSNathan Ridge TEST(ParameterHints, ConstructorStdInitList) { 949cbc9c4eaSNathan Ridge // Do not show hints for std::initializer_list constructors. 950cbc9c4eaSNathan Ridge assertParameterHints(R"cpp( 951cbc9c4eaSNathan Ridge namespace std { 952482c41e9SMital Ashok template <typename E> class initializer_list { const E *a, *b; }; 953cbc9c4eaSNathan Ridge } 954cbc9c4eaSNathan Ridge struct S { 955cbc9c4eaSNathan Ridge S(std::initializer_list<int> param); 956cbc9c4eaSNathan Ridge }; 957cbc9c4eaSNathan Ridge void bar() { 958cbc9c4eaSNathan Ridge S obj{42, 43}; 959cbc9c4eaSNathan Ridge } 960cbc9c4eaSNathan Ridge )cpp"); 961cbc9c4eaSNathan Ridge } 962cbc9c4eaSNathan Ridge 963cbc9c4eaSNathan Ridge TEST(ParameterHints, MemberInit) { 964cbc9c4eaSNathan Ridge assertParameterHints(R"cpp( 965cbc9c4eaSNathan Ridge struct S { 966cbc9c4eaSNathan Ridge S(int param); 967cbc9c4eaSNathan Ridge }; 968cbc9c4eaSNathan Ridge struct T { 969cbc9c4eaSNathan Ridge S member; 970cbc9c4eaSNathan Ridge T() : member($param[[42]]) {} 971cbc9c4eaSNathan Ridge }; 972cbc9c4eaSNathan Ridge )cpp", 973cbc9c4eaSNathan Ridge ExpectedHint{"param: ", "param"}); 974cbc9c4eaSNathan Ridge } 975cbc9c4eaSNathan Ridge 976cbc9c4eaSNathan Ridge TEST(ParameterHints, ImplicitConstructor) { 977cbc9c4eaSNathan Ridge assertParameterHints(R"cpp( 978cbc9c4eaSNathan Ridge struct S { 979cbc9c4eaSNathan Ridge S(int param); 980cbc9c4eaSNathan Ridge }; 981cbc9c4eaSNathan Ridge void bar(S); 982cbc9c4eaSNathan Ridge S foo() { 983cbc9c4eaSNathan Ridge // Do not show hint for implicit constructor call in argument. 984cbc9c4eaSNathan Ridge bar(42); 985cbc9c4eaSNathan Ridge // Do not show hint for implicit constructor call in return. 986cbc9c4eaSNathan Ridge return 42; 987cbc9c4eaSNathan Ridge } 988cbc9c4eaSNathan Ridge )cpp"); 989cbc9c4eaSNathan Ridge } 990cbc9c4eaSNathan Ridge 9918ee710a4SNathan Ridge TEST(ParameterHints, FunctionPointer) { 9928ee710a4SNathan Ridge assertParameterHints( 9938ee710a4SNathan Ridge R"cpp( 9948ee710a4SNathan Ridge void (*f1)(int param); 9958ee710a4SNathan Ridge void (__stdcall *f2)(int param); 9968ee710a4SNathan Ridge using f3_t = void(*)(int param); 9978ee710a4SNathan Ridge f3_t f3; 9988ee710a4SNathan Ridge using f4_t = void(__stdcall *)(int param); 9998ee710a4SNathan Ridge f4_t f4; 10008ee710a4SNathan Ridge void bar() { 10018ee710a4SNathan Ridge f1($f1[[42]]); 10028ee710a4SNathan Ridge f2($f2[[42]]); 10038ee710a4SNathan Ridge f3($f3[[42]]); 10048ee710a4SNathan Ridge f4($f4[[42]]); 10058ee710a4SNathan Ridge } 10068ee710a4SNathan Ridge )cpp", 10078ee710a4SNathan Ridge ExpectedHint{"param: ", "f1"}, ExpectedHint{"param: ", "f2"}, 10088ee710a4SNathan Ridge ExpectedHint{"param: ", "f3"}, ExpectedHint{"param: ", "f4"}); 10098ee710a4SNathan Ridge } 10108ee710a4SNathan Ridge 1011cbc9c4eaSNathan Ridge TEST(ParameterHints, ArgMatchesParam) { 1012cbc9c4eaSNathan Ridge assertParameterHints(R"cpp( 1013cbc9c4eaSNathan Ridge void foo(int param); 1014cbc9c4eaSNathan Ridge struct S { 1015cbc9c4eaSNathan Ridge static const int param = 42; 1016cbc9c4eaSNathan Ridge }; 1017cbc9c4eaSNathan Ridge void bar() { 1018cbc9c4eaSNathan Ridge int param = 42; 1019cbc9c4eaSNathan Ridge // Do not show redundant "param: param". 1020cbc9c4eaSNathan Ridge foo(param); 1021cbc9c4eaSNathan Ridge // But show it if the argument is qualified. 1022cbc9c4eaSNathan Ridge foo($param[[S::param]]); 1023cbc9c4eaSNathan Ridge } 1024cbc9c4eaSNathan Ridge struct A { 1025cbc9c4eaSNathan Ridge int param; 1026cbc9c4eaSNathan Ridge void bar() { 1027cbc9c4eaSNathan Ridge // Do not show "param: param" for member-expr. 1028cbc9c4eaSNathan Ridge foo(param); 1029cbc9c4eaSNathan Ridge } 1030cbc9c4eaSNathan Ridge }; 1031cbc9c4eaSNathan Ridge )cpp", 1032cbc9c4eaSNathan Ridge ExpectedHint{"param: ", "param"}); 1033cbc9c4eaSNathan Ridge } 1034cbc9c4eaSNathan Ridge 1035043e9650STobias Ribizel TEST(ParameterHints, ArgMatchesParamReference) { 1036043e9650STobias Ribizel assertParameterHints(R"cpp( 1037043e9650STobias Ribizel void foo(int& param); 1038043e9650STobias Ribizel void foo2(const int& param); 1039043e9650STobias Ribizel void bar() { 1040043e9650STobias Ribizel int param; 1041043e9650STobias Ribizel // show reference hint on mutable reference 1042043e9650STobias Ribizel foo($param[[param]]); 1043043e9650STobias Ribizel // but not on const reference 1044043e9650STobias Ribizel foo2(param); 1045043e9650STobias Ribizel } 1046043e9650STobias Ribizel )cpp", 1047043e9650STobias Ribizel ExpectedHint{"&: ", "param"}); 1048043e9650STobias Ribizel } 1049043e9650STobias Ribizel 1050cbc9c4eaSNathan Ridge TEST(ParameterHints, LeadingUnderscore) { 1051cbc9c4eaSNathan Ridge assertParameterHints(R"cpp( 1052cbc9c4eaSNathan Ridge void foo(int p1, int _p2, int __p3); 1053cbc9c4eaSNathan Ridge void bar() { 1054cbc9c4eaSNathan Ridge foo($p1[[41]], $p2[[42]], $p3[[43]]); 1055cbc9c4eaSNathan Ridge } 1056cbc9c4eaSNathan Ridge )cpp", 1057cbc9c4eaSNathan Ridge ExpectedHint{"p1: ", "p1"}, ExpectedHint{"p2: ", "p2"}, 1058cbc9c4eaSNathan Ridge ExpectedHint{"p3: ", "p3"}); 1059cbc9c4eaSNathan Ridge } 1060cbc9c4eaSNathan Ridge 10611f8963c8SNathan Ridge TEST(ParameterHints, DependentCalls) { 1062cbc9c4eaSNathan Ridge assertParameterHints(R"cpp( 1063cbc9c4eaSNathan Ridge template <typename T> 10641f8963c8SNathan Ridge void nonmember(T par1); 10651f8963c8SNathan Ridge 10661f8963c8SNathan Ridge template <typename T> 10671f8963c8SNathan Ridge struct A { 10681f8963c8SNathan Ridge void member(T par2); 10691f8963c8SNathan Ridge static void static_member(T par3); 10701f8963c8SNathan Ridge }; 10711f8963c8SNathan Ridge 10721f8963c8SNathan Ridge void overload(int anInt); 10731f8963c8SNathan Ridge void overload(double aDouble); 1074cbc9c4eaSNathan Ridge 1075cbc9c4eaSNathan Ridge template <typename T> 1076cbc9c4eaSNathan Ridge struct S { 10771f8963c8SNathan Ridge void bar(A<T> a, T t) { 10781f8963c8SNathan Ridge nonmember($par1[[t]]); 10791f8963c8SNathan Ridge a.member($par2[[t]]); 10801f8963c8SNathan Ridge A<T>::static_member($par3[[t]]); 10811f8963c8SNathan Ridge // We don't want to arbitrarily pick between 10821f8963c8SNathan Ridge // "anInt" or "aDouble", so just show no hint. 10831f8963c8SNathan Ridge overload(T{}); 1084cbc9c4eaSNathan Ridge } 1085cbc9c4eaSNathan Ridge }; 10861f8963c8SNathan Ridge )cpp", 10871f8963c8SNathan Ridge ExpectedHint{"par1: ", "par1"}, 1088f976b999SNathan Ridge ExpectedHint{"par2: ", "par2"}, 1089f976b999SNathan Ridge ExpectedHint{"par3: ", "par3"}); 1090cbc9c4eaSNathan Ridge } 1091cbc9c4eaSNathan Ridge 1092cbc9c4eaSNathan Ridge TEST(ParameterHints, VariadicFunction) { 1093cbc9c4eaSNathan Ridge assertParameterHints(R"cpp( 1094cbc9c4eaSNathan Ridge template <typename... T> 1095cbc9c4eaSNathan Ridge void foo(int fixed, T... variadic); 1096cbc9c4eaSNathan Ridge 1097cbc9c4eaSNathan Ridge void bar() { 1098cbc9c4eaSNathan Ridge foo($fixed[[41]], 42, 43); 1099cbc9c4eaSNathan Ridge } 1100cbc9c4eaSNathan Ridge )cpp", 1101cbc9c4eaSNathan Ridge ExpectedHint{"fixed: ", "fixed"}); 1102cbc9c4eaSNathan Ridge } 1103cbc9c4eaSNathan Ridge 1104cbc9c4eaSNathan Ridge TEST(ParameterHints, VarargsFunction) { 1105cbc9c4eaSNathan Ridge assertParameterHints(R"cpp( 1106cbc9c4eaSNathan Ridge void foo(int fixed, ...); 1107cbc9c4eaSNathan Ridge 1108cbc9c4eaSNathan Ridge void bar() { 1109cbc9c4eaSNathan Ridge foo($fixed[[41]], 42, 43); 1110cbc9c4eaSNathan Ridge } 1111cbc9c4eaSNathan Ridge )cpp", 1112cbc9c4eaSNathan Ridge ExpectedHint{"fixed: ", "fixed"}); 1113cbc9c4eaSNathan Ridge } 1114cbc9c4eaSNathan Ridge 1115cbc9c4eaSNathan Ridge TEST(ParameterHints, CopyOrMoveConstructor) { 1116cbc9c4eaSNathan Ridge // Do not show hint for parameter of copy or move constructor. 1117cbc9c4eaSNathan Ridge assertParameterHints(R"cpp( 1118cbc9c4eaSNathan Ridge struct S { 1119cbc9c4eaSNathan Ridge S(); 1120cbc9c4eaSNathan Ridge S(const S& other); 1121cbc9c4eaSNathan Ridge S(S&& other); 1122cbc9c4eaSNathan Ridge }; 1123cbc9c4eaSNathan Ridge void bar() { 1124cbc9c4eaSNathan Ridge S a; 1125cbc9c4eaSNathan Ridge S b(a); // copy 1126cbc9c4eaSNathan Ridge S c(S()); // move 1127cbc9c4eaSNathan Ridge } 1128cbc9c4eaSNathan Ridge )cpp"); 1129cbc9c4eaSNathan Ridge } 1130cbc9c4eaSNathan Ridge 1131cbc9c4eaSNathan Ridge TEST(ParameterHints, AggregateInit) { 1132cbc9c4eaSNathan Ridge // FIXME: This is not implemented yet, but it would be a natural 1133cbc9c4eaSNathan Ridge // extension to show member names as hints here. 1134cbc9c4eaSNathan Ridge assertParameterHints(R"cpp( 1135cbc9c4eaSNathan Ridge struct Point { 1136cbc9c4eaSNathan Ridge int x; 1137cbc9c4eaSNathan Ridge int y; 1138cbc9c4eaSNathan Ridge }; 1139cbc9c4eaSNathan Ridge void bar() { 1140cbc9c4eaSNathan Ridge Point p{41, 42}; 1141cbc9c4eaSNathan Ridge } 1142cbc9c4eaSNathan Ridge )cpp"); 1143cbc9c4eaSNathan Ridge } 1144cbc9c4eaSNathan Ridge 1145cbc9c4eaSNathan Ridge TEST(ParameterHints, UserDefinedLiteral) { 1146cbc9c4eaSNathan Ridge // Do not hint call to user-defined literal operator. 1147cbc9c4eaSNathan Ridge assertParameterHints(R"cpp( 1148cbc9c4eaSNathan Ridge long double operator"" _w(long double param); 1149cbc9c4eaSNathan Ridge void bar() { 1150cbc9c4eaSNathan Ridge 1.2_w; 1151cbc9c4eaSNathan Ridge } 1152cbc9c4eaSNathan Ridge )cpp"); 1153cbc9c4eaSNathan Ridge } 1154cbc9c4eaSNathan Ridge 1155753b247dSNathan Ridge TEST(ParameterHints, ParamNameComment) { 1156753b247dSNathan Ridge // Do not hint an argument which already has a comment 1157753b247dSNathan Ridge // with the parameter name preceding it. 1158753b247dSNathan Ridge assertParameterHints(R"cpp( 1159753b247dSNathan Ridge void foo(int param); 1160753b247dSNathan Ridge void bar() { 1161753b247dSNathan Ridge foo(/*param*/42); 1162753b247dSNathan Ridge foo( /* param = */ 42); 1163cb2d04d4SYounan Zhang #define X 42 1164cb2d04d4SYounan Zhang #define Y X 1165cb2d04d4SYounan Zhang #define Z(...) Y 1166cb2d04d4SYounan Zhang foo(/*param=*/Z(a)); 1167cb2d04d4SYounan Zhang foo($macro[[Z(a)]]); 1168753b247dSNathan Ridge foo(/* the answer */$param[[42]]); 1169753b247dSNathan Ridge } 1170753b247dSNathan Ridge )cpp", 1171cb2d04d4SYounan Zhang ExpectedHint{"param: ", "macro"}, 1172753b247dSNathan Ridge ExpectedHint{"param: ", "param"}); 1173753b247dSNathan Ridge } 1174753b247dSNathan Ridge 11756f6cf2daSNathan Ridge TEST(ParameterHints, SetterFunctions) { 11766f6cf2daSNathan Ridge assertParameterHints(R"cpp( 11776f6cf2daSNathan Ridge struct S { 11786f6cf2daSNathan Ridge void setParent(S* parent); 11796f6cf2daSNathan Ridge void set_parent(S* parent); 11806f6cf2daSNathan Ridge void setTimeout(int timeoutMillis); 11816f6cf2daSNathan Ridge void setTimeoutMillis(int timeout_millis); 11826f6cf2daSNathan Ridge }; 11836f6cf2daSNathan Ridge void bar() { 11846f6cf2daSNathan Ridge S s; 11856f6cf2daSNathan Ridge // Parameter name matches setter name - omit hint. 11866f6cf2daSNathan Ridge s.setParent(nullptr); 11876f6cf2daSNathan Ridge // Support snake_case 11886f6cf2daSNathan Ridge s.set_parent(nullptr); 11896f6cf2daSNathan Ridge // Parameter name may contain extra info - show hint. 11906f6cf2daSNathan Ridge s.setTimeout($timeoutMillis[[120]]); 11916f6cf2daSNathan Ridge // FIXME: Ideally we'd want to omit this. 11926f6cf2daSNathan Ridge s.setTimeoutMillis($timeout_millis[[120]]); 11936f6cf2daSNathan Ridge } 11946f6cf2daSNathan Ridge )cpp", 11956f6cf2daSNathan Ridge ExpectedHint{"timeoutMillis: ", "timeoutMillis"}, 11966f6cf2daSNathan Ridge ExpectedHint{"timeout_millis: ", "timeout_millis"}); 11976f6cf2daSNathan Ridge } 11986f6cf2daSNathan Ridge 1199e984e1cdSTobias Ribizel TEST(ParameterHints, BuiltinFunctions) { 1200e984e1cdSTobias Ribizel // This prototype of std::forward is sufficient for clang to recognize it 1201e984e1cdSTobias Ribizel assertParameterHints(R"cpp( 1202e984e1cdSTobias Ribizel namespace std { template <typename T> T&& forward(T&); } 1203e984e1cdSTobias Ribizel void foo() { 1204e984e1cdSTobias Ribizel int i; 1205e984e1cdSTobias Ribizel std::forward(i); 1206e984e1cdSTobias Ribizel } 1207e984e1cdSTobias Ribizel )cpp"); 1208e984e1cdSTobias Ribizel } 1209e984e1cdSTobias Ribizel 121047d9d55cSNathan Ridge TEST(ParameterHints, IncludeAtNonGlobalScope) { 121147d9d55cSNathan Ridge Annotations FooInc(R"cpp( 121247d9d55cSNathan Ridge void bar() { foo(42); } 121347d9d55cSNathan Ridge )cpp"); 121447d9d55cSNathan Ridge Annotations FooCC(R"cpp( 121547d9d55cSNathan Ridge struct S { 121647d9d55cSNathan Ridge void foo(int param); 121747d9d55cSNathan Ridge #include "foo.inc" 121847d9d55cSNathan Ridge }; 121947d9d55cSNathan Ridge )cpp"); 122047d9d55cSNathan Ridge 122147d9d55cSNathan Ridge TestWorkspace Workspace; 122247d9d55cSNathan Ridge Workspace.addSource("foo.inc", FooInc.code()); 122347d9d55cSNathan Ridge Workspace.addMainFile("foo.cc", FooCC.code()); 122447d9d55cSNathan Ridge 122547d9d55cSNathan Ridge auto AST = Workspace.openFile("foo.cc"); 122647d9d55cSNathan Ridge ASSERT_TRUE(bool(AST)); 122747d9d55cSNathan Ridge 122847d9d55cSNathan Ridge // Ensure the hint for the call in foo.inc is NOT materialized in foo.cc. 12293137ca80SKadir Cetinkaya EXPECT_EQ(hintsOfKind(*AST, InlayHintKind::Parameter).size(), 0u); 123047d9d55cSNathan Ridge } 123147d9d55cSNathan Ridge 12320be2657cSNathan Ridge TEST(TypeHints, Smoke) { 12330be2657cSNathan Ridge assertTypeHints(R"cpp( 12340be2657cSNathan Ridge auto $waldo[[waldo]] = 42; 12350be2657cSNathan Ridge )cpp", 12360be2657cSNathan Ridge ExpectedHint{": int", "waldo"}); 12370be2657cSNathan Ridge } 12380be2657cSNathan Ridge 12390be2657cSNathan Ridge TEST(TypeHints, Decorations) { 12400be2657cSNathan Ridge assertTypeHints(R"cpp( 12410be2657cSNathan Ridge int x = 42; 12420be2657cSNathan Ridge auto* $var1[[var1]] = &x; 12430be2657cSNathan Ridge auto&& $var2[[var2]] = x; 12440be2657cSNathan Ridge const auto& $var3[[var3]] = x; 12450be2657cSNathan Ridge )cpp", 12460be2657cSNathan Ridge ExpectedHint{": int *", "var1"}, 12470be2657cSNathan Ridge ExpectedHint{": int &", "var2"}, 12480be2657cSNathan Ridge ExpectedHint{": const int &", "var3"}); 12490be2657cSNathan Ridge } 12500be2657cSNathan Ridge 12510be2657cSNathan Ridge TEST(TypeHints, DecltypeAuto) { 12520be2657cSNathan Ridge assertTypeHints(R"cpp( 12530be2657cSNathan Ridge int x = 42; 12540be2657cSNathan Ridge int& y = x; 12550be2657cSNathan Ridge decltype(auto) $z[[z]] = y; 12560be2657cSNathan Ridge )cpp", 12570be2657cSNathan Ridge ExpectedHint{": int &", "z"}); 12580be2657cSNathan Ridge } 12590be2657cSNathan Ridge 12600be2657cSNathan Ridge TEST(TypeHints, NoQualifiers) { 12610be2657cSNathan Ridge assertTypeHints(R"cpp( 12620be2657cSNathan Ridge namespace A { 12630be2657cSNathan Ridge namespace B { 12640be2657cSNathan Ridge struct S1 {}; 12650be2657cSNathan Ridge S1 foo(); 12660be2657cSNathan Ridge auto $x[[x]] = foo(); 12670be2657cSNathan Ridge 12680be2657cSNathan Ridge struct S2 { 12690be2657cSNathan Ridge template <typename T> 12700be2657cSNathan Ridge struct Inner {}; 12710be2657cSNathan Ridge }; 12720be2657cSNathan Ridge S2::Inner<int> bar(); 12730be2657cSNathan Ridge auto $y[[y]] = bar(); 12740be2657cSNathan Ridge } 12750be2657cSNathan Ridge } 12760be2657cSNathan Ridge )cpp", 1277c9e46219SMatheus Izvekov ExpectedHint{": S1", "x"}, 1278c9e46219SMatheus Izvekov // FIXME: We want to suppress scope specifiers 1279c9e46219SMatheus Izvekov // here because we are into the whole 1280c9e46219SMatheus Izvekov // brevity thing, but the ElaboratedType 1281c9e46219SMatheus Izvekov // printer does not honor the SuppressScope 1282c9e46219SMatheus Izvekov // flag by design, so we need to extend the 1283c9e46219SMatheus Izvekov // PrintingPolicy to support this use case. 1284c9e46219SMatheus Izvekov ExpectedHint{": S2::Inner<int>", "y"}); 12850be2657cSNathan Ridge } 12860be2657cSNathan Ridge 12870be2657cSNathan Ridge TEST(TypeHints, Lambda) { 12880be2657cSNathan Ridge // Do not print something overly verbose like the lambda's location. 12890be2657cSNathan Ridge // Show hints for init-captures (but not regular captures). 12900be2657cSNathan Ridge assertTypeHints(R"cpp( 12910be2657cSNathan Ridge void f() { 12920be2657cSNathan Ridge int cap = 42; 1293257559edSSam McCall auto $L[[L]] = [cap, $init[[init]] = 1 + 1](int a$ret[[)]] { 12940be2657cSNathan Ridge return a + cap + init; 12950be2657cSNathan Ridge }; 12960be2657cSNathan Ridge } 12970be2657cSNathan Ridge )cpp", 12980be2657cSNathan Ridge ExpectedHint{": (lambda)", "L"}, 1299257559edSSam McCall ExpectedHint{": int", "init"}, ExpectedHint{"-> int", "ret"}); 1300257559edSSam McCall 1301257559edSSam McCall // Lambda return hint shown even if no param list. 130242cb812dSSam McCall // (The digraph :> is just a ] that doesn't conflict with the annotations). 1303257559edSSam McCall assertTypeHints("auto $L[[x]] = <:$ret[[:>]]{return 42;};", 1304257559edSSam McCall ExpectedHint{": (lambda)", "L"}, 1305257559edSSam McCall ExpectedHint{"-> int", "ret"}); 13060be2657cSNathan Ridge } 13070be2657cSNathan Ridge 1308a15adbcdSNathan Ridge // Structured bindings tests. 1309a15adbcdSNathan Ridge // Note, we hint the individual bindings, not the aggregate. 1310a15adbcdSNathan Ridge 1311a15adbcdSNathan Ridge TEST(TypeHints, StructuredBindings_PublicStruct) { 13120be2657cSNathan Ridge assertTypeHints(R"cpp( 1313a15adbcdSNathan Ridge // Struct with public fields. 13140be2657cSNathan Ridge struct Point { 13150be2657cSNathan Ridge int x; 13160be2657cSNathan Ridge int y; 13170be2657cSNathan Ridge }; 13180be2657cSNathan Ridge Point foo(); 1319a15adbcdSNathan Ridge auto [$x[[x]], $y[[y]]] = foo(); 1320a15adbcdSNathan Ridge )cpp", 1321a15adbcdSNathan Ridge ExpectedHint{": int", "x"}, ExpectedHint{": int", "y"}); 1322a15adbcdSNathan Ridge } 1323a15adbcdSNathan Ridge 1324a15adbcdSNathan Ridge TEST(TypeHints, StructuredBindings_Array) { 1325a15adbcdSNathan Ridge assertTypeHints(R"cpp( 1326a15adbcdSNathan Ridge int arr[2]; 1327a15adbcdSNathan Ridge auto [$x[[x]], $y[[y]]] = arr; 1328a15adbcdSNathan Ridge )cpp", 1329a15adbcdSNathan Ridge ExpectedHint{": int", "x"}, ExpectedHint{": int", "y"}); 1330a15adbcdSNathan Ridge } 1331a15adbcdSNathan Ridge 1332a15adbcdSNathan Ridge TEST(TypeHints, StructuredBindings_TupleLike) { 1333a15adbcdSNathan Ridge assertTypeHints(R"cpp( 1334a15adbcdSNathan Ridge // Tuple-like type. 1335a15adbcdSNathan Ridge struct IntPair { 1336a15adbcdSNathan Ridge int a; 1337a15adbcdSNathan Ridge int b; 1338a15adbcdSNathan Ridge }; 1339a15adbcdSNathan Ridge namespace std { 1340a15adbcdSNathan Ridge template <typename T> 1341a15adbcdSNathan Ridge struct tuple_size {}; 1342a15adbcdSNathan Ridge template <> 1343a15adbcdSNathan Ridge struct tuple_size<IntPair> { 1344a15adbcdSNathan Ridge constexpr static unsigned value = 2; 1345a15adbcdSNathan Ridge }; 1346a15adbcdSNathan Ridge template <unsigned I, typename T> 1347a15adbcdSNathan Ridge struct tuple_element {}; 1348a15adbcdSNathan Ridge template <unsigned I> 1349a15adbcdSNathan Ridge struct tuple_element<I, IntPair> { 1350a15adbcdSNathan Ridge using type = int; 1351a15adbcdSNathan Ridge }; 1352a15adbcdSNathan Ridge } 1353a15adbcdSNathan Ridge template <unsigned I> 1354a15adbcdSNathan Ridge int get(const IntPair& p) { 1355a15adbcdSNathan Ridge if constexpr (I == 0) { 1356a15adbcdSNathan Ridge return p.a; 1357a15adbcdSNathan Ridge } else if constexpr (I == 1) { 1358a15adbcdSNathan Ridge return p.b; 1359a15adbcdSNathan Ridge } 1360a15adbcdSNathan Ridge } 1361a15adbcdSNathan Ridge IntPair bar(); 1362a15adbcdSNathan Ridge auto [$x[[x]], $y[[y]]] = bar(); 1363a15adbcdSNathan Ridge )cpp", 1364a15adbcdSNathan Ridge ExpectedHint{": int", "x"}, ExpectedHint{": int", "y"}); 1365a15adbcdSNathan Ridge } 1366a15adbcdSNathan Ridge 1367a15adbcdSNathan Ridge TEST(TypeHints, StructuredBindings_NoInitializer) { 1368a15adbcdSNathan Ridge assertTypeHints(R"cpp( 1369a15adbcdSNathan Ridge // No initializer (ill-formed). 1370a15adbcdSNathan Ridge // Do not show useless "NULL TYPE" hint. 1371a15adbcdSNathan Ridge auto [x, y]; /*error-ok*/ 13720be2657cSNathan Ridge )cpp"); 13730be2657cSNathan Ridge } 13740be2657cSNathan Ridge 137568d7f690SNathan Ridge TEST(TypeHints, InvalidType) { 137668d7f690SNathan Ridge assertTypeHints(R"cpp( 137768d7f690SNathan Ridge auto x = (unknown_type)42; /*error-ok*/ 137868d7f690SNathan Ridge auto *y = (unknown_ptr)nullptr; 137968d7f690SNathan Ridge )cpp"); 138068d7f690SNathan Ridge } 138168d7f690SNathan Ridge 13820be2657cSNathan Ridge TEST(TypeHints, ReturnTypeDeduction) { 1383e37653daSNathan Ridge assertTypeHints( 1384e37653daSNathan Ridge R"cpp( 1385e37653daSNathan Ridge auto f1(int x$ret1a[[)]]; // Hint forward declaration too 1386e37653daSNathan Ridge auto f1(int x$ret1b[[)]] { return x + 1; } 1387e37653daSNathan Ridge 1388e37653daSNathan Ridge // Include pointer operators in hint 1389e37653daSNathan Ridge int s; 1390e37653daSNathan Ridge auto& f2($ret2[[)]] { return s; } 1391e37653daSNathan Ridge 1392e37653daSNathan Ridge // Do not hint `auto` for trailing return type. 1393e37653daSNathan Ridge auto f3() -> int; 1394e37653daSNathan Ridge 1395257559edSSam McCall // Do not hint when a trailing return type is specified. 1396257559edSSam McCall auto f4() -> auto* { return "foo"; } 1397257559edSSam McCall 139842cb812dSSam McCall auto f5($noreturn[[)]] {} 139942cb812dSSam McCall 1400e37653daSNathan Ridge // `auto` conversion operator 1401e37653daSNathan Ridge struct A { 1402e37653daSNathan Ridge operator auto($retConv[[)]] { return 42; } 1403e37653daSNathan Ridge }; 1404e37653daSNathan Ridge 1405e37653daSNathan Ridge // FIXME: Dependent types do not work yet. 1406e37653daSNathan Ridge template <typename T> 1407e37653daSNathan Ridge struct S { 1408e37653daSNathan Ridge auto method() { return T(); } 1409e37653daSNathan Ridge }; 1410e37653daSNathan Ridge )cpp", 1411e37653daSNathan Ridge ExpectedHint{"-> int", "ret1a"}, ExpectedHint{"-> int", "ret1b"}, 141242cb812dSSam McCall ExpectedHint{"-> int &", "ret2"}, ExpectedHint{"-> void", "noreturn"}, 141342cb812dSSam McCall ExpectedHint{"-> int", "retConv"}); 14140be2657cSNathan Ridge } 14150be2657cSNathan Ridge 14160be2657cSNathan Ridge TEST(TypeHints, DependentType) { 14170be2657cSNathan Ridge assertTypeHints(R"cpp( 14180be2657cSNathan Ridge template <typename T> 14190be2657cSNathan Ridge void foo(T arg) { 14200be2657cSNathan Ridge // The hint would just be "auto" and we can't do any better. 14210be2657cSNathan Ridge auto var1 = arg.method(); 14220be2657cSNathan Ridge // FIXME: It would be nice to show "T" as the hint. 14230be2657cSNathan Ridge auto $var2[[var2]] = arg; 14240be2657cSNathan Ridge } 1425dc10bd43SYuanjing Hong 1426dc10bd43SYuanjing Hong template <typename T> 1427dc10bd43SYuanjing Hong void bar(T arg) { 1428dc10bd43SYuanjing Hong auto [a, b] = arg; 1429dc10bd43SYuanjing Hong } 14300be2657cSNathan Ridge )cpp"); 14310be2657cSNathan Ridge } 14320be2657cSNathan Ridge 1433c2810f2cSNathan Ridge TEST(TypeHints, LongTypeName) { 1434c2810f2cSNathan Ridge assertTypeHints(R"cpp( 1435c2810f2cSNathan Ridge template <typename, typename, typename> 1436c2810f2cSNathan Ridge struct A {}; 1437c2810f2cSNathan Ridge struct MultipleWords {}; 1438c2810f2cSNathan Ridge A<MultipleWords, MultipleWords, MultipleWords> foo(); 1439c2810f2cSNathan Ridge // Omit type hint past a certain length (currently 32) 1440c2810f2cSNathan Ridge auto var = foo(); 1441c2810f2cSNathan Ridge )cpp"); 14422ae1aa9dSzhangyi1357 14432ae1aa9dSzhangyi1357 Config Cfg; 14442ae1aa9dSzhangyi1357 Cfg.InlayHints.TypeNameLimit = 0; 14452ae1aa9dSzhangyi1357 WithContextValue WithCfg(Config::Key, std::move(Cfg)); 14462ae1aa9dSzhangyi1357 14472ae1aa9dSzhangyi1357 assertTypeHints( 14482ae1aa9dSzhangyi1357 R"cpp( 14492ae1aa9dSzhangyi1357 template <typename, typename, typename> 14502ae1aa9dSzhangyi1357 struct A {}; 14512ae1aa9dSzhangyi1357 struct MultipleWords {}; 14522ae1aa9dSzhangyi1357 A<MultipleWords, MultipleWords, MultipleWords> foo(); 14532ae1aa9dSzhangyi1357 // Should have type hint with TypeNameLimit = 0 14542ae1aa9dSzhangyi1357 auto $var[[var]] = foo(); 14552ae1aa9dSzhangyi1357 )cpp", 14562ae1aa9dSzhangyi1357 ExpectedHint{": A<MultipleWords, MultipleWords, MultipleWords>", "var"}); 1457c2810f2cSNathan Ridge } 1458c2810f2cSNathan Ridge 14593e03d92eSNathan Ridge TEST(TypeHints, DefaultTemplateArgs) { 14603e03d92eSNathan Ridge assertTypeHints(R"cpp( 14613e03d92eSNathan Ridge template <typename, typename = int> 14623e03d92eSNathan Ridge struct A {}; 14633e03d92eSNathan Ridge A<float> foo(); 14643e03d92eSNathan Ridge auto $var[[var]] = foo(); 14655cdb906fSYounan Zhang A<float> bar[1]; 14665cdb906fSYounan Zhang auto [$binding[[value]]] = bar; 14673e03d92eSNathan Ridge )cpp", 14685cdb906fSYounan Zhang ExpectedHint{": A<float>", "var"}, 14695cdb906fSYounan Zhang ExpectedHint{": A<float>", "binding"}); 14703e03d92eSNathan Ridge } 14713e03d92eSNathan Ridge 14722eb16991STor Shepherd TEST(DefaultArguments, Smoke) { 14732eb16991STor Shepherd Config Cfg; 14742eb16991STor Shepherd Cfg.InlayHints.Parameters = 14752eb16991STor Shepherd true; // To test interplay of parameters and default parameters 14762eb16991STor Shepherd Cfg.InlayHints.DeducedTypes = false; 14772eb16991STor Shepherd Cfg.InlayHints.Designators = false; 14782eb16991STor Shepherd Cfg.InlayHints.BlockEnd = false; 14792eb16991STor Shepherd 14802eb16991STor Shepherd Cfg.InlayHints.DefaultArguments = true; 14812eb16991STor Shepherd WithContextValue WithCfg(Config::Key, std::move(Cfg)); 14822eb16991STor Shepherd 14832eb16991STor Shepherd const auto *Code = R"cpp( 14842eb16991STor Shepherd int foo(int A = 4) { return A; } 14852eb16991STor Shepherd int bar(int A, int B = 1, bool C = foo($default1[[)]]) { return A; } 14862eb16991STor Shepherd int A = bar($explicit[[2]]$default2[[)]]; 14872eb16991STor Shepherd 14882eb16991STor Shepherd void baz(int = 5) { if (false) baz($unnamed[[)]]; }; 14892eb16991STor Shepherd )cpp"; 14902eb16991STor Shepherd 14912eb16991STor Shepherd assertHints(InlayHintKind::DefaultArgument, Code, 14922eb16991STor Shepherd ExpectedHint{"A: 4", "default1", Left}, 14932eb16991STor Shepherd ExpectedHint{", B: 1, C: foo()", "default2", Left}, 14942eb16991STor Shepherd ExpectedHint{"5", "unnamed", Left}); 14952eb16991STor Shepherd 14962eb16991STor Shepherd assertHints(InlayHintKind::Parameter, Code, 14972eb16991STor Shepherd ExpectedHint{"A: ", "explicit", Left}); 14982eb16991STor Shepherd } 14992eb16991STor Shepherd 15002eb16991STor Shepherd TEST(DefaultArguments, WithoutParameterNames) { 15012eb16991STor Shepherd Config Cfg; 15022eb16991STor Shepherd Cfg.InlayHints.Parameters = false; // To test just default args this time 15032eb16991STor Shepherd Cfg.InlayHints.DeducedTypes = false; 15042eb16991STor Shepherd Cfg.InlayHints.Designators = false; 15052eb16991STor Shepherd Cfg.InlayHints.BlockEnd = false; 15062eb16991STor Shepherd 15072eb16991STor Shepherd Cfg.InlayHints.DefaultArguments = true; 15082eb16991STor Shepherd WithContextValue WithCfg(Config::Key, std::move(Cfg)); 15092eb16991STor Shepherd 15102eb16991STor Shepherd const auto *Code = R"cpp( 15112eb16991STor Shepherd struct Baz { 15122eb16991STor Shepherd Baz(float a = 3 // 15132eb16991STor Shepherd + 2); 15142eb16991STor Shepherd }; 15152eb16991STor Shepherd struct Foo { 15162eb16991STor Shepherd Foo(int, Baz baz = // 15172eb16991STor Shepherd Baz{$abbreviated[[}]] 15182eb16991STor Shepherd 15192eb16991STor Shepherd // 15202eb16991STor Shepherd ) {} 15212eb16991STor Shepherd }; 15222eb16991STor Shepherd 15232eb16991STor Shepherd int main() { 15242eb16991STor Shepherd Foo foo1(1$paren[[)]]; 15252eb16991STor Shepherd Foo foo2{2$brace1[[}]]; 15262eb16991STor Shepherd Foo foo3 = {3$brace2[[}]]; 15272eb16991STor Shepherd auto foo4 = Foo{4$brace3[[}]]; 15282eb16991STor Shepherd } 15292eb16991STor Shepherd )cpp"; 15302eb16991STor Shepherd 15312eb16991STor Shepherd assertHints(InlayHintKind::DefaultArgument, Code, 15322eb16991STor Shepherd ExpectedHint{"...", "abbreviated", Left}, 15332eb16991STor Shepherd ExpectedHint{", Baz{}", "paren", Left}, 15342eb16991STor Shepherd ExpectedHint{", Baz{}", "brace1", Left}, 15352eb16991STor Shepherd ExpectedHint{", Baz{}", "brace2", Left}, 15362eb16991STor Shepherd ExpectedHint{", Baz{}", "brace3", Left}); 15372eb16991STor Shepherd 15382eb16991STor Shepherd assertHints(InlayHintKind::Parameter, Code); 15392eb16991STor Shepherd } 15402eb16991STor Shepherd 1541d87d1aa0SNathan Ridge TEST(TypeHints, Deduplication) { 1542d87d1aa0SNathan Ridge assertTypeHints(R"cpp( 1543d87d1aa0SNathan Ridge template <typename T> 1544d87d1aa0SNathan Ridge void foo() { 1545d87d1aa0SNathan Ridge auto $var[[var]] = 42; 1546d87d1aa0SNathan Ridge } 1547d87d1aa0SNathan Ridge template void foo<int>(); 1548d87d1aa0SNathan Ridge template void foo<float>(); 1549d87d1aa0SNathan Ridge )cpp", 1550d87d1aa0SNathan Ridge ExpectedHint{": int", "var"}); 1551d87d1aa0SNathan Ridge } 1552d87d1aa0SNathan Ridge 15532da5c578SSam McCall TEST(TypeHints, SinglyInstantiatedTemplate) { 15542da5c578SSam McCall assertTypeHints(R"cpp( 15552da5c578SSam McCall auto $lambda[[x]] = [](auto *$param[[y]], auto) { return 42; }; 15562da5c578SSam McCall int m = x("foo", 3); 15572da5c578SSam McCall )cpp", 15582da5c578SSam McCall ExpectedHint{": (lambda)", "lambda"}, 15592da5c578SSam McCall ExpectedHint{": const char *", "param"}); 15602da5c578SSam McCall 15612da5c578SSam McCall // No hint for packs, or auto params following packs 15622da5c578SSam McCall assertTypeHints(R"cpp( 15632da5c578SSam McCall int x(auto $a[[a]], auto... b, auto c) { return 42; } 15642da5c578SSam McCall int m = x<void*, char, float>(nullptr, 'c', 2.0, 2); 15652da5c578SSam McCall )cpp", 15662da5c578SSam McCall ExpectedHint{": void *", "a"}); 15672da5c578SSam McCall } 15682da5c578SSam McCall 15696385c039SSam McCall TEST(TypeHints, Aliased) { 15706385c039SSam McCall // Check that we don't crash for functions without a FunctionTypeLoc. 15716385c039SSam McCall // https://github.com/clangd/clangd/issues/1140 15726385c039SSam McCall TestTU TU = TestTU::withCode("void foo(void){} extern typeof(foo) foo;"); 15736385c039SSam McCall TU.ExtraArgs.push_back("-xc"); 15746385c039SSam McCall auto AST = TU.build(); 15756385c039SSam McCall 15763137ca80SKadir Cetinkaya EXPECT_THAT(hintsOfKind(AST, InlayHintKind::Type), IsEmpty()); 15776385c039SSam McCall } 15786385c039SSam McCall 1579*5518bb21SNathan Ridge TEST(TypeHints, CallingConvention) { 1580*5518bb21SNathan Ridge // Check that we don't crash for lambdas without a FunctionTypeLoc 1581*5518bb21SNathan Ridge // https://github.com/clangd/clangd/issues/2223 1582*5518bb21SNathan Ridge std::string Code = R"cpp( 1583*5518bb21SNathan Ridge void test() { 1584*5518bb21SNathan Ridge []() __cdecl {}; 1585*5518bb21SNathan Ridge } 1586*5518bb21SNathan Ridge )cpp"; 1587*5518bb21SNathan Ridge TestTU TU = TestTU::withCode(Code); 1588*5518bb21SNathan Ridge TU.ExtraArgs.push_back("--target=x86_64-w64-mingw32"); 1589*5518bb21SNathan Ridge TU.PredefineMacros = true; // for the __cdecl 1590*5518bb21SNathan Ridge auto AST = TU.build(); 1591*5518bb21SNathan Ridge 1592*5518bb21SNathan Ridge EXPECT_THAT(hintsOfKind(AST, InlayHintKind::Type), IsEmpty()); 1593*5518bb21SNathan Ridge } 1594*5518bb21SNathan Ridge 15954b2cf982Sv1nh1shungry TEST(TypeHints, Decltype) { 15964b2cf982Sv1nh1shungry assertTypeHints(R"cpp( 15974b2cf982Sv1nh1shungry $a[[decltype(0)]] a; 15984b2cf982Sv1nh1shungry $b[[decltype(a)]] b; 15994b2cf982Sv1nh1shungry const $c[[decltype(0)]] &c = b; 16004b2cf982Sv1nh1shungry 16014b2cf982Sv1nh1shungry // Don't show for dependent type 16024b2cf982Sv1nh1shungry template <class T> 16034b2cf982Sv1nh1shungry constexpr decltype(T{}) d; 16044b2cf982Sv1nh1shungry 16054b2cf982Sv1nh1shungry $e[[decltype(0)]] e(); 16064b2cf982Sv1nh1shungry auto f() -> $f[[decltype(0)]]; 16074b2cf982Sv1nh1shungry 16084b2cf982Sv1nh1shungry template <class, class> struct Foo; 16094b2cf982Sv1nh1shungry using G = Foo<$g[[decltype(0)]], float>; 1610dde8a0feSv1nh1shungry 1611dde8a0feSv1nh1shungry auto $h[[h]] = $i[[decltype(0)]]{}; 1612f7e9d5b4SHaojian Wu 1613f7e9d5b4SHaojian Wu // No crash 1614f7e9d5b4SHaojian Wu /* error-ok */ 1615f7e9d5b4SHaojian Wu auto $j[[s]]; 16164b2cf982Sv1nh1shungry )cpp", 1617dde8a0feSv1nh1shungry ExpectedHint{": int", "a"}, ExpectedHint{": int", "b"}, 16184b2cf982Sv1nh1shungry ExpectedHint{": int", "c"}, ExpectedHint{": int", "e"}, 1619dde8a0feSv1nh1shungry ExpectedHint{": int", "f"}, ExpectedHint{": int", "g"}, 162089590cd1SNathan Ridge ExpectedHint{": int", "h"}, ExpectedHint{": int", "i"}); 16214b2cf982Sv1nh1shungry } 16224b2cf982Sv1nh1shungry 16237d68f2efSYounan Zhang TEST(TypeHints, SubstTemplateParameterAliases) { 1624b1193c13SYounan Zhang llvm::StringRef Header = R"cpp( 16257d68f2efSYounan Zhang template <class T> struct allocator {}; 16267d68f2efSYounan Zhang 16277d68f2efSYounan Zhang template <class T, class A> 16287d68f2efSYounan Zhang struct vector_base { 16297d68f2efSYounan Zhang using pointer = T*; 16307d68f2efSYounan Zhang }; 16317d68f2efSYounan Zhang 16327d68f2efSYounan Zhang template <class T, class A> 16337d68f2efSYounan Zhang struct internal_iterator_type_template_we_dont_expect {}; 16347d68f2efSYounan Zhang 16357d68f2efSYounan Zhang struct my_iterator {}; 16367d68f2efSYounan Zhang 16377d68f2efSYounan Zhang template <class T, class A = allocator<T>> 16387d68f2efSYounan Zhang struct vector : vector_base<T, A> { 16397d68f2efSYounan Zhang using base = vector_base<T, A>; 16407d68f2efSYounan Zhang typedef T value_type; 16417d68f2efSYounan Zhang typedef base::pointer pointer; 16427d68f2efSYounan Zhang using allocator_type = A; 16437d68f2efSYounan Zhang using size_type = int; 16447d68f2efSYounan Zhang using iterator = internal_iterator_type_template_we_dont_expect<T, A>; 16457d68f2efSYounan Zhang using non_template_iterator = my_iterator; 16467d68f2efSYounan Zhang 16477d68f2efSYounan Zhang value_type& operator[](int index) { return elements[index]; } 16487d68f2efSYounan Zhang const value_type& at(int index) const { return elements[index]; } 16497d68f2efSYounan Zhang pointer data() { return &elements[0]; } 16507d68f2efSYounan Zhang allocator_type get_allocator() { return A(); } 16517d68f2efSYounan Zhang size_type size() const { return 10; } 16527d68f2efSYounan Zhang iterator begin() { return iterator(); } 16537d68f2efSYounan Zhang non_template_iterator end() { return non_template_iterator(); } 16547d68f2efSYounan Zhang 16557d68f2efSYounan Zhang T elements[10]; 16567d68f2efSYounan Zhang }; 1657b1193c13SYounan Zhang )cpp"; 16587d68f2efSYounan Zhang 1659b1193c13SYounan Zhang llvm::StringRef VectorIntPtr = R"cpp( 1660b1193c13SYounan Zhang vector<int *> array; 1661b1193c13SYounan Zhang auto $no_modifier[[x]] = array[3]; 1662b1193c13SYounan Zhang auto* $ptr_modifier[[ptr]] = &array[3]; 1663b1193c13SYounan Zhang auto& $ref_modifier[[ref]] = array[3]; 1664b1193c13SYounan Zhang auto& $at[[immutable]] = array.at(3); 1665b1193c13SYounan Zhang 1666b1193c13SYounan Zhang auto $data[[data]] = array.data(); 1667b1193c13SYounan Zhang auto $allocator[[alloc]] = array.get_allocator(); 1668b1193c13SYounan Zhang auto $size[[size]] = array.size(); 1669b1193c13SYounan Zhang auto $begin[[begin]] = array.begin(); 1670b1193c13SYounan Zhang auto $end[[end]] = array.end(); 1671b1193c13SYounan Zhang )cpp"; 1672b1193c13SYounan Zhang 1673b1193c13SYounan Zhang assertHintsWithHeader( 1674b1193c13SYounan Zhang InlayHintKind::Type, VectorIntPtr, Header, 1675b1193c13SYounan Zhang ExpectedHint{": int *", "no_modifier"}, 1676b1193c13SYounan Zhang ExpectedHint{": int **", "ptr_modifier"}, 1677b1193c13SYounan Zhang ExpectedHint{": int *&", "ref_modifier"}, 1678b1193c13SYounan Zhang ExpectedHint{": int *const &", "at"}, ExpectedHint{": int **", "data"}, 1679b1193c13SYounan Zhang ExpectedHint{": allocator<int *>", "allocator"}, 1680b1193c13SYounan Zhang ExpectedHint{": size_type", "size"}, ExpectedHint{": iterator", "begin"}, 1681b1193c13SYounan Zhang ExpectedHint{": non_template_iterator", "end"}); 1682b1193c13SYounan Zhang 1683b1193c13SYounan Zhang llvm::StringRef VectorInt = R"cpp( 16847d68f2efSYounan Zhang vector<int> array; 16857d68f2efSYounan Zhang auto $no_modifier[[by_value]] = array[3]; 16867d68f2efSYounan Zhang auto* $ptr_modifier[[ptr]] = &array[3]; 16877d68f2efSYounan Zhang auto& $ref_modifier[[ref]] = array[3]; 16887d68f2efSYounan Zhang auto& $at[[immutable]] = array.at(3); 16897d68f2efSYounan Zhang 16907d68f2efSYounan Zhang auto $data[[data]] = array.data(); 16917d68f2efSYounan Zhang auto $allocator[[alloc]] = array.get_allocator(); 16927d68f2efSYounan Zhang auto $size[[size]] = array.size(); 16937d68f2efSYounan Zhang auto $begin[[begin]] = array.begin(); 16947d68f2efSYounan Zhang auto $end[[end]] = array.end(); 1695b1193c13SYounan Zhang )cpp"; 16967d68f2efSYounan Zhang 1697b1193c13SYounan Zhang assertHintsWithHeader( 1698b1193c13SYounan Zhang InlayHintKind::Type, VectorInt, Header, 1699b1193c13SYounan Zhang ExpectedHint{": int", "no_modifier"}, 1700b1193c13SYounan Zhang ExpectedHint{": int *", "ptr_modifier"}, 1701b1193c13SYounan Zhang ExpectedHint{": int &", "ref_modifier"}, 1702b1193c13SYounan Zhang ExpectedHint{": const int &", "at"}, ExpectedHint{": int *", "data"}, 1703b1193c13SYounan Zhang ExpectedHint{": allocator<int>", "allocator"}, 1704b1193c13SYounan Zhang ExpectedHint{": size_type", "size"}, ExpectedHint{": iterator", "begin"}, 1705b1193c13SYounan Zhang ExpectedHint{": non_template_iterator", "end"}); 17067d68f2efSYounan Zhang 1707b1193c13SYounan Zhang llvm::StringRef TypeAlias = R"cpp( 17087d68f2efSYounan Zhang // If the type alias is not of substituted template parameter type, 17097d68f2efSYounan Zhang // do not show desugared type. 17107d68f2efSYounan Zhang using VeryLongLongTypeName = my_iterator; 17117d68f2efSYounan Zhang using Short = VeryLongLongTypeName; 17127d68f2efSYounan Zhang 17137d68f2efSYounan Zhang auto $short_name[[my_value]] = Short(); 17147d68f2efSYounan Zhang 17157d68f2efSYounan Zhang // Same applies with templates. 17167d68f2efSYounan Zhang template <typename T, typename A> 17177d68f2efSYounan Zhang using basic_static_vector = vector<T, A>; 17187d68f2efSYounan Zhang template <typename T> 17197d68f2efSYounan Zhang using static_vector = basic_static_vector<T, allocator<T>>; 17207d68f2efSYounan Zhang 17217d68f2efSYounan Zhang auto $vector_name[[vec]] = static_vector<int>(); 1722b1193c13SYounan Zhang )cpp"; 1723b1193c13SYounan Zhang 1724b1193c13SYounan Zhang assertHintsWithHeader(InlayHintKind::Type, TypeAlias, Header, 17257d68f2efSYounan Zhang ExpectedHint{": Short", "short_name"}, 17267d68f2efSYounan Zhang ExpectedHint{": static_vector<int>", "vector_name"}); 17277d68f2efSYounan Zhang } 17287d68f2efSYounan Zhang 1729ce944327SSam McCall TEST(DesignatorHints, Basic) { 1730ce944327SSam McCall assertDesignatorHints(R"cpp( 1731ce944327SSam McCall struct S { int x, y, z; }; 1732ce944327SSam McCall S s {$x[[1]], $y[[2+2]]}; 1733ce944327SSam McCall 1734ce944327SSam McCall int x[] = {$0[[0]], $1[[1]]}; 1735ce944327SSam McCall )cpp", 1736ce944327SSam McCall ExpectedHint{".x=", "x"}, ExpectedHint{".y=", "y"}, 1737ce944327SSam McCall ExpectedHint{"[0]=", "0"}, ExpectedHint{"[1]=", "1"}); 1738ce944327SSam McCall } 1739ce944327SSam McCall 1740ce944327SSam McCall TEST(DesignatorHints, Nested) { 1741ce944327SSam McCall assertDesignatorHints(R"cpp( 1742ce944327SSam McCall struct Inner { int x, y; }; 1743ce944327SSam McCall struct Outer { Inner a, b; }; 1744ce944327SSam McCall Outer o{ $a[[{ $x[[1]], $y[[2]] }]], $bx[[3]] }; 1745ce944327SSam McCall )cpp", 1746ce944327SSam McCall ExpectedHint{".a=", "a"}, ExpectedHint{".x=", "x"}, 1747ce944327SSam McCall ExpectedHint{".y=", "y"}, ExpectedHint{".b.x=", "bx"}); 1748ce944327SSam McCall } 1749ce944327SSam McCall 1750ce944327SSam McCall TEST(DesignatorHints, AnonymousRecord) { 1751ce944327SSam McCall assertDesignatorHints(R"cpp( 1752ce944327SSam McCall struct S { 1753ce944327SSam McCall union { 1754ce944327SSam McCall struct { 1755ce944327SSam McCall struct { 1756ce944327SSam McCall int y; 1757ce944327SSam McCall }; 1758ce944327SSam McCall } x; 1759ce944327SSam McCall }; 1760ce944327SSam McCall }; 1761ce944327SSam McCall S s{$xy[[42]]}; 1762ce944327SSam McCall )cpp", 1763ce944327SSam McCall ExpectedHint{".x.y=", "xy"}); 1764ce944327SSam McCall } 1765ce944327SSam McCall 1766ce944327SSam McCall TEST(DesignatorHints, Suppression) { 1767ce944327SSam McCall assertDesignatorHints(R"cpp( 1768ce944327SSam McCall struct Point { int a, b, c, d, e, f, g, h; }; 1769ce944327SSam McCall Point p{/*a=*/1, .c=2, /* .d = */3, $e[[4]]}; 1770ce944327SSam McCall )cpp", 1771ce944327SSam McCall ExpectedHint{".e=", "e"}); 1772ce944327SSam McCall } 1773ce944327SSam McCall 1774ce944327SSam McCall TEST(DesignatorHints, StdArray) { 1775ce944327SSam McCall // Designators for std::array should be [0] rather than .__elements[0]. 1776ce944327SSam McCall // While technically correct, the designator is useless and horrible to read. 1777ce944327SSam McCall assertDesignatorHints(R"cpp( 1778ce944327SSam McCall template <typename T, int N> struct Array { T __elements[N]; }; 1779ce944327SSam McCall Array<int, 2> x = {$0[[0]], $1[[1]]}; 1780ce944327SSam McCall )cpp", 1781ce944327SSam McCall ExpectedHint{"[0]=", "0"}, ExpectedHint{"[1]=", "1"}); 1782ce944327SSam McCall } 1783ce944327SSam McCall 1784ce944327SSam McCall TEST(DesignatorHints, OnlyAggregateInit) { 1785ce944327SSam McCall assertDesignatorHints(R"cpp( 1786ce944327SSam McCall struct Copyable { int x; } c; 1787ce944327SSam McCall Copyable d{c}; 1788ce944327SSam McCall 1789ce944327SSam McCall struct Constructible { Constructible(int x); }; 1790ce944327SSam McCall Constructible x{42}; 1791ce944327SSam McCall )cpp" /*no designator hints expected (but param hints!)*/); 1792ce944327SSam McCall } 1793ce944327SSam McCall 179406b97b49SHaojian Wu TEST(DesignatorHints, NoCrash) { 179506b97b49SHaojian Wu assertDesignatorHints(R"cpp( 179606b97b49SHaojian Wu /*error-ok*/ 179706b97b49SHaojian Wu struct A {}; 179806b97b49SHaojian Wu struct Foo {int a; int b;}; 179906b97b49SHaojian Wu void test() { 180006b97b49SHaojian Wu Foo f{A(), $b[[1]]}; 180106b97b49SHaojian Wu } 18020fc69b14SYounan Zhang )cpp", 18030fc69b14SYounan Zhang ExpectedHint{".b=", "b"}); 180406b97b49SHaojian Wu } 180506b97b49SHaojian Wu 18067c19fdd5SSam McCall TEST(InlayHints, RestrictRange) { 18077c19fdd5SSam McCall Annotations Code(R"cpp( 18087c19fdd5SSam McCall auto a = false; 18097c19fdd5SSam McCall [[auto b = 1; 18107c19fdd5SSam McCall auto c = '2';]] 18117c19fdd5SSam McCall auto d = 3.f; 18127c19fdd5SSam McCall )cpp"); 18137c19fdd5SSam McCall auto AST = TestTU::withCode(Code.code()).build(); 18147c19fdd5SSam McCall EXPECT_THAT(inlayHints(AST, Code.range()), 18157c19fdd5SSam McCall ElementsAre(labelIs(": int"), labelIs(": char"))); 18167c19fdd5SSam McCall } 18177c19fdd5SSam McCall 18180fc69b14SYounan Zhang TEST(ParameterHints, PseudoObjectExpr) { 18190fc69b14SYounan Zhang Annotations Code(R"cpp( 18200fc69b14SYounan Zhang struct S { 18210fc69b14SYounan Zhang __declspec(property(get=GetX, put=PutX)) int x[]; 18220fc69b14SYounan Zhang int GetX(int y, int z) { return 42 + y; } 18230fc69b14SYounan Zhang void PutX(int) { } 18240fc69b14SYounan Zhang 18250fc69b14SYounan Zhang // This is a PseudoObjectExpression whose syntactic form is a binary 18260fc69b14SYounan Zhang // operator. 18270fc69b14SYounan Zhang void Work(int y) { x = y; } // Not `x = y: y`. 18280fc69b14SYounan Zhang }; 18290fc69b14SYounan Zhang 18300fc69b14SYounan Zhang int printf(const char *Format, ...); 18310fc69b14SYounan Zhang 18320fc69b14SYounan Zhang int main() { 18330fc69b14SYounan Zhang S s; 18340fc69b14SYounan Zhang __builtin_dump_struct(&s, printf); // Not `Format: __builtin_dump_struct()` 18350fc69b14SYounan Zhang printf($Param[["Hello, %d"]], 42); // Normal calls are not affected. 18360fc69b14SYounan Zhang // This builds a PseudoObjectExpr, but here it's useful for showing the 18370fc69b14SYounan Zhang // arguments from the semantic form. 18380fc69b14SYounan Zhang return s.x[ $one[[1]] ][ $two[[2]] ]; // `x[y: 1][z: 2]` 18390fc69b14SYounan Zhang } 18400fc69b14SYounan Zhang )cpp"); 18410fc69b14SYounan Zhang auto TU = TestTU::withCode(Code.code()); 18420fc69b14SYounan Zhang TU.ExtraArgs.push_back("-fms-extensions"); 18430fc69b14SYounan Zhang auto AST = TU.build(); 18440fc69b14SYounan Zhang EXPECT_THAT(inlayHints(AST, std::nullopt), 18450fc69b14SYounan Zhang ElementsAre(HintMatcher(ExpectedHint{"Format: ", "Param"}, Code), 18460fc69b14SYounan Zhang HintMatcher(ExpectedHint{"y: ", "one"}, Code), 18470fc69b14SYounan Zhang HintMatcher(ExpectedHint{"z: ", "two"}, Code))); 18480fc69b14SYounan Zhang } 18490fc69b14SYounan Zhang 18504839929bSKadir Cetinkaya TEST(ParameterHints, ArgPacksAndConstructors) { 18514839929bSKadir Cetinkaya assertParameterHints( 18524839929bSKadir Cetinkaya R"cpp( 18534839929bSKadir Cetinkaya struct Foo{ Foo(); Foo(int x); }; 18544839929bSKadir Cetinkaya void foo(Foo a, int b); 18554839929bSKadir Cetinkaya template <typename... Args> 18564839929bSKadir Cetinkaya void bar(Args... args) { 18574839929bSKadir Cetinkaya foo(args...); 18584839929bSKadir Cetinkaya } 18594839929bSKadir Cetinkaya template <typename... Args> 18604839929bSKadir Cetinkaya void baz(Args... args) { foo($param1[[Foo{args...}]], $param2[[1]]); } 18614839929bSKadir Cetinkaya 18624839929bSKadir Cetinkaya template <typename... Args> 18634839929bSKadir Cetinkaya void bax(Args... args) { foo($param3[[{args...}]], args...); } 18644839929bSKadir Cetinkaya 18654839929bSKadir Cetinkaya void foo() { 18664839929bSKadir Cetinkaya bar($param4[[Foo{}]], $param5[[42]]); 18674839929bSKadir Cetinkaya bar($param6[[42]], $param7[[42]]); 18684839929bSKadir Cetinkaya baz($param8[[42]]); 18694839929bSKadir Cetinkaya bax($param9[[42]]); 18704839929bSKadir Cetinkaya } 18714839929bSKadir Cetinkaya )cpp", 18724839929bSKadir Cetinkaya ExpectedHint{"a: ", "param1"}, ExpectedHint{"b: ", "param2"}, 18734839929bSKadir Cetinkaya ExpectedHint{"a: ", "param3"}, ExpectedHint{"a: ", "param4"}, 18744839929bSKadir Cetinkaya ExpectedHint{"b: ", "param5"}, ExpectedHint{"a: ", "param6"}, 18754839929bSKadir Cetinkaya ExpectedHint{"b: ", "param7"}, ExpectedHint{"x: ", "param8"}, 18764839929bSKadir Cetinkaya ExpectedHint{"b: ", "param9"}); 18774839929bSKadir Cetinkaya } 18784839929bSKadir Cetinkaya 18794839929bSKadir Cetinkaya TEST(ParameterHints, DoesntExpandAllArgs) { 18804839929bSKadir Cetinkaya assertParameterHints( 18814839929bSKadir Cetinkaya R"cpp( 18824839929bSKadir Cetinkaya void foo(int x, int y); 18834839929bSKadir Cetinkaya int id(int a, int b, int c); 18844839929bSKadir Cetinkaya template <typename... Args> 18854839929bSKadir Cetinkaya void bar(Args... args) { 18864839929bSKadir Cetinkaya foo(id($param1[[args]], $param2[[1]], $param3[[args]])...); 18874839929bSKadir Cetinkaya } 18884839929bSKadir Cetinkaya void foo() { 18894839929bSKadir Cetinkaya bar(1, 2); // FIXME: We could have `bar(a: 1, a: 2)` here. 18904839929bSKadir Cetinkaya } 18914839929bSKadir Cetinkaya )cpp", 18924839929bSKadir Cetinkaya ExpectedHint{"a: ", "param1"}, ExpectedHint{"b: ", "param2"}, 18934839929bSKadir Cetinkaya ExpectedHint{"c: ", "param3"}); 18944839929bSKadir Cetinkaya } 18959e6a342fSdaiyousei-qz 18969e6a342fSdaiyousei-qz TEST(BlockEndHints, Functions) { 18979e6a342fSdaiyousei-qz assertBlockEndHints(R"cpp( 18989e6a342fSdaiyousei-qz int foo() { 18999e6a342fSdaiyousei-qz return 41; 19009e6a342fSdaiyousei-qz $foo[[}]] 19019e6a342fSdaiyousei-qz 19029e6a342fSdaiyousei-qz template<int X> 19039e6a342fSdaiyousei-qz int bar() { 19049e6a342fSdaiyousei-qz // No hint for lambda for now 19059e6a342fSdaiyousei-qz auto f = []() { 19069e6a342fSdaiyousei-qz return X; 19079e6a342fSdaiyousei-qz }; 19089e6a342fSdaiyousei-qz return f(); 19099e6a342fSdaiyousei-qz $bar[[}]] 19109e6a342fSdaiyousei-qz 19119e6a342fSdaiyousei-qz // No hint because this isn't a definition 19129e6a342fSdaiyousei-qz int buz(); 19139e6a342fSdaiyousei-qz 19149e6a342fSdaiyousei-qz struct S{}; 19159e6a342fSdaiyousei-qz bool operator==(S, S) { 19169e6a342fSdaiyousei-qz return true; 19179e6a342fSdaiyousei-qz $opEqual[[}]] 19189e6a342fSdaiyousei-qz )cpp", 19199e6a342fSdaiyousei-qz ExpectedHint{" // foo", "foo"}, 19209e6a342fSdaiyousei-qz ExpectedHint{" // bar", "bar"}, 19219e6a342fSdaiyousei-qz ExpectedHint{" // operator==", "opEqual"}); 19229e6a342fSdaiyousei-qz } 19239e6a342fSdaiyousei-qz 19249e6a342fSdaiyousei-qz TEST(BlockEndHints, Methods) { 19259e6a342fSdaiyousei-qz assertBlockEndHints(R"cpp( 19269e6a342fSdaiyousei-qz struct Test { 19279e6a342fSdaiyousei-qz // No hint because there's no function body 19289e6a342fSdaiyousei-qz Test() = default; 19299e6a342fSdaiyousei-qz 19309e6a342fSdaiyousei-qz ~Test() { 19319e6a342fSdaiyousei-qz $dtor[[}]] 19329e6a342fSdaiyousei-qz 19339e6a342fSdaiyousei-qz void method1() { 19349e6a342fSdaiyousei-qz $method1[[}]] 19359e6a342fSdaiyousei-qz 19369e6a342fSdaiyousei-qz // No hint because this isn't a definition 19379e6a342fSdaiyousei-qz void method2(); 19389e6a342fSdaiyousei-qz 19399e6a342fSdaiyousei-qz template <typename T> 19409e6a342fSdaiyousei-qz void method3() { 19419e6a342fSdaiyousei-qz $method3[[}]] 19429e6a342fSdaiyousei-qz 19439e6a342fSdaiyousei-qz // No hint because this isn't a definition 19449e6a342fSdaiyousei-qz template <typename T> 19459e6a342fSdaiyousei-qz void method4(); 19469e6a342fSdaiyousei-qz 19479e6a342fSdaiyousei-qz Test operator+(int) const { 19489e6a342fSdaiyousei-qz return *this; 19499e6a342fSdaiyousei-qz $opIdentity[[}]] 19509e6a342fSdaiyousei-qz 19519e6a342fSdaiyousei-qz operator bool() const { 19529e6a342fSdaiyousei-qz return true; 19539e6a342fSdaiyousei-qz $opBool[[}]] 19549e6a342fSdaiyousei-qz 19559e6a342fSdaiyousei-qz // No hint because there's no function body 19569e6a342fSdaiyousei-qz operator int() const = delete; 19579e6a342fSdaiyousei-qz } x; 19589e6a342fSdaiyousei-qz 19599e6a342fSdaiyousei-qz void Test::method2() { 19609e6a342fSdaiyousei-qz $method2[[}]] 19619e6a342fSdaiyousei-qz 19629e6a342fSdaiyousei-qz template <typename T> 19639e6a342fSdaiyousei-qz void Test::method4() { 19649e6a342fSdaiyousei-qz $method4[[}]] 19659e6a342fSdaiyousei-qz )cpp", 19669e6a342fSdaiyousei-qz ExpectedHint{" // ~Test", "dtor"}, 19679e6a342fSdaiyousei-qz ExpectedHint{" // method1", "method1"}, 19689e6a342fSdaiyousei-qz ExpectedHint{" // method3", "method3"}, 19699e6a342fSdaiyousei-qz ExpectedHint{" // operator+", "opIdentity"}, 19709e6a342fSdaiyousei-qz ExpectedHint{" // operator bool", "opBool"}, 19719e6a342fSdaiyousei-qz ExpectedHint{" // Test::method2", "method2"}, 19729e6a342fSdaiyousei-qz ExpectedHint{" // Test::method4", "method4"}); 19739e6a342fSdaiyousei-qz } 19749e6a342fSdaiyousei-qz 19759e6a342fSdaiyousei-qz TEST(BlockEndHints, Namespaces) { 19769e6a342fSdaiyousei-qz assertBlockEndHints( 19779e6a342fSdaiyousei-qz R"cpp( 19789e6a342fSdaiyousei-qz namespace { 19799e6a342fSdaiyousei-qz void foo(); 19809e6a342fSdaiyousei-qz $anon[[}]] 19819e6a342fSdaiyousei-qz 19829e6a342fSdaiyousei-qz namespace ns { 19839e6a342fSdaiyousei-qz void bar(); 19849e6a342fSdaiyousei-qz $ns[[}]] 19859e6a342fSdaiyousei-qz )cpp", 19869e6a342fSdaiyousei-qz ExpectedHint{" // namespace", "anon"}, 19879e6a342fSdaiyousei-qz ExpectedHint{" // namespace ns", "ns"}); 19889e6a342fSdaiyousei-qz } 19899e6a342fSdaiyousei-qz 19909e6a342fSdaiyousei-qz TEST(BlockEndHints, Types) { 19919e6a342fSdaiyousei-qz assertBlockEndHints( 19929e6a342fSdaiyousei-qz R"cpp( 19939e6a342fSdaiyousei-qz struct S { 19949e6a342fSdaiyousei-qz $S[[};]] 19959e6a342fSdaiyousei-qz 19969e6a342fSdaiyousei-qz class C { 19979e6a342fSdaiyousei-qz $C[[};]] 19989e6a342fSdaiyousei-qz 19999e6a342fSdaiyousei-qz union U { 20009e6a342fSdaiyousei-qz $U[[};]] 20019e6a342fSdaiyousei-qz 20029e6a342fSdaiyousei-qz enum E1 { 20039e6a342fSdaiyousei-qz $E1[[};]] 20049e6a342fSdaiyousei-qz 20059e6a342fSdaiyousei-qz enum class E2 { 20069e6a342fSdaiyousei-qz $E2[[};]] 20079e6a342fSdaiyousei-qz )cpp", 20089e6a342fSdaiyousei-qz ExpectedHint{" // struct S", "S"}, ExpectedHint{" // class C", "C"}, 20099e6a342fSdaiyousei-qz ExpectedHint{" // union U", "U"}, ExpectedHint{" // enum E1", "E1"}, 20109e6a342fSdaiyousei-qz ExpectedHint{" // enum class E2", "E2"}); 20119e6a342fSdaiyousei-qz } 20129e6a342fSdaiyousei-qz 2013ee032bccSSam McCall TEST(BlockEndHints, If) { 2014ee032bccSSam McCall assertBlockEndHints( 2015ee032bccSSam McCall R"cpp( 2016ee032bccSSam McCall void foo(bool cond) { 2017ee032bccSSam McCall if (cond) 2018ee032bccSSam McCall ; 2019ee032bccSSam McCall 2020ee032bccSSam McCall if (cond) { 2021ee032bccSSam McCall $simple[[}]] 2022ee032bccSSam McCall 2023ee032bccSSam McCall if (cond) { 2024ee032bccSSam McCall } else { 2025ee032bccSSam McCall $ifelse[[}]] 2026ee032bccSSam McCall 2027ee032bccSSam McCall if (cond) { 2028ee032bccSSam McCall } else if (!cond) { 2029ee032bccSSam McCall $elseif[[}]] 2030ee032bccSSam McCall 2031ee032bccSSam McCall if (cond) { 2032ee032bccSSam McCall } else { 2033ee032bccSSam McCall if (!cond) { 2034ee032bccSSam McCall $inner[[}]] 2035ee032bccSSam McCall $outer[[}]] 2036ee032bccSSam McCall 2037ee032bccSSam McCall if (auto X = cond) { 2038ee032bccSSam McCall $init[[}]] 2039ee032bccSSam McCall 2040ee032bccSSam McCall if (int i = 0; i > 10) { 2041ee032bccSSam McCall $init_cond[[}]] 2042ee032bccSSam McCall } // suppress 2043ee032bccSSam McCall )cpp", 2044ee032bccSSam McCall ExpectedHint{" // if cond", "simple"}, 2045ee032bccSSam McCall ExpectedHint{" // if cond", "ifelse"}, ExpectedHint{" // if", "elseif"}, 2046ee032bccSSam McCall ExpectedHint{" // if !cond", "inner"}, 2047ee032bccSSam McCall ExpectedHint{" // if cond", "outer"}, ExpectedHint{" // if X", "init"}, 2048ee032bccSSam McCall ExpectedHint{" // if i > 10", "init_cond"}); 2049ee032bccSSam McCall } 2050ee032bccSSam McCall 2051ee032bccSSam McCall TEST(BlockEndHints, Loops) { 2052ee032bccSSam McCall assertBlockEndHints( 2053ee032bccSSam McCall R"cpp( 2054ee032bccSSam McCall void foo() { 2055ee032bccSSam McCall while (true) 2056ee032bccSSam McCall ; 2057ee032bccSSam McCall 2058ee032bccSSam McCall while (true) { 2059ee032bccSSam McCall $while[[}]] 2060ee032bccSSam McCall 2061ee032bccSSam McCall do { 2062ee032bccSSam McCall } while (true); 2063ee032bccSSam McCall 2064ee032bccSSam McCall for (;true;) { 2065ee032bccSSam McCall $forcond[[}]] 2066ee032bccSSam McCall 2067ee032bccSSam McCall for (int I = 0; I < 10; ++I) { 2068ee032bccSSam McCall $forvar[[}]] 2069ee032bccSSam McCall 2070ee032bccSSam McCall int Vs[] = {1,2,3}; 2071ee032bccSSam McCall for (auto V : Vs) { 2072ee032bccSSam McCall $foreach[[}]] 2073ee032bccSSam McCall } // suppress 2074ee032bccSSam McCall )cpp", 2075ee032bccSSam McCall ExpectedHint{" // while true", "while"}, 2076ee032bccSSam McCall ExpectedHint{" // for true", "forcond"}, 2077ee032bccSSam McCall ExpectedHint{" // for I", "forvar"}, 2078ee032bccSSam McCall ExpectedHint{" // for V", "foreach"}); 2079ee032bccSSam McCall } 2080ee032bccSSam McCall 2081ee032bccSSam McCall TEST(BlockEndHints, Switch) { 2082ee032bccSSam McCall assertBlockEndHints( 2083ee032bccSSam McCall R"cpp( 2084ee032bccSSam McCall void foo(int I) { 2085ee032bccSSam McCall switch (I) { 2086ee032bccSSam McCall case 0: break; 2087ee032bccSSam McCall $switch[[}]] 2088ee032bccSSam McCall } // suppress 2089ee032bccSSam McCall )cpp", 2090ee032bccSSam McCall ExpectedHint{" // switch I", "switch"}); 2091ee032bccSSam McCall } 2092ee032bccSSam McCall 2093ee032bccSSam McCall TEST(BlockEndHints, PrintLiterals) { 2094ee032bccSSam McCall assertBlockEndHints( 2095ee032bccSSam McCall R"cpp( 2096ee032bccSSam McCall void foo() { 2097ee032bccSSam McCall while ("foo") { 2098ee032bccSSam McCall $string[[}]] 2099ee032bccSSam McCall 2100ee032bccSSam McCall while ("foo but this time it is very long") { 2101ee032bccSSam McCall $string_long[[}]] 2102ee032bccSSam McCall 2103ee032bccSSam McCall while (true) { 2104ee032bccSSam McCall $boolean[[}]] 2105ee032bccSSam McCall 2106ee032bccSSam McCall while (1) { 2107ee032bccSSam McCall $integer[[}]] 2108ee032bccSSam McCall 2109ee032bccSSam McCall while (1.5) { 2110ee032bccSSam McCall $float[[}]] 2111ee032bccSSam McCall } // suppress 2112ee032bccSSam McCall )cpp", 2113ee032bccSSam McCall ExpectedHint{" // while \"foo\"", "string"}, 2114ee032bccSSam McCall ExpectedHint{" // while \"foo but...\"", "string_long"}, 2115ee032bccSSam McCall ExpectedHint{" // while true", "boolean"}, 2116ee032bccSSam McCall ExpectedHint{" // while 1", "integer"}, 2117ee032bccSSam McCall ExpectedHint{" // while 1.5", "float"}); 2118ee032bccSSam McCall } 2119ee032bccSSam McCall 2120ee032bccSSam McCall TEST(BlockEndHints, PrintRefs) { 2121ee032bccSSam McCall assertBlockEndHints( 2122ee032bccSSam McCall R"cpp( 2123ee032bccSSam McCall namespace ns { 2124ee032bccSSam McCall int Var; 2125ee032bccSSam McCall int func(); 2126ee032bccSSam McCall struct S { 2127ee032bccSSam McCall int Field; 2128ee032bccSSam McCall int method() const; 2129ee032bccSSam McCall }; // suppress 2130ee032bccSSam McCall } // suppress 2131ee032bccSSam McCall void foo() { 2132ee032bccSSam McCall while (ns::Var) { 2133ee032bccSSam McCall $var[[}]] 2134ee032bccSSam McCall 2135ee032bccSSam McCall while (ns::func()) { 2136ee032bccSSam McCall $func[[}]] 2137ee032bccSSam McCall 2138ee032bccSSam McCall while (ns::S{}.Field) { 2139ee032bccSSam McCall $field[[}]] 2140ee032bccSSam McCall 2141ee032bccSSam McCall while (ns::S{}.method()) { 2142ee032bccSSam McCall $method[[}]] 2143ee032bccSSam McCall } // suppress 2144ee032bccSSam McCall )cpp", 2145ee032bccSSam McCall ExpectedHint{" // while Var", "var"}, 2146ee032bccSSam McCall ExpectedHint{" // while func", "func"}, 2147ee032bccSSam McCall ExpectedHint{" // while Field", "field"}, 2148ee032bccSSam McCall ExpectedHint{" // while method", "method"}); 2149ee032bccSSam McCall } 2150ee032bccSSam McCall 2151ee032bccSSam McCall TEST(BlockEndHints, PrintConversions) { 2152ee032bccSSam McCall assertBlockEndHints( 2153ee032bccSSam McCall R"cpp( 2154ee032bccSSam McCall struct S { 2155ee032bccSSam McCall S(int); 2156ee032bccSSam McCall S(int, int); 2157ee032bccSSam McCall explicit operator bool(); 2158ee032bccSSam McCall }; // suppress 2159ee032bccSSam McCall void foo(int I) { 2160ee032bccSSam McCall while (float(I)) { 2161ee032bccSSam McCall $convert_primitive[[}]] 2162ee032bccSSam McCall 2163ee032bccSSam McCall while (S(I)) { 2164ee032bccSSam McCall $convert_class[[}]] 2165ee032bccSSam McCall 2166ee032bccSSam McCall while (S(I, I)) { 2167ee032bccSSam McCall $construct_class[[}]] 2168ee032bccSSam McCall } // suppress 2169ee032bccSSam McCall )cpp", 2170ee032bccSSam McCall ExpectedHint{" // while float", "convert_primitive"}, 2171ee032bccSSam McCall ExpectedHint{" // while S", "convert_class"}, 2172ee032bccSSam McCall ExpectedHint{" // while S", "construct_class"}); 2173ee032bccSSam McCall } 2174ee032bccSSam McCall 2175ee032bccSSam McCall TEST(BlockEndHints, PrintOperators) { 2176ee032bccSSam McCall std::string AnnotatedCode = R"cpp( 2177ee032bccSSam McCall void foo(Integer I) { 2178ee032bccSSam McCall while(++I){ 2179ee032bccSSam McCall $preinc[[}]] 2180ee032bccSSam McCall 2181ee032bccSSam McCall while(I++){ 2182ee032bccSSam McCall $postinc[[}]] 2183ee032bccSSam McCall 2184ee032bccSSam McCall while(+(I + I)){ 2185ee032bccSSam McCall $unary_complex[[}]] 2186ee032bccSSam McCall 2187ee032bccSSam McCall while(I < 0){ 2188ee032bccSSam McCall $compare[[}]] 2189ee032bccSSam McCall 2190ee032bccSSam McCall while((I + I) < I){ 2191ee032bccSSam McCall $lhs_complex[[}]] 2192ee032bccSSam McCall 2193ee032bccSSam McCall while(I < (I + I)){ 2194ee032bccSSam McCall $rhs_complex[[}]] 2195ee032bccSSam McCall 2196ee032bccSSam McCall while((I + I) < (I + I)){ 2197ee032bccSSam McCall $binary_complex[[}]] 2198ee032bccSSam McCall } // suppress 2199ee032bccSSam McCall )cpp"; 2200ee032bccSSam McCall 2201ee032bccSSam McCall // We can't store shared expectations in a vector, assertHints uses varargs. 2202ee032bccSSam McCall auto AssertExpectedHints = [&](llvm::StringRef Code) { 2203ee032bccSSam McCall assertBlockEndHints(Code, ExpectedHint{" // while ++I", "preinc"}, 2204ee032bccSSam McCall ExpectedHint{" // while I++", "postinc"}, 2205ee032bccSSam McCall ExpectedHint{" // while", "unary_complex"}, 2206ee032bccSSam McCall ExpectedHint{" // while I < 0", "compare"}, 2207ee032bccSSam McCall ExpectedHint{" // while ... < I", "lhs_complex"}, 2208ee032bccSSam McCall ExpectedHint{" // while I < ...", "rhs_complex"}, 2209ee032bccSSam McCall ExpectedHint{" // while", "binary_complex"}); 2210ee032bccSSam McCall }; 2211ee032bccSSam McCall 2212ee032bccSSam McCall // First with built-in operators. 2213ee032bccSSam McCall AssertExpectedHints("using Integer = int;" + AnnotatedCode); 2214ee032bccSSam McCall // And now with overloading! 2215ee032bccSSam McCall AssertExpectedHints(R"cpp( 2216ee032bccSSam McCall struct Integer { 2217ee032bccSSam McCall explicit operator bool(); 2218ee032bccSSam McCall Integer operator++(); 2219ee032bccSSam McCall Integer operator++(int); 2220ee032bccSSam McCall Integer operator+(Integer); 2221ee032bccSSam McCall Integer operator+(); 2222ee032bccSSam McCall bool operator<(Integer); 2223ee032bccSSam McCall bool operator<(int); 2224ee032bccSSam McCall }; // suppress 2225ee032bccSSam McCall )cpp" + AnnotatedCode); 2226ee032bccSSam McCall } 2227ee032bccSSam McCall 22289e6a342fSdaiyousei-qz TEST(BlockEndHints, TrailingSemicolon) { 22299e6a342fSdaiyousei-qz assertBlockEndHints(R"cpp( 22309e6a342fSdaiyousei-qz // The hint is placed after the trailing ';' 22319e6a342fSdaiyousei-qz struct S1 { 22329e6a342fSdaiyousei-qz $S1[[} ;]] 22339e6a342fSdaiyousei-qz 22349e6a342fSdaiyousei-qz // The hint is always placed in the same line with the closing '}'. 22359e6a342fSdaiyousei-qz // So in this case where ';' is missing, it is attached to '}'. 22369e6a342fSdaiyousei-qz struct S2 { 22379e6a342fSdaiyousei-qz $S2[[}]] 22389e6a342fSdaiyousei-qz 22399e6a342fSdaiyousei-qz ; 22409e6a342fSdaiyousei-qz 22419e6a342fSdaiyousei-qz // No hint because only one trailing ';' is allowed 22429e6a342fSdaiyousei-qz struct S3 { 22439e6a342fSdaiyousei-qz };; 22449e6a342fSdaiyousei-qz 22459e6a342fSdaiyousei-qz // No hint because trailing ';' is only allowed for class/struct/union/enum 22469e6a342fSdaiyousei-qz void foo() { 22479e6a342fSdaiyousei-qz }; 22489e6a342fSdaiyousei-qz 22499e6a342fSdaiyousei-qz // Rare case, but yes we'll have a hint here. 22509e6a342fSdaiyousei-qz struct { 22519e6a342fSdaiyousei-qz int x; 22529e6a342fSdaiyousei-qz $anon[[}]] 22539e6a342fSdaiyousei-qz 22549e6a342fSdaiyousei-qz s2; 22559e6a342fSdaiyousei-qz )cpp", 22569e6a342fSdaiyousei-qz ExpectedHint{" // struct S1", "S1"}, 22579e6a342fSdaiyousei-qz ExpectedHint{" // struct S2", "S2"}, 22589e6a342fSdaiyousei-qz ExpectedHint{" // struct", "anon"}); 22599e6a342fSdaiyousei-qz } 22609e6a342fSdaiyousei-qz 22619e6a342fSdaiyousei-qz TEST(BlockEndHints, TrailingText) { 22629e6a342fSdaiyousei-qz assertBlockEndHints(R"cpp( 22639e6a342fSdaiyousei-qz struct S1 { 22649e6a342fSdaiyousei-qz $S1[[} ;]] 22659e6a342fSdaiyousei-qz 22669e6a342fSdaiyousei-qz // No hint for S2 because of the trailing comment 22679e6a342fSdaiyousei-qz struct S2 { 22689e6a342fSdaiyousei-qz }; /* Put anything here */ 22699e6a342fSdaiyousei-qz 22709e6a342fSdaiyousei-qz struct S3 { 22719e6a342fSdaiyousei-qz // No hint for S4 because of the trailing source code 22729e6a342fSdaiyousei-qz struct S4 { 22739e6a342fSdaiyousei-qz };$S3[[};]] 22749e6a342fSdaiyousei-qz 22759e6a342fSdaiyousei-qz // No hint for ns because of the trailing comment 22769e6a342fSdaiyousei-qz namespace ns { 22779e6a342fSdaiyousei-qz } // namespace ns 22789e6a342fSdaiyousei-qz )cpp", 22799e6a342fSdaiyousei-qz ExpectedHint{" // struct S1", "S1"}, 22809e6a342fSdaiyousei-qz ExpectedHint{" // struct S3", "S3"}); 22819e6a342fSdaiyousei-qz } 22829e6a342fSdaiyousei-qz 22839e6a342fSdaiyousei-qz TEST(BlockEndHints, Macro) { 22849e6a342fSdaiyousei-qz assertBlockEndHints(R"cpp( 22859e6a342fSdaiyousei-qz #define DECL_STRUCT(NAME) struct NAME { 22869e6a342fSdaiyousei-qz #define RBRACE } 22879e6a342fSdaiyousei-qz 22889e6a342fSdaiyousei-qz DECL_STRUCT(S1) 22899e6a342fSdaiyousei-qz $S1[[};]] 22909e6a342fSdaiyousei-qz 22919e6a342fSdaiyousei-qz // No hint because we require a '}' 22929e6a342fSdaiyousei-qz DECL_STRUCT(S2) 22939e6a342fSdaiyousei-qz RBRACE; 22949e6a342fSdaiyousei-qz )cpp", 22959e6a342fSdaiyousei-qz ExpectedHint{" // struct S1", "S1"}); 22969e6a342fSdaiyousei-qz } 22979e6a342fSdaiyousei-qz 2298dbd1fb8eSNathan Ridge TEST(BlockEndHints, PointerToMemberFunction) { 2299dbd1fb8eSNathan Ridge // Do not crash trying to summarize `a->*p`. 2300dbd1fb8eSNathan Ridge assertBlockEndHints(R"cpp( 2301dbd1fb8eSNathan Ridge class A {}; 2302dbd1fb8eSNathan Ridge using Predicate = bool(A::*)(); 2303dbd1fb8eSNathan Ridge void foo(A* a, Predicate p) { 2304dbd1fb8eSNathan Ridge if ((a->*p)()) { 2305dbd1fb8eSNathan Ridge $ptrmem[[}]] 2306dbd1fb8eSNathan Ridge } // suppress 2307dbd1fb8eSNathan Ridge )cpp", 2308dbd1fb8eSNathan Ridge ExpectedHint{" // if", "ptrmem"}); 2309dbd1fb8eSNathan Ridge } 2310dbd1fb8eSNathan Ridge 23110be2657cSNathan Ridge // FIXME: Low-hanging fruit where we could omit a type hint: 23120be2657cSNathan Ridge // - auto x = TypeName(...); 23130be2657cSNathan Ridge // - auto x = (TypeName) (...); 23140be2657cSNathan Ridge // - auto x = static_cast<TypeName>(...); // and other built-in casts 23150be2657cSNathan Ridge 23160be2657cSNathan Ridge // Annoyances for which a heuristic is not obvious: 23170be2657cSNathan Ridge // - auto x = llvm::dyn_cast<LongTypeName>(y); // and similar 23180be2657cSNathan Ridge // - stdlib algos return unwieldy __normal_iterator<X*, ...> type 23190be2657cSNathan Ridge // (For this one, perhaps we should omit type hints that start 23200be2657cSNathan Ridge // with a double underscore.) 23210be2657cSNathan Ridge 2322cbc9c4eaSNathan Ridge } // namespace 2323cbc9c4eaSNathan Ridge } // namespace clangd 2324cbc9c4eaSNathan Ridge } // namespace clang 2325