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 "../clang-tidy/utils/DesignatedInitializers.h" 10 #include "AST.h" 11 #include "Config.h" 12 #include "ParsedAST.h" 13 #include "Protocol.h" 14 #include "SourceCode.h" 15 #include "clang/AST/ASTDiagnostic.h" 16 #include "clang/AST/Decl.h" 17 #include "clang/AST/DeclBase.h" 18 #include "clang/AST/DeclarationName.h" 19 #include "clang/AST/Expr.h" 20 #include "clang/AST/ExprCXX.h" 21 #include "clang/AST/RecursiveASTVisitor.h" 22 #include "clang/AST/Stmt.h" 23 #include "clang/AST/StmtVisitor.h" 24 #include "clang/AST/Type.h" 25 #include "clang/Basic/Builtins.h" 26 #include "clang/Basic/OperatorKinds.h" 27 #include "clang/Basic/SourceLocation.h" 28 #include "clang/Basic/SourceManager.h" 29 #include "clang/Sema/HeuristicResolver.h" 30 #include "llvm/ADT/DenseSet.h" 31 #include "llvm/ADT/STLExtras.h" 32 #include "llvm/ADT/SmallVector.h" 33 #include "llvm/ADT/StringExtras.h" 34 #include "llvm/ADT/StringRef.h" 35 #include "llvm/ADT/Twine.h" 36 #include "llvm/Support/Casting.h" 37 #include "llvm/Support/ErrorHandling.h" 38 #include "llvm/Support/FormatVariadic.h" 39 #include "llvm/Support/SaveAndRestore.h" 40 #include "llvm/Support/ScopedPrinter.h" 41 #include "llvm/Support/raw_ostream.h" 42 #include <algorithm> 43 #include <iterator> 44 #include <optional> 45 #include <string> 46 47 namespace clang { 48 namespace clangd { 49 namespace { 50 51 // For now, inlay hints are always anchored at the left or right of their range. 52 enum class HintSide { Left, Right }; 53 54 void stripLeadingUnderscores(StringRef &Name) { Name = Name.ltrim('_'); } 55 56 // getDeclForType() returns the decl responsible for Type's spelling. 57 // This is the inverse of ASTContext::getTypeDeclType(). 58 template <typename Ty, typename = decltype(((Ty *)nullptr)->getDecl())> 59 const NamedDecl *getDeclForTypeImpl(const Ty *T) { 60 return T->getDecl(); 61 } 62 const NamedDecl *getDeclForTypeImpl(const void *T) { return nullptr; } 63 const NamedDecl *getDeclForType(const Type *T) { 64 switch (T->getTypeClass()) { 65 #define ABSTRACT_TYPE(TY, BASE) 66 #define TYPE(TY, BASE) \ 67 case Type::TY: \ 68 return getDeclForTypeImpl(llvm::cast<TY##Type>(T)); 69 #include "clang/AST/TypeNodes.inc" 70 } 71 llvm_unreachable("Unknown TypeClass enum"); 72 } 73 74 // getSimpleName() returns the plain identifier for an entity, if any. 75 llvm::StringRef getSimpleName(const DeclarationName &DN) { 76 if (IdentifierInfo *Ident = DN.getAsIdentifierInfo()) 77 return Ident->getName(); 78 return ""; 79 } 80 llvm::StringRef getSimpleName(const NamedDecl &D) { 81 return getSimpleName(D.getDeclName()); 82 } 83 llvm::StringRef getSimpleName(QualType T) { 84 if (const auto *ET = llvm::dyn_cast<ElaboratedType>(T)) 85 return getSimpleName(ET->getNamedType()); 86 if (const auto *BT = llvm::dyn_cast<BuiltinType>(T)) { 87 PrintingPolicy PP(LangOptions{}); 88 PP.adjustForCPlusPlus(); 89 return BT->getName(PP); 90 } 91 if (const auto *D = getDeclForType(T.getTypePtr())) 92 return getSimpleName(D->getDeclName()); 93 return ""; 94 } 95 96 // Returns a very abbreviated form of an expression, or "" if it's too complex. 97 // For example: `foo->bar()` would produce "bar". 98 // This is used to summarize e.g. the condition of a while loop. 99 std::string summarizeExpr(const Expr *E) { 100 struct Namer : ConstStmtVisitor<Namer, std::string> { 101 std::string Visit(const Expr *E) { 102 if (E == nullptr) 103 return ""; 104 return ConstStmtVisitor::Visit(E->IgnoreImplicit()); 105 } 106 107 // Any sort of decl reference, we just use the unqualified name. 108 std::string VisitMemberExpr(const MemberExpr *E) { 109 return getSimpleName(*E->getMemberDecl()).str(); 110 } 111 std::string VisitDeclRefExpr(const DeclRefExpr *E) { 112 return getSimpleName(*E->getFoundDecl()).str(); 113 } 114 std::string VisitCallExpr(const CallExpr *E) { 115 return Visit(E->getCallee()); 116 } 117 std::string 118 VisitCXXDependentScopeMemberExpr(const CXXDependentScopeMemberExpr *E) { 119 return getSimpleName(E->getMember()).str(); 120 } 121 std::string 122 VisitDependentScopeDeclRefExpr(const DependentScopeDeclRefExpr *E) { 123 return getSimpleName(E->getDeclName()).str(); 124 } 125 std::string VisitCXXFunctionalCastExpr(const CXXFunctionalCastExpr *E) { 126 return getSimpleName(E->getType()).str(); 127 } 128 std::string VisitCXXTemporaryObjectExpr(const CXXTemporaryObjectExpr *E) { 129 return getSimpleName(E->getType()).str(); 130 } 131 132 // Step through implicit nodes that clang doesn't classify as such. 133 std::string VisitCXXMemberCallExpr(const CXXMemberCallExpr *E) { 134 // Call to operator bool() inside if (X): dispatch to X. 135 if (E->getNumArgs() == 0 && E->getMethodDecl() && 136 E->getMethodDecl()->getDeclName().getNameKind() == 137 DeclarationName::CXXConversionFunctionName && 138 E->getSourceRange() == 139 E->getImplicitObjectArgument()->getSourceRange()) 140 return Visit(E->getImplicitObjectArgument()); 141 return ConstStmtVisitor::VisitCXXMemberCallExpr(E); 142 } 143 std::string VisitCXXConstructExpr(const CXXConstructExpr *E) { 144 if (E->getNumArgs() == 1) 145 return Visit(E->getArg(0)); 146 return ""; 147 } 148 149 // Literals are just printed 150 std::string VisitCXXBoolLiteralExpr(const CXXBoolLiteralExpr *E) { 151 return E->getValue() ? "true" : "false"; 152 } 153 std::string VisitIntegerLiteral(const IntegerLiteral *E) { 154 return llvm::to_string(E->getValue()); 155 } 156 std::string VisitFloatingLiteral(const FloatingLiteral *E) { 157 std::string Result; 158 llvm::raw_string_ostream OS(Result); 159 E->getValue().print(OS); 160 // Printer adds newlines?! 161 Result.resize(llvm::StringRef(Result).rtrim().size()); 162 return Result; 163 } 164 std::string VisitStringLiteral(const StringLiteral *E) { 165 std::string Result = "\""; 166 if (E->containsNonAscii()) { 167 Result += "..."; 168 } else if (E->getLength() > 10) { 169 Result += E->getString().take_front(7); 170 Result += "..."; 171 } else { 172 llvm::raw_string_ostream OS(Result); 173 llvm::printEscapedString(E->getString(), OS); 174 } 175 Result.push_back('"'); 176 return Result; 177 } 178 179 // Simple operators. Motivating cases are `!x` and `I < Length`. 180 std::string printUnary(llvm::StringRef Spelling, const Expr *Operand, 181 bool Prefix) { 182 std::string Sub = Visit(Operand); 183 if (Sub.empty()) 184 return ""; 185 if (Prefix) 186 return (Spelling + Sub).str(); 187 Sub += Spelling; 188 return Sub; 189 } 190 bool InsideBinary = false; // No recursing into binary expressions. 191 std::string printBinary(llvm::StringRef Spelling, const Expr *LHSOp, 192 const Expr *RHSOp) { 193 if (InsideBinary) 194 return ""; 195 llvm::SaveAndRestore InBinary(InsideBinary, true); 196 197 std::string LHS = Visit(LHSOp); 198 std::string RHS = Visit(RHSOp); 199 if (LHS.empty() && RHS.empty()) 200 return ""; 201 202 if (LHS.empty()) 203 LHS = "..."; 204 LHS.push_back(' '); 205 LHS += Spelling; 206 LHS.push_back(' '); 207 if (RHS.empty()) 208 LHS += "..."; 209 else 210 LHS += RHS; 211 return LHS; 212 } 213 std::string VisitUnaryOperator(const UnaryOperator *E) { 214 return printUnary(E->getOpcodeStr(E->getOpcode()), E->getSubExpr(), 215 !E->isPostfix()); 216 } 217 std::string VisitBinaryOperator(const BinaryOperator *E) { 218 return printBinary(E->getOpcodeStr(E->getOpcode()), E->getLHS(), 219 E->getRHS()); 220 } 221 std::string VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *E) { 222 const char *Spelling = getOperatorSpelling(E->getOperator()); 223 // Handle weird unary-that-look-like-binary postfix operators. 224 if ((E->getOperator() == OO_PlusPlus || 225 E->getOperator() == OO_MinusMinus) && 226 E->getNumArgs() == 2) 227 return printUnary(Spelling, E->getArg(0), false); 228 if (E->isInfixBinaryOp()) 229 return printBinary(Spelling, E->getArg(0), E->getArg(1)); 230 if (E->getNumArgs() == 1) { 231 switch (E->getOperator()) { 232 case OO_Plus: 233 case OO_Minus: 234 case OO_Star: 235 case OO_Amp: 236 case OO_Tilde: 237 case OO_Exclaim: 238 case OO_PlusPlus: 239 case OO_MinusMinus: 240 return printUnary(Spelling, E->getArg(0), true); 241 default: 242 break; 243 } 244 } 245 return ""; 246 } 247 }; 248 return Namer{}.Visit(E); 249 } 250 251 // Determines if any intermediate type in desugaring QualType QT is of 252 // substituted template parameter type. Ignore pointer or reference wrappers. 253 bool isSugaredTemplateParameter(QualType QT) { 254 static auto PeelWrapper = [](QualType QT) { 255 // Neither `PointerType` nor `ReferenceType` is considered as sugared 256 // type. Peel it. 257 QualType Peeled = QT->getPointeeType(); 258 return Peeled.isNull() ? QT : Peeled; 259 }; 260 261 // This is a bit tricky: we traverse the type structure and find whether or 262 // not a type in the desugaring process is of SubstTemplateTypeParmType. 263 // During the process, we may encounter pointer or reference types that are 264 // not marked as sugared; therefore, the desugar function won't apply. To 265 // move forward the traversal, we retrieve the pointees using 266 // QualType::getPointeeType(). 267 // 268 // However, getPointeeType could leap over our interests: The QT::getAs<T>() 269 // invoked would implicitly desugar the type. Consequently, if the 270 // SubstTemplateTypeParmType is encompassed within a TypedefType, we may lose 271 // the chance to visit it. 272 // For example, given a QT that represents `std::vector<int *>::value_type`: 273 // `-ElaboratedType 'value_type' sugar 274 // `-TypedefType 'vector<int *>::value_type' sugar 275 // |-Typedef 'value_type' 276 // `-SubstTemplateTypeParmType 'int *' sugar class depth 0 index 0 T 277 // |-ClassTemplateSpecialization 'vector' 278 // `-PointerType 'int *' 279 // `-BuiltinType 'int' 280 // Applying `getPointeeType` to QT results in 'int', a child of our target 281 // node SubstTemplateTypeParmType. 282 // 283 // As such, we always prefer the desugared over the pointee for next type 284 // in the iteration. It could avoid the getPointeeType's implicit desugaring. 285 while (true) { 286 if (QT->getAs<SubstTemplateTypeParmType>()) 287 return true; 288 QualType Desugared = QT->getLocallyUnqualifiedSingleStepDesugaredType(); 289 if (Desugared != QT) 290 QT = Desugared; 291 else if (auto Peeled = PeelWrapper(Desugared); Peeled != QT) 292 QT = Peeled; 293 else 294 break; 295 } 296 return false; 297 } 298 299 // A simple wrapper for `clang::desugarForDiagnostic` that provides optional 300 // semantic. 301 std::optional<QualType> desugar(ASTContext &AST, QualType QT) { 302 bool ShouldAKA = false; 303 auto Desugared = clang::desugarForDiagnostic(AST, QT, ShouldAKA); 304 if (!ShouldAKA) 305 return std::nullopt; 306 return Desugared; 307 } 308 309 // Apply a series of heuristic methods to determine whether or not a QualType QT 310 // is suitable for desugaring (e.g. getting the real name behind the using-alias 311 // name). If so, return the desugared type. Otherwise, return the unchanged 312 // parameter QT. 313 // 314 // This could be refined further. See 315 // https://github.com/clangd/clangd/issues/1298. 316 QualType maybeDesugar(ASTContext &AST, QualType QT) { 317 // Prefer desugared type for name that aliases the template parameters. 318 // This can prevent things like printing opaque `: type` when accessing std 319 // containers. 320 if (isSugaredTemplateParameter(QT)) 321 return desugar(AST, QT).value_or(QT); 322 323 // Prefer desugared type for `decltype(expr)` specifiers. 324 if (QT->isDecltypeType()) 325 return QT.getCanonicalType(); 326 if (const AutoType *AT = QT->getContainedAutoType()) 327 if (!AT->getDeducedType().isNull() && 328 AT->getDeducedType()->isDecltypeType()) 329 return QT.getCanonicalType(); 330 331 return QT; 332 } 333 334 // Given a callee expression `Fn`, if the call is through a function pointer, 335 // try to find the declaration of the corresponding function pointer type, 336 // so that we can recover argument names from it. 337 // FIXME: This function is mostly duplicated in SemaCodeComplete.cpp; unify. 338 static FunctionProtoTypeLoc getPrototypeLoc(Expr *Fn) { 339 TypeLoc Target; 340 Expr *NakedFn = Fn->IgnoreParenCasts(); 341 if (const auto *T = NakedFn->getType().getTypePtr()->getAs<TypedefType>()) { 342 Target = T->getDecl()->getTypeSourceInfo()->getTypeLoc(); 343 } else if (const auto *DR = dyn_cast<DeclRefExpr>(NakedFn)) { 344 const auto *D = DR->getDecl(); 345 if (const auto *const VD = dyn_cast<VarDecl>(D)) { 346 Target = VD->getTypeSourceInfo()->getTypeLoc(); 347 } 348 } 349 350 if (!Target) 351 return {}; 352 353 // Unwrap types that may be wrapping the function type 354 while (true) { 355 if (auto P = Target.getAs<PointerTypeLoc>()) { 356 Target = P.getPointeeLoc(); 357 continue; 358 } 359 if (auto A = Target.getAs<AttributedTypeLoc>()) { 360 Target = A.getModifiedLoc(); 361 continue; 362 } 363 if (auto P = Target.getAs<ParenTypeLoc>()) { 364 Target = P.getInnerLoc(); 365 continue; 366 } 367 break; 368 } 369 370 if (auto F = Target.getAs<FunctionProtoTypeLoc>()) { 371 return F; 372 } 373 374 return {}; 375 } 376 377 ArrayRef<const ParmVarDecl *> 378 maybeDropCxxExplicitObjectParameters(ArrayRef<const ParmVarDecl *> Params) { 379 if (!Params.empty() && Params.front()->isExplicitObjectParameter()) 380 Params = Params.drop_front(1); 381 return Params; 382 } 383 384 template <typename R> 385 std::string joinAndTruncate(const R &Range, size_t MaxLength) { 386 std::string Out; 387 llvm::raw_string_ostream OS(Out); 388 llvm::ListSeparator Sep(", "); 389 for (auto &&Element : Range) { 390 OS << Sep; 391 if (Out.size() + Element.size() >= MaxLength) { 392 OS << "..."; 393 break; 394 } 395 OS << Element; 396 } 397 OS.flush(); 398 return Out; 399 } 400 401 struct Callee { 402 // Only one of Decl or Loc is set. 403 // Loc is for calls through function pointers. 404 const FunctionDecl *Decl = nullptr; 405 FunctionProtoTypeLoc Loc; 406 }; 407 408 class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> { 409 public: 410 InlayHintVisitor(std::vector<InlayHint> &Results, ParsedAST &AST, 411 const Config &Cfg, std::optional<Range> RestrictRange) 412 : Results(Results), AST(AST.getASTContext()), Tokens(AST.getTokens()), 413 Cfg(Cfg), RestrictRange(std::move(RestrictRange)), 414 MainFileID(AST.getSourceManager().getMainFileID()), 415 Resolver(AST.getHeuristicResolver()), 416 TypeHintPolicy(this->AST.getPrintingPolicy()) { 417 bool Invalid = false; 418 llvm::StringRef Buf = 419 AST.getSourceManager().getBufferData(MainFileID, &Invalid); 420 MainFileBuf = Invalid ? StringRef{} : Buf; 421 422 TypeHintPolicy.SuppressScope = true; // keep type names short 423 TypeHintPolicy.AnonymousTagLocations = 424 false; // do not print lambda locations 425 426 // Not setting PrintCanonicalTypes for "auto" allows 427 // SuppressDefaultTemplateArgs (set by default) to have an effect. 428 } 429 430 bool VisitTypeLoc(TypeLoc TL) { 431 if (const auto *DT = llvm::dyn_cast<DecltypeType>(TL.getType())) 432 if (QualType UT = DT->getUnderlyingType(); !UT->isDependentType()) 433 addTypeHint(TL.getSourceRange(), UT, ": "); 434 return true; 435 } 436 437 bool VisitCXXConstructExpr(CXXConstructExpr *E) { 438 // Weed out constructor calls that don't look like a function call with 439 // an argument list, by checking the validity of getParenOrBraceRange(). 440 // Also weed out std::initializer_list constructors as there are no names 441 // for the individual arguments. 442 if (!E->getParenOrBraceRange().isValid() || 443 E->isStdInitListInitialization()) { 444 return true; 445 } 446 447 Callee Callee; 448 Callee.Decl = E->getConstructor(); 449 if (!Callee.Decl) 450 return true; 451 processCall(Callee, E->getParenOrBraceRange().getEnd(), 452 {E->getArgs(), E->getNumArgs()}); 453 return true; 454 } 455 456 // Carefully recurse into PseudoObjectExprs, which typically incorporate 457 // a syntactic expression and several semantic expressions. 458 bool TraversePseudoObjectExpr(PseudoObjectExpr *E) { 459 Expr *SyntacticExpr = E->getSyntacticForm(); 460 if (isa<CallExpr>(SyntacticExpr)) 461 // Since the counterpart semantics usually get the identical source 462 // locations as the syntactic one, visiting those would end up presenting 463 // confusing hints e.g., __builtin_dump_struct. 464 // Thus, only traverse the syntactic forms if this is written as a 465 // CallExpr. This leaves the door open in case the arguments in the 466 // syntactic form could possibly get parameter names. 467 return RecursiveASTVisitor<InlayHintVisitor>::TraverseStmt(SyntacticExpr); 468 // We don't want the hints for some of the MS property extensions. 469 // e.g. 470 // struct S { 471 // __declspec(property(get=GetX, put=PutX)) int x[]; 472 // void PutX(int y); 473 // void Work(int y) { x = y; } // Bad: `x = y: y`. 474 // }; 475 if (isa<BinaryOperator>(SyntacticExpr)) 476 return true; 477 // FIXME: Handle other forms of a pseudo object expression. 478 return RecursiveASTVisitor<InlayHintVisitor>::TraversePseudoObjectExpr(E); 479 } 480 481 bool VisitCallExpr(CallExpr *E) { 482 if (!Cfg.InlayHints.Parameters) 483 return true; 484 485 bool IsFunctor = isFunctionObjectCallExpr(E); 486 // Do not show parameter hints for user-defined literals or 487 // operator calls except for operator(). (Among other reasons, the resulting 488 // hints can look awkward, e.g. the expression can itself be a function 489 // argument and then we'd get two hints side by side). 490 if ((isa<CXXOperatorCallExpr>(E) && !IsFunctor) || 491 isa<UserDefinedLiteral>(E)) 492 return true; 493 494 auto CalleeDecls = Resolver->resolveCalleeOfCallExpr(E); 495 if (CalleeDecls.size() != 1) 496 return true; 497 498 Callee Callee; 499 if (const auto *FD = dyn_cast<FunctionDecl>(CalleeDecls[0])) 500 Callee.Decl = FD; 501 else if (const auto *FTD = dyn_cast<FunctionTemplateDecl>(CalleeDecls[0])) 502 Callee.Decl = FTD->getTemplatedDecl(); 503 else if (FunctionProtoTypeLoc Loc = getPrototypeLoc(E->getCallee())) 504 Callee.Loc = Loc; 505 else 506 return true; 507 508 // N4868 [over.call.object]p3 says, 509 // The argument list submitted to overload resolution consists of the 510 // argument expressions present in the function call syntax preceded by the 511 // implied object argument (E). 512 // 513 // As well as the provision from P0847R7 Deducing This [expr.call]p7: 514 // ...If the function is an explicit object member function and there is an 515 // implied object argument ([over.call.func]), the list of provided 516 // arguments is preceded by the implied object argument for the purposes of 517 // this correspondence... 518 llvm::ArrayRef<const Expr *> Args = {E->getArgs(), E->getNumArgs()}; 519 // We don't have the implied object argument through a function pointer 520 // either. 521 if (const CXXMethodDecl *Method = 522 dyn_cast_or_null<CXXMethodDecl>(Callee.Decl)) 523 if (IsFunctor || Method->hasCXXExplicitFunctionObjectParameter()) 524 Args = Args.drop_front(1); 525 processCall(Callee, E->getRParenLoc(), Args); 526 return true; 527 } 528 529 bool VisitFunctionDecl(FunctionDecl *D) { 530 if (auto *FPT = 531 llvm::dyn_cast<FunctionProtoType>(D->getType().getTypePtr())) { 532 if (!FPT->hasTrailingReturn()) { 533 if (auto FTL = D->getFunctionTypeLoc()) 534 addReturnTypeHint(D, FTL.getRParenLoc()); 535 } 536 } 537 if (Cfg.InlayHints.BlockEnd && D->isThisDeclarationADefinition()) { 538 // We use `printName` here to properly print name of ctor/dtor/operator 539 // overload. 540 if (const Stmt *Body = D->getBody()) 541 addBlockEndHint(Body->getSourceRange(), "", printName(AST, *D), ""); 542 } 543 return true; 544 } 545 546 bool VisitForStmt(ForStmt *S) { 547 if (Cfg.InlayHints.BlockEnd) { 548 std::string Name; 549 // Common case: for (int I = 0; I < N; I++). Use "I" as the name. 550 if (auto *DS = llvm::dyn_cast_or_null<DeclStmt>(S->getInit()); 551 DS && DS->isSingleDecl()) 552 Name = getSimpleName(llvm::cast<NamedDecl>(*DS->getSingleDecl())); 553 else 554 Name = summarizeExpr(S->getCond()); 555 markBlockEnd(S->getBody(), "for", Name); 556 } 557 return true; 558 } 559 560 bool VisitCXXForRangeStmt(CXXForRangeStmt *S) { 561 if (Cfg.InlayHints.BlockEnd) 562 markBlockEnd(S->getBody(), "for", getSimpleName(*S->getLoopVariable())); 563 return true; 564 } 565 566 bool VisitWhileStmt(WhileStmt *S) { 567 if (Cfg.InlayHints.BlockEnd) 568 markBlockEnd(S->getBody(), "while", summarizeExpr(S->getCond())); 569 return true; 570 } 571 572 bool VisitSwitchStmt(SwitchStmt *S) { 573 if (Cfg.InlayHints.BlockEnd) 574 markBlockEnd(S->getBody(), "switch", summarizeExpr(S->getCond())); 575 return true; 576 } 577 578 // If/else chains are tricky. 579 // if (cond1) { 580 // } else if (cond2) { 581 // } // mark as "cond1" or "cond2"? 582 // For now, the answer is neither, just mark as "if". 583 // The ElseIf is a different IfStmt that doesn't know about the outer one. 584 llvm::DenseSet<const IfStmt *> ElseIfs; // not eligible for names 585 bool VisitIfStmt(IfStmt *S) { 586 if (Cfg.InlayHints.BlockEnd) { 587 if (const auto *ElseIf = llvm::dyn_cast_or_null<IfStmt>(S->getElse())) 588 ElseIfs.insert(ElseIf); 589 // Don't use markBlockEnd: the relevant range is [then.begin, else.end]. 590 if (const auto *EndCS = llvm::dyn_cast<CompoundStmt>( 591 S->getElse() ? S->getElse() : S->getThen())) { 592 addBlockEndHint( 593 {S->getThen()->getBeginLoc(), EndCS->getRBracLoc()}, "if", 594 ElseIfs.contains(S) ? "" : summarizeExpr(S->getCond()), ""); 595 } 596 } 597 return true; 598 } 599 600 void markBlockEnd(const Stmt *Body, llvm::StringRef Label, 601 llvm::StringRef Name = "") { 602 if (const auto *CS = llvm::dyn_cast_or_null<CompoundStmt>(Body)) 603 addBlockEndHint(CS->getSourceRange(), Label, Name, ""); 604 } 605 606 bool VisitTagDecl(TagDecl *D) { 607 if (Cfg.InlayHints.BlockEnd && D->isThisDeclarationADefinition()) { 608 std::string DeclPrefix = D->getKindName().str(); 609 if (const auto *ED = dyn_cast<EnumDecl>(D)) { 610 if (ED->isScoped()) 611 DeclPrefix += ED->isScopedUsingClassTag() ? " class" : " struct"; 612 }; 613 addBlockEndHint(D->getBraceRange(), DeclPrefix, getSimpleName(*D), ";"); 614 } 615 return true; 616 } 617 618 bool VisitNamespaceDecl(NamespaceDecl *D) { 619 if (Cfg.InlayHints.BlockEnd) { 620 // For namespace, the range actually starts at the namespace keyword. But 621 // it should be fine since it's usually very short. 622 addBlockEndHint(D->getSourceRange(), "namespace", getSimpleName(*D), ""); 623 } 624 return true; 625 } 626 627 bool VisitLambdaExpr(LambdaExpr *E) { 628 FunctionDecl *D = E->getCallOperator(); 629 if (!E->hasExplicitResultType()) { 630 SourceLocation TypeHintLoc; 631 if (!E->hasExplicitParameters()) 632 TypeHintLoc = E->getIntroducerRange().getEnd(); 633 else if (auto FTL = D->getFunctionTypeLoc()) 634 TypeHintLoc = FTL.getRParenLoc(); 635 if (TypeHintLoc.isValid()) 636 addReturnTypeHint(D, TypeHintLoc); 637 } 638 return true; 639 } 640 641 void addReturnTypeHint(FunctionDecl *D, SourceRange Range) { 642 auto *AT = D->getReturnType()->getContainedAutoType(); 643 if (!AT || AT->getDeducedType().isNull()) 644 return; 645 addTypeHint(Range, D->getReturnType(), /*Prefix=*/"-> "); 646 } 647 648 bool VisitVarDecl(VarDecl *D) { 649 // Do not show hints for the aggregate in a structured binding, 650 // but show hints for the individual bindings. 651 if (auto *DD = dyn_cast<DecompositionDecl>(D)) { 652 for (auto *Binding : DD->bindings()) { 653 // For structured bindings, print canonical types. This is important 654 // because for bindings that use the tuple_element protocol, the 655 // non-canonical types would be "tuple_element<I, A>::type". 656 if (auto Type = Binding->getType(); 657 !Type.isNull() && !Type->isDependentType()) 658 addTypeHint(Binding->getLocation(), Type.getCanonicalType(), 659 /*Prefix=*/": "); 660 } 661 return true; 662 } 663 664 if (auto *AT = D->getType()->getContainedAutoType()) { 665 if (AT->isDeduced() && !D->getType()->isDependentType()) { 666 // Our current approach is to place the hint on the variable 667 // and accordingly print the full type 668 // (e.g. for `const auto& x = 42`, print `const int&`). 669 // Alternatively, we could place the hint on the `auto` 670 // (and then just print the type deduced for the `auto`). 671 addTypeHint(D->getLocation(), D->getType(), /*Prefix=*/": "); 672 } 673 } 674 675 // Handle templates like `int foo(auto x)` with exactly one instantiation. 676 if (auto *PVD = llvm::dyn_cast<ParmVarDecl>(D)) { 677 if (D->getIdentifier() && PVD->getType()->isDependentType() && 678 !getContainedAutoParamType(D->getTypeSourceInfo()->getTypeLoc()) 679 .isNull()) { 680 if (auto *IPVD = getOnlyParamInstantiation(PVD)) 681 addTypeHint(D->getLocation(), IPVD->getType(), /*Prefix=*/": "); 682 } 683 } 684 685 return true; 686 } 687 688 ParmVarDecl *getOnlyParamInstantiation(ParmVarDecl *D) { 689 auto *TemplateFunction = llvm::dyn_cast<FunctionDecl>(D->getDeclContext()); 690 if (!TemplateFunction) 691 return nullptr; 692 auto *InstantiatedFunction = llvm::dyn_cast_or_null<FunctionDecl>( 693 getOnlyInstantiation(TemplateFunction)); 694 if (!InstantiatedFunction) 695 return nullptr; 696 697 unsigned ParamIdx = 0; 698 for (auto *Param : TemplateFunction->parameters()) { 699 // Can't reason about param indexes in the presence of preceding packs. 700 // And if this param is a pack, it may expand to multiple params. 701 if (Param->isParameterPack()) 702 return nullptr; 703 if (Param == D) 704 break; 705 ++ParamIdx; 706 } 707 assert(ParamIdx < TemplateFunction->getNumParams() && 708 "Couldn't find param in list?"); 709 assert(ParamIdx < InstantiatedFunction->getNumParams() && 710 "Instantiated function has fewer (non-pack) parameters?"); 711 return InstantiatedFunction->getParamDecl(ParamIdx); 712 } 713 714 bool VisitInitListExpr(InitListExpr *Syn) { 715 // We receive the syntactic form here (shouldVisitImplicitCode() is false). 716 // This is the one we will ultimately attach designators to. 717 // It may have subobject initializers inlined without braces. The *semantic* 718 // form of the init-list has nested init-lists for these. 719 // getUnwrittenDesignators will look at the semantic form to determine the 720 // labels. 721 assert(Syn->isSyntacticForm() && "RAV should not visit implicit code!"); 722 if (!Cfg.InlayHints.Designators) 723 return true; 724 if (Syn->isIdiomaticZeroInitializer(AST.getLangOpts())) 725 return true; 726 llvm::DenseMap<SourceLocation, std::string> Designators = 727 tidy::utils::getUnwrittenDesignators(Syn); 728 for (const Expr *Init : Syn->inits()) { 729 if (llvm::isa<DesignatedInitExpr>(Init)) 730 continue; 731 auto It = Designators.find(Init->getBeginLoc()); 732 if (It != Designators.end() && 733 !isPrecededByParamNameComment(Init, It->second)) 734 addDesignatorHint(Init->getSourceRange(), It->second); 735 } 736 return true; 737 } 738 739 // FIXME: Handle RecoveryExpr to try to hint some invalid calls. 740 741 private: 742 using NameVec = SmallVector<StringRef, 8>; 743 744 void processCall(Callee Callee, SourceLocation RParenOrBraceLoc, 745 llvm::ArrayRef<const Expr *> Args) { 746 assert(Callee.Decl || Callee.Loc); 747 748 if ((!Cfg.InlayHints.Parameters && !Cfg.InlayHints.DefaultArguments) || 749 Args.size() == 0) 750 return; 751 752 // The parameter name of a move or copy constructor is not very interesting. 753 if (Callee.Decl) 754 if (auto *Ctor = dyn_cast<CXXConstructorDecl>(Callee.Decl)) 755 if (Ctor->isCopyOrMoveConstructor()) 756 return; 757 758 SmallVector<std::string> FormattedDefaultArgs; 759 bool HasNonDefaultArgs = false; 760 761 ArrayRef<const ParmVarDecl *> Params, ForwardedParams; 762 // Resolve parameter packs to their forwarded parameter 763 SmallVector<const ParmVarDecl *> ForwardedParamsStorage; 764 if (Callee.Decl) { 765 Params = maybeDropCxxExplicitObjectParameters(Callee.Decl->parameters()); 766 ForwardedParamsStorage = resolveForwardingParameters(Callee.Decl); 767 ForwardedParams = 768 maybeDropCxxExplicitObjectParameters(ForwardedParamsStorage); 769 } else { 770 Params = maybeDropCxxExplicitObjectParameters(Callee.Loc.getParams()); 771 ForwardedParams = {Params.begin(), Params.end()}; 772 } 773 774 NameVec ParameterNames = chooseParameterNames(ForwardedParams); 775 776 // Exclude setters (i.e. functions with one argument whose name begins with 777 // "set"), and builtins like std::move/forward/... as their parameter name 778 // is also not likely to be interesting. 779 if (Callee.Decl && 780 (isSetter(Callee.Decl, ParameterNames) || isSimpleBuiltin(Callee.Decl))) 781 return; 782 783 for (size_t I = 0; I < ParameterNames.size() && I < Args.size(); ++I) { 784 // Pack expansion expressions cause the 1:1 mapping between arguments and 785 // parameters to break down, so we don't add further inlay hints if we 786 // encounter one. 787 if (isa<PackExpansionExpr>(Args[I])) { 788 break; 789 } 790 791 StringRef Name = ParameterNames[I]; 792 const bool NameHint = 793 shouldHintName(Args[I], Name) && Cfg.InlayHints.Parameters; 794 const bool ReferenceHint = 795 shouldHintReference(Params[I], ForwardedParams[I]) && 796 Cfg.InlayHints.Parameters; 797 798 const bool IsDefault = isa<CXXDefaultArgExpr>(Args[I]); 799 HasNonDefaultArgs |= !IsDefault; 800 if (IsDefault) { 801 if (Cfg.InlayHints.DefaultArguments) { 802 const auto SourceText = Lexer::getSourceText( 803 CharSourceRange::getTokenRange(Params[I]->getDefaultArgRange()), 804 AST.getSourceManager(), AST.getLangOpts()); 805 const auto Abbrev = 806 (SourceText.size() > Cfg.InlayHints.TypeNameLimit || 807 SourceText.contains("\n")) 808 ? "..." 809 : SourceText; 810 if (NameHint) 811 FormattedDefaultArgs.emplace_back( 812 llvm::formatv("{0}: {1}", Name, Abbrev)); 813 else 814 FormattedDefaultArgs.emplace_back(llvm::formatv("{0}", Abbrev)); 815 } 816 } else if (NameHint || ReferenceHint) { 817 addInlayHint(Args[I]->getSourceRange(), HintSide::Left, 818 InlayHintKind::Parameter, ReferenceHint ? "&" : "", 819 NameHint ? Name : "", ": "); 820 } 821 } 822 823 if (!FormattedDefaultArgs.empty()) { 824 std::string Hint = 825 joinAndTruncate(FormattedDefaultArgs, Cfg.InlayHints.TypeNameLimit); 826 addInlayHint(SourceRange{RParenOrBraceLoc}, HintSide::Left, 827 InlayHintKind::DefaultArgument, 828 HasNonDefaultArgs ? ", " : "", Hint, ""); 829 } 830 } 831 832 static bool isSetter(const FunctionDecl *Callee, const NameVec &ParamNames) { 833 if (ParamNames.size() != 1) 834 return false; 835 836 StringRef Name = getSimpleName(*Callee); 837 if (!Name.starts_with_insensitive("set")) 838 return false; 839 840 // In addition to checking that the function has one parameter and its 841 // name starts with "set", also check that the part after "set" matches 842 // the name of the parameter (ignoring case). The idea here is that if 843 // the parameter name differs, it may contain extra information that 844 // may be useful to show in a hint, as in: 845 // void setTimeout(int timeoutMillis); 846 // This currently doesn't handle cases where params use snake_case 847 // and functions don't, e.g. 848 // void setExceptionHandler(EHFunc exception_handler); 849 // We could improve this by replacing `equals_insensitive` with some 850 // `sloppy_equals` which ignores case and also skips underscores. 851 StringRef WhatItIsSetting = Name.substr(3).ltrim("_"); 852 return WhatItIsSetting.equals_insensitive(ParamNames[0]); 853 } 854 855 // Checks if the callee is one of the builtins 856 // addressof, as_const, forward, move(_if_noexcept) 857 static bool isSimpleBuiltin(const FunctionDecl *Callee) { 858 switch (Callee->getBuiltinID()) { 859 case Builtin::BIaddressof: 860 case Builtin::BIas_const: 861 case Builtin::BIforward: 862 case Builtin::BImove: 863 case Builtin::BImove_if_noexcept: 864 return true; 865 default: 866 return false; 867 } 868 } 869 870 bool shouldHintName(const Expr *Arg, StringRef ParamName) { 871 if (ParamName.empty()) 872 return false; 873 874 // If the argument expression is a single name and it matches the 875 // parameter name exactly, omit the name hint. 876 if (ParamName == getSpelledIdentifier(Arg)) 877 return false; 878 879 // Exclude argument expressions preceded by a /*paramName*/. 880 if (isPrecededByParamNameComment(Arg, ParamName)) 881 return false; 882 883 return true; 884 } 885 886 bool shouldHintReference(const ParmVarDecl *Param, 887 const ParmVarDecl *ForwardedParam) { 888 // We add a & hint only when the argument is passed as mutable reference. 889 // For parameters that are not part of an expanded pack, this is 890 // straightforward. For expanded pack parameters, it's likely that they will 891 // be forwarded to another function. In this situation, we only want to add 892 // the reference hint if the argument is actually being used via mutable 893 // reference. This means we need to check 894 // 1. whether the value category of the argument is preserved, i.e. each 895 // pack expansion uses std::forward correctly. 896 // 2. whether the argument is ever copied/cast instead of passed 897 // by-reference 898 // Instead of checking this explicitly, we use the following proxy: 899 // 1. the value category can only change from rvalue to lvalue during 900 // forwarding, so checking whether both the parameter of the forwarding 901 // function and the forwarded function are lvalue references detects such 902 // a conversion. 903 // 2. if the argument is copied/cast somewhere in the chain of forwarding 904 // calls, it can only be passed on to an rvalue reference or const lvalue 905 // reference parameter. Thus if the forwarded parameter is a mutable 906 // lvalue reference, it cannot have been copied/cast to on the way. 907 // Additionally, we should not add a reference hint if the forwarded 908 // parameter was only partially resolved, i.e. points to an expanded pack 909 // parameter, since we do not know how it will be used eventually. 910 auto Type = Param->getType(); 911 auto ForwardedType = ForwardedParam->getType(); 912 return Type->isLValueReferenceType() && 913 ForwardedType->isLValueReferenceType() && 914 !ForwardedType.getNonReferenceType().isConstQualified() && 915 !isExpandedFromParameterPack(ForwardedParam); 916 } 917 918 // Checks if "E" is spelled in the main file and preceded by a C-style comment 919 // whose contents match ParamName (allowing for whitespace and an optional "=" 920 // at the end. 921 bool isPrecededByParamNameComment(const Expr *E, StringRef ParamName) { 922 auto &SM = AST.getSourceManager(); 923 auto FileLoc = SM.getFileLoc(E->getBeginLoc()); 924 auto Decomposed = SM.getDecomposedLoc(FileLoc); 925 if (Decomposed.first != MainFileID) 926 return false; 927 928 StringRef SourcePrefix = MainFileBuf.substr(0, Decomposed.second); 929 // Allow whitespace between comment and expression. 930 SourcePrefix = SourcePrefix.rtrim(); 931 // Check for comment ending. 932 if (!SourcePrefix.consume_back("*/")) 933 return false; 934 // Ignore some punctuation and whitespace around comment. 935 // In particular this allows designators to match nicely. 936 llvm::StringLiteral IgnoreChars = " =."; 937 SourcePrefix = SourcePrefix.rtrim(IgnoreChars); 938 ParamName = ParamName.trim(IgnoreChars); 939 // Other than that, the comment must contain exactly ParamName. 940 if (!SourcePrefix.consume_back(ParamName)) 941 return false; 942 SourcePrefix = SourcePrefix.rtrim(IgnoreChars); 943 return SourcePrefix.ends_with("/*"); 944 } 945 946 // If "E" spells a single unqualified identifier, return that name. 947 // Otherwise, return an empty string. 948 static StringRef getSpelledIdentifier(const Expr *E) { 949 E = E->IgnoreUnlessSpelledInSource(); 950 951 if (auto *DRE = dyn_cast<DeclRefExpr>(E)) 952 if (!DRE->getQualifier()) 953 return getSimpleName(*DRE->getDecl()); 954 955 if (auto *ME = dyn_cast<MemberExpr>(E)) 956 if (!ME->getQualifier() && ME->isImplicitAccess()) 957 return getSimpleName(*ME->getMemberDecl()); 958 959 return {}; 960 } 961 962 NameVec chooseParameterNames(ArrayRef<const ParmVarDecl *> Parameters) { 963 NameVec ParameterNames; 964 for (const auto *P : Parameters) { 965 if (isExpandedFromParameterPack(P)) { 966 // If we haven't resolved a pack paramater (e.g. foo(Args... args)) to a 967 // non-pack parameter, then hinting as foo(args: 1, args: 2, args: 3) is 968 // unlikely to be useful. 969 ParameterNames.emplace_back(); 970 } else { 971 auto SimpleName = getSimpleName(*P); 972 // If the parameter is unnamed in the declaration: 973 // attempt to get its name from the definition 974 if (SimpleName.empty()) { 975 if (const auto *PD = getParamDefinition(P)) { 976 SimpleName = getSimpleName(*PD); 977 } 978 } 979 ParameterNames.emplace_back(SimpleName); 980 } 981 } 982 983 // Standard library functions often have parameter names that start 984 // with underscores, which makes the hints noisy, so strip them out. 985 for (auto &Name : ParameterNames) 986 stripLeadingUnderscores(Name); 987 988 return ParameterNames; 989 } 990 991 // for a ParmVarDecl from a function declaration, returns the corresponding 992 // ParmVarDecl from the definition if possible, nullptr otherwise. 993 static const ParmVarDecl *getParamDefinition(const ParmVarDecl *P) { 994 if (auto *Callee = dyn_cast<FunctionDecl>(P->getDeclContext())) { 995 if (auto *Def = Callee->getDefinition()) { 996 auto I = std::distance(Callee->param_begin(), 997 llvm::find(Callee->parameters(), P)); 998 if (I < (int)Callee->getNumParams()) { 999 return Def->getParamDecl(I); 1000 } 1001 } 1002 } 1003 return nullptr; 1004 } 1005 1006 // We pass HintSide rather than SourceLocation because we want to ensure 1007 // it is in the same file as the common file range. 1008 void addInlayHint(SourceRange R, HintSide Side, InlayHintKind Kind, 1009 llvm::StringRef Prefix, llvm::StringRef Label, 1010 llvm::StringRef Suffix) { 1011 auto LSPRange = getHintRange(R); 1012 if (!LSPRange) 1013 return; 1014 1015 addInlayHint(*LSPRange, Side, Kind, Prefix, Label, Suffix); 1016 } 1017 1018 void addInlayHint(Range LSPRange, HintSide Side, InlayHintKind Kind, 1019 llvm::StringRef Prefix, llvm::StringRef Label, 1020 llvm::StringRef Suffix) { 1021 // We shouldn't get as far as adding a hint if the category is disabled. 1022 // We'd like to disable as much of the analysis as possible above instead. 1023 // Assert in debug mode but add a dynamic check in production. 1024 assert(Cfg.InlayHints.Enabled && "Shouldn't get here if disabled!"); 1025 switch (Kind) { 1026 #define CHECK_KIND(Enumerator, ConfigProperty) \ 1027 case InlayHintKind::Enumerator: \ 1028 assert(Cfg.InlayHints.ConfigProperty && \ 1029 "Shouldn't get here if kind is disabled!"); \ 1030 if (!Cfg.InlayHints.ConfigProperty) \ 1031 return; \ 1032 break 1033 CHECK_KIND(Parameter, Parameters); 1034 CHECK_KIND(Type, DeducedTypes); 1035 CHECK_KIND(Designator, Designators); 1036 CHECK_KIND(BlockEnd, BlockEnd); 1037 CHECK_KIND(DefaultArgument, DefaultArguments); 1038 #undef CHECK_KIND 1039 } 1040 1041 Position LSPPos = Side == HintSide::Left ? LSPRange.start : LSPRange.end; 1042 if (RestrictRange && 1043 (LSPPos < RestrictRange->start || !(LSPPos < RestrictRange->end))) 1044 return; 1045 bool PadLeft = Prefix.consume_front(" "); 1046 bool PadRight = Suffix.consume_back(" "); 1047 Results.push_back(InlayHint{LSPPos, 1048 /*label=*/{(Prefix + Label + Suffix).str()}, 1049 Kind, PadLeft, PadRight, LSPRange}); 1050 } 1051 1052 // Get the range of the main file that *exactly* corresponds to R. 1053 std::optional<Range> getHintRange(SourceRange R) { 1054 const auto &SM = AST.getSourceManager(); 1055 auto Spelled = Tokens.spelledForExpanded(Tokens.expandedTokens(R)); 1056 // TokenBuffer will return null if e.g. R corresponds to only part of a 1057 // macro expansion. 1058 if (!Spelled || Spelled->empty()) 1059 return std::nullopt; 1060 // Hint must be within the main file, not e.g. a non-preamble include. 1061 if (SM.getFileID(Spelled->front().location()) != SM.getMainFileID() || 1062 SM.getFileID(Spelled->back().location()) != SM.getMainFileID()) 1063 return std::nullopt; 1064 return Range{sourceLocToPosition(SM, Spelled->front().location()), 1065 sourceLocToPosition(SM, Spelled->back().endLocation())}; 1066 } 1067 1068 void addTypeHint(SourceRange R, QualType T, llvm::StringRef Prefix) { 1069 if (!Cfg.InlayHints.DeducedTypes || T.isNull()) 1070 return; 1071 1072 // The sugared type is more useful in some cases, and the canonical 1073 // type in other cases. 1074 auto Desugared = maybeDesugar(AST, T); 1075 std::string TypeName = Desugared.getAsString(TypeHintPolicy); 1076 if (T != Desugared && !shouldPrintTypeHint(TypeName)) { 1077 // If the desugared type is too long to display, fallback to the sugared 1078 // type. 1079 TypeName = T.getAsString(TypeHintPolicy); 1080 } 1081 if (shouldPrintTypeHint(TypeName)) 1082 addInlayHint(R, HintSide::Right, InlayHintKind::Type, Prefix, TypeName, 1083 /*Suffix=*/""); 1084 } 1085 1086 void addDesignatorHint(SourceRange R, llvm::StringRef Text) { 1087 addInlayHint(R, HintSide::Left, InlayHintKind::Designator, 1088 /*Prefix=*/"", Text, /*Suffix=*/"="); 1089 } 1090 1091 bool shouldPrintTypeHint(llvm::StringRef TypeName) const noexcept { 1092 return Cfg.InlayHints.TypeNameLimit == 0 || 1093 TypeName.size() < Cfg.InlayHints.TypeNameLimit; 1094 } 1095 1096 void addBlockEndHint(SourceRange BraceRange, StringRef DeclPrefix, 1097 StringRef Name, StringRef OptionalPunctuation) { 1098 auto HintRange = computeBlockEndHintRange(BraceRange, OptionalPunctuation); 1099 if (!HintRange) 1100 return; 1101 1102 std::string Label = DeclPrefix.str(); 1103 if (!Label.empty() && !Name.empty()) 1104 Label += ' '; 1105 Label += Name; 1106 1107 constexpr unsigned HintMaxLengthLimit = 60; 1108 if (Label.length() > HintMaxLengthLimit) 1109 return; 1110 1111 addInlayHint(*HintRange, HintSide::Right, InlayHintKind::BlockEnd, " // ", 1112 Label, ""); 1113 } 1114 1115 // Compute the LSP range to attach the block end hint to, if any allowed. 1116 // 1. "}" is the last non-whitespace character on the line. The range of "}" 1117 // is returned. 1118 // 2. After "}", if the trimmed trailing text is exactly 1119 // `OptionalPunctuation`, say ";". The range of "} ... ;" is returned. 1120 // Otherwise, the hint shouldn't be shown. 1121 std::optional<Range> computeBlockEndHintRange(SourceRange BraceRange, 1122 StringRef OptionalPunctuation) { 1123 constexpr unsigned HintMinLineLimit = 2; 1124 1125 auto &SM = AST.getSourceManager(); 1126 auto [BlockBeginFileId, BlockBeginOffset] = 1127 SM.getDecomposedLoc(SM.getFileLoc(BraceRange.getBegin())); 1128 auto RBraceLoc = SM.getFileLoc(BraceRange.getEnd()); 1129 auto [RBraceFileId, RBraceOffset] = SM.getDecomposedLoc(RBraceLoc); 1130 1131 // Because we need to check the block satisfies the minimum line limit, we 1132 // require both source location to be in the main file. This prevents hint 1133 // to be shown in weird cases like '{' is actually in a "#include", but it's 1134 // rare anyway. 1135 if (BlockBeginFileId != MainFileID || RBraceFileId != MainFileID) 1136 return std::nullopt; 1137 1138 StringRef RestOfLine = MainFileBuf.substr(RBraceOffset).split('\n').first; 1139 if (!RestOfLine.starts_with("}")) 1140 return std::nullopt; 1141 1142 StringRef TrimmedTrailingText = RestOfLine.drop_front().trim(); 1143 if (!TrimmedTrailingText.empty() && 1144 TrimmedTrailingText != OptionalPunctuation) 1145 return std::nullopt; 1146 1147 auto BlockBeginLine = SM.getLineNumber(BlockBeginFileId, BlockBeginOffset); 1148 auto RBraceLine = SM.getLineNumber(RBraceFileId, RBraceOffset); 1149 1150 // Don't show hint on trivial blocks like `class X {};` 1151 if (BlockBeginLine + HintMinLineLimit - 1 > RBraceLine) 1152 return std::nullopt; 1153 1154 // This is what we attach the hint to, usually "}" or "};". 1155 StringRef HintRangeText = RestOfLine.take_front( 1156 TrimmedTrailingText.empty() 1157 ? 1 1158 : TrimmedTrailingText.bytes_end() - RestOfLine.bytes_begin()); 1159 1160 Position HintStart = sourceLocToPosition(SM, RBraceLoc); 1161 Position HintEnd = sourceLocToPosition( 1162 SM, RBraceLoc.getLocWithOffset(HintRangeText.size())); 1163 return Range{HintStart, HintEnd}; 1164 } 1165 1166 static bool isFunctionObjectCallExpr(CallExpr *E) noexcept { 1167 if (auto *CallExpr = dyn_cast<CXXOperatorCallExpr>(E)) 1168 return CallExpr->getOperator() == OverloadedOperatorKind::OO_Call; 1169 return false; 1170 } 1171 1172 std::vector<InlayHint> &Results; 1173 ASTContext &AST; 1174 const syntax::TokenBuffer &Tokens; 1175 const Config &Cfg; 1176 std::optional<Range> RestrictRange; 1177 FileID MainFileID; 1178 StringRef MainFileBuf; 1179 const HeuristicResolver *Resolver; 1180 PrintingPolicy TypeHintPolicy; 1181 }; 1182 1183 } // namespace 1184 1185 std::vector<InlayHint> inlayHints(ParsedAST &AST, 1186 std::optional<Range> RestrictRange) { 1187 std::vector<InlayHint> Results; 1188 const auto &Cfg = Config::current(); 1189 if (!Cfg.InlayHints.Enabled) 1190 return Results; 1191 InlayHintVisitor Visitor(Results, AST, Cfg, std::move(RestrictRange)); 1192 Visitor.TraverseAST(AST.getASTContext()); 1193 1194 // De-duplicate hints. Duplicates can sometimes occur due to e.g. explicit 1195 // template instantiations. 1196 llvm::sort(Results); 1197 Results.erase(std::unique(Results.begin(), Results.end()), Results.end()); 1198 1199 return Results; 1200 } 1201 1202 } // namespace clangd 1203 } // namespace clang 1204