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 | |
23 | namespace clang { |
24 | namespace clangd { |
25 | |
26 | llvm::raw_ostream &operator<<(llvm::raw_ostream &Stream, |
27 | const InlayHint &Hint) { |
28 | return Stream << Hint.joinLabels() << "@" << Hint.range; |
29 | } |
30 | |
31 | namespace { |
32 | |
33 | using ::testing::ElementsAre; |
34 | using ::testing::IsEmpty; |
35 | |
36 | std::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 | |
45 | enum HintSide { Left, Right }; |
46 | |
47 | struct 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 | |
58 | MATCHER_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 | |
76 | MATCHER_P(labelIs, Label, "" ) { return arg.joinLabels() == Label; } |
77 | |
78 | Config 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 | |
87 | template <typename... ExpectedHints> |
88 | void (InlayHintKind Kind, llvm::StringRef AnnotatedSource, |
89 | llvm::StringRef , |
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 | |
105 | template <typename... ExpectedHints> |
106 | void 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. |
113 | template <typename... T> void ignore(T &&...) {} |
114 | |
115 | template <typename... ExpectedHints> |
116 | void assertParameterHints(llvm::StringRef AnnotatedSource, |
117 | ExpectedHints... Expected) { |
118 | ignore(Expected.Side = Left...); |
119 | assertHints(InlayHintKind::Parameter, AnnotatedSource, Expected...); |
120 | } |
121 | |
122 | template <typename... ExpectedHints> |
123 | void assertTypeHints(llvm::StringRef AnnotatedSource, |
124 | ExpectedHints... Expected) { |
125 | ignore(Expected.Side = Right...); |
126 | assertHints(InlayHintKind::Type, AnnotatedSource, Expected...); |
127 | } |
128 | |
129 | template <typename... ExpectedHints> |
130 | void 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 | |
138 | template <typename... ExpectedHints> |
139 | void 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 | |
147 | TEST(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 | |
157 | TEST(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 | |
167 | TEST(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 | |
177 | TEST(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 | |
189 | TEST(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 | |
199 | TEST(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 | |
210 | TEST(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 | |
224 | TEST(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 | |
236 | TEST(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 | |
248 | TEST(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 | |
261 | TEST(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 | |
278 | TEST(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 | |
290 | TEST(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 | |
301 | TEST(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 | |
314 | TEST(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: "¶m: " , .RangeName: "param" }); |
324 | } |
325 | |
326 | TEST(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: "¶m: " , .RangeName: "param" }); |
337 | } |
338 | |
339 | TEST(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 | |
350 | TEST(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 | |
366 | TEST(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 | |
380 | TEST(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 | |
396 | TEST(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 | |
410 | TEST(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 | |
426 | TEST(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 | |
439 | TEST(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 | |
453 | TEST(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 | |
474 | TEST(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 | |
492 | TEST(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 | |
514 | TEST(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 | |
540 | TEST(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 | |
562 | TEST(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 | |
577 | TEST(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 | |
594 | TEST(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 | |
619 | TEST(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 | |
642 | TEST(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 | |
666 | TEST(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 | |
705 | TEST(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 | |
718 | TEST(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 | |
732 | TEST(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 | |
746 | TEST(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 | |
760 | TEST(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 | |
771 | TEST(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 | |
786 | TEST(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 | |
799 | TEST(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 | |
811 | TEST(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 | |
847 | TEST(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 | |
875 | TEST(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 | |
920 | TEST(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 | |
932 | TEST(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 | |
944 | TEST(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 | |
959 | TEST(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 | |
972 | TEST(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 | |
987 | TEST(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 | |
1007 | TEST(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 | |
1031 | TEST(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 | |
1046 | TEST(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 | |
1057 | TEST(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 | |
1088 | TEST(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 | |
1100 | TEST(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 | |
1111 | TEST(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 | |
1127 | TEST(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 | |
1141 | TEST(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 | |
1151 | TEST(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 | |
1171 | TEST(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 | |
1195 | TEST(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 | |
1206 | TEST(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 | |
1228 | TEST(TypeHints, Smoke) { |
1229 | assertTypeHints(AnnotatedSource: R"cpp( |
1230 | auto $waldo[[waldo]] = 42; |
1231 | )cpp" , |
1232 | Expected: ExpectedHint{.Label: ": int" , .RangeName: "waldo" }); |
1233 | } |
1234 | |
1235 | TEST(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 | |
1247 | TEST(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 | |
1256 | TEST(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 | |
1283 | TEST(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 | |
1307 | TEST(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 | |
1320 | TEST(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 | |
1328 | TEST(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 | |
1363 | TEST(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 | |
1371 | TEST(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 | |
1378 | TEST(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 | |
1412 | TEST(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 | |
1429 | TEST(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 | |
1455 | TEST(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 | |
1468 | TEST(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 | |
1480 | TEST(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 | |
1496 | TEST(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 | |
1506 | TEST(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 | |
1534 | TEST(TypeHints, SubstTemplateParameterAliases) { |
1535 | llvm::StringRef = 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 | |
1640 | TEST(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 | |
1651 | TEST(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 | |
1661 | TEST(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 | |
1677 | TEST(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 | |
1685 | TEST(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 | |
1695 | TEST(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 | |
1705 | TEST(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 | |
1717 | TEST(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 | |
1729 | TEST(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 | |
1761 | TEST(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 | |
1790 | TEST(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 | |
1807 | TEST(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 | |
1835 | TEST(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 | |
1886 | TEST(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 | |
1901 | TEST(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 | |
1924 | TEST(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 | |
1962 | TEST(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 | |
1992 | TEST(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 | |
2004 | TEST(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 | |
2031 | TEST(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 | |
2062 | TEST(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 | |
2086 | TEST(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 | |
2139 | TEST(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 | |
2172 | TEST(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 | |
2194 | TEST(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 | |
2209 | TEST(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 | |