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