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 return Results; 370 } 371 372 } // namespace clangd 373 } // namespace clang 374