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

source code of clang-tools-extra/clangd/unittests/InlayHintTests.cpp