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