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