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