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

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