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"
54void stripLeadingUnderscores(StringRef &Name) { Name = Name.ltrim(
'_'); }
58const NamedDecl *getDeclForType(
const Type *T) {
59 switch (
T->getTypeClass()) {
62 case Type::InjectedClassName:
63 return cast<TagType>(T)->getDecl();
64 case Type::TemplateSpecialization:
65 return cast<TemplateSpecializationType>(T)
67 .getAsTemplateDecl(
true);
69 return cast<TypedefType>(T)->getDecl();
70 case Type::UnresolvedUsing:
71 return cast<UnresolvedUsingType>(T)->getDecl();
73 return cast<UsingType>(T)->getDecl();
77 llvm_unreachable(
"Unknown TypeClass enum");
81llvm::StringRef getSimpleName(
const DeclarationName &DN) {
82 if (IdentifierInfo *Ident = DN.getAsIdentifierInfo())
83 return Ident->getName();
86llvm::StringRef getSimpleName(
const NamedDecl &D) {
87 return getSimpleName(
D.getDeclName());
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);
95 if (
const auto *D = getDeclForType(
T.getTypePtr()))
96 return getSimpleName(
D->getDeclName());
103std::string summarizeExpr(
const Expr *E) {
104 struct Namer : ConstStmtVisitor<Namer, std::string> {
105 std::string Visit(
const Expr *E) {
108 return ConstStmtVisitor::Visit(E->IgnoreImplicit());
112 std::string VisitMemberExpr(
const MemberExpr *E) {
113 return getSimpleName(*E->getMemberDecl()).str();
115 std::string VisitDeclRefExpr(
const DeclRefExpr *E) {
116 return getSimpleName(*E->getFoundDecl()).str();
118 std::string VisitCallExpr(
const CallExpr *E) {
119 std::string Result = Visit(E->getCallee());
120 Result += E->getNumArgs() == 0 ?
"()" :
"(...)";
124 VisitCXXDependentScopeMemberExpr(
const CXXDependentScopeMemberExpr *E) {
125 return getSimpleName(E->getMember()).str();
128 VisitDependentScopeDeclRefExpr(
const DependentScopeDeclRefExpr *E) {
129 return getSimpleName(E->getDeclName()).str();
131 std::string VisitCXXFunctionalCastExpr(
const CXXFunctionalCastExpr *E) {
132 return getSimpleName(E->getType()).str();
134 std::string VisitCXXTemporaryObjectExpr(
const CXXTemporaryObjectExpr *E) {
135 return getSimpleName(E->getType()).str();
139 std::string VisitCXXMemberCallExpr(
const CXXMemberCallExpr *E) {
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);
149 std::string VisitCXXConstructExpr(
const CXXConstructExpr *E) {
150 if (E->getNumArgs() == 1)
151 return Visit(E->getArg(0));
156 std::string VisitCXXNullPtrLiteralExpr(
const CXXNullPtrLiteralExpr *E) {
159 std::string VisitCXXBoolLiteralExpr(
const CXXBoolLiteralExpr *E) {
160 return E->getValue() ?
"true" :
"false";
162 std::string VisitIntegerLiteral(
const IntegerLiteral *E) {
163 return llvm::to_string(E->getValue());
165 std::string VisitFloatingLiteral(
const FloatingLiteral *E) {
167 llvm::raw_string_ostream OS(Result);
168 E->getValue().print(OS);
170 Result.resize(llvm::StringRef(Result).rtrim().size());
173 std::string VisitStringLiteral(
const StringLiteral *E) {
174 std::string Result =
"\"";
175 if (E->containsNonAscii()) {
178 llvm::raw_string_ostream OS(Result);
179 if (E->getLength() > 10) {
180 llvm::printEscapedString(E->getString().take_front(7), OS);
183 llvm::printEscapedString(E->getString(), OS);
186 Result.push_back(
'"');
191 std::string printUnary(llvm::StringRef Spelling,
const Expr *Operand,
193 std::string Sub = Visit(Operand);
197 return (Spelling + Sub).str();
201 bool InsideBinary =
false;
202 std::string printBinary(llvm::StringRef Spelling,
const Expr *LHSOp,
206 llvm::SaveAndRestore InBinary(InsideBinary,
true);
208 std::string LHS = Visit(LHSOp);
209 std::string RHS = Visit(RHSOp);
210 if (LHS.empty() && RHS.empty())
224 std::string VisitUnaryOperator(
const UnaryOperator *E) {
225 return printUnary(E->getOpcodeStr(E->getOpcode()), E->getSubExpr(),
228 std::string VisitBinaryOperator(
const BinaryOperator *E) {
229 return printBinary(E->getOpcodeStr(E->getOpcode()), E->getLHS(),
232 std::string VisitCXXOperatorCallExpr(
const CXXOperatorCallExpr *E) {
233 const char *Spelling = getOperatorSpelling(E->getOperator());
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()) {
251 return printUnary(Spelling, E->getArg(0),
true);
259 return Namer{}.Visit(E);
264bool isSugaredTemplateParameter(QualType QT) {
265 static auto PeelWrapper = [](QualType QT) {
268 QualType Peeled = QT->getPointeeType();
269 return Peeled.isNull() ? QT : Peeled;
297 if (QT->getAs<SubstTemplateTypeParmType>())
299 QualType Desugared = QT->getLocallyUnqualifiedSingleStepDesugaredType();
302 else if (
auto Peeled = PeelWrapper(Desugared); Peeled != QT)
312std::optional<QualType> desugar(ASTContext &
AST, QualType QT) {
313 bool ShouldAKA =
false;
314 auto Desugared = clang::desugarForDiagnostic(
AST, QT, ShouldAKA);
327QualType maybeDesugar(ASTContext &
AST, QualType QT) {
331 if (isSugaredTemplateParameter(QT))
332 return desugar(
AST, QT).value_or(QT);
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();
345ArrayRef<const ParmVarDecl *>
346maybeDropCxxExplicitObjectParameters(ArrayRef<const ParmVarDecl *> Params) {
347 if (!Params.empty() && Params.front()->isExplicitObjectParameter())
348 Params = Params.drop_front(1);
353std::string joinAndTruncate(
const R &
Range,
size_t MaxLength) {
355 llvm::raw_string_ostream OS(Out);
356 llvm::ListSeparator Sep(
", ");
357 for (
auto &&Element :
Range) {
359 if (Out.size() + Element.size() >= MaxLength) {
372 const FunctionDecl *Decl =
nullptr;
373 FunctionProtoTypeLoc Loc;
376class InlayHintVisitor :
public RecursiveASTVisitor<InlayHintVisitor> {
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;
392 TypeHintPolicy.SuppressScope =
true;
393 TypeHintPolicy.AnonymousTagNameStyle = llvm::to_underlying(
394 PrintingPolicy::AnonymousTagMode::Plain);
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,
": ");
408 bool VisitCXXConstructExpr(CXXConstructExpr *E) {
413 if (!E->getParenOrBraceRange().isValid() ||
414 E->isStdInitListInitialization()) {
419 Callee.Decl = E->getConstructor();
422 processCall(Callee, E->getParenOrBraceRange().getEnd(),
423 {E->getArgs(), E->getNumArgs()});
429 bool TraversePseudoObjectExpr(PseudoObjectExpr *E) {
430 Expr *SyntacticExpr = E->getSyntacticForm();
431 if (isa<CallExpr>(SyntacticExpr))
438 return RecursiveASTVisitor<InlayHintVisitor>::TraverseStmt(SyntacticExpr);
446 if (isa<BinaryOperator>(SyntacticExpr))
449 return RecursiveASTVisitor<InlayHintVisitor>::TraversePseudoObjectExpr(E);
452 bool VisitCallExpr(CallExpr *E) {
453 if (!Cfg.InlayHints.Parameters)
456 bool IsFunctor = isFunctionObjectCallExpr(E);
461 if ((isa<CXXOperatorCallExpr>(E) && !IsFunctor) ||
462 isa<UserDefinedLiteral>(E))
465 auto CalleeDecls = Resolver->resolveCalleeOfCallExpr(E);
466 if (CalleeDecls.size() != 1)
470 if (
const auto *FD = dyn_cast<FunctionDecl>(CalleeDecls[0]))
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()))
490 llvm::ArrayRef<const Expr *> Args = {E->getArgs(), E->getNumArgs()};
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);
502 bool VisitFunctionDecl(FunctionDecl *D) {
504 llvm::dyn_cast<FunctionProtoType>(
D->getType().getTypePtr())) {
505 if (!FPT->hasTrailingReturn()) {
506 if (
auto FTL =
D->getFunctionTypeLoc())
507 addReturnTypeHint(D, FTL.getRParenLoc());
510 if (Cfg.InlayHints.BlockEnd &&
D->isThisDeclarationADefinition()) {
513 if (
const Stmt *Body =
D->getBody())
514 addBlockEndHint(Body->getSourceRange(),
"",
printName(AST, *D),
"");
519 bool VisitForStmt(ForStmt *S) {
520 if (Cfg.InlayHints.BlockEnd) {
523 if (
auto *DS = llvm::dyn_cast_or_null<DeclStmt>(S->getInit());
524 DS && DS->isSingleDecl())
525 Name = getSimpleName(llvm::cast<NamedDecl>(*DS->getSingleDecl()));
527 Name = summarizeExpr(S->getCond());
528 markBlockEnd(S->getBody(),
"for", Name);
533 bool VisitCXXForRangeStmt(CXXForRangeStmt *S) {
534 if (Cfg.InlayHints.BlockEnd)
535 markBlockEnd(S->getBody(),
"for", getSimpleName(*S->getLoopVariable()));
539 bool VisitWhileStmt(WhileStmt *S) {
540 if (Cfg.InlayHints.BlockEnd)
541 markBlockEnd(S->getBody(),
"while", summarizeExpr(S->getCond()));
545 bool VisitSwitchStmt(SwitchStmt *S) {
546 if (Cfg.InlayHints.BlockEnd)
547 markBlockEnd(S->getBody(),
"switch", summarizeExpr(S->getCond()));
557 llvm::DenseSet<const IfStmt *> ElseIfs;
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);
563 if (
const auto *EndCS = llvm::dyn_cast<CompoundStmt>(
564 S->getElse() ? S->getElse() : S->getThen())) {
566 {S->getThen()->getBeginLoc(), EndCS->getRBracLoc()},
"if",
567 ElseIfs.contains(S) ?
"" : summarizeExpr(S->getCond()),
"");
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,
"");
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)) {
584 DeclPrefix += ED->isScopedUsingClassTag() ?
" class" :
" struct";
586 addBlockEndHint(
D->getBraceRange(), DeclPrefix, getSimpleName(*D),
";");
591 bool VisitNamespaceDecl(NamespaceDecl *D) {
592 if (Cfg.InlayHints.BlockEnd) {
595 addBlockEndHint(
D->getSourceRange(),
"namespace", getSimpleName(*D),
"");
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);
614 void addReturnTypeHint(FunctionDecl *D, SourceRange Range) {
615 auto *AT =
D->getReturnType()->getContainedAutoType();
616 if (!AT || AT->getDeducedType().isNull())
618 addTypeHint(Range,
D->getReturnType(),
"-> ");
621 bool VisitVarDecl(VarDecl *D) {
624 if (
auto *DD = dyn_cast<DecompositionDecl>(D)) {
625 for (
auto *Binding : DD->bindings()) {
629 if (
auto Type = Binding->getType();
630 !
Type.isNull() && !
Type->isDependentType())
631 addTypeHint(Binding->getLocation(),
Type.getCanonicalType(),
637 if (
auto *AT =
D->getType()->getContainedAutoType()) {
638 if (AT->isDeduced()) {
644 if (
D->getType()->isDependentType()) {
646 QualType Resolved = Resolver->resolveExprToType(
D->getInit());
647 if (Resolved != AST.DependentTy) {
660 addTypeHint(
D->getLocation(), T,
": ");
666 if (
auto *PVD = llvm::dyn_cast<ParmVarDecl>(D)) {
667 if (
D->getIdentifier() && PVD->getType()->isDependentType() &&
670 if (
auto *IPVD = getOnlyParamInstantiation(PVD))
671 addTypeHint(
D->getLocation(), IPVD->getType(),
": ");
678 ParmVarDecl *getOnlyParamInstantiation(ParmVarDecl *D) {
679 auto *TemplateFunction = llvm::dyn_cast<FunctionDecl>(
D->getDeclContext());
680 if (!TemplateFunction)
682 auto *InstantiatedFunction = llvm::dyn_cast_or_null<FunctionDecl>(
684 if (!InstantiatedFunction)
687 unsigned ParamIdx = 0;
688 for (
auto *Param : TemplateFunction->parameters()) {
691 if (Param->isParameterPack())
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);
704 bool VisitCXXParenListInitExpr(CXXParenListInitExpr *E) {
705 if (!Cfg.InlayHints.Designators)
708 if (
const auto *CXXRecord = E->getType()->getAsCXXRecordDecl()) {
709 const auto &InitExprs = E->getUserSpecifiedInitExprs();
711 if (InitExprs.size() <= CXXRecord->getNumBases())
716 const auto &MemberInitExprs =
717 InitExprs.drop_front(CXXRecord->getNumBases());
720 for (
const auto &[InitExpr,
Field] :
721 llvm::zip(MemberInitExprs, CXXRecord->fields())) {
722 addDesignatorHint(InitExpr->getSourceRange(),
723 "." +
Field->getName().str());
730 bool VisitInitListExpr(InitListExpr *Syn) {
737 assert(Syn->isSyntacticForm() &&
"RAV should not visit implicit code!");
738 if (!Cfg.InlayHints.Designators)
740 if (Syn->isIdiomaticZeroInitializer(AST.getLangOpts()))
742 llvm::DenseMap<SourceLocation, std::string> Designators =
744 for (
const Expr *Init : Syn->inits()) {
745 if (llvm::isa<DesignatedInitExpr>(Init))
747 auto It = Designators.find(Init->getBeginLoc());
748 if (It != Designators.end() &&
749 !isPrecededByParamNameComment(Init, It->second))
750 addDesignatorHint(Init->getSourceRange(), It->second);
758 using NameVec = SmallVector<StringRef, 8>;
760 void processCall(Callee Callee, SourceLocation RParenOrBraceLoc,
761 llvm::ArrayRef<const Expr *> Args) {
762 assert(Callee.Decl || Callee.Loc);
764 if ((!Cfg.InlayHints.Parameters && !Cfg.InlayHints.DefaultArguments) ||
770 if (
auto *Ctor = dyn_cast<CXXConstructorDecl>(Callee.Decl))
771 if (Ctor->isCopyOrMoveConstructor())
774 SmallVector<std::string> FormattedDefaultArgs;
775 bool HasNonDefaultArgs =
false;
777 ArrayRef<const ParmVarDecl *> Params, ForwardedParams;
779 SmallVector<const ParmVarDecl *> ForwardedParamsStorage;
781 Params = maybeDropCxxExplicitObjectParameters(Callee.Decl->parameters());
784 maybeDropCxxExplicitObjectParameters(ForwardedParamsStorage);
786 Params = maybeDropCxxExplicitObjectParameters(Callee.Loc.getParams());
787 ForwardedParams = {Params.begin(), Params.end()};
790 NameVec ParameterNames = chooseParameterNames(ForwardedParams);
796 (isSetter(Callee.Decl, ParameterNames) || isSimpleBuiltin(Callee.Decl)))
799 for (
size_t I = 0; I < ParameterNames.size() && I < Args.size(); ++I) {
803 if (isa<PackExpansionExpr>(Args[I])) {
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;
814 const bool IsDefault = isa<CXXDefaultArgExpr>(Args[I]);
815 HasNonDefaultArgs |= !IsDefault;
817 if (Cfg.InlayHints.DefaultArguments) {
818 const auto SourceText = Lexer::getSourceText(
819 CharSourceRange::getTokenRange(Params[I]->getDefaultArgRange()),
820 AST.getSourceManager(), AST.getLangOpts());
822 (SourceText.size() > Cfg.InlayHints.TypeNameLimit ||
823 SourceText.contains(
"\n"))
827 FormattedDefaultArgs.emplace_back(
828 llvm::formatv(
"{0}: {1}", Name, Abbrev));
830 FormattedDefaultArgs.emplace_back(llvm::formatv(
"{0}", Abbrev));
832 }
else if (NameHint || ReferenceHint) {
833 addInlayHint(Args[I]->getSourceRange(), HintSide::Left,
835 NameHint ? Name :
"",
": ");
839 if (!FormattedDefaultArgs.empty()) {
841 joinAndTruncate(FormattedDefaultArgs, Cfg.InlayHints.TypeNameLimit);
842 addInlayHint(SourceRange{RParenOrBraceLoc}, HintSide::Left,
844 HasNonDefaultArgs ?
", " :
"", Hint,
"");
848 static bool isSetter(
const FunctionDecl *Callee,
const NameVec &ParamNames) {
849 if (ParamNames.size() != 1)
852 StringRef Name = getSimpleName(*Callee);
853 if (!Name.starts_with_insensitive(
"set"))
867 StringRef WhatItIsSetting = Name.substr(3).ltrim(
"_");
868 return WhatItIsSetting.equals_insensitive(ParamNames[0]);
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:
886 bool shouldHintName(
const Expr *Arg, StringRef ParamName) {
887 if (ParamName.empty())
892 if (ParamName == getSpelledIdentifier(Arg))
896 if (isPrecededByParamNameComment(Arg, ParamName))
902 bool shouldHintReference(
const ParmVarDecl *Param,
903 const ParmVarDecl *ForwardedParam) {
926 auto Type = Param->getType();
927 auto ForwardedType = ForwardedParam->getType();
928 return Type->isLValueReferenceType() &&
929 ForwardedType->isLValueReferenceType() &&
930 !ForwardedType.getNonReferenceType().isConstQualified() &&
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)
944 StringRef SourcePrefix = MainFileBuf.substr(0, Decomposed.second);
946 SourcePrefix = SourcePrefix.rtrim();
948 if (!SourcePrefix.consume_back(
"*/"))
952 llvm::StringLiteral IgnoreChars =
" =.";
953 SourcePrefix = SourcePrefix.rtrim(IgnoreChars);
954 ParamName = ParamName.trim(IgnoreChars);
956 if (!SourcePrefix.consume_back(ParamName))
958 SourcePrefix = SourcePrefix.rtrim(IgnoreChars);
959 return SourcePrefix.ends_with(
"/*");
964 static StringRef getSpelledIdentifier(
const Expr *E) {
965 E = E->IgnoreUnlessSpelledInSource();
967 if (
auto *DRE = dyn_cast<DeclRefExpr>(E))
968 if (!DRE->getQualifier())
969 return getSimpleName(*DRE->getDecl());
971 if (
auto *ME = dyn_cast<MemberExpr>(E))
972 if (!ME->getQualifier() && ME->isImplicitAccess())
973 return getSimpleName(*ME->getMemberDecl());
978 NameVec chooseParameterNames(ArrayRef<const ParmVarDecl *> Parameters) {
979 NameVec ParameterNames;
980 for (
const auto *P : Parameters) {
985 ParameterNames.emplace_back();
987 auto SimpleName = getSimpleName(*P);
990 if (SimpleName.empty()) {
991 if (
const auto *PD = getParamDefinition(P)) {
992 SimpleName = getSimpleName(*PD);
995 ParameterNames.emplace_back(SimpleName);
1001 for (
auto &Name : ParameterNames)
1002 stripLeadingUnderscores(Name);
1004 return ParameterNames;
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);
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);
1031 addInlayHint(*LSPRange, Side, Kind, Prefix,
Label, Suffix);
1034 void addInlayHint(Range LSPRange, HintSide Side,
InlayHintKind Kind,
1035 llvm::StringRef Prefix, llvm::StringRef
Label,
1036 llvm::StringRef Suffix) {
1040 assert(Cfg.InlayHints.Enabled &&
"Shouldn't get here if disabled!");
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) \
1057 Position LSPPos = Side == HintSide::Left ? LSPRange.start : LSPRange.end;
1058 if (RestrictRange &&
1059 (LSPPos < RestrictRange->start || !(LSPPos < RestrictRange->end)))
1061 bool PadLeft = Prefix.consume_front(
" ");
1062 bool PadRight = Suffix.consume_back(
" ");
1063 Results.push_back(InlayHint{LSPPos,
1064 {(Prefix +
Label + Suffix).str()},
1065 Kind, PadLeft, PadRight, LSPRange});
1069 std::optional<Range> getHintRange(SourceRange R) {
1070 const auto &SM = AST.getSourceManager();
1071 auto Spelled = Tokens.spelledForExpanded(Tokens.expandedTokens(R));
1075 return std::nullopt;
1077 if (SM.getFileID(
Spelled->front().location()) != SM.getMainFileID() ||
1078 SM.getFileID(
Spelled->back().location()) != SM.getMainFileID())
1079 return std::nullopt;
1084 void addTypeHint(SourceRange R, QualType T, llvm::StringRef Prefix) {
1085 if (!Cfg.InlayHints.DeducedTypes ||
T.isNull())
1090 auto Desugared = maybeDesugar(AST, T);
1091 std::string TypeName = Desugared.getAsString(TypeHintPolicy);
1092 if (T != Desugared && !shouldPrintTypeHint(TypeName)) {
1095 TypeName =
T.getAsString(TypeHintPolicy);
1097 if (shouldPrintTypeHint(TypeName))
1102 void addDesignatorHint(SourceRange R, llvm::StringRef
Text) {
1107 bool shouldPrintTypeHint(llvm::StringRef TypeName)
const noexcept {
1108 return Cfg.InlayHints.TypeNameLimit == 0 ||
1109 TypeName.size() < Cfg.InlayHints.TypeNameLimit;
1112 void addBlockEndHint(SourceRange BraceRange, StringRef DeclPrefix,
1113 StringRef Name, StringRef OptionalPunctuation) {
1114 auto HintRange = computeBlockEndHintRange(BraceRange, OptionalPunctuation);
1118 std::string
Label = DeclPrefix.str();
1119 if (!
Label.empty() && !Name.empty())
1123 constexpr unsigned HintMaxLengthLimit = 60;
1124 if (
Label.length() > HintMaxLengthLimit)
1137 std::optional<Range> computeBlockEndHintRange(SourceRange BraceRange,
1138 StringRef OptionalPunctuation) {
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);
1150 if (BlockBeginFileId != MainFileID || RBraceFileId != MainFileID)
1151 return std::nullopt;
1153 StringRef RestOfLine = MainFileBuf.substr(RBraceOffset).split(
'\n').first;
1154 if (!RestOfLine.starts_with(
"}"))
1155 return std::nullopt;
1157 StringRef TrimmedTrailingText = RestOfLine.drop_front().trim();
1158 if (!TrimmedTrailingText.empty() &&
1159 TrimmedTrailingText != OptionalPunctuation)
1160 return std::nullopt;
1162 auto BlockBeginLine = SM.getLineNumber(BlockBeginFileId, BlockBeginOffset);
1163 auto RBraceLine = SM.getLineNumber(RBraceFileId, RBraceOffset);
1166 if (BlockBeginLine + HintOptions.HintMinLineLimit - 1 > RBraceLine)
1167 return std::nullopt;
1170 StringRef HintRangeText = RestOfLine.take_front(
1171 TrimmedTrailingText.empty()
1173 : TrimmedTrailingText.bytes_end() - RestOfLine.bytes_begin());
1177 SM, RBraceLoc.getLocWithOffset(HintRangeText.size()));
1178 return Range{HintStart, HintEnd};
1181 static bool isFunctionObjectCallExpr(CallExpr *E)
noexcept {
1182 if (
auto *CallExpr = dyn_cast<CXXOperatorCallExpr>(E))
1183 return CallExpr->getOperator() == OverloadedOperatorKind::OO_Call;
1187 std::vector<InlayHint> &Results;
1189 const syntax::TokenBuffer &Tokens;
1191 std::optional<Range> RestrictRange;
1193 StringRef MainFileBuf;
1194 const HeuristicResolver *Resolver;
1195 PrintingPolicy TypeHintPolicy;
1196 InlayHintOptions HintOptions;
1202 std::optional<Range> RestrictRange,
1204 std::vector<InlayHint> Results;
1206 if (!Cfg.InlayHints.Enabled)
1208 InlayHintVisitor Visitor(Results,
AST, Cfg, std::move(RestrictRange),
1210 Visitor.TraverseAST(
AST.getASTContext());
1214 llvm::sort(Results);
1215 Results.erase(llvm::unique(Results), Results.end());
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.
FIXME: Skip testing on windows temporarily due to the different escaping code mode.
SmallVector< const ParmVarDecl * > resolveForwardingParameters(const FunctionDecl *D, unsigned MaxDepth)
Recursively resolves the parameters of a FunctionDecl that forwards its parameters to another functio...
std::string printName(const ASTContext &Ctx, const NamedDecl &ND)
Prints unqualified name of the decl for the purpose of displaying it to the user.
NamedDecl * getOnlyInstantiation(NamedDecl *TemplatedDecl)
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...
InlayHintKind
Inlay hint kinds.
@ BlockEnd
A hint after function, type or namespace definition, indicating the defined symbol name of the defini...
@ DefaultArgument
An inlay hint that is for a default argument.
@ Parameter
An inlay hint that is for a parameter.
@ Type
An inlay hint that for a type annotation.
@ Designator
A hint before an element of an aggregate braced initializer list, indicating what it is initializing.
TemplateTypeParmTypeLoc getContainedAutoParamType(TypeLoc TL)
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.