1 //===--- InlayHints.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 "InlayHints.h"
9 #include "Config.h"
10 #include "HeuristicResolver.h"
11 #include "ParsedAST.h"
12 #include "clang/AST/DeclarationName.h"
13 #include "clang/AST/ExprCXX.h"
14 #include "clang/AST/RecursiveASTVisitor.h"
15 #include "clang/Basic/SourceManager.h"
16 
17 namespace clang {
18 namespace clangd {
19 namespace {
20 
21 // For now, inlay hints are always anchored at the left or right of their range.
22 enum class HintSide { Left, Right };
23 
24 class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
25 public:
26   InlayHintVisitor(std::vector<InlayHint> &Results, ParsedAST &AST,
27                    const Config &Cfg, llvm::Optional<Range> RestrictRange)
28       : Results(Results), AST(AST.getASTContext()), Cfg(Cfg),
29         RestrictRange(std::move(RestrictRange)),
30         MainFileID(AST.getSourceManager().getMainFileID()),
31         Resolver(AST.getHeuristicResolver()),
32         TypeHintPolicy(this->AST.getPrintingPolicy()),
33         StructuredBindingPolicy(this->AST.getPrintingPolicy()) {
34     bool Invalid = false;
35     llvm::StringRef Buf =
36         AST.getSourceManager().getBufferData(MainFileID, &Invalid);
37     MainFileBuf = Invalid ? StringRef{} : Buf;
38 
39     TypeHintPolicy.SuppressScope = true; // keep type names short
40     TypeHintPolicy.AnonymousTagLocations =
41         false; // do not print lambda locations
42 
43     // For structured bindings, print canonical types. This is important because
44     // for bindings that use the tuple_element protocol, the non-canonical types
45     // would be "tuple_element<I, A>::type".
46     // For "auto", we often prefer sugared types.
47     // Not setting PrintCanonicalTypes for "auto" allows
48     // SuppressDefaultTemplateArgs (set by default) to have an effect.
49     StructuredBindingPolicy = TypeHintPolicy;
50     StructuredBindingPolicy.PrintCanonicalTypes = true;
51   }
52 
53   bool VisitCXXConstructExpr(CXXConstructExpr *E) {
54     // Weed out constructor calls that don't look like a function call with
55     // an argument list, by checking the validity of getParenOrBraceRange().
56     // Also weed out std::initializer_list constructors as there are no names
57     // for the individual arguments.
58     if (!E->getParenOrBraceRange().isValid() ||
59         E->isStdInitListInitialization()) {
60       return true;
61     }
62 
63     processCall(E->getParenOrBraceRange().getBegin(), E->getConstructor(),
64                 {E->getArgs(), E->getNumArgs()});
65     return true;
66   }
67 
68   bool VisitCallExpr(CallExpr *E) {
69     if (!Cfg.InlayHints.Parameters)
70       return true;
71 
72     // Do not show parameter hints for operator calls written using operator
73     // syntax or user-defined literals. (Among other reasons, the resulting
74     // hints can look awkard, e.g. the expression can itself be a function
75     // argument and then we'd get two hints side by side).
76     if (isa<CXXOperatorCallExpr>(E) || isa<UserDefinedLiteral>(E))
77       return true;
78 
79     auto CalleeDecls = Resolver->resolveCalleeOfCallExpr(E);
80     if (CalleeDecls.size() != 1)
81       return true;
82     const FunctionDecl *Callee = nullptr;
83     if (const auto *FD = dyn_cast<FunctionDecl>(CalleeDecls[0]))
84       Callee = FD;
85     else if (const auto *FTD = dyn_cast<FunctionTemplateDecl>(CalleeDecls[0]))
86       Callee = FTD->getTemplatedDecl();
87     if (!Callee)
88       return true;
89 
90     processCall(E->getRParenLoc(), Callee, {E->getArgs(), E->getNumArgs()});
91     return true;
92   }
93 
94   bool VisitFunctionDecl(FunctionDecl *D) {
95     if (auto *AT = D->getReturnType()->getContainedAutoType()) {
96       QualType Deduced = AT->getDeducedType();
97       if (!Deduced.isNull()) {
98         addTypeHint(D->getFunctionTypeLoc().getRParenLoc(), D->getReturnType(),
99                     /*Prefix=*/"-> ");
100       }
101     }
102 
103     return true;
104   }
105 
106   bool VisitVarDecl(VarDecl *D) {
107     // Do not show hints for the aggregate in a structured binding,
108     // but show hints for the individual bindings.
109     if (auto *DD = dyn_cast<DecompositionDecl>(D)) {
110       for (auto *Binding : DD->bindings()) {
111         addTypeHint(Binding->getLocation(), Binding->getType(), /*Prefix=*/": ",
112                     StructuredBindingPolicy);
113       }
114       return true;
115     }
116 
117     if (D->getType()->getContainedAutoType()) {
118       if (!D->getType()->isDependentType()) {
119         // Our current approach is to place the hint on the variable
120         // and accordingly print the full type
121         // (e.g. for `const auto& x = 42`, print `const int&`).
122         // Alternatively, we could place the hint on the `auto`
123         // (and then just print the type deduced for the `auto`).
124         addTypeHint(D->getLocation(), D->getType(), /*Prefix=*/": ");
125       }
126     }
127     return true;
128   }
129 
130   // FIXME: Handle RecoveryExpr to try to hint some invalid calls.
131 
132 private:
133   using NameVec = SmallVector<StringRef, 8>;
134 
135   // The purpose of Anchor is to deal with macros. It should be the call's
136   // opening or closing parenthesis or brace. (Always using the opening would
137   // make more sense but CallExpr only exposes the closing.) We heuristically
138   // assume that if this location does not come from a macro definition, then
139   // the entire argument list likely appears in the main file and can be hinted.
140   void processCall(SourceLocation Anchor, const FunctionDecl *Callee,
141                    llvm::ArrayRef<const Expr *const> Args) {
142     if (!Cfg.InlayHints.Parameters || Args.size() == 0 || !Callee)
143       return;
144 
145     // If the anchor location comes from a macro defintion, there's nowhere to
146     // put hints.
147     if (!AST.getSourceManager().getTopMacroCallerLoc(Anchor).isFileID())
148       return;
149 
150     // The parameter name of a move or copy constructor is not very interesting.
151     if (auto *Ctor = dyn_cast<CXXConstructorDecl>(Callee))
152       if (Ctor->isCopyOrMoveConstructor())
153         return;
154 
155     // Don't show hints for variadic parameters.
156     size_t FixedParamCount = getFixedParamCount(Callee);
157     size_t ArgCount = std::min(FixedParamCount, Args.size());
158 
159     NameVec ParameterNames = chooseParameterNames(Callee, ArgCount);
160 
161     // Exclude setters (i.e. functions with one argument whose name begins with
162     // "set"), as their parameter name is also not likely to be interesting.
163     if (isSetter(Callee, ParameterNames))
164       return;
165 
166     for (size_t I = 0; I < ArgCount; ++I) {
167       StringRef Name = ParameterNames[I];
168       if (!shouldHint(Args[I], Name))
169         continue;
170 
171       addInlayHint(Args[I]->getSourceRange(), HintSide::Left,
172                    InlayHintKind::ParameterHint, /*Prefix=*/"", Name,
173                    /*Suffix=*/": ");
174     }
175   }
176 
177   static bool isSetter(const FunctionDecl *Callee, const NameVec &ParamNames) {
178     if (ParamNames.size() != 1)
179       return false;
180 
181     StringRef Name = getSimpleName(*Callee);
182     if (!Name.startswith_insensitive("set"))
183       return false;
184 
185     // In addition to checking that the function has one parameter and its
186     // name starts with "set", also check that the part after "set" matches
187     // the name of the parameter (ignoring case). The idea here is that if
188     // the parameter name differs, it may contain extra information that
189     // may be useful to show in a hint, as in:
190     //   void setTimeout(int timeoutMillis);
191     // This currently doesn't handle cases where params use snake_case
192     // and functions don't, e.g.
193     //   void setExceptionHandler(EHFunc exception_handler);
194     // We could improve this by replacing `equals_insensitive` with some
195     // `sloppy_equals` which ignores case and also skips underscores.
196     StringRef WhatItIsSetting = Name.substr(3).ltrim("_");
197     return WhatItIsSetting.equals_insensitive(ParamNames[0]);
198   }
199 
200   bool shouldHint(const Expr *Arg, StringRef ParamName) {
201     if (ParamName.empty())
202       return false;
203 
204     // If the argument expression is a single name and it matches the
205     // parameter name exactly, omit the hint.
206     if (ParamName == getSpelledIdentifier(Arg))
207       return false;
208 
209     // Exclude argument expressions preceded by a /*paramName*/.
210     if (isPrecededByParamNameComment(Arg, ParamName))
211       return false;
212 
213     return true;
214   }
215 
216   // Checks if "E" is spelled in the main file and preceded by a C-style comment
217   // whose contents match ParamName (allowing for whitespace and an optional "="
218   // at the end.
219   bool isPrecededByParamNameComment(const Expr *E, StringRef ParamName) {
220     auto &SM = AST.getSourceManager();
221     auto ExprStartLoc = SM.getTopMacroCallerLoc(E->getBeginLoc());
222     auto Decomposed = SM.getDecomposedLoc(ExprStartLoc);
223     if (Decomposed.first != MainFileID)
224       return false;
225 
226     StringRef SourcePrefix = MainFileBuf.substr(0, Decomposed.second);
227     // Allow whitespace between comment and expression.
228     SourcePrefix = SourcePrefix.rtrim();
229     // Check for comment ending.
230     if (!SourcePrefix.consume_back("*/"))
231       return false;
232     // Allow whitespace and "=" at end of comment.
233     SourcePrefix = SourcePrefix.rtrim().rtrim('=').rtrim();
234     // Other than that, the comment must contain exactly ParamName.
235     if (!SourcePrefix.consume_back(ParamName))
236       return false;
237     return SourcePrefix.rtrim().endswith("/*");
238   }
239 
240   // If "E" spells a single unqualified identifier, return that name.
241   // Otherwise, return an empty string.
242   static StringRef getSpelledIdentifier(const Expr *E) {
243     E = E->IgnoreUnlessSpelledInSource();
244 
245     if (auto *DRE = dyn_cast<DeclRefExpr>(E))
246       if (!DRE->getQualifier())
247         return getSimpleName(*DRE->getDecl());
248 
249     if (auto *ME = dyn_cast<MemberExpr>(E))
250       if (!ME->getQualifier() && ME->isImplicitAccess())
251         return getSimpleName(*ME->getMemberDecl());
252 
253     return {};
254   }
255 
256   NameVec chooseParameterNames(const FunctionDecl *Callee, size_t ArgCount) {
257     // The current strategy here is to use all the parameter names from the
258     // canonical declaration, unless they're all empty, in which case we
259     // use all the parameter names from the definition (in present in the
260     // translation unit).
261     // We could try a bit harder, e.g.:
262     //   - try all re-declarations, not just canonical + definition
263     //   - fall back arg-by-arg rather than wholesale
264 
265     NameVec ParameterNames = getParameterNamesForDecl(Callee, ArgCount);
266 
267     if (llvm::all_of(ParameterNames, std::mem_fn(&StringRef::empty))) {
268       if (const FunctionDecl *Def = Callee->getDefinition()) {
269         ParameterNames = getParameterNamesForDecl(Def, ArgCount);
270       }
271     }
272     assert(ParameterNames.size() == ArgCount);
273 
274     // Standard library functions often have parameter names that start
275     // with underscores, which makes the hints noisy, so strip them out.
276     for (auto &Name : ParameterNames)
277       stripLeadingUnderscores(Name);
278 
279     return ParameterNames;
280   }
281 
282   static void stripLeadingUnderscores(StringRef &Name) {
283     Name = Name.ltrim('_');
284   }
285 
286   // Return the number of fixed parameters Function has, that is, not counting
287   // parameters that are variadic (instantiated from a parameter pack) or
288   // C-style varargs.
289   static size_t getFixedParamCount(const FunctionDecl *Function) {
290     if (FunctionTemplateDecl *Template = Function->getPrimaryTemplate()) {
291       FunctionDecl *F = Template->getTemplatedDecl();
292       size_t Result = 0;
293       for (ParmVarDecl *Parm : F->parameters()) {
294         if (Parm->isParameterPack()) {
295           break;
296         }
297         ++Result;
298       }
299       return Result;
300     }
301     // C-style varargs don't need special handling, they're already
302     // not included in getNumParams().
303     return Function->getNumParams();
304   }
305 
306   static StringRef getSimpleName(const NamedDecl &D) {
307     if (IdentifierInfo *Ident = D.getDeclName().getAsIdentifierInfo()) {
308       return Ident->getName();
309     }
310 
311     return StringRef();
312   }
313 
314   NameVec getParameterNamesForDecl(const FunctionDecl *Function,
315                                    size_t ArgCount) {
316     NameVec Result;
317     for (size_t I = 0; I < ArgCount; ++I) {
318       const ParmVarDecl *Parm = Function->getParamDecl(I);
319       assert(Parm);
320       Result.emplace_back(getSimpleName(*Parm));
321     }
322     return Result;
323   }
324 
325   // We pass HintSide rather than SourceLocation because we want to ensure
326   // it is in the same file as the common file range.
327   void addInlayHint(SourceRange R, HintSide Side, InlayHintKind Kind,
328                     llvm::StringRef Prefix, llvm::StringRef Label,
329                     llvm::StringRef Suffix) {
330     // We shouldn't get as far as adding a hint if the category is disabled.
331     // We'd like to disable as much of the analysis as possible above instead.
332     // Assert in debug mode but add a dynamic check in production.
333     assert(Cfg.InlayHints.Enabled && "Shouldn't get here if disabled!");
334     switch (Kind) {
335 #define CHECK_KIND(Enumerator, ConfigProperty)                                 \
336   case InlayHintKind::Enumerator:                                              \
337     assert(Cfg.InlayHints.ConfigProperty &&                                    \
338            "Shouldn't get here if kind is disabled!");                         \
339     if (!Cfg.InlayHints.ConfigProperty)                                        \
340       return;                                                                  \
341     break
342       CHECK_KIND(ParameterHint, Parameters);
343       CHECK_KIND(TypeHint, DeducedTypes);
344 #undef CHECK_KIND
345     }
346 
347     auto FileRange =
348         toHalfOpenFileRange(AST.getSourceManager(), AST.getLangOpts(), R);
349     if (!FileRange)
350       return;
351     Range LSPRange{
352         sourceLocToPosition(AST.getSourceManager(), FileRange->getBegin()),
353         sourceLocToPosition(AST.getSourceManager(), FileRange->getEnd())};
354     Position LSPPos = Side == HintSide::Left ? LSPRange.start : LSPRange.end;
355     if (RestrictRange &&
356         (LSPPos < RestrictRange->start || !(LSPPos < RestrictRange->end)))
357       return;
358     // The hint may be in a file other than the main file (for example, a header
359     // file that was included after the preamble), do not show in that case.
360     if (!AST.getSourceManager().isWrittenInMainFile(FileRange->getBegin()))
361       return;
362     Results.push_back(
363         InlayHint{LSPPos, LSPRange, Kind, (Prefix + Label + Suffix).str()});
364   }
365 
366   void addTypeHint(SourceRange R, QualType T, llvm::StringRef Prefix) {
367     addTypeHint(R, T, Prefix, TypeHintPolicy);
368   }
369 
370   void addTypeHint(SourceRange R, QualType T, llvm::StringRef Prefix,
371                    const PrintingPolicy &Policy) {
372     if (!Cfg.InlayHints.DeducedTypes || T.isNull())
373       return;
374 
375     std::string TypeName = T.getAsString(Policy);
376     if (TypeName.length() < TypeNameLimit)
377       addInlayHint(R, HintSide::Right, InlayHintKind::TypeHint, Prefix,
378                    TypeName, /*Suffix=*/"");
379   }
380 
381   std::vector<InlayHint> &Results;
382   ASTContext &AST;
383   const Config &Cfg;
384   llvm::Optional<Range> RestrictRange;
385   FileID MainFileID;
386   StringRef MainFileBuf;
387   const HeuristicResolver *Resolver;
388   // We want to suppress default template arguments, but otherwise print
389   // canonical types. Unfortunately, they're conflicting policies so we can't
390   // have both. For regular types, suppressing template arguments is more
391   // important, whereas printing canonical types is crucial for structured
392   // bindings, so we use two separate policies. (See the constructor where
393   // the policies are initialized for more details.)
394   PrintingPolicy TypeHintPolicy;
395   PrintingPolicy StructuredBindingPolicy;
396 
397   static const size_t TypeNameLimit = 32;
398 };
399 
400 } // namespace
401 
402 std::vector<InlayHint> inlayHints(ParsedAST &AST,
403                                   llvm::Optional<Range> RestrictRange) {
404   std::vector<InlayHint> Results;
405   const auto &Cfg = Config::current();
406   if (!Cfg.InlayHints.Enabled)
407     return Results;
408   InlayHintVisitor Visitor(Results, AST, Cfg, std::move(RestrictRange));
409   Visitor.TraverseAST(AST.getASTContext());
410 
411   // De-duplicate hints. Duplicates can sometimes occur due to e.g. explicit
412   // template instantiations.
413   llvm::sort(Results);
414   Results.erase(std::unique(Results.begin(), Results.end()), Results.end());
415 
416   return Results;
417 }
418 
419 } // namespace clangd
420 } // namespace clang
421