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

source code of clang-tools-extra/clangd/InlayHints.cpp