clang-tools  14.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 "InlayHints.h"
10 #include "Protocol.h"
11 #include "TestTU.h"
12 #include "TestWorkspace.h"
13 #include "XRefs.h"
14 #include "gmock/gmock.h"
15 #include "gtest/gtest.h"
16 
17 namespace clang {
18 namespace clangd {
19 
20 std::ostream &operator<<(std::ostream &Stream, const InlayHint &Hint) {
21  return Stream << Hint.label;
22 }
23 
24 namespace {
25 
26 using ::testing::UnorderedElementsAre;
27 
28 std::vector<InlayHint> hintsOfKind(ParsedAST &AST, InlayHintKind Kind) {
29  std::vector<InlayHint> Result;
30  for (auto &Hint : inlayHints(AST)) {
31  if (Hint.kind == Kind)
32  Result.push_back(Hint);
33  }
34  return Result;
35 }
36 
37 struct ExpectedHint {
38  std::string Label;
39  std::string RangeName;
40 
41  friend std::ostream &operator<<(std::ostream &Stream,
42  const ExpectedHint &Hint) {
43  return Stream << Hint.RangeName << ": " << Hint.Label;
44  }
45 };
46 
47 MATCHER_P2(HintMatcher, Expected, Code, "") {
48  return arg.label == Expected.Label &&
49  arg.range == Code.range(Expected.RangeName);
50 }
51 
52 template <typename... ExpectedHints>
53 void assertHints(InlayHintKind Kind, llvm::StringRef AnnotatedSource,
54  ExpectedHints... Expected) {
55  Annotations Source(AnnotatedSource);
56  TestTU TU = TestTU::withCode(Source.code());
57  TU.ExtraArgs.push_back("-std=c++14");
58  auto AST = TU.build();
59 
60  EXPECT_THAT(hintsOfKind(AST, Kind),
61  UnorderedElementsAre(HintMatcher(Expected, Source)...));
62 }
63 
64 template <typename... ExpectedHints>
65 void assertParameterHints(llvm::StringRef AnnotatedSource,
66  ExpectedHints... Expected) {
67  assertHints(InlayHintKind::ParameterHint, AnnotatedSource, Expected...);
68 }
69 
70 template <typename... ExpectedHints>
71 void assertTypeHints(llvm::StringRef AnnotatedSource,
72  ExpectedHints... Expected) {
73  assertHints(InlayHintKind::TypeHint, AnnotatedSource, Expected...);
74 }
75 
76 TEST(ParameterHints, Smoke) {
77  assertParameterHints(R"cpp(
78  void foo(int param);
79  void bar() {
80  foo($param[[42]]);
81  }
82  )cpp",
83  ExpectedHint{"param: ", "param"});
84 }
85 
86 TEST(ParameterHints, NoName) {
87  // No hint for anonymous parameter.
88  assertParameterHints(R"cpp(
89  void foo(int);
90  void bar() {
91  foo(42);
92  }
93  )cpp");
94 }
95 
96 TEST(ParameterHints, NameInDefinition) {
97  // Parameter name picked up from definition if necessary.
98  assertParameterHints(R"cpp(
99  void foo(int);
100  void bar() {
101  foo($param[[42]]);
102  }
103  void foo(int param) {};
104  )cpp",
105  ExpectedHint{"param: ", "param"});
106 }
107 
108 TEST(ParameterHints, NameMismatch) {
109  // Prefer name from declaration.
110  assertParameterHints(R"cpp(
111  void foo(int good);
112  void bar() {
113  foo($good[[42]]);
114  }
115  void foo(int bad) {};
116  )cpp",
117  ExpectedHint{"good: ", "good"});
118 }
119 
120 TEST(ParameterHints, Operator) {
121  // No hint for operator call with operator syntax.
122  assertParameterHints(R"cpp(
123  struct S {};
124  void operator+(S lhs, S rhs);
125  void bar() {
126  S a, b;
127  a + b;
128  }
129  )cpp");
130 }
131 
132 TEST(ParameterHints, Macros) {
133  // Handling of macros depends on where the call's argument list comes from.
134 
135  // If it comes from a macro definition, there's nothing to hint
136  // at the invocation site.
137  assertParameterHints(R"cpp(
138  void foo(int param);
139  #define ExpandsToCall() foo(42)
140  void bar() {
141  ExpandsToCall();
142  }
143  )cpp");
144 
145  // The argument expression being a macro invocation shouldn't interfere
146  // with hinting.
147  assertParameterHints(R"cpp(
148  #define PI 3.14
149  void foo(double param);
150  void bar() {
151  foo($param[[PI]]);
152  }
153  )cpp",
154  ExpectedHint{"param: ", "param"});
155 
156  // If the whole argument list comes from a macro parameter, hint it.
157  assertParameterHints(R"cpp(
158  void abort();
159  #define ASSERT(expr) if (!expr) abort()
160  int foo(int param);
161  void bar() {
162  ASSERT(foo($param[[42]]) == 0);
163  }
164  )cpp",
165  ExpectedHint{"param: ", "param"});
166 }
167 
168 TEST(ParameterHints, ConstructorParens) {
169  assertParameterHints(R"cpp(
170  struct S {
171  S(int param);
172  };
173  void bar() {
174  S obj($param[[42]]);
175  }
176  )cpp",
177  ExpectedHint{"param: ", "param"});
178 }
179 
180 TEST(ParameterHints, ConstructorBraces) {
181  assertParameterHints(R"cpp(
182  struct S {
183  S(int param);
184  };
185  void bar() {
186  S obj{$param[[42]]};
187  }
188  )cpp",
189  ExpectedHint{"param: ", "param"});
190 }
191 
192 TEST(ParameterHints, ConstructorStdInitList) {
193  // Do not show hints for std::initializer_list constructors.
194  assertParameterHints(R"cpp(
195  namespace std {
196  template <typename> class initializer_list {};
197  }
198  struct S {
199  S(std::initializer_list<int> param);
200  };
201  void bar() {
202  S obj{42, 43};
203  }
204  )cpp");
205 }
206 
207 TEST(ParameterHints, MemberInit) {
208  assertParameterHints(R"cpp(
209  struct S {
210  S(int param);
211  };
212  struct T {
213  S member;
214  T() : member($param[[42]]) {}
215  };
216  )cpp",
217  ExpectedHint{"param: ", "param"});
218 }
219 
220 TEST(ParameterHints, ImplicitConstructor) {
221  assertParameterHints(R"cpp(
222  struct S {
223  S(int param);
224  };
225  void bar(S);
226  S foo() {
227  // Do not show hint for implicit constructor call in argument.
228  bar(42);
229  // Do not show hint for implicit constructor call in return.
230  return 42;
231  }
232  )cpp");
233 }
234 
235 TEST(ParameterHints, ArgMatchesParam) {
236  assertParameterHints(R"cpp(
237  void foo(int param);
238  struct S {
239  static const int param = 42;
240  };
241  void bar() {
242  int param = 42;
243  // Do not show redundant "param: param".
244  foo(param);
245  // But show it if the argument is qualified.
246  foo($param[[S::param]]);
247  }
248  struct A {
249  int param;
250  void bar() {
251  // Do not show "param: param" for member-expr.
252  foo(param);
253  }
254  };
255  )cpp",
256  ExpectedHint{"param: ", "param"});
257 }
258 
259 TEST(ParameterHints, LeadingUnderscore) {
260  assertParameterHints(R"cpp(
261  void foo(int p1, int _p2, int __p3);
262  void bar() {
263  foo($p1[[41]], $p2[[42]], $p3[[43]]);
264  }
265  )cpp",
266  ExpectedHint{"p1: ", "p1"}, ExpectedHint{"p2: ", "p2"},
267  ExpectedHint{"p3: ", "p3"});
268 }
269 
270 TEST(ParameterHints, DependentCalls) {
271  assertParameterHints(R"cpp(
272  template <typename T>
273  void nonmember(T par1);
274 
275  template <typename T>
276  struct A {
277  void member(T par2);
278  static void static_member(T par3);
279  };
280 
281  void overload(int anInt);
282  void overload(double aDouble);
283 
284  template <typename T>
285  struct S {
286  void bar(A<T> a, T t) {
287  nonmember($par1[[t]]);
288  a.member($par2[[t]]);
289  A<T>::static_member($par3[[t]]);
290  // We don't want to arbitrarily pick between
291  // "anInt" or "aDouble", so just show no hint.
292  overload(T{});
293  }
294  };
295  )cpp",
296  ExpectedHint{"par1: ", "par1"},
297  ExpectedHint{"par2: ", "par2"},
298  ExpectedHint{"par3: ", "par3"});
299 }
300 
301 TEST(ParameterHints, VariadicFunction) {
302  assertParameterHints(R"cpp(
303  template <typename... T>
304  void foo(int fixed, T... variadic);
305 
306  void bar() {
307  foo($fixed[[41]], 42, 43);
308  }
309  )cpp",
310  ExpectedHint{"fixed: ", "fixed"});
311 }
312 
313 TEST(ParameterHints, VarargsFunction) {
314  assertParameterHints(R"cpp(
315  void foo(int fixed, ...);
316 
317  void bar() {
318  foo($fixed[[41]], 42, 43);
319  }
320  )cpp",
321  ExpectedHint{"fixed: ", "fixed"});
322 }
323 
324 TEST(ParameterHints, CopyOrMoveConstructor) {
325  // Do not show hint for parameter of copy or move constructor.
326  assertParameterHints(R"cpp(
327  struct S {
328  S();
329  S(const S& other);
330  S(S&& other);
331  };
332  void bar() {
333  S a;
334  S b(a); // copy
335  S c(S()); // move
336  }
337  )cpp");
338 }
339 
340 TEST(ParameterHints, AggregateInit) {
341  // FIXME: This is not implemented yet, but it would be a natural
342  // extension to show member names as hints here.
343  assertParameterHints(R"cpp(
344  struct Point {
345  int x;
346  int y;
347  };
348  void bar() {
349  Point p{41, 42};
350  }
351  )cpp");
352 }
353 
354 TEST(ParameterHints, UserDefinedLiteral) {
355  // Do not hint call to user-defined literal operator.
356  assertParameterHints(R"cpp(
357  long double operator"" _w(long double param);
358  void bar() {
359  1.2_w;
360  }
361  )cpp");
362 }
363 
364 TEST(ParameterHints, ParamNameComment) {
365  // Do not hint an argument which already has a comment
366  // with the parameter name preceding it.
367  assertParameterHints(R"cpp(
368  void foo(int param);
369  void bar() {
370  foo(/*param*/42);
371  foo( /* param = */ 42);
372  foo(/* the answer */$param[[42]]);
373  }
374  )cpp",
375  ExpectedHint{"param: ", "param"});
376 }
377 
378 TEST(ParameterHints, SetterFunctions) {
379  assertParameterHints(R"cpp(
380  struct S {
381  void setParent(S* parent);
382  void set_parent(S* parent);
383  void setTimeout(int timeoutMillis);
384  void setTimeoutMillis(int timeout_millis);
385  };
386  void bar() {
387  S s;
388  // Parameter name matches setter name - omit hint.
389  s.setParent(nullptr);
390  // Support snake_case
391  s.set_parent(nullptr);
392  // Parameter name may contain extra info - show hint.
393  s.setTimeout($timeoutMillis[[120]]);
394  // FIXME: Ideally we'd want to omit this.
395  s.setTimeoutMillis($timeout_millis[[120]]);
396  }
397  )cpp",
398  ExpectedHint{"timeoutMillis: ", "timeoutMillis"},
399  ExpectedHint{"timeout_millis: ", "timeout_millis"});
400 }
401 
402 TEST(ParameterHints, IncludeAtNonGlobalScope) {
403  Annotations FooInc(R"cpp(
404  void bar() { foo(42); }
405  )cpp");
406  Annotations FooCC(R"cpp(
407  struct S {
408  void foo(int param);
409  #include "foo.inc"
410  };
411  )cpp");
412 
413  TestWorkspace Workspace;
414  Workspace.addSource("foo.inc", FooInc.code());
415  Workspace.addMainFile("foo.cc", FooCC.code());
416 
417  auto AST = Workspace.openFile("foo.cc");
418  ASSERT_TRUE(bool(AST));
419 
420  // Ensure the hint for the call in foo.inc is NOT materialized in foo.cc.
421  EXPECT_EQ(hintsOfKind(*AST, InlayHintKind::ParameterHint).size(), 0u);
422 }
423 
424 TEST(TypeHints, Smoke) {
425  assertTypeHints(R"cpp(
426  auto $waldo[[waldo]] = 42;
427  )cpp",
428  ExpectedHint{": int", "waldo"});
429 }
430 
431 TEST(TypeHints, Decorations) {
432  assertTypeHints(R"cpp(
433  int x = 42;
434  auto* $var1[[var1]] = &x;
435  auto&& $var2[[var2]] = x;
436  const auto& $var3[[var3]] = x;
437  )cpp",
438  ExpectedHint{": int *", "var1"},
439  ExpectedHint{": int &", "var2"},
440  ExpectedHint{": const int &", "var3"});
441 }
442 
443 TEST(TypeHints, DecltypeAuto) {
444  assertTypeHints(R"cpp(
445  int x = 42;
446  int& y = x;
447  decltype(auto) $z[[z]] = y;
448  )cpp",
449  ExpectedHint{": int &", "z"});
450 }
451 
452 TEST(TypeHints, NoQualifiers) {
453  assertTypeHints(R"cpp(
454  namespace A {
455  namespace B {
456  struct S1 {};
457  S1 foo();
458  auto $x[[x]] = foo();
459 
460  struct S2 {
461  template <typename T>
462  struct Inner {};
463  };
464  S2::Inner<int> bar();
465  auto $y[[y]] = bar();
466  }
467  }
468  )cpp",
469  ExpectedHint{": S1", "x"}, ExpectedHint{": Inner<int>", "y"});
470 }
471 
472 TEST(TypeHints, Lambda) {
473  // Do not print something overly verbose like the lambda's location.
474  // Show hints for init-captures (but not regular captures).
475  assertTypeHints(R"cpp(
476  void f() {
477  int cap = 42;
478  auto $L[[L]] = [cap, $init[[init]] = 1 + 1](int a) {
479  return a + cap + init;
480  };
481  }
482  )cpp",
483  ExpectedHint{": (lambda)", "L"},
484  ExpectedHint{": int", "init"});
485 }
486 
487 // Structured bindings tests.
488 // Note, we hint the individual bindings, not the aggregate.
489 
490 TEST(TypeHints, StructuredBindings_PublicStruct) {
491  assertTypeHints(R"cpp(
492  // Struct with public fields.
493  struct Point {
494  int x;
495  int y;
496  };
497  Point foo();
498  auto [$x[[x]], $y[[y]]] = foo();
499  )cpp",
500  ExpectedHint{": int", "x"}, ExpectedHint{": int", "y"});
501 }
502 
503 TEST(TypeHints, StructuredBindings_Array) {
504  assertTypeHints(R"cpp(
505  int arr[2];
506  auto [$x[[x]], $y[[y]]] = arr;
507  )cpp",
508  ExpectedHint{": int", "x"}, ExpectedHint{": int", "y"});
509 }
510 
511 TEST(TypeHints, StructuredBindings_TupleLike) {
512  assertTypeHints(R"cpp(
513  // Tuple-like type.
514  struct IntPair {
515  int a;
516  int b;
517  };
518  namespace std {
519  template <typename T>
520  struct tuple_size {};
521  template <>
522  struct tuple_size<IntPair> {
523  constexpr static unsigned value = 2;
524  };
525  template <unsigned I, typename T>
526  struct tuple_element {};
527  template <unsigned I>
528  struct tuple_element<I, IntPair> {
529  using type = int;
530  };
531  }
532  template <unsigned I>
533  int get(const IntPair& p) {
534  if constexpr (I == 0) {
535  return p.a;
536  } else if constexpr (I == 1) {
537  return p.b;
538  }
539  }
540  IntPair bar();
541  auto [$x[[x]], $y[[y]]] = bar();
542  )cpp",
543  ExpectedHint{": int", "x"}, ExpectedHint{": int", "y"});
544 }
545 
546 TEST(TypeHints, StructuredBindings_NoInitializer) {
547  assertTypeHints(R"cpp(
548  // No initializer (ill-formed).
549  // Do not show useless "NULL TYPE" hint.
550  auto [x, y]; /*error-ok*/
551  )cpp");
552 }
553 
554 TEST(TypeHints, ReturnTypeDeduction) {
555  assertTypeHints(
556  R"cpp(
557  auto f1(int x$ret1a[[)]]; // Hint forward declaration too
558  auto f1(int x$ret1b[[)]] { return x + 1; }
559 
560  // Include pointer operators in hint
561  int s;
562  auto& f2($ret2[[)]] { return s; }
563 
564  // Do not hint `auto` for trailing return type.
565  auto f3() -> int;
566 
567  // `auto` conversion operator
568  struct A {
569  operator auto($retConv[[)]] { return 42; }
570  };
571 
572  // FIXME: Dependent types do not work yet.
573  template <typename T>
574  struct S {
575  auto method() { return T(); }
576  };
577  )cpp",
578  ExpectedHint{"-> int", "ret1a"}, ExpectedHint{"-> int", "ret1b"},
579  ExpectedHint{"-> int &", "ret2"}, ExpectedHint{"-> int", "retConv"});
580 }
581 
582 TEST(TypeHints, DependentType) {
583  assertTypeHints(R"cpp(
584  template <typename T>
585  void foo(T arg) {
586  // The hint would just be "auto" and we can't do any better.
587  auto var1 = arg.method();
588  // FIXME: It would be nice to show "T" as the hint.
589  auto $var2[[var2]] = arg;
590  }
591  )cpp");
592 }
593 
594 TEST(TypeHints, LongTypeName) {
595  assertTypeHints(R"cpp(
596  template <typename, typename, typename>
597  struct A {};
598  struct MultipleWords {};
599  A<MultipleWords, MultipleWords, MultipleWords> foo();
600  // Omit type hint past a certain length (currently 32)
601  auto var = foo();
602  )cpp");
603 }
604 
605 TEST(TypeHints, DefaultTemplateArgs) {
606  assertTypeHints(R"cpp(
607  template <typename, typename = int>
608  struct A {};
609  A<float> foo();
610  auto $var[[var]] = foo();
611  )cpp",
612  ExpectedHint{": A<float>", "var"});
613 }
614 
615 TEST(TypeHints, Deduplication) {
616  assertTypeHints(R"cpp(
617  template <typename T>
618  void foo() {
619  auto $var[[var]] = 42;
620  }
621  template void foo<int>();
622  template void foo<float>();
623  )cpp",
624  ExpectedHint{": int", "var"});
625 }
626 
627 // FIXME: Low-hanging fruit where we could omit a type hint:
628 // - auto x = TypeName(...);
629 // - auto x = (TypeName) (...);
630 // - auto x = static_cast<TypeName>(...); // and other built-in casts
631 
632 // Annoyances for which a heuristic is not obvious:
633 // - auto x = llvm::dyn_cast<LongTypeName>(y); // and similar
634 // - stdlib algos return unwieldy __normal_iterator<X*, ...> type
635 // (For this one, perhaps we should omit type hints that start
636 // with a double underscore.)
637 
638 } // namespace
639 } // namespace clangd
640 } // namespace clang
XRefs.h
clang::clangd::TEST
TEST(BackgroundQueueTest, Priority)
Definition: BackgroundIndexTests.cpp:751
clang::clangd::InlayHint::label
std::string label
The label that is displayed in the editor.
Definition: Protocol.h:1548
clang::clangd::MATCHER_P2
MATCHER_P2(hasFlag, Flag, Path, "")
Definition: GlobalCompilationDatabaseTests.cpp:451
Label
std::string Label
Definition: InlayHintTests.cpp:38
Macros
llvm::DenseSet< FileID > Macros
Definition: IncludeCleaner.cpp:107
Expected
std::vector< const char * > Expected
Definition: PrintASTTests.cpp:27
TestTU.h
Kind
BindArgumentKind Kind
Definition: AvoidBindCheck.cpp:59
TestWorkspace.h
RangeName
std::string RangeName
Definition: InlayHintTests.cpp:39
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:248
clang::clangd::InlayHint
An annotation to be displayed inline next to a range of source code.
Definition: Protocol.h:1535
Code
std::string Code
Definition: FindTargetTests.cpp:67
clang::clangd::InlayHintKind::TypeHint
@ TypeHint
The hint corresponds to information about a deduced type.
clang::clangd::TestTU::withCode
static TestTU withCode(llvm::StringRef Code)
Definition: TestTU.h:37
clang::clangd::operator<<
llvm::raw_ostream & operator<<(llvm::raw_ostream &OS, const CodeCompletion &C)
Definition: CodeComplete.cpp:2126
Annotations.h
clang::clangd::InlayHintKind::ParameterHint
@ ParameterHint
The hint corresponds to parameter information.
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
clang::clangd::InlayHintKind
InlayHintKind
A set of predefined hint kinds.
Definition: Protocol.h:1513
clang::clangd::inlayHints
std::vector< InlayHint > inlayHints(ParsedAST &AST)
Definition: InlayHints.cpp:365