clang-tools  15.0.0git
InlayHintTests.cpp
Go to the documentation of this file.
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 
21 namespace clang {
22 namespace clangd {
23 
24 llvm::raw_ostream &operator<<(llvm::raw_ostream &Stream,
25  const InlayHint &Hint) {
26  return Stream << Hint.label << "@" << Hint.range;
27 }
28 
29 namespace {
30 
31 using ::testing::ElementsAre;
32 using ::testing::IsEmpty;
33 
34 std::vector<InlayHint> hintsOfKind(ParsedAST &AST, InlayHintKind Kind) {
35  std::vector<InlayHint> Result;
36  for (auto &Hint : inlayHints(AST, /*RestrictRange=*/llvm::None)) {
37  if (Hint.kind == Kind)
38  Result.push_back(Hint);
39  }
40  return Result;
41 }
42 
43 enum HintSide { Left, Right };
44 
45 struct ExpectedHint {
46  std::string Label;
47  std::string RangeName;
48  HintSide Side = Left;
49 
50  friend llvm::raw_ostream &operator<<(llvm::raw_ostream &Stream,
51  const ExpectedHint &Hint) {
52  return Stream << Hint.Label << "@$" << Hint.RangeName;
53  }
54 };
55 
56 MATCHER_P2(HintMatcher, Expected, Code, llvm::to_string(Expected)) {
57  llvm::StringRef ExpectedView(Expected.Label);
58  if (arg.label != ExpectedView.trim(" ") ||
59  arg.paddingLeft != ExpectedView.startswith(" ") ||
60  arg.paddingRight != ExpectedView.endswith(" ")) {
61  *result_listener << "label is '" << arg.label << "'";
62  return false;
63  }
64  if (arg.range != Code.range(Expected.RangeName)) {
65  *result_listener << "range is " << llvm::to_string(arg.range) << " but $"
66  << Expected.RangeName << " is "
67  << llvm::to_string(Code.range(Expected.RangeName));
68  return false;
69  }
70  return true;
71 }
72 
73 MATCHER_P(labelIs, Label, "") { return arg.label == Label; }
74 
75 Config noHintsConfig() {
76  Config C;
77  C.InlayHints.Parameters = false;
78  C.InlayHints.DeducedTypes = false;
79  C.InlayHints.Designators = false;
80  return C;
81 }
82 
83 template <typename... ExpectedHints>
84 void assertHints(InlayHintKind Kind, llvm::StringRef AnnotatedSource,
85  ExpectedHints... Expected) {
86  Annotations Source(AnnotatedSource);
87  TestTU TU = TestTU::withCode(Source.code());
88  TU.ExtraArgs.push_back("-std=c++20");
89  auto AST = TU.build();
90 
91  EXPECT_THAT(hintsOfKind(AST, Kind),
92  ElementsAre(HintMatcher(Expected, Source)...));
93  // Sneak in a cross-cutting check that hints are disabled by config.
94  // We'll hit an assertion failure if addInlayHint still gets called.
95  WithContextValue WithCfg(Config::Key, noHintsConfig());
96  EXPECT_THAT(inlayHints(AST, llvm::None), IsEmpty());
97 }
98 
99 // Hack to allow expression-statements operating on parameter packs in C++14.
100 template <typename... T> void ignore(T &&...) {}
101 
102 template <typename... ExpectedHints>
103 void assertParameterHints(llvm::StringRef AnnotatedSource,
104  ExpectedHints... Expected) {
105  ignore(Expected.Side = Left...);
106  assertHints(InlayHintKind::Parameter, AnnotatedSource, Expected...);
107 }
108 
109 template <typename... ExpectedHints>
110 void assertTypeHints(llvm::StringRef AnnotatedSource,
111  ExpectedHints... Expected) {
112  ignore(Expected.Side = Right...);
113  assertHints(InlayHintKind::Type, AnnotatedSource, Expected...);
114 }
115 
116 template <typename... ExpectedHints>
117 void assertDesignatorHints(llvm::StringRef AnnotatedSource,
118  ExpectedHints... Expected) {
119  Config Cfg;
120  Cfg.InlayHints.Designators = true;
121  WithContextValue WithCfg(Config::Key, std::move(Cfg));
122  assertHints(InlayHintKind::Designator, AnnotatedSource, Expected...);
123 }
124 
125 TEST(ParameterHints, Smoke) {
126  assertParameterHints(R"cpp(
127  void foo(int param);
128  void bar() {
129  foo($param[[42]]);
130  }
131  )cpp",
132  ExpectedHint{"param: ", "param"});
133 }
134 
135 TEST(ParameterHints, NoName) {
136  // No hint for anonymous parameter.
137  assertParameterHints(R"cpp(
138  void foo(int);
139  void bar() {
140  foo(42);
141  }
142  )cpp");
143 }
144 
145 TEST(ParameterHints, NoNameConstReference) {
146  // No hint for anonymous const l-value ref parameter.
147  assertParameterHints(R"cpp(
148  void foo(const int&);
149  void bar() {
150  foo(42);
151  }
152  )cpp");
153 }
154 
155 TEST(ParameterHints, NoNameReference) {
156  // Reference hint for anonymous l-value ref parameter.
157  assertParameterHints(R"cpp(
158  void foo(int&);
159  void bar() {
160  int i;
161  foo($param[[i]]);
162  }
163  )cpp",
164  ExpectedHint{"&: ", "param"});
165 }
166 
167 TEST(ParameterHints, NoNameRValueReference) {
168  // No reference hint for anonymous r-value ref parameter.
169  assertParameterHints(R"cpp(
170  void foo(int&&);
171  void bar() {
172  foo(42);
173  }
174  )cpp");
175 }
176 
177 TEST(ParameterHints, NameInDefinition) {
178  // Parameter name picked up from definition if necessary.
179  assertParameterHints(R"cpp(
180  void foo(int);
181  void bar() {
182  foo($param[[42]]);
183  }
184  void foo(int param) {};
185  )cpp",
186  ExpectedHint{"param: ", "param"});
187 }
188 
189 TEST(ParameterHints, NameMismatch) {
190  // Prefer name from declaration.
191  assertParameterHints(R"cpp(
192  void foo(int good);
193  void bar() {
194  foo($good[[42]]);
195  }
196  void foo(int bad) {};
197  )cpp",
198  ExpectedHint{"good: ", "good"});
199 }
200 
201 TEST(ParameterHints, NameConstReference) {
202  // Only name hint for const l-value ref parameter.
203  assertParameterHints(R"cpp(
204  void foo(const int& param);
205  void bar() {
206  foo($param[[42]]);
207  }
208  )cpp",
209  ExpectedHint{"param: ", "param"});
210 }
211 
212 TEST(ParameterHints, NameTypeAliasConstReference) {
213  // Only name hint for const l-value ref parameter via type alias.
214  assertParameterHints(R"cpp(
215  using alias = const int&;
216  void foo(alias param);
217  void bar() {
218  int i;
219  foo($param[[i]]);
220  }
221  )cpp",
222  ExpectedHint{"param: ", "param"});
223 }
224 
225 TEST(ParameterHints, NameReference) {
226  // Reference and name hint for l-value ref parameter.
227  assertParameterHints(R"cpp(
228  void foo(int& param);
229  void bar() {
230  int i;
231  foo($param[[i]]);
232  }
233  )cpp",
234  ExpectedHint{"&param: ", "param"});
235 }
236 
237 TEST(ParameterHints, NameTypeAliasReference) {
238  // Reference and name hint for l-value ref parameter via type alias.
239  assertParameterHints(R"cpp(
240  using alias = int&;
241  void foo(alias param);
242  void bar() {
243  int i;
244  foo($param[[i]]);
245  }
246  )cpp",
247  ExpectedHint{"&param: ", "param"});
248 }
249 
250 TEST(ParameterHints, NameRValueReference) {
251  // Only name hint for r-value ref parameter.
252  assertParameterHints(R"cpp(
253  void foo(int&& param);
254  void bar() {
255  foo($param[[42]]);
256  }
257  )cpp",
258  ExpectedHint{"param: ", "param"});
259 }
260 
261 TEST(ParameterHints, Operator) {
262  // No hint for operator call with operator syntax.
263  assertParameterHints(R"cpp(
264  struct S {};
265  void operator+(S lhs, S rhs);
266  void bar() {
267  S a, b;
268  a + b;
269  }
270  )cpp");
271 }
272 
273 TEST(ParameterHints, Macros) {
274  // Handling of macros depends on where the call's argument list comes from.
275 
276  // If it comes from a macro definition, there's nothing to hint
277  // at the invocation site.
278  assertParameterHints(R"cpp(
279  void foo(int param);
280  #define ExpandsToCall() foo(42)
281  void bar() {
282  ExpandsToCall();
283  }
284  )cpp");
285 
286  // The argument expression being a macro invocation shouldn't interfere
287  // with hinting.
288  assertParameterHints(R"cpp(
289  #define PI 3.14
290  void foo(double param);
291  void bar() {
292  foo($param[[PI]]);
293  }
294  )cpp",
295  ExpectedHint{"param: ", "param"});
296 
297  // If the whole argument list comes from a macro parameter, hint it.
298  assertParameterHints(R"cpp(
299  void abort();
300  #define ASSERT(expr) if (!expr) abort()
301  int foo(int param);
302  void bar() {
303  ASSERT(foo($param[[42]]) == 0);
304  }
305  )cpp",
306  ExpectedHint{"param: ", "param"});
307 }
308 
309 TEST(ParameterHints, ConstructorParens) {
310  assertParameterHints(R"cpp(
311  struct S {
312  S(int param);
313  };
314  void bar() {
315  S obj($param[[42]]);
316  }
317  )cpp",
318  ExpectedHint{"param: ", "param"});
319 }
320 
321 TEST(ParameterHints, ConstructorBraces) {
322  assertParameterHints(R"cpp(
323  struct S {
324  S(int param);
325  };
326  void bar() {
327  S obj{$param[[42]]};
328  }
329  )cpp",
330  ExpectedHint{"param: ", "param"});
331 }
332 
333 TEST(ParameterHints, ConstructorStdInitList) {
334  // Do not show hints for std::initializer_list constructors.
335  assertParameterHints(R"cpp(
336  namespace std {
337  template <typename> class initializer_list {};
338  }
339  struct S {
340  S(std::initializer_list<int> param);
341  };
342  void bar() {
343  S obj{42, 43};
344  }
345  )cpp");
346 }
347 
348 TEST(ParameterHints, MemberInit) {
349  assertParameterHints(R"cpp(
350  struct S {
351  S(int param);
352  };
353  struct T {
354  S member;
355  T() : member($param[[42]]) {}
356  };
357  )cpp",
358  ExpectedHint{"param: ", "param"});
359 }
360 
361 TEST(ParameterHints, ImplicitConstructor) {
362  assertParameterHints(R"cpp(
363  struct S {
364  S(int param);
365  };
366  void bar(S);
367  S foo() {
368  // Do not show hint for implicit constructor call in argument.
369  bar(42);
370  // Do not show hint for implicit constructor call in return.
371  return 42;
372  }
373  )cpp");
374 }
375 
376 TEST(ParameterHints, ArgMatchesParam) {
377  assertParameterHints(R"cpp(
378  void foo(int param);
379  struct S {
380  static const int param = 42;
381  };
382  void bar() {
383  int param = 42;
384  // Do not show redundant "param: param".
385  foo(param);
386  // But show it if the argument is qualified.
387  foo($param[[S::param]]);
388  }
389  struct A {
390  int param;
391  void bar() {
392  // Do not show "param: param" for member-expr.
393  foo(param);
394  }
395  };
396  )cpp",
397  ExpectedHint{"param: ", "param"});
398 }
399 
400 TEST(ParameterHints, ArgMatchesParamReference) {
401  assertParameterHints(R"cpp(
402  void foo(int& param);
403  void foo2(const int& param);
404  void bar() {
405  int param;
406  // show reference hint on mutable reference
407  foo($param[[param]]);
408  // but not on const reference
409  foo2(param);
410  }
411  )cpp",
412  ExpectedHint{"&: ", "param"});
413 }
414 
415 TEST(ParameterHints, LeadingUnderscore) {
416  assertParameterHints(R"cpp(
417  void foo(int p1, int _p2, int __p3);
418  void bar() {
419  foo($p1[[41]], $p2[[42]], $p3[[43]]);
420  }
421  )cpp",
422  ExpectedHint{"p1: ", "p1"}, ExpectedHint{"p2: ", "p2"},
423  ExpectedHint{"p3: ", "p3"});
424 }
425 
426 TEST(ParameterHints, DependentCalls) {
427  assertParameterHints(R"cpp(
428  template <typename T>
429  void nonmember(T par1);
430 
431  template <typename T>
432  struct A {
433  void member(T par2);
434  static void static_member(T par3);
435  };
436 
437  void overload(int anInt);
438  void overload(double aDouble);
439 
440  template <typename T>
441  struct S {
442  void bar(A<T> a, T t) {
443  nonmember($par1[[t]]);
444  a.member($par2[[t]]);
445  A<T>::static_member($par3[[t]]);
446  // We don't want to arbitrarily pick between
447  // "anInt" or "aDouble", so just show no hint.
448  overload(T{});
449  }
450  };
451  )cpp",
452  ExpectedHint{"par1: ", "par1"},
453  ExpectedHint{"par2: ", "par2"},
454  ExpectedHint{"par3: ", "par3"});
455 }
456 
457 TEST(ParameterHints, VariadicFunction) {
458  assertParameterHints(R"cpp(
459  template <typename... T>
460  void foo(int fixed, T... variadic);
461 
462  void bar() {
463  foo($fixed[[41]], 42, 43);
464  }
465  )cpp",
466  ExpectedHint{"fixed: ", "fixed"});
467 }
468 
469 TEST(ParameterHints, VarargsFunction) {
470  assertParameterHints(R"cpp(
471  void foo(int fixed, ...);
472 
473  void bar() {
474  foo($fixed[[41]], 42, 43);
475  }
476  )cpp",
477  ExpectedHint{"fixed: ", "fixed"});
478 }
479 
480 TEST(ParameterHints, CopyOrMoveConstructor) {
481  // Do not show hint for parameter of copy or move constructor.
482  assertParameterHints(R"cpp(
483  struct S {
484  S();
485  S(const S& other);
486  S(S&& other);
487  };
488  void bar() {
489  S a;
490  S b(a); // copy
491  S c(S()); // move
492  }
493  )cpp");
494 }
495 
496 TEST(ParameterHints, AggregateInit) {
497  // FIXME: This is not implemented yet, but it would be a natural
498  // extension to show member names as hints here.
499  assertParameterHints(R"cpp(
500  struct Point {
501  int x;
502  int y;
503  };
504  void bar() {
505  Point p{41, 42};
506  }
507  )cpp");
508 }
509 
510 TEST(ParameterHints, UserDefinedLiteral) {
511  // Do not hint call to user-defined literal operator.
512  assertParameterHints(R"cpp(
513  long double operator"" _w(long double param);
514  void bar() {
515  1.2_w;
516  }
517  )cpp");
518 }
519 
520 TEST(ParameterHints, ParamNameComment) {
521  // Do not hint an argument which already has a comment
522  // with the parameter name preceding it.
523  assertParameterHints(R"cpp(
524  void foo(int param);
525  void bar() {
526  foo(/*param*/42);
527  foo( /* param = */ 42);
528  foo(/* the answer */$param[[42]]);
529  }
530  )cpp",
531  ExpectedHint{"param: ", "param"});
532 }
533 
534 TEST(ParameterHints, SetterFunctions) {
535  assertParameterHints(R"cpp(
536  struct S {
537  void setParent(S* parent);
538  void set_parent(S* parent);
539  void setTimeout(int timeoutMillis);
540  void setTimeoutMillis(int timeout_millis);
541  };
542  void bar() {
543  S s;
544  // Parameter name matches setter name - omit hint.
545  s.setParent(nullptr);
546  // Support snake_case
547  s.set_parent(nullptr);
548  // Parameter name may contain extra info - show hint.
549  s.setTimeout($timeoutMillis[[120]]);
550  // FIXME: Ideally we'd want to omit this.
551  s.setTimeoutMillis($timeout_millis[[120]]);
552  }
553  )cpp",
554  ExpectedHint{"timeoutMillis: ", "timeoutMillis"},
555  ExpectedHint{"timeout_millis: ", "timeout_millis"});
556 }
557 
558 TEST(ParameterHints, BuiltinFunctions) {
559  // This prototype of std::forward is sufficient for clang to recognize it
560  assertParameterHints(R"cpp(
561  namespace std { template <typename T> T&& forward(T&); }
562  void foo() {
563  int i;
564  std::forward(i);
565  }
566  )cpp");
567 }
568 
569 TEST(ParameterHints, IncludeAtNonGlobalScope) {
570  Annotations FooInc(R"cpp(
571  void bar() { foo(42); }
572  )cpp");
573  Annotations FooCC(R"cpp(
574  struct S {
575  void foo(int param);
576  #include "foo.inc"
577  };
578  )cpp");
579 
580  TestWorkspace Workspace;
581  Workspace.addSource("foo.inc", FooInc.code());
582  Workspace.addMainFile("foo.cc", FooCC.code());
583 
584  auto AST = Workspace.openFile("foo.cc");
585  ASSERT_TRUE(bool(AST));
586 
587  // Ensure the hint for the call in foo.inc is NOT materialized in foo.cc.
588  EXPECT_EQ(hintsOfKind(*AST, InlayHintKind::Parameter).size(), 0u);
589 }
590 
591 TEST(TypeHints, Smoke) {
592  assertTypeHints(R"cpp(
593  auto $waldo[[waldo]] = 42;
594  )cpp",
595  ExpectedHint{": int", "waldo"});
596 }
597 
598 TEST(TypeHints, Decorations) {
599  assertTypeHints(R"cpp(
600  int x = 42;
601  auto* $var1[[var1]] = &x;
602  auto&& $var2[[var2]] = x;
603  const auto& $var3[[var3]] = x;
604  )cpp",
605  ExpectedHint{": int *", "var1"},
606  ExpectedHint{": int &", "var2"},
607  ExpectedHint{": const int &", "var3"});
608 }
609 
610 TEST(TypeHints, DecltypeAuto) {
611  assertTypeHints(R"cpp(
612  int x = 42;
613  int& y = x;
614  decltype(auto) $z[[z]] = y;
615  )cpp",
616  ExpectedHint{": int &", "z"});
617 }
618 
619 TEST(TypeHints, NoQualifiers) {
620  assertTypeHints(R"cpp(
621  namespace A {
622  namespace B {
623  struct S1 {};
624  S1 foo();
625  auto $x[[x]] = foo();
626 
627  struct S2 {
628  template <typename T>
629  struct Inner {};
630  };
631  S2::Inner<int> bar();
632  auto $y[[y]] = bar();
633  }
634  }
635  )cpp",
636  ExpectedHint{": S1", "x"},
637  // FIXME: We want to suppress scope specifiers
638  // here because we are into the whole
639  // brevity thing, but the ElaboratedType
640  // printer does not honor the SuppressScope
641  // flag by design, so we need to extend the
642  // PrintingPolicy to support this use case.
643  ExpectedHint{": S2::Inner<int>", "y"});
644 }
645 
646 TEST(TypeHints, Lambda) {
647  // Do not print something overly verbose like the lambda's location.
648  // Show hints for init-captures (but not regular captures).
649  assertTypeHints(R"cpp(
650  void f() {
651  int cap = 42;
652  auto $L[[L]] = [cap, $init[[init]] = 1 + 1](int a$ret[[)]] {
653  return a + cap + init;
654  };
655  }
656  )cpp",
657  ExpectedHint{": (lambda)", "L"},
658  ExpectedHint{": int", "init"}, ExpectedHint{"-> int", "ret"});
659 
660  // Lambda return hint shown even if no param list.
661  // (The digraph :> is just a ] that doesn't conflict with the annotations).
662  assertTypeHints("auto $L[[x]] = <:$ret[[:>]]{return 42;};",
663  ExpectedHint{": (lambda)", "L"},
664  ExpectedHint{"-> int", "ret"});
665 }
666 
667 // Structured bindings tests.
668 // Note, we hint the individual bindings, not the aggregate.
669 
670 TEST(TypeHints, StructuredBindings_PublicStruct) {
671  assertTypeHints(R"cpp(
672  // Struct with public fields.
673  struct Point {
674  int x;
675  int y;
676  };
677  Point foo();
678  auto [$x[[x]], $y[[y]]] = foo();
679  )cpp",
680  ExpectedHint{": int", "x"}, ExpectedHint{": int", "y"});
681 }
682 
683 TEST(TypeHints, StructuredBindings_Array) {
684  assertTypeHints(R"cpp(
685  int arr[2];
686  auto [$x[[x]], $y[[y]]] = arr;
687  )cpp",
688  ExpectedHint{": int", "x"}, ExpectedHint{": int", "y"});
689 }
690 
691 TEST(TypeHints, StructuredBindings_TupleLike) {
692  assertTypeHints(R"cpp(
693  // Tuple-like type.
694  struct IntPair {
695  int a;
696  int b;
697  };
698  namespace std {
699  template <typename T>
700  struct tuple_size {};
701  template <>
702  struct tuple_size<IntPair> {
703  constexpr static unsigned value = 2;
704  };
705  template <unsigned I, typename T>
706  struct tuple_element {};
707  template <unsigned I>
708  struct tuple_element<I, IntPair> {
709  using type = int;
710  };
711  }
712  template <unsigned I>
713  int get(const IntPair& p) {
714  if constexpr (I == 0) {
715  return p.a;
716  } else if constexpr (I == 1) {
717  return p.b;
718  }
719  }
720  IntPair bar();
721  auto [$x[[x]], $y[[y]]] = bar();
722  )cpp",
723  ExpectedHint{": int", "x"}, ExpectedHint{": int", "y"});
724 }
725 
726 TEST(TypeHints, StructuredBindings_NoInitializer) {
727  assertTypeHints(R"cpp(
728  // No initializer (ill-formed).
729  // Do not show useless "NULL TYPE" hint.
730  auto [x, y]; /*error-ok*/
731  )cpp");
732 }
733 
734 TEST(TypeHints, ReturnTypeDeduction) {
735  assertTypeHints(
736  R"cpp(
737  auto f1(int x$ret1a[[)]]; // Hint forward declaration too
738  auto f1(int x$ret1b[[)]] { return x + 1; }
739 
740  // Include pointer operators in hint
741  int s;
742  auto& f2($ret2[[)]] { return s; }
743 
744  // Do not hint `auto` for trailing return type.
745  auto f3() -> int;
746 
747  // Do not hint when a trailing return type is specified.
748  auto f4() -> auto* { return "foo"; }
749 
750  auto f5($noreturn[[)]] {}
751 
752  // `auto` conversion operator
753  struct A {
754  operator auto($retConv[[)]] { return 42; }
755  };
756 
757  // FIXME: Dependent types do not work yet.
758  template <typename T>
759  struct S {
760  auto method() { return T(); }
761  };
762  )cpp",
763  ExpectedHint{"-> int", "ret1a"}, ExpectedHint{"-> int", "ret1b"},
764  ExpectedHint{"-> int &", "ret2"}, ExpectedHint{"-> void", "noreturn"},
765  ExpectedHint{"-> int", "retConv"});
766 }
767 
768 TEST(TypeHints, DependentType) {
769  assertTypeHints(R"cpp(
770  template <typename T>
771  void foo(T arg) {
772  // The hint would just be "auto" and we can't do any better.
773  auto var1 = arg.method();
774  // FIXME: It would be nice to show "T" as the hint.
775  auto $var2[[var2]] = arg;
776  }
777  )cpp");
778 }
779 
780 TEST(TypeHints, LongTypeName) {
781  assertTypeHints(R"cpp(
782  template <typename, typename, typename>
783  struct A {};
784  struct MultipleWords {};
785  A<MultipleWords, MultipleWords, MultipleWords> foo();
786  // Omit type hint past a certain length (currently 32)
787  auto var = foo();
788  )cpp");
789 }
790 
791 TEST(TypeHints, DefaultTemplateArgs) {
792  assertTypeHints(R"cpp(
793  template <typename, typename = int>
794  struct A {};
795  A<float> foo();
796  auto $var[[var]] = foo();
797  )cpp",
798  ExpectedHint{": A<float>", "var"});
799 }
800 
801 TEST(TypeHints, Deduplication) {
802  assertTypeHints(R"cpp(
803  template <typename T>
804  void foo() {
805  auto $var[[var]] = 42;
806  }
807  template void foo<int>();
808  template void foo<float>();
809  )cpp",
810  ExpectedHint{": int", "var"});
811 }
812 
813 TEST(TypeHints, SinglyInstantiatedTemplate) {
814  assertTypeHints(R"cpp(
815  auto $lambda[[x]] = [](auto *$param[[y]], auto) { return 42; };
816  int m = x("foo", 3);
817  )cpp",
818  ExpectedHint{": (lambda)", "lambda"},
819  ExpectedHint{": const char *", "param"});
820 
821  // No hint for packs, or auto params following packs
822  assertTypeHints(R"cpp(
823  int x(auto $a[[a]], auto... b, auto c) { return 42; }
824  int m = x<void*, char, float>(nullptr, 'c', 2.0, 2);
825  )cpp",
826  ExpectedHint{": void *", "a"});
827 }
828 
829 TEST(TypeHints, Aliased) {
830  // Check that we don't crash for functions without a FunctionTypeLoc.
831  // https://github.com/clangd/clangd/issues/1140
832  TestTU TU = TestTU::withCode("void foo(void){} extern typeof(foo) foo;");
833  TU.ExtraArgs.push_back("-xc");
834  auto AST = TU.build();
835 
836  EXPECT_THAT(hintsOfKind(AST, InlayHintKind::Type), IsEmpty());
837 }
838 
839 TEST(DesignatorHints, Basic) {
840  assertDesignatorHints(R"cpp(
841  struct S { int x, y, z; };
842  S s {$x[[1]], $y[[2+2]]};
843 
844  int x[] = {$0[[0]], $1[[1]]};
845  )cpp",
846  ExpectedHint{".x=", "x"}, ExpectedHint{".y=", "y"},
847  ExpectedHint{"[0]=", "0"}, ExpectedHint{"[1]=", "1"});
848 }
849 
850 TEST(DesignatorHints, Nested) {
851  assertDesignatorHints(R"cpp(
852  struct Inner { int x, y; };
853  struct Outer { Inner a, b; };
854  Outer o{ $a[[{ $x[[1]], $y[[2]] }]], $bx[[3]] };
855  )cpp",
856  ExpectedHint{".a=", "a"}, ExpectedHint{".x=", "x"},
857  ExpectedHint{".y=", "y"}, ExpectedHint{".b.x=", "bx"});
858 }
859 
860 TEST(DesignatorHints, AnonymousRecord) {
861  assertDesignatorHints(R"cpp(
862  struct S {
863  union {
864  struct {
865  struct {
866  int y;
867  };
868  } x;
869  };
870  };
871  S s{$xy[[42]]};
872  )cpp",
873  ExpectedHint{".x.y=", "xy"});
874 }
875 
876 TEST(DesignatorHints, Suppression) {
877  assertDesignatorHints(R"cpp(
878  struct Point { int a, b, c, d, e, f, g, h; };
879  Point p{/*a=*/1, .c=2, /* .d = */3, $e[[4]]};
880  )cpp",
881  ExpectedHint{".e=", "e"});
882 }
883 
884 TEST(DesignatorHints, StdArray) {
885  // Designators for std::array should be [0] rather than .__elements[0].
886  // While technically correct, the designator is useless and horrible to read.
887  assertDesignatorHints(R"cpp(
888  template <typename T, int N> struct Array { T __elements[N]; };
889  Array<int, 2> x = {$0[[0]], $1[[1]]};
890  )cpp",
891  ExpectedHint{"[0]=", "0"}, ExpectedHint{"[1]=", "1"});
892 }
893 
894 TEST(DesignatorHints, OnlyAggregateInit) {
895  assertDesignatorHints(R"cpp(
896  struct Copyable { int x; } c;
897  Copyable d{c};
898 
899  struct Constructible { Constructible(int x); };
900  Constructible x{42};
901  )cpp" /*no designator hints expected (but param hints!)*/);
902 }
903 
904 TEST(InlayHints, RestrictRange) {
905  Annotations Code(R"cpp(
906  auto a = false;
907  [[auto b = 1;
908  auto c = '2';]]
909  auto d = 3.f;
910  )cpp");
911  auto AST = TestTU::withCode(Code.code()).build();
912  EXPECT_THAT(inlayHints(AST, Code.range()),
913  ElementsAre(labelIs(": int"), labelIs(": char")));
914 }
915 
916 // FIXME: Low-hanging fruit where we could omit a type hint:
917 // - auto x = TypeName(...);
918 // - auto x = (TypeName) (...);
919 // - auto x = static_cast<TypeName>(...); // and other built-in casts
920 
921 // Annoyances for which a heuristic is not obvious:
922 // - auto x = llvm::dyn_cast<LongTypeName>(y); // and similar
923 // - stdlib algos return unwieldy __normal_iterator<X*, ...> type
924 // (For this one, perhaps we should omit type hints that start
925 // with a double underscore.)
926 
927 } // namespace
928 } // namespace clangd
929 } // namespace clang
XRefs.h
clang::clangd::InlayHintKind::Designator
@ Designator
A hint before an element of an aggregate braced initializer list, indicating what it is initializing.
clang::clangd::TEST
TEST(BackgroundQueueTest, Priority)
Definition: BackgroundIndexTests.cpp:750
clang::clangd::InlayHint::label
std::string label
The label of this hint.
Definition: Protocol.h:1578
clang::clangd::MATCHER_P2
MATCHER_P2(hasFlag, Flag, Path, "")
Definition: GlobalCompilationDatabaseTests.cpp:446
Label
std::string Label
Definition: InlayHintTests.cpp:46
clang::tidy::utils::fixit::QualifierPolicy::Right
@ Right
Macros
llvm::DenseSet< FileID > Macros
Definition: IncludeCleaner.cpp:195
clang::clangd::MATCHER_P
MATCHER_P(named, N, "")
Definition: BackgroundIndexTests.cpp:30
clang::clangd::TestTU::build
ParsedAST build() const
Definition: TestTU.cpp:109
Expected
std::vector< const char * > Expected
Definition: PrintASTTests.cpp:26
TestTU.h
Kind
BindArgumentKind Kind
Definition: AvoidBindCheck.cpp:59
TestWorkspace.h
RangeName
std::string RangeName
Definition: InlayHintTests.cpp:47
InlayHints.h
Protocol.h
clang::clangd::ParsedAST::build
static llvm::Optional< ParsedAST > build(llvm::StringRef Filename, const ParseInputs &Inputs, std::unique_ptr< clang::CompilerInvocation > CI, llvm::ArrayRef< Diag > CompilerInvocationDiags, std::shared_ptr< const PreambleData > Preamble)
Attempts to run Clang and store the parsed AST.
Definition: ParsedAST.cpp:342
clang::clangd::InlayHint
Inlay hint information.
Definition: Protocol.h:1570
clang::tidy::utils::fixit::QualifierPolicy::Left
@ Left
Code
std::string Code
Definition: FindTargetTests.cpp:67
clang::clangd::Config::Designators
bool Designators
Definition: Config.h:142
Config
static cl::opt< std::string > Config("config", cl::desc(R"( Specifies a configuration in YAML/JSON format: -config="{Checks:' *', CheckOptions:{x:y}}" When the value is empty, clang-tidy will attempt to find a file named .clang-tidy for each source file in its parent directories. )"), cl::init(""), cl::cat(ClangTidyCategory))
clang::clangd::TestTU::withCode
static TestTU withCode(llvm::StringRef Code)
Definition: TestTU.h:36
clang::clangd::operator<<
llvm::raw_ostream & operator<<(llvm::raw_ostream &OS, const CodeCompletion &C)
Definition: CodeComplete.cpp:2182
Annotations.h
Config.h
clang::clangd::InlayHintKind::Type
@ Type
An inlay hint that for a type annotation.
clang::clangd::Config::InlayHints
struct clang::clangd::Config::@8 InlayHints
C
const Criteria C
Definition: FunctionCognitiveComplexityCheck.cpp:93
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
clang::clangd::Config::Key
static clangd::Key< Config > Key
Context key which can be used to set the current Config.
Definition: Config.h:49
clang::clangd::inlayHints
std::vector< InlayHint > inlayHints(ParsedAST &AST, llvm::Optional< Range > RestrictRange)
Compute and return inlay hints for a file.
Definition: InlayHints.cpp:681
Side
HintSide Side
Definition: InlayHintTests.cpp:48
clang::clangd::InlayHintKind
InlayHintKind
Inlay hint kinds.
Definition: Protocol.h:1539
clang::clangd::InlayHint::range
Range range
The range of source code to which the hint applies.
Definition: Protocol.h:1603
Context.h
clang::clangd::InlayHintKind::Parameter
@ Parameter
An inlay hint that is for a parameter.