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 #include "llvm/ADT/ScopeExit.h" 17 18 namespace clang { 19 namespace clangd { 20 namespace { 21 22 // For now, inlay hints are always anchored at the left or right of their range. 23 enum class HintSide { Left, Right }; 24 25 // Helper class to iterate over the designator names of an aggregate type. 26 // 27 // For an array type, yields [0], [1], [2]... 28 // For aggregate classes, yields null for each base, then .field1, .field2, ... 29 class AggregateDesignatorNames { 30 public: 31 AggregateDesignatorNames(QualType T) { 32 if (!T.isNull()) { 33 T = T.getCanonicalType(); 34 if (T->isArrayType()) { 35 IsArray = true; 36 Valid = true; 37 return; 38 } 39 if (const RecordDecl *RD = T->getAsRecordDecl()) { 40 Valid = true; 41 FieldsIt = RD->field_begin(); 42 FieldsEnd = RD->field_end(); 43 if (const auto *CRD = llvm::dyn_cast<CXXRecordDecl>(RD)) { 44 BasesIt = CRD->bases_begin(); 45 BasesEnd = CRD->bases_end(); 46 Valid = CRD->isAggregate(); 47 } 48 OneField = Valid && BasesIt == BasesEnd && FieldsIt != FieldsEnd && 49 std::next(FieldsIt) == FieldsEnd; 50 } 51 } 52 } 53 // Returns false if the type was not an aggregate. 54 operator bool() { return Valid; } 55 // Advance to the next element in the aggregate. 56 void next() { 57 if (IsArray) 58 ++Index; 59 else if (BasesIt != BasesEnd) 60 ++BasesIt; 61 else if (FieldsIt != FieldsEnd) 62 ++FieldsIt; 63 } 64 // Print the designator to Out. 65 // Returns false if we could not produce a designator for this element. 66 bool append(std::string &Out, bool ForSubobject) { 67 if (IsArray) { 68 Out.push_back('['); 69 Out.append(std::to_string(Index)); 70 Out.push_back(']'); 71 return true; 72 } 73 if (BasesIt != BasesEnd) 74 return false; // Bases can't be designated. Should we make one up? 75 if (FieldsIt != FieldsEnd) { 76 llvm::StringRef FieldName; 77 if (const IdentifierInfo *II = FieldsIt->getIdentifier()) 78 FieldName = II->getName(); 79 80 // For certain objects, their subobjects may be named directly. 81 if (ForSubobject && 82 (FieldsIt->isAnonymousStructOrUnion() || 83 // std::array<int,3> x = {1,2,3}. Designators not strictly valid! 84 (OneField && isReservedName(FieldName)))) 85 return true; 86 87 if (!FieldName.empty() && !isReservedName(FieldName)) { 88 Out.push_back('.'); 89 Out.append(FieldName.begin(), FieldName.end()); 90 return true; 91 } 92 return false; 93 } 94 return false; 95 } 96 97 private: 98 bool Valid = false; 99 bool IsArray = false; 100 bool OneField = false; // e.g. std::array { T __elements[N]; } 101 unsigned Index = 0; 102 CXXRecordDecl::base_class_const_iterator BasesIt; 103 CXXRecordDecl::base_class_const_iterator BasesEnd; 104 RecordDecl::field_iterator FieldsIt; 105 RecordDecl::field_iterator FieldsEnd; 106 }; 107 108 // Collect designator labels describing the elements of an init list. 109 // 110 // This function contributes the designators of some (sub)object, which is 111 // represented by the semantic InitListExpr Sem. 112 // This includes any nested subobjects, but *only* if they are part of the same 113 // original syntactic init list (due to brace elision). 114 // In other words, it may descend into subobjects but not written init-lists. 115 // 116 // For example: struct Outer { Inner a,b; }; struct Inner { int x, y; } 117 // Outer o{{1, 2}, 3}; 118 // This function will be called with Sem = { {1, 2}, {3, ImplicitValue} } 119 // It should generate designators '.a:' and '.b.x:'. 120 // '.a:' is produced directly without recursing into the written sublist. 121 // (The written sublist will have a separate collectDesignators() call later). 122 // Recursion with Prefix='.b' and Sem = {3, ImplicitValue} produces '.b.x:'. 123 void collectDesignators(const InitListExpr *Sem, 124 llvm::DenseMap<SourceLocation, std::string> &Out, 125 const llvm::DenseSet<SourceLocation> &NestedBraces, 126 std::string &Prefix) { 127 if (!Sem || Sem->isTransparent()) 128 return; 129 assert(Sem->isSemanticForm()); 130 131 // The elements of the semantic form all correspond to direct subobjects of 132 // the aggregate type. `Fields` iterates over these subobject names. 133 AggregateDesignatorNames Fields(Sem->getType()); 134 if (!Fields) 135 return; 136 for (const Expr *Init : Sem->inits()) { 137 auto Next = llvm::make_scope_exit([&, Size(Prefix.size())] { 138 Fields.next(); // Always advance to the next subobject name. 139 Prefix.resize(Size); // Erase any designator we appended. 140 }); 141 if (llvm::isa<ImplicitValueInitExpr>(Init)) 142 continue; // a "hole" for a subobject that was not explicitly initialized 143 144 const auto *BraceElidedSubobject = llvm::dyn_cast<InitListExpr>(Init); 145 if (BraceElidedSubobject && 146 NestedBraces.contains(BraceElidedSubobject->getLBraceLoc())) 147 BraceElidedSubobject = nullptr; // there were braces! 148 149 if (!Fields.append(Prefix, BraceElidedSubobject != nullptr)) 150 continue; // no designator available for this subobject 151 if (BraceElidedSubobject) { 152 // If the braces were elided, this aggregate subobject is initialized 153 // inline in the same syntactic list. 154 // Descend into the semantic list describing the subobject. 155 // (NestedBraces are still correct, they're from the same syntactic list). 156 collectDesignators(BraceElidedSubobject, Out, NestedBraces, Prefix); 157 continue; 158 } 159 Out.try_emplace(Init->getBeginLoc(), Prefix); 160 } 161 } 162 163 // Get designators describing the elements of a (syntactic) init list. 164 // This does not produce designators for any explicitly-written nested lists. 165 llvm::DenseMap<SourceLocation, std::string> 166 getDesignators(const InitListExpr *Syn) { 167 assert(Syn->isSyntacticForm()); 168 169 // collectDesignators needs to know which InitListExprs in the semantic tree 170 // were actually written, but InitListExpr::isExplicit() lies. 171 // Instead, record where braces of sub-init-lists occur in the syntactic form. 172 llvm::DenseSet<SourceLocation> NestedBraces; 173 for (const Expr *Init : Syn->inits()) 174 if (auto *Nested = llvm::dyn_cast<InitListExpr>(Init)) 175 NestedBraces.insert(Nested->getLBraceLoc()); 176 177 // Traverse the semantic form to find the designators. 178 // We use their SourceLocation to correlate with the syntactic form later. 179 llvm::DenseMap<SourceLocation, std::string> Designators; 180 std::string EmptyPrefix; 181 collectDesignators(Syn->isSemanticForm() ? Syn : Syn->getSemanticForm(), 182 Designators, NestedBraces, EmptyPrefix); 183 return Designators; 184 } 185 186 class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> { 187 public: 188 InlayHintVisitor(std::vector<InlayHint> &Results, ParsedAST &AST, 189 const Config &Cfg, llvm::Optional<Range> RestrictRange) 190 : Results(Results), AST(AST.getASTContext()), Cfg(Cfg), 191 RestrictRange(std::move(RestrictRange)), 192 MainFileID(AST.getSourceManager().getMainFileID()), 193 Resolver(AST.getHeuristicResolver()), 194 TypeHintPolicy(this->AST.getPrintingPolicy()), 195 StructuredBindingPolicy(this->AST.getPrintingPolicy()) { 196 bool Invalid = false; 197 llvm::StringRef Buf = 198 AST.getSourceManager().getBufferData(MainFileID, &Invalid); 199 MainFileBuf = Invalid ? StringRef{} : Buf; 200 201 TypeHintPolicy.SuppressScope = true; // keep type names short 202 TypeHintPolicy.AnonymousTagLocations = 203 false; // do not print lambda locations 204 205 // For structured bindings, print canonical types. This is important because 206 // for bindings that use the tuple_element protocol, the non-canonical types 207 // would be "tuple_element<I, A>::type". 208 // For "auto", we often prefer sugared types. 209 // Not setting PrintCanonicalTypes for "auto" allows 210 // SuppressDefaultTemplateArgs (set by default) to have an effect. 211 StructuredBindingPolicy = TypeHintPolicy; 212 StructuredBindingPolicy.PrintCanonicalTypes = true; 213 } 214 215 bool VisitCXXConstructExpr(CXXConstructExpr *E) { 216 // Weed out constructor calls that don't look like a function call with 217 // an argument list, by checking the validity of getParenOrBraceRange(). 218 // Also weed out std::initializer_list constructors as there are no names 219 // for the individual arguments. 220 if (!E->getParenOrBraceRange().isValid() || 221 E->isStdInitListInitialization()) { 222 return true; 223 } 224 225 processCall(E->getParenOrBraceRange().getBegin(), E->getConstructor(), 226 {E->getArgs(), E->getNumArgs()}); 227 return true; 228 } 229 230 bool VisitCallExpr(CallExpr *E) { 231 if (!Cfg.InlayHints.Parameters) 232 return true; 233 234 // Do not show parameter hints for operator calls written using operator 235 // syntax or user-defined literals. (Among other reasons, the resulting 236 // hints can look awkard, e.g. the expression can itself be a function 237 // argument and then we'd get two hints side by side). 238 if (isa<CXXOperatorCallExpr>(E) || isa<UserDefinedLiteral>(E)) 239 return true; 240 241 auto CalleeDecls = Resolver->resolveCalleeOfCallExpr(E); 242 if (CalleeDecls.size() != 1) 243 return true; 244 const FunctionDecl *Callee = nullptr; 245 if (const auto *FD = dyn_cast<FunctionDecl>(CalleeDecls[0])) 246 Callee = FD; 247 else if (const auto *FTD = dyn_cast<FunctionTemplateDecl>(CalleeDecls[0])) 248 Callee = FTD->getTemplatedDecl(); 249 if (!Callee) 250 return true; 251 252 processCall(E->getRParenLoc(), Callee, {E->getArgs(), E->getNumArgs()}); 253 return true; 254 } 255 256 bool VisitFunctionDecl(FunctionDecl *D) { 257 if (auto *AT = D->getReturnType()->getContainedAutoType()) { 258 QualType Deduced = AT->getDeducedType(); 259 if (!Deduced.isNull()) { 260 addTypeHint(D->getFunctionTypeLoc().getRParenLoc(), D->getReturnType(), 261 /*Prefix=*/"-> "); 262 } 263 } 264 265 return true; 266 } 267 268 bool VisitVarDecl(VarDecl *D) { 269 // Do not show hints for the aggregate in a structured binding, 270 // but show hints for the individual bindings. 271 if (auto *DD = dyn_cast<DecompositionDecl>(D)) { 272 for (auto *Binding : DD->bindings()) { 273 addTypeHint(Binding->getLocation(), Binding->getType(), /*Prefix=*/": ", 274 StructuredBindingPolicy); 275 } 276 return true; 277 } 278 279 if (D->getType()->getContainedAutoType()) { 280 if (!D->getType()->isDependentType()) { 281 // Our current approach is to place the hint on the variable 282 // and accordingly print the full type 283 // (e.g. for `const auto& x = 42`, print `const int&`). 284 // Alternatively, we could place the hint on the `auto` 285 // (and then just print the type deduced for the `auto`). 286 addTypeHint(D->getLocation(), D->getType(), /*Prefix=*/": "); 287 } 288 } 289 return true; 290 } 291 292 bool VisitInitListExpr(InitListExpr *Syn) { 293 // We receive the syntactic form here (shouldVisitImplicitCode() is false). 294 // This is the one we will ultimately attach designators to. 295 // It may have subobject initializers inlined without braces. The *semantic* 296 // form of the init-list has nested init-lists for these. 297 // getDesignators will look at the semantic form to determine the labels. 298 assert(Syn->isSyntacticForm() && "RAV should not visit implicit code!"); 299 if (!Cfg.InlayHints.Designators) 300 return true; 301 if (Syn->isIdiomaticZeroInitializer(AST.getLangOpts())) 302 return true; 303 llvm::DenseMap<SourceLocation, std::string> Designators = 304 getDesignators(Syn); 305 for (const Expr *Init : Syn->inits()) { 306 if (llvm::isa<DesignatedInitExpr>(Init)) 307 continue; 308 auto It = Designators.find(Init->getBeginLoc()); 309 if (It != Designators.end() && 310 !isPrecededByParamNameComment(Init, It->second)) 311 addDesignatorHint(Init->getSourceRange(), It->second); 312 } 313 return true; 314 } 315 316 // FIXME: Handle RecoveryExpr to try to hint some invalid calls. 317 318 private: 319 using NameVec = SmallVector<StringRef, 8>; 320 321 // The purpose of Anchor is to deal with macros. It should be the call's 322 // opening or closing parenthesis or brace. (Always using the opening would 323 // make more sense but CallExpr only exposes the closing.) We heuristically 324 // assume that if this location does not come from a macro definition, then 325 // the entire argument list likely appears in the main file and can be hinted. 326 void processCall(SourceLocation Anchor, const FunctionDecl *Callee, 327 llvm::ArrayRef<const Expr *const> Args) { 328 if (!Cfg.InlayHints.Parameters || Args.size() == 0 || !Callee) 329 return; 330 331 // If the anchor location comes from a macro defintion, there's nowhere to 332 // put hints. 333 if (!AST.getSourceManager().getTopMacroCallerLoc(Anchor).isFileID()) 334 return; 335 336 // The parameter name of a move or copy constructor is not very interesting. 337 if (auto *Ctor = dyn_cast<CXXConstructorDecl>(Callee)) 338 if (Ctor->isCopyOrMoveConstructor()) 339 return; 340 341 // Don't show hints for variadic parameters. 342 size_t FixedParamCount = getFixedParamCount(Callee); 343 size_t ArgCount = std::min(FixedParamCount, Args.size()); 344 345 NameVec ParameterNames = chooseParameterNames(Callee, ArgCount); 346 347 // Exclude setters (i.e. functions with one argument whose name begins with 348 // "set"), as their parameter name is also not likely to be interesting. 349 if (isSetter(Callee, ParameterNames)) 350 return; 351 352 for (size_t I = 0; I < ArgCount; ++I) { 353 StringRef Name = ParameterNames[I]; 354 if (!shouldHint(Args[I], Name)) 355 continue; 356 357 addInlayHint(Args[I]->getSourceRange(), HintSide::Left, 358 InlayHintKind::ParameterHint, /*Prefix=*/"", Name, 359 /*Suffix=*/": "); 360 } 361 } 362 363 static bool isSetter(const FunctionDecl *Callee, const NameVec &ParamNames) { 364 if (ParamNames.size() != 1) 365 return false; 366 367 StringRef Name = getSimpleName(*Callee); 368 if (!Name.startswith_insensitive("set")) 369 return false; 370 371 // In addition to checking that the function has one parameter and its 372 // name starts with "set", also check that the part after "set" matches 373 // the name of the parameter (ignoring case). The idea here is that if 374 // the parameter name differs, it may contain extra information that 375 // may be useful to show in a hint, as in: 376 // void setTimeout(int timeoutMillis); 377 // This currently doesn't handle cases where params use snake_case 378 // and functions don't, e.g. 379 // void setExceptionHandler(EHFunc exception_handler); 380 // We could improve this by replacing `equals_insensitive` with some 381 // `sloppy_equals` which ignores case and also skips underscores. 382 StringRef WhatItIsSetting = Name.substr(3).ltrim("_"); 383 return WhatItIsSetting.equals_insensitive(ParamNames[0]); 384 } 385 386 bool shouldHint(const Expr *Arg, StringRef ParamName) { 387 if (ParamName.empty()) 388 return false; 389 390 // If the argument expression is a single name and it matches the 391 // parameter name exactly, omit the hint. 392 if (ParamName == getSpelledIdentifier(Arg)) 393 return false; 394 395 // Exclude argument expressions preceded by a /*paramName*/. 396 if (isPrecededByParamNameComment(Arg, ParamName)) 397 return false; 398 399 return true; 400 } 401 402 // Checks if "E" is spelled in the main file and preceded by a C-style comment 403 // whose contents match ParamName (allowing for whitespace and an optional "=" 404 // at the end. 405 bool isPrecededByParamNameComment(const Expr *E, StringRef ParamName) { 406 auto &SM = AST.getSourceManager(); 407 auto ExprStartLoc = SM.getTopMacroCallerLoc(E->getBeginLoc()); 408 auto Decomposed = SM.getDecomposedLoc(ExprStartLoc); 409 if (Decomposed.first != MainFileID) 410 return false; 411 412 StringRef SourcePrefix = MainFileBuf.substr(0, Decomposed.second); 413 // Allow whitespace between comment and expression. 414 SourcePrefix = SourcePrefix.rtrim(); 415 // Check for comment ending. 416 if (!SourcePrefix.consume_back("*/")) 417 return false; 418 // Ignore some punctuation and whitespace around comment. 419 // In particular this allows designators to match nicely. 420 llvm::StringLiteral IgnoreChars = " =."; 421 SourcePrefix = SourcePrefix.rtrim(IgnoreChars); 422 ParamName = ParamName.trim(IgnoreChars); 423 // Other than that, the comment must contain exactly ParamName. 424 if (!SourcePrefix.consume_back(ParamName)) 425 return false; 426 SourcePrefix = SourcePrefix.rtrim(IgnoreChars); 427 return SourcePrefix.endswith("/*"); 428 } 429 430 // If "E" spells a single unqualified identifier, return that name. 431 // Otherwise, return an empty string. 432 static StringRef getSpelledIdentifier(const Expr *E) { 433 E = E->IgnoreUnlessSpelledInSource(); 434 435 if (auto *DRE = dyn_cast<DeclRefExpr>(E)) 436 if (!DRE->getQualifier()) 437 return getSimpleName(*DRE->getDecl()); 438 439 if (auto *ME = dyn_cast<MemberExpr>(E)) 440 if (!ME->getQualifier() && ME->isImplicitAccess()) 441 return getSimpleName(*ME->getMemberDecl()); 442 443 return {}; 444 } 445 446 NameVec chooseParameterNames(const FunctionDecl *Callee, size_t ArgCount) { 447 // The current strategy here is to use all the parameter names from the 448 // canonical declaration, unless they're all empty, in which case we 449 // use all the parameter names from the definition (in present in the 450 // translation unit). 451 // We could try a bit harder, e.g.: 452 // - try all re-declarations, not just canonical + definition 453 // - fall back arg-by-arg rather than wholesale 454 455 NameVec ParameterNames = getParameterNamesForDecl(Callee, ArgCount); 456 457 if (llvm::all_of(ParameterNames, std::mem_fn(&StringRef::empty))) { 458 if (const FunctionDecl *Def = Callee->getDefinition()) { 459 ParameterNames = getParameterNamesForDecl(Def, ArgCount); 460 } 461 } 462 assert(ParameterNames.size() == ArgCount); 463 464 // Standard library functions often have parameter names that start 465 // with underscores, which makes the hints noisy, so strip them out. 466 for (auto &Name : ParameterNames) 467 stripLeadingUnderscores(Name); 468 469 return ParameterNames; 470 } 471 472 static void stripLeadingUnderscores(StringRef &Name) { 473 Name = Name.ltrim('_'); 474 } 475 476 // Return the number of fixed parameters Function has, that is, not counting 477 // parameters that are variadic (instantiated from a parameter pack) or 478 // C-style varargs. 479 static size_t getFixedParamCount(const FunctionDecl *Function) { 480 if (FunctionTemplateDecl *Template = Function->getPrimaryTemplate()) { 481 FunctionDecl *F = Template->getTemplatedDecl(); 482 size_t Result = 0; 483 for (ParmVarDecl *Parm : F->parameters()) { 484 if (Parm->isParameterPack()) { 485 break; 486 } 487 ++Result; 488 } 489 return Result; 490 } 491 // C-style varargs don't need special handling, they're already 492 // not included in getNumParams(). 493 return Function->getNumParams(); 494 } 495 496 static StringRef getSimpleName(const NamedDecl &D) { 497 if (IdentifierInfo *Ident = D.getDeclName().getAsIdentifierInfo()) { 498 return Ident->getName(); 499 } 500 501 return StringRef(); 502 } 503 504 NameVec getParameterNamesForDecl(const FunctionDecl *Function, 505 size_t ArgCount) { 506 NameVec Result; 507 for (size_t I = 0; I < ArgCount; ++I) { 508 const ParmVarDecl *Parm = Function->getParamDecl(I); 509 assert(Parm); 510 Result.emplace_back(getSimpleName(*Parm)); 511 } 512 return Result; 513 } 514 515 // We pass HintSide rather than SourceLocation because we want to ensure 516 // it is in the same file as the common file range. 517 void addInlayHint(SourceRange R, HintSide Side, InlayHintKind Kind, 518 llvm::StringRef Prefix, llvm::StringRef Label, 519 llvm::StringRef Suffix) { 520 // We shouldn't get as far as adding a hint if the category is disabled. 521 // We'd like to disable as much of the analysis as possible above instead. 522 // Assert in debug mode but add a dynamic check in production. 523 assert(Cfg.InlayHints.Enabled && "Shouldn't get here if disabled!"); 524 switch (Kind) { 525 #define CHECK_KIND(Enumerator, ConfigProperty) \ 526 case InlayHintKind::Enumerator: \ 527 assert(Cfg.InlayHints.ConfigProperty && \ 528 "Shouldn't get here if kind is disabled!"); \ 529 if (!Cfg.InlayHints.ConfigProperty) \ 530 return; \ 531 break 532 CHECK_KIND(ParameterHint, Parameters); 533 CHECK_KIND(TypeHint, DeducedTypes); 534 CHECK_KIND(DesignatorHint, Designators); 535 #undef CHECK_KIND 536 } 537 538 auto FileRange = 539 toHalfOpenFileRange(AST.getSourceManager(), AST.getLangOpts(), R); 540 if (!FileRange) 541 return; 542 Range LSPRange{ 543 sourceLocToPosition(AST.getSourceManager(), FileRange->getBegin()), 544 sourceLocToPosition(AST.getSourceManager(), FileRange->getEnd())}; 545 Position LSPPos = Side == HintSide::Left ? LSPRange.start : LSPRange.end; 546 if (RestrictRange && 547 (LSPPos < RestrictRange->start || !(LSPPos < RestrictRange->end))) 548 return; 549 // The hint may be in a file other than the main file (for example, a header 550 // file that was included after the preamble), do not show in that case. 551 if (!AST.getSourceManager().isWrittenInMainFile(FileRange->getBegin())) 552 return; 553 Results.push_back( 554 InlayHint{LSPPos, LSPRange, Kind, (Prefix + Label + Suffix).str()}); 555 } 556 557 void addTypeHint(SourceRange R, QualType T, llvm::StringRef Prefix) { 558 addTypeHint(R, T, Prefix, TypeHintPolicy); 559 } 560 561 void addTypeHint(SourceRange R, QualType T, llvm::StringRef Prefix, 562 const PrintingPolicy &Policy) { 563 if (!Cfg.InlayHints.DeducedTypes || T.isNull()) 564 return; 565 566 std::string TypeName = T.getAsString(Policy); 567 if (TypeName.length() < TypeNameLimit) 568 addInlayHint(R, HintSide::Right, InlayHintKind::TypeHint, Prefix, 569 TypeName, /*Suffix=*/""); 570 } 571 572 void addDesignatorHint(SourceRange R, llvm::StringRef Text) { 573 addInlayHint(R, HintSide::Left, InlayHintKind::DesignatorHint, 574 /*Prefix=*/"", Text, /*Suffix=*/"="); 575 } 576 577 std::vector<InlayHint> &Results; 578 ASTContext &AST; 579 const Config &Cfg; 580 llvm::Optional<Range> RestrictRange; 581 FileID MainFileID; 582 StringRef MainFileBuf; 583 const HeuristicResolver *Resolver; 584 // We want to suppress default template arguments, but otherwise print 585 // canonical types. Unfortunately, they're conflicting policies so we can't 586 // have both. For regular types, suppressing template arguments is more 587 // important, whereas printing canonical types is crucial for structured 588 // bindings, so we use two separate policies. (See the constructor where 589 // the policies are initialized for more details.) 590 PrintingPolicy TypeHintPolicy; 591 PrintingPolicy StructuredBindingPolicy; 592 593 static const size_t TypeNameLimit = 32; 594 }; 595 596 } // namespace 597 598 std::vector<InlayHint> inlayHints(ParsedAST &AST, 599 llvm::Optional<Range> RestrictRange) { 600 std::vector<InlayHint> Results; 601 const auto &Cfg = Config::current(); 602 if (!Cfg.InlayHints.Enabled) 603 return Results; 604 InlayHintVisitor Visitor(Results, AST, Cfg, std::move(RestrictRange)); 605 Visitor.TraverseAST(AST.getASTContext()); 606 607 // De-duplicate hints. Duplicates can sometimes occur due to e.g. explicit 608 // template instantiations. 609 llvm::sort(Results); 610 Results.erase(std::unique(Results.begin(), Results.end()), Results.end()); 611 612 return Results; 613 } 614 615 } // namespace clangd 616 } // namespace clang 617