1832 lines · cpp
1//===--- Hover.cpp - Information about code at the cursor location --------===//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-exception6//7//===----------------------------------------------------------------------===//8 9#include "Hover.h"10 11#include "AST.h"12#include "CodeCompletionStrings.h"13#include "Config.h"14#include "FindTarget.h"15#include "Headers.h"16#include "IncludeCleaner.h"17#include "ParsedAST.h"18#include "Protocol.h"19#include "Selection.h"20#include "SourceCode.h"21#include "SymbolDocumentation.h"22#include "clang-include-cleaner/Analysis.h"23#include "clang-include-cleaner/IncludeSpeller.h"24#include "clang-include-cleaner/Types.h"25#include "index/SymbolCollector.h"26#include "support/Markup.h"27#include "support/Trace.h"28#include "clang/AST/ASTContext.h"29#include "clang/AST/ASTDiagnostic.h"30#include "clang/AST/ASTTypeTraits.h"31#include "clang/AST/Attr.h"32#include "clang/AST/Decl.h"33#include "clang/AST/DeclBase.h"34#include "clang/AST/DeclCXX.h"35#include "clang/AST/DeclObjC.h"36#include "clang/AST/DeclTemplate.h"37#include "clang/AST/Expr.h"38#include "clang/AST/ExprCXX.h"39#include "clang/AST/OperationKinds.h"40#include "clang/AST/PrettyPrinter.h"41#include "clang/AST/RecordLayout.h"42#include "clang/AST/Type.h"43#include "clang/Basic/CharInfo.h"44#include "clang/Basic/LLVM.h"45#include "clang/Basic/LangOptions.h"46#include "clang/Basic/SourceLocation.h"47#include "clang/Basic/SourceManager.h"48#include "clang/Basic/Specifiers.h"49#include "clang/Basic/TokenKinds.h"50#include "clang/Index/IndexSymbol.h"51#include "clang/Tooling/Syntax/Tokens.h"52#include "llvm/ADT/ArrayRef.h"53#include "llvm/ADT/DenseSet.h"54#include "llvm/ADT/STLExtras.h"55#include "llvm/ADT/SmallVector.h"56#include "llvm/ADT/StringExtras.h"57#include "llvm/ADT/StringRef.h"58#include "llvm/Support/Casting.h"59#include "llvm/Support/Error.h"60#include "llvm/Support/Format.h"61#include "llvm/Support/ScopedPrinter.h"62#include "llvm/Support/raw_ostream.h"63#include <algorithm>64#include <optional>65#include <string>66#include <vector>67 68namespace clang {69namespace clangd {70namespace {71 72PrintingPolicy getPrintingPolicy(PrintingPolicy Base) {73 Base.AnonymousTagLocations = false;74 Base.TerseOutput = true;75 Base.PolishForDeclaration = true;76 Base.ConstantsAsWritten = true;77 Base.SuppressTemplateArgsInCXXConstructors = true;78 return Base;79}80 81/// Given a declaration \p D, return a human-readable string representing the82/// local scope in which it is declared, i.e. class(es) and method name. Returns83/// an empty string if it is not local.84std::string getLocalScope(const Decl *D) {85 std::vector<std::string> Scopes;86 const DeclContext *DC = D->getDeclContext();87 88 // ObjC scopes won't have multiple components for us to join, instead:89 // - Methods: "-[Class methodParam1:methodParam2]"90 // - Classes, categories, and protocols: "MyClass(Category)"91 if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(DC))92 return printObjCMethod(*MD);93 if (const ObjCContainerDecl *CD = dyn_cast<ObjCContainerDecl>(DC))94 return printObjCContainer(*CD);95 96 auto GetName = [](const TypeDecl *D) {97 if (!D->getDeclName().isEmpty()) {98 PrintingPolicy Policy = D->getASTContext().getPrintingPolicy();99 Policy.SuppressScope = true;100 return declaredType(D).getAsString(Policy);101 }102 if (auto *RD = dyn_cast<RecordDecl>(D))103 return ("(anonymous " + RD->getKindName() + ")").str();104 return std::string("");105 };106 while (DC) {107 if (const TypeDecl *TD = dyn_cast<TypeDecl>(DC))108 Scopes.push_back(GetName(TD));109 else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(DC))110 Scopes.push_back(FD->getNameAsString());111 DC = DC->getParent();112 }113 114 return llvm::join(llvm::reverse(Scopes), "::");115}116 117/// Returns the human-readable representation for namespace containing the118/// declaration \p D. Returns empty if it is contained global namespace.119std::string getNamespaceScope(const Decl *D) {120 const DeclContext *DC = D->getDeclContext();121 122 // ObjC does not have the concept of namespaces, so instead we support123 // local scopes.124 if (isa<ObjCMethodDecl, ObjCContainerDecl>(DC))125 return "";126 127 if (const TagDecl *TD = dyn_cast<TagDecl>(DC))128 return getNamespaceScope(TD);129 if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(DC))130 return getNamespaceScope(FD);131 if (const NamespaceDecl *NSD = dyn_cast<NamespaceDecl>(DC)) {132 // Skip inline/anon namespaces.133 if (NSD->isInline() || NSD->isAnonymousNamespace())134 return getNamespaceScope(NSD);135 }136 if (const NamedDecl *ND = dyn_cast<NamedDecl>(DC))137 return printQualifiedName(*ND);138 139 return "";140}141 142std::string printDefinition(const Decl *D, PrintingPolicy PP,143 const syntax::TokenBuffer &TB) {144 if (auto *VD = llvm::dyn_cast<VarDecl>(D)) {145 if (auto *IE = VD->getInit()) {146 // Initializers might be huge and result in lots of memory allocations in147 // some catostrophic cases. Such long lists are not useful in hover cards148 // anyway.149 if (200 < TB.expandedTokens(IE->getSourceRange()).size())150 PP.SuppressInitializers = true;151 }152 }153 std::string Definition;154 llvm::raw_string_ostream OS(Definition);155 D->print(OS, PP);156 return Definition;157}158 159const char *getMarkdownLanguage(const ASTContext &Ctx) {160 const auto &LangOpts = Ctx.getLangOpts();161 if (LangOpts.ObjC && LangOpts.CPlusPlus)162 return "objective-cpp";163 return LangOpts.ObjC ? "objective-c" : "cpp";164}165 166HoverInfo::PrintedType printType(QualType QT, ASTContext &ASTCtx,167 const PrintingPolicy &PP) {168 // TypePrinter doesn't resolve decltypes, so resolve them here.169 // FIXME: This doesn't handle composite types that contain a decltype in them.170 // We should rather have a printing policy for that.171 while (!QT.isNull() && QT->isDecltypeType())172 QT = QT->castAs<DecltypeType>()->getUnderlyingType();173 HoverInfo::PrintedType Result;174 llvm::raw_string_ostream OS(Result.Type);175 // Special case: if the outer type is a canonical tag type, then include the176 // tag for extra clarity. This isn't very idiomatic, so don't attempt it for177 // complex cases, including pointers/references, template specializations,178 // etc.179 if (!QT.isNull() && !QT.hasQualifiers() && PP.SuppressTagKeyword) {180 if (auto *TT = llvm::dyn_cast<TagType>(QT.getTypePtr());181 TT && TT->isCanonicalUnqualified())182 OS << TT->getDecl()->getKindName() << " ";183 }184 QT.print(OS, PP);185 186 const Config &Cfg = Config::current();187 if (!QT.isNull() && Cfg.Hover.ShowAKA) {188 bool ShouldAKA = false;189 QualType DesugaredTy = clang::desugarForDiagnostic(ASTCtx, QT, ShouldAKA);190 if (ShouldAKA)191 Result.AKA = DesugaredTy.getAsString(PP);192 }193 return Result;194}195 196HoverInfo::PrintedType printType(const TemplateTypeParmDecl *TTP) {197 HoverInfo::PrintedType Result;198 Result.Type = TTP->wasDeclaredWithTypename() ? "typename" : "class";199 if (TTP->isParameterPack())200 Result.Type += "...";201 return Result;202}203 204HoverInfo::PrintedType printType(const NonTypeTemplateParmDecl *NTTP,205 const PrintingPolicy &PP) {206 auto PrintedType = printType(NTTP->getType(), NTTP->getASTContext(), PP);207 if (NTTP->isParameterPack()) {208 PrintedType.Type += "...";209 if (PrintedType.AKA)210 *PrintedType.AKA += "...";211 }212 return PrintedType;213}214 215HoverInfo::PrintedType printType(const TemplateTemplateParmDecl *TTP,216 const PrintingPolicy &PP) {217 HoverInfo::PrintedType Result;218 llvm::raw_string_ostream OS(Result.Type);219 OS << "template <";220 llvm::StringRef Sep = "";221 for (const Decl *Param : *TTP->getTemplateParameters()) {222 OS << Sep;223 Sep = ", ";224 if (const auto *TTP = dyn_cast<TemplateTypeParmDecl>(Param))225 OS << printType(TTP).Type;226 else if (const auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(Param))227 OS << printType(NTTP, PP).Type;228 else if (const auto *TTPD = dyn_cast<TemplateTemplateParmDecl>(Param))229 OS << printType(TTPD, PP).Type;230 }231 // FIXME: TemplateTemplateParameter doesn't store the info on whether this232 // param was a "typename" or "class".233 OS << "> class";234 return Result;235}236 237std::vector<HoverInfo::Param>238fetchTemplateParameters(const TemplateParameterList *Params,239 const PrintingPolicy &PP) {240 assert(Params);241 std::vector<HoverInfo::Param> TempParameters;242 243 for (const Decl *Param : *Params) {244 HoverInfo::Param P;245 if (const auto *TTP = dyn_cast<TemplateTypeParmDecl>(Param)) {246 P.Type = printType(TTP);247 248 if (!TTP->getName().empty())249 P.Name = TTP->getNameAsString();250 251 if (TTP->hasDefaultArgument()) {252 P.Default.emplace();253 llvm::raw_string_ostream Out(*P.Default);254 TTP->getDefaultArgument().getArgument().print(PP, Out,255 /*IncludeType=*/false);256 }257 } else if (const auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(Param)) {258 P.Type = printType(NTTP, PP);259 260 if (IdentifierInfo *II = NTTP->getIdentifier())261 P.Name = II->getName().str();262 263 if (NTTP->hasDefaultArgument()) {264 P.Default.emplace();265 llvm::raw_string_ostream Out(*P.Default);266 NTTP->getDefaultArgument().getArgument().print(PP, Out,267 /*IncludeType=*/false);268 }269 } else if (const auto *TTPD = dyn_cast<TemplateTemplateParmDecl>(Param)) {270 P.Type = printType(TTPD, PP);271 272 if (!TTPD->getName().empty())273 P.Name = TTPD->getNameAsString();274 275 if (TTPD->hasDefaultArgument()) {276 P.Default.emplace();277 llvm::raw_string_ostream Out(*P.Default);278 TTPD->getDefaultArgument().getArgument().print(PP, Out,279 /*IncludeType*/ false);280 }281 }282 TempParameters.push_back(std::move(P));283 }284 285 return TempParameters;286}287 288const FunctionDecl *getUnderlyingFunction(const Decl *D) {289 // Extract lambda from variables.290 if (const VarDecl *VD = llvm::dyn_cast<VarDecl>(D)) {291 auto QT = VD->getType();292 if (!QT.isNull()) {293 while (!QT->getPointeeType().isNull())294 QT = QT->getPointeeType();295 296 if (const auto *CD = QT->getAsCXXRecordDecl())297 return CD->getLambdaCallOperator();298 }299 }300 301 // Non-lambda functions.302 return D->getAsFunction();303}304 305// Returns the decl that should be used for querying comments, either from index306// or AST.307const NamedDecl *getDeclForComment(const NamedDecl *D) {308 const NamedDecl *DeclForComment = D;309 if (const auto *TSD = llvm::dyn_cast<ClassTemplateSpecializationDecl>(D)) {310 // Template may not be instantiated e.g. if the type didn't need to be311 // complete; fallback to primary template.312 if (TSD->getTemplateSpecializationKind() == TSK_Undeclared)313 DeclForComment = TSD->getSpecializedTemplate();314 else if (const auto *TIP = TSD->getTemplateInstantiationPattern())315 DeclForComment = TIP;316 } else if (const auto *TSD =317 llvm::dyn_cast<VarTemplateSpecializationDecl>(D)) {318 if (TSD->getTemplateSpecializationKind() == TSK_Undeclared)319 DeclForComment = TSD->getSpecializedTemplate();320 else if (const auto *TIP = TSD->getTemplateInstantiationPattern())321 DeclForComment = TIP;322 } else if (const auto *FD = D->getAsFunction())323 if (const auto *TIP = FD->getTemplateInstantiationPattern())324 DeclForComment = TIP;325 // Ensure that getDeclForComment(getDeclForComment(X)) = getDeclForComment(X).326 // This is usually not needed, but in strange cases of comparision operators327 // being instantiated from spasceship operater, which itself is a template328 // instantiation the recursrive call is necessary.329 if (D != DeclForComment)330 DeclForComment = getDeclForComment(DeclForComment);331 return DeclForComment;332}333 334// Look up information about D from the index, and add it to Hover.335void enhanceFromIndex(HoverInfo &Hover, const NamedDecl &ND,336 const SymbolIndex *Index) {337 assert(&ND == getDeclForComment(&ND));338 // We only add documentation, so don't bother if we already have some.339 if (!Hover.Documentation.empty() || !Index)340 return;341 342 // Skip querying for non-indexable symbols, there's no point.343 // We're searching for symbols that might be indexed outside this main file.344 if (!SymbolCollector::shouldCollectSymbol(ND, ND.getASTContext(),345 SymbolCollector::Options(),346 /*IsMainFileOnly=*/false))347 return;348 auto ID = getSymbolID(&ND);349 if (!ID)350 return;351 LookupRequest Req;352 Req.IDs.insert(ID);353 Index->lookup(Req, [&](const Symbol &S) {354 Hover.Documentation = std::string(S.Documentation);355 });356}357 358// Default argument might exist but be unavailable, in the case of unparsed359// arguments for example. This function returns the default argument if it is360// available.361const Expr *getDefaultArg(const ParmVarDecl *PVD) {362 // Default argument can be unparsed or uninstantiated. For the former we363 // can't do much, as token information is only stored in Sema and not364 // attached to the AST node. For the latter though, it is safe to proceed as365 // the expression is still valid.366 if (!PVD->hasDefaultArg() || PVD->hasUnparsedDefaultArg())367 return nullptr;368 return PVD->hasUninstantiatedDefaultArg() ? PVD->getUninstantiatedDefaultArg()369 : PVD->getDefaultArg();370}371 372HoverInfo::Param toHoverInfoParam(const ParmVarDecl *PVD,373 const PrintingPolicy &PP) {374 HoverInfo::Param Out;375 Out.Type = printType(PVD->getType(), PVD->getASTContext(), PP);376 if (!PVD->getName().empty())377 Out.Name = PVD->getNameAsString();378 if (const Expr *DefArg = getDefaultArg(PVD)) {379 Out.Default.emplace();380 llvm::raw_string_ostream OS(*Out.Default);381 DefArg->printPretty(OS, nullptr, PP);382 }383 return Out;384}385 386// Populates Type, ReturnType, and Parameters for function-like decls.387void fillFunctionTypeAndParams(HoverInfo &HI, const Decl *D,388 const FunctionDecl *FD,389 const PrintingPolicy &PP) {390 HI.Parameters.emplace();391 for (const ParmVarDecl *PVD : FD->parameters())392 HI.Parameters->emplace_back(toHoverInfoParam(PVD, PP));393 394 // We don't want any type info, if name already contains it. This is true for395 // constructors/destructors and conversion operators.396 const auto NK = FD->getDeclName().getNameKind();397 if (NK == DeclarationName::CXXConstructorName ||398 NK == DeclarationName::CXXDestructorName ||399 NK == DeclarationName::CXXConversionFunctionName)400 return;401 402 HI.ReturnType = printType(FD->getReturnType(), FD->getASTContext(), PP);403 QualType QT = FD->getType();404 if (const VarDecl *VD = llvm::dyn_cast<VarDecl>(D)) // Lambdas405 QT = VD->getType().getDesugaredType(D->getASTContext());406 HI.Type = printType(QT, D->getASTContext(), PP);407 // FIXME: handle variadics.408}409 410// Non-negative numbers are printed using min digits411// 0 => 0x0412// 100 => 0x64413// Negative numbers are sign-extended to 32/64 bits414// -2 => 0xfffffffe415// -2^32 => 0xffffffff00000000416static llvm::FormattedNumber printHex(const llvm::APSInt &V) {417 assert(V.getSignificantBits() <= 64 && "Can't print more than 64 bits.");418 uint64_t Bits =419 V.getBitWidth() > 64 ? V.trunc(64).getZExtValue() : V.getZExtValue();420 if (V.isNegative() && V.getSignificantBits() <= 32)421 return llvm::format_hex(uint32_t(Bits), 0);422 return llvm::format_hex(Bits, 0);423}424 425std::optional<std::string> printExprValue(const Expr *E,426 const ASTContext &Ctx) {427 // InitListExpr has two forms, syntactic and semantic. They are the same thing428 // (refer to a same AST node) in most cases.429 // When they are different, RAV returns the syntactic form, and we should feed430 // the semantic form to EvaluateAsRValue.431 if (const auto *ILE = llvm::dyn_cast<InitListExpr>(E)) {432 if (!ILE->isSemanticForm())433 E = ILE->getSemanticForm();434 }435 436 // Evaluating [[foo]]() as "&foo" isn't useful, and prevents us walking up437 // to the enclosing call. Evaluating an expression of void type doesn't438 // produce a meaningful result.439 QualType T = E->getType();440 if (T.isNull() || T->isFunctionType() || T->isFunctionPointerType() ||441 T->isFunctionReferenceType() || T->isVoidType())442 return std::nullopt;443 444 Expr::EvalResult Constant;445 // Attempt to evaluate. If expr is dependent, evaluation crashes!446 if (E->isValueDependent() || !E->EvaluateAsRValue(Constant, Ctx) ||447 // Disable printing for record-types, as they are usually confusing and448 // might make clang crash while printing the expressions.449 Constant.Val.isStruct() || Constant.Val.isUnion())450 return std::nullopt;451 452 // Show enums symbolically, not numerically like APValue::printPretty().453 if (T->isEnumeralType() && Constant.Val.isInt() &&454 Constant.Val.getInt().getSignificantBits() <= 64) {455 // Compare to int64_t to avoid bit-width match requirements.456 int64_t Val = Constant.Val.getInt().getExtValue();457 for (const EnumConstantDecl *ECD : T->castAsEnumDecl()->enumerators())458 if (ECD->getInitVal() == Val)459 return llvm::formatv("{0} ({1})", ECD->getNameAsString(),460 printHex(Constant.Val.getInt()))461 .str();462 }463 // Show hex value of integers if they're at least 10 (or negative!)464 if (T->isIntegralOrEnumerationType() && Constant.Val.isInt() &&465 Constant.Val.getInt().getSignificantBits() <= 64 &&466 Constant.Val.getInt().uge(10))467 return llvm::formatv("{0} ({1})", Constant.Val.getAsString(Ctx, T),468 printHex(Constant.Val.getInt()))469 .str();470 return Constant.Val.getAsString(Ctx, T);471}472 473struct PrintExprResult {474 /// The evaluation result on expression `Expr`.475 std::optional<std::string> PrintedValue;476 /// The Expr object that represents the closest evaluable477 /// expression.478 const clang::Expr *TheExpr;479 /// The node of selection tree where the traversal stops.480 const SelectionTree::Node *TheNode;481};482 483// Seek the closest evaluable expression along the ancestors of node N484// in a selection tree. If a node in the path can be converted to an evaluable485// Expr, a possible evaluation would happen and the associated context486// is returned.487// If evaluation couldn't be done, return the node where the traversal ends.488PrintExprResult printExprValue(const SelectionTree::Node *N,489 const ASTContext &Ctx) {490 for (; N; N = N->Parent) {491 // Try to evaluate the first evaluatable enclosing expression.492 if (const Expr *E = N->ASTNode.get<Expr>()) {493 // Once we cross an expression of type 'cv void', the evaluated result494 // has nothing to do with our original cursor position.495 if (!E->getType().isNull() && E->getType()->isVoidType())496 break;497 if (auto Val = printExprValue(E, Ctx))498 return PrintExprResult{/*PrintedValue=*/std::move(Val), /*Expr=*/E,499 /*Node=*/N};500 } else if (N->ASTNode.get<Decl>() || N->ASTNode.get<Stmt>()) {501 // Refuse to cross certain non-exprs. (TypeLoc are OK as part of Exprs).502 // This tries to ensure we're showing a value related to the cursor.503 break;504 }505 }506 return PrintExprResult{/*PrintedValue=*/std::nullopt, /*Expr=*/nullptr,507 /*Node=*/N};508}509 510std::optional<StringRef> fieldName(const Expr *E) {511 const auto *ME = llvm::dyn_cast<MemberExpr>(E->IgnoreCasts());512 if (!ME || !llvm::isa<CXXThisExpr>(ME->getBase()->IgnoreCasts()))513 return std::nullopt;514 const auto *Field = llvm::dyn_cast<FieldDecl>(ME->getMemberDecl());515 if (!Field || !Field->getDeclName().isIdentifier())516 return std::nullopt;517 return Field->getDeclName().getAsIdentifierInfo()->getName();518}519 520// If CMD is of the form T foo() { return FieldName; } then returns "FieldName".521std::optional<StringRef> getterVariableName(const CXXMethodDecl *CMD) {522 assert(CMD->hasBody());523 if (CMD->getNumParams() != 0 || CMD->isVariadic())524 return std::nullopt;525 const auto *Body = llvm::dyn_cast<CompoundStmt>(CMD->getBody());526 const auto *OnlyReturn = (Body && Body->size() == 1)527 ? llvm::dyn_cast<ReturnStmt>(Body->body_front())528 : nullptr;529 if (!OnlyReturn || !OnlyReturn->getRetValue())530 return std::nullopt;531 return fieldName(OnlyReturn->getRetValue());532}533 534// If CMD is one of the forms:535// void foo(T arg) { FieldName = arg; }536// R foo(T arg) { FieldName = arg; return *this; }537// void foo(T arg) { FieldName = std::move(arg); }538// R foo(T arg) { FieldName = std::move(arg); return *this; }539// then returns "FieldName"540std::optional<StringRef> setterVariableName(const CXXMethodDecl *CMD) {541 assert(CMD->hasBody());542 if (CMD->isConst() || CMD->getNumParams() != 1 || CMD->isVariadic())543 return std::nullopt;544 const ParmVarDecl *Arg = CMD->getParamDecl(0);545 if (Arg->isParameterPack())546 return std::nullopt;547 548 const auto *Body = llvm::dyn_cast<CompoundStmt>(CMD->getBody());549 if (!Body || Body->size() == 0 || Body->size() > 2)550 return std::nullopt;551 // If the second statement exists, it must be `return this` or `return *this`.552 if (Body->size() == 2) {553 auto *Ret = llvm::dyn_cast<ReturnStmt>(Body->body_back());554 if (!Ret || !Ret->getRetValue())555 return std::nullopt;556 const Expr *RetVal = Ret->getRetValue()->IgnoreCasts();557 if (const auto *UO = llvm::dyn_cast<UnaryOperator>(RetVal)) {558 if (UO->getOpcode() != UO_Deref)559 return std::nullopt;560 RetVal = UO->getSubExpr()->IgnoreCasts();561 }562 if (!llvm::isa<CXXThisExpr>(RetVal))563 return std::nullopt;564 }565 // The first statement must be an assignment of the arg to a field.566 const Expr *LHS, *RHS;567 if (const auto *BO = llvm::dyn_cast<BinaryOperator>(Body->body_front())) {568 if (BO->getOpcode() != BO_Assign)569 return std::nullopt;570 LHS = BO->getLHS();571 RHS = BO->getRHS();572 } else if (const auto *COCE =573 llvm::dyn_cast<CXXOperatorCallExpr>(Body->body_front())) {574 if (COCE->getOperator() != OO_Equal || COCE->getNumArgs() != 2)575 return std::nullopt;576 LHS = COCE->getArg(0);577 RHS = COCE->getArg(1);578 } else {579 return std::nullopt;580 }581 582 // Detect the case when the item is moved into the field.583 if (auto *CE = llvm::dyn_cast<CallExpr>(RHS->IgnoreCasts())) {584 if (CE->getNumArgs() != 1)585 return std::nullopt;586 auto *ND = llvm::dyn_cast_or_null<NamedDecl>(CE->getCalleeDecl());587 if (!ND || !ND->getIdentifier() || ND->getName() != "move" ||588 !ND->isInStdNamespace())589 return std::nullopt;590 RHS = CE->getArg(0);591 }592 593 auto *DRE = llvm::dyn_cast<DeclRefExpr>(RHS->IgnoreCasts());594 if (!DRE || DRE->getDecl() != Arg)595 return std::nullopt;596 return fieldName(LHS);597}598 599std::string synthesizeDocumentation(const NamedDecl *ND) {600 if (const auto *CMD = llvm::dyn_cast<CXXMethodDecl>(ND)) {601 // Is this an ordinary, non-static method whose definition is visible?602 if (CMD->getDeclName().isIdentifier() && !CMD->isStatic() &&603 (CMD = llvm::dyn_cast_or_null<CXXMethodDecl>(CMD->getDefinition())) &&604 CMD->hasBody()) {605 if (const auto GetterField = getterVariableName(CMD))606 return llvm::formatv("Trivial accessor for `{0}`.", *GetterField);607 if (const auto SetterField = setterVariableName(CMD))608 return llvm::formatv("Trivial setter for `{0}`.", *SetterField);609 }610 }611 return "";612}613 614/// Generate a \p Hover object given the declaration \p D.615HoverInfo getHoverContents(const NamedDecl *D, const PrintingPolicy &PP,616 const SymbolIndex *Index,617 const syntax::TokenBuffer &TB) {618 HoverInfo HI;619 auto &Ctx = D->getASTContext();620 621 HI.AccessSpecifier = getAccessSpelling(D->getAccess()).str();622 HI.NamespaceScope = getNamespaceScope(D);623 if (!HI.NamespaceScope->empty())624 HI.NamespaceScope->append("::");625 HI.LocalScope = getLocalScope(D);626 if (!HI.LocalScope.empty())627 HI.LocalScope.append("::");628 629 HI.Name = printName(Ctx, *D);630 const auto *CommentD = getDeclForComment(D);631 HI.Documentation = getDeclComment(Ctx, *CommentD);632 // save the language options to be able to create the comment::CommandTraits633 // to parse the documentation634 HI.CommentOpts = D->getASTContext().getLangOpts().CommentOpts;635 enhanceFromIndex(HI, *CommentD, Index);636 if (HI.Documentation.empty())637 HI.Documentation = synthesizeDocumentation(D);638 639 HI.Kind = index::getSymbolInfo(D).Kind;640 641 // Fill in template params.642 if (const TemplateDecl *TD = D->getDescribedTemplate()) {643 HI.TemplateParameters =644 fetchTemplateParameters(TD->getTemplateParameters(), PP);645 D = TD;646 } else if (const FunctionDecl *FD = D->getAsFunction()) {647 if (const auto *FTD = FD->getDescribedTemplate()) {648 HI.TemplateParameters =649 fetchTemplateParameters(FTD->getTemplateParameters(), PP);650 D = FTD;651 }652 }653 654 // Fill in types and params.655 if (const FunctionDecl *FD = getUnderlyingFunction(D))656 fillFunctionTypeAndParams(HI, D, FD, PP);657 else if (const auto *VD = dyn_cast<ValueDecl>(D))658 HI.Type = printType(VD->getType(), Ctx, PP);659 else if (const auto *TTP = dyn_cast<TemplateTypeParmDecl>(D))660 HI.Type = TTP->wasDeclaredWithTypename() ? "typename" : "class";661 else if (const auto *TTP = dyn_cast<TemplateTemplateParmDecl>(D))662 HI.Type = printType(TTP, PP);663 else if (const auto *VT = dyn_cast<VarTemplateDecl>(D))664 HI.Type = printType(VT->getTemplatedDecl()->getType(), Ctx, PP);665 else if (const auto *TN = dyn_cast<TypedefNameDecl>(D))666 HI.Type = printType(TN->getUnderlyingType().getDesugaredType(Ctx), Ctx, PP);667 else if (const auto *TAT = dyn_cast<TypeAliasTemplateDecl>(D))668 HI.Type = printType(TAT->getTemplatedDecl()->getUnderlyingType(), Ctx, PP);669 670 // Fill in value with evaluated initializer if possible.671 if (const auto *Var = dyn_cast<VarDecl>(D); Var && !Var->isInvalidDecl()) {672 if (const Expr *Init = Var->getInit())673 HI.Value = printExprValue(Init, Ctx);674 } else if (const auto *ECD = dyn_cast<EnumConstantDecl>(D)) {675 // Dependent enums (e.g. nested in template classes) don't have values yet.676 if (!ECD->getType()->isDependentType())677 HI.Value = toString(ECD->getInitVal(), 10);678 }679 680 HI.Definition = printDefinition(D, PP, TB);681 return HI;682}683 684/// The standard defines __func__ as a "predefined variable".685std::optional<HoverInfo>686getPredefinedExprHoverContents(const PredefinedExpr &PE, ASTContext &Ctx,687 const PrintingPolicy &PP) {688 HoverInfo HI;689 HI.Name = PE.getIdentKindName();690 HI.Kind = index::SymbolKind::Variable;691 HI.Documentation = "Name of the current function (predefined variable)";692 if (const StringLiteral *Name = PE.getFunctionName()) {693 HI.Value.emplace();694 llvm::raw_string_ostream OS(*HI.Value);695 Name->outputString(OS);696 HI.Type = printType(Name->getType(), Ctx, PP);697 } else {698 // Inside templates, the approximate type `const char[]` is still useful.699 QualType StringType = Ctx.getIncompleteArrayType(Ctx.CharTy.withConst(),700 ArraySizeModifier::Normal,701 /*IndexTypeQuals=*/0);702 HI.Type = printType(StringType, Ctx, PP);703 }704 return HI;705}706 707HoverInfo evaluateMacroExpansion(unsigned int SpellingBeginOffset,708 unsigned int SpellingEndOffset,709 llvm::ArrayRef<syntax::Token> Expanded,710 ParsedAST &AST) {711 auto &Context = AST.getASTContext();712 auto &Tokens = AST.getTokens();713 auto PP = getPrintingPolicy(Context.getPrintingPolicy());714 auto Tree = SelectionTree::createRight(Context, Tokens, SpellingBeginOffset,715 SpellingEndOffset);716 717 // If macro expands to one single token, rule out punctuator or digraph.718 // E.g., for the case `array L_BRACKET 42 R_BRACKET;` where L_BRACKET and719 // R_BRACKET expand to720 // '[' and ']' respectively, we don't want the type of721 // 'array[42]' when user hovers on L_BRACKET.722 if (Expanded.size() == 1)723 if (tok::getPunctuatorSpelling(Expanded[0].kind()))724 return {};725 726 auto *StartNode = Tree.commonAncestor();727 if (!StartNode)728 return {};729 // If the common ancestor is partially selected, do evaluate if it has no730 // children, thus we can disallow evaluation on incomplete expression.731 // For example,732 // #define PLUS_2 +2733 // 40 PL^US_2734 // In this case we don't want to present 'value: 2' as PLUS_2 actually expands735 // to a non-value rather than a binary operand.736 if (StartNode->Selected == SelectionTree::Selection::Partial)737 if (!StartNode->Children.empty())738 return {};739 740 HoverInfo HI;741 // Attempt to evaluate it from Expr first.742 auto ExprResult = printExprValue(StartNode, Context);743 HI.Value = std::move(ExprResult.PrintedValue);744 if (auto *E = ExprResult.TheExpr)745 HI.Type = printType(E->getType(), Context, PP);746 747 // If failed, extract the type from Decl if possible.748 if (!HI.Value && !HI.Type && ExprResult.TheNode)749 if (auto *VD = ExprResult.TheNode->ASTNode.get<VarDecl>())750 HI.Type = printType(VD->getType(), Context, PP);751 752 return HI;753}754 755/// Generate a \p Hover object given the macro \p MacroDecl.756HoverInfo getHoverContents(const DefinedMacro &Macro, const syntax::Token &Tok,757 ParsedAST &AST) {758 HoverInfo HI;759 SourceManager &SM = AST.getSourceManager();760 HI.Name = std::string(Macro.Name);761 HI.Kind = index::SymbolKind::Macro;762 // FIXME: Populate documentation763 // FIXME: Populate parameters764 765 // Try to get the full definition, not just the name766 SourceLocation StartLoc = Macro.Info->getDefinitionLoc();767 SourceLocation EndLoc = Macro.Info->getDefinitionEndLoc();768 // Ensure that EndLoc is a valid offset. For example it might come from769 // preamble, and source file might've changed, in such a scenario EndLoc still770 // stays valid, but getLocForEndOfToken will fail as it is no longer a valid771 // offset.772 // Note that this check is just to ensure there's text data inside the range.773 // It will still succeed even when the data inside the range is irrelevant to774 // macro definition.775 if (SM.getPresumedLoc(EndLoc, /*UseLineDirectives=*/false).isValid()) {776 EndLoc = Lexer::getLocForEndOfToken(EndLoc, 0, SM, AST.getLangOpts());777 bool Invalid;778 StringRef Buffer = SM.getBufferData(SM.getFileID(StartLoc), &Invalid);779 if (!Invalid) {780 unsigned StartOffset = SM.getFileOffset(StartLoc);781 unsigned EndOffset = SM.getFileOffset(EndLoc);782 if (EndOffset <= Buffer.size() && StartOffset < EndOffset)783 HI.Definition =784 ("#define " + Buffer.substr(StartOffset, EndOffset - StartOffset))785 .str();786 }787 }788 789 if (auto Expansion = AST.getTokens().expansionStartingAt(&Tok)) {790 // We drop expansion that's longer than the threshold.791 // For extremely long expansion text, it's not readable from hover card792 // anyway.793 std::string ExpansionText;794 for (const auto &ExpandedTok : Expansion->Expanded) {795 ExpansionText += ExpandedTok.text(SM);796 ExpansionText += " ";797 const Config &Cfg = Config::current();798 const size_t Limit = static_cast<size_t>(Cfg.Hover.MacroContentsLimit);799 if (Limit && ExpansionText.size() > Limit) {800 ExpansionText.clear();801 break;802 }803 }804 805 if (!ExpansionText.empty()) {806 if (!HI.Definition.empty()) {807 HI.Definition += "\n\n";808 }809 HI.Definition += "// Expands to\n";810 HI.Definition += ExpansionText;811 }812 813 auto Evaluated = evaluateMacroExpansion(814 /*SpellingBeginOffset=*/SM.getFileOffset(Tok.location()),815 /*SpellingEndOffset=*/SM.getFileOffset(Tok.endLocation()),816 /*Expanded=*/Expansion->Expanded, AST);817 HI.Value = std::move(Evaluated.Value);818 HI.Type = std::move(Evaluated.Type);819 }820 return HI;821}822 823std::string typeAsDefinition(const HoverInfo::PrintedType &PType) {824 std::string Result;825 llvm::raw_string_ostream OS(Result);826 OS << PType.Type;827 if (PType.AKA)828 OS << " // aka: " << *PType.AKA;829 return Result;830}831 832std::optional<HoverInfo> getThisExprHoverContents(const CXXThisExpr *CTE,833 ASTContext &ASTCtx,834 const PrintingPolicy &PP) {835 QualType OriginThisType = CTE->getType()->getPointeeType();836 QualType ClassType = declaredType(OriginThisType->castAsTagDecl());837 // For partial specialization class, origin `this` pointee type will be838 // parsed as `InjectedClassNameType`, which will ouput template arguments839 // like "type-parameter-0-0". So we retrieve user written class type in this840 // case.841 QualType PrettyThisType = ASTCtx.getPointerType(842 QualType(ClassType.getTypePtr(), OriginThisType.getCVRQualifiers()));843 844 HoverInfo HI;845 HI.Name = "this";846 HI.Definition = typeAsDefinition(printType(PrettyThisType, ASTCtx, PP));847 return HI;848}849 850/// Generate a HoverInfo object given the deduced type \p QT851HoverInfo getDeducedTypeHoverContents(QualType QT, const syntax::Token &Tok,852 ASTContext &ASTCtx,853 const PrintingPolicy &PP,854 const SymbolIndex *Index) {855 HoverInfo HI;856 // FIXME: distinguish decltype(auto) vs decltype(expr)857 HI.Name = tok::getTokenName(Tok.kind());858 HI.Kind = index::SymbolKind::TypeAlias;859 860 if (QT->isUndeducedAutoType()) {861 HI.Definition = "/* not deduced */";862 } else {863 HI.Definition = typeAsDefinition(printType(QT, ASTCtx, PP));864 865 if (const auto *D = QT->getAsTagDecl()) {866 const auto *CommentD = getDeclForComment(D);867 HI.Documentation = getDeclComment(ASTCtx, *CommentD);868 enhanceFromIndex(HI, *CommentD, Index);869 }870 }871 872 return HI;873}874 875HoverInfo getStringLiteralContents(const StringLiteral *SL,876 const PrintingPolicy &PP) {877 HoverInfo HI;878 879 HI.Name = "string-literal";880 HI.Size = (SL->getLength() + 1) * SL->getCharByteWidth() * 8;881 HI.Type = SL->getType().getAsString(PP).c_str();882 883 return HI;884}885 886bool isLiteral(const Expr *E) {887 // Unfortunately there's no common base Literal classes inherits from888 // (apart from Expr), therefore these exclusions.889 return llvm::isa<CompoundLiteralExpr>(E) ||890 llvm::isa<CXXBoolLiteralExpr>(E) ||891 llvm::isa<CXXNullPtrLiteralExpr>(E) ||892 llvm::isa<FixedPointLiteral>(E) || llvm::isa<FloatingLiteral>(E) ||893 llvm::isa<ImaginaryLiteral>(E) || llvm::isa<IntegerLiteral>(E) ||894 llvm::isa<StringLiteral>(E) || llvm::isa<UserDefinedLiteral>(E);895}896 897llvm::StringLiteral getNameForExpr(const Expr *E) {898 // FIXME: Come up with names for `special` expressions.899 //900 // It's an known issue for GCC5, https://godbolt.org/z/Z_tbgi. Work around901 // that by using explicit conversion constructor.902 //903 // TODO: Once GCC5 is fully retired and not the minimal requirement as stated904 // in `GettingStarted`, please remove the explicit conversion constructor.905 return llvm::StringLiteral("expression");906}907 908void maybeAddCalleeArgInfo(const SelectionTree::Node *N, HoverInfo &HI,909 const PrintingPolicy &PP);910 911// Generates hover info for `this` and evaluatable expressions.912// FIXME: Support hover for literals (esp user-defined)913std::optional<HoverInfo> getHoverContents(const SelectionTree::Node *N,914 const Expr *E, ParsedAST &AST,915 const PrintingPolicy &PP,916 const SymbolIndex *Index) {917 std::optional<HoverInfo> HI;918 919 if (const StringLiteral *SL = dyn_cast<StringLiteral>(E)) {920 // Print the type and the size for string literals921 HI = getStringLiteralContents(SL, PP);922 } else if (isLiteral(E)) {923 // There's not much value in hovering over "42" and getting a hover card924 // saying "42 is an int", similar for most other literals.925 // However, if we have CalleeArgInfo, it's still useful to show it.926 maybeAddCalleeArgInfo(N, HI.emplace(), PP);927 if (HI->CalleeArgInfo) {928 // FIXME Might want to show the expression's value here instead?929 // E.g. if the literal is in hex it might be useful to show the decimal930 // value here.931 HI->Name = "literal";932 return HI;933 }934 return std::nullopt;935 }936 937 // For `this` expr we currently generate hover with pointee type.938 if (const CXXThisExpr *CTE = dyn_cast<CXXThisExpr>(E))939 HI = getThisExprHoverContents(CTE, AST.getASTContext(), PP);940 if (const PredefinedExpr *PE = dyn_cast<PredefinedExpr>(E))941 HI = getPredefinedExprHoverContents(*PE, AST.getASTContext(), PP);942 // For expressions we currently print the type and the value, iff it is943 // evaluatable.944 if (auto Val = printExprValue(E, AST.getASTContext())) {945 HI.emplace();946 HI->Type = printType(E->getType(), AST.getASTContext(), PP);947 HI->Value = *Val;948 HI->Name = std::string(getNameForExpr(E));949 }950 951 if (HI)952 maybeAddCalleeArgInfo(N, *HI, PP);953 954 return HI;955}956 957// Generates hover info for attributes.958std::optional<HoverInfo> getHoverContents(const Attr *A, ParsedAST &AST) {959 HoverInfo HI;960 HI.Name = A->getSpelling();961 if (A->hasScope())962 HI.LocalScope = A->getScopeName()->getName().str();963 {964 llvm::raw_string_ostream OS(HI.Definition);965 A->printPretty(OS, AST.getASTContext().getPrintingPolicy());966 }967 HI.Documentation = Attr::getDocumentation(A->getKind()).str();968 return HI;969}970 971void addLayoutInfo(const NamedDecl &ND, HoverInfo &HI) {972 if (ND.isInvalidDecl())973 return;974 975 const auto &Ctx = ND.getASTContext();976 if (auto *RD = llvm::dyn_cast<RecordDecl>(&ND)) {977 CanQualType RT = Ctx.getCanonicalTagType(RD);978 if (auto Size = Ctx.getTypeSizeInCharsIfKnown(RT))979 HI.Size = Size->getQuantity() * 8;980 if (!RD->isDependentType() && RD->isCompleteDefinition())981 HI.Align = Ctx.getTypeAlign(RT);982 return;983 }984 985 if (const auto *FD = llvm::dyn_cast<FieldDecl>(&ND)) {986 const auto *Record = FD->getParent();987 if (Record)988 Record = Record->getDefinition();989 if (Record && !Record->isInvalidDecl() && !Record->isDependentType()) {990 HI.Align = Ctx.getTypeAlign(FD->getType());991 const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(Record);992 HI.Offset = Layout.getFieldOffset(FD->getFieldIndex());993 if (FD->isBitField())994 HI.Size = FD->getBitWidthValue();995 else if (auto Size = Ctx.getTypeSizeInCharsIfKnown(FD->getType()))996 HI.Size = FD->isZeroSize(Ctx) ? 0 : Size->getQuantity() * 8;997 if (HI.Size) {998 unsigned EndOfField = *HI.Offset + *HI.Size;999 1000 // Calculate padding following the field.1001 if (!Record->isUnion() &&1002 FD->getFieldIndex() + 1 < Layout.getFieldCount()) {1003 // Measure padding up to the next class field.1004 unsigned NextOffset = Layout.getFieldOffset(FD->getFieldIndex() + 1);1005 if (NextOffset >= EndOfField) // next field could be a bitfield!1006 HI.Padding = NextOffset - EndOfField;1007 } else {1008 // Measure padding up to the end of the object.1009 HI.Padding = Layout.getSize().getQuantity() * 8 - EndOfField;1010 }1011 }1012 // Offset in a union is always zero, so not really useful to report.1013 if (Record->isUnion())1014 HI.Offset.reset();1015 }1016 return;1017 }1018}1019 1020HoverInfo::PassType::PassMode getPassMode(QualType ParmType) {1021 if (ParmType->isReferenceType()) {1022 if (ParmType->getPointeeType().isConstQualified())1023 return HoverInfo::PassType::ConstRef;1024 return HoverInfo::PassType::Ref;1025 }1026 return HoverInfo::PassType::Value;1027}1028 1029// If N is passed as argument to a function, fill HI.CalleeArgInfo with1030// information about that argument.1031void maybeAddCalleeArgInfo(const SelectionTree::Node *N, HoverInfo &HI,1032 const PrintingPolicy &PP) {1033 const auto &OuterNode = N->outerImplicit();1034 if (!OuterNode.Parent)1035 return;1036 1037 const FunctionDecl *FD = nullptr;1038 llvm::ArrayRef<const Expr *> Args;1039 1040 if (const auto *CE = OuterNode.Parent->ASTNode.get<CallExpr>()) {1041 FD = CE->getDirectCallee();1042 Args = {CE->getArgs(), CE->getNumArgs()};1043 } else if (const auto *CE =1044 OuterNode.Parent->ASTNode.get<CXXConstructExpr>()) {1045 FD = CE->getConstructor();1046 Args = {CE->getArgs(), CE->getNumArgs()};1047 }1048 if (!FD)1049 return;1050 1051 // For non-function-call-like operators (e.g. operator+, operator<<) it's1052 // not immediately obvious what the "passed as" would refer to and, given1053 // fixed function signature, the value would be very low anyway, so we choose1054 // to not support that.1055 // Both variadic functions and operator() (especially relevant for lambdas)1056 // should be supported in the future.1057 if (!FD || FD->isOverloadedOperator() || FD->isVariadic())1058 return;1059 1060 HoverInfo::PassType PassType;1061 1062 auto Parameters = resolveForwardingParameters(FD);1063 1064 // Find argument index for N.1065 for (unsigned I = 0; I < Args.size() && I < Parameters.size(); ++I) {1066 if (Args[I] != OuterNode.ASTNode.get<Expr>())1067 continue;1068 1069 // Extract matching argument from function declaration.1070 if (const ParmVarDecl *PVD = Parameters[I]) {1071 HI.CalleeArgInfo.emplace(toHoverInfoParam(PVD, PP));1072 if (N == &OuterNode)1073 PassType.PassBy = getPassMode(PVD->getType());1074 }1075 break;1076 }1077 if (!HI.CalleeArgInfo)1078 return;1079 1080 // If we found a matching argument, also figure out if it's a1081 // [const-]reference. For this we need to walk up the AST from the arg itself1082 // to CallExpr and check all implicit casts, constructor calls, etc.1083 if (const auto *E = N->ASTNode.get<Expr>()) {1084 if (E->getType().isConstQualified())1085 PassType.PassBy = HoverInfo::PassType::ConstRef;1086 }1087 1088 for (auto *CastNode = N->Parent;1089 CastNode != OuterNode.Parent && !PassType.Converted;1090 CastNode = CastNode->Parent) {1091 if (const auto *ImplicitCast = CastNode->ASTNode.get<ImplicitCastExpr>()) {1092 switch (ImplicitCast->getCastKind()) {1093 case CK_NoOp:1094 case CK_DerivedToBase:1095 case CK_UncheckedDerivedToBase:1096 // If it was a reference before, it's still a reference.1097 if (PassType.PassBy != HoverInfo::PassType::Value)1098 PassType.PassBy = ImplicitCast->getType().isConstQualified()1099 ? HoverInfo::PassType::ConstRef1100 : HoverInfo::PassType::Ref;1101 break;1102 case CK_LValueToRValue:1103 case CK_ArrayToPointerDecay:1104 case CK_FunctionToPointerDecay:1105 case CK_NullToPointer:1106 case CK_NullToMemberPointer:1107 // No longer a reference, but we do not show this as type conversion.1108 PassType.PassBy = HoverInfo::PassType::Value;1109 break;1110 default:1111 PassType.PassBy = HoverInfo::PassType::Value;1112 PassType.Converted = true;1113 break;1114 }1115 } else if (const auto *CtorCall =1116 CastNode->ASTNode.get<CXXConstructExpr>()) {1117 // We want to be smart about copy constructors. They should not show up as1118 // type conversion, but instead as passing by value.1119 if (CtorCall->getConstructor()->isCopyConstructor())1120 PassType.PassBy = HoverInfo::PassType::Value;1121 else1122 PassType.Converted = true;1123 } else if (CastNode->ASTNode.get<MaterializeTemporaryExpr>()) {1124 // Can't bind a non-const-ref to a temporary, so has to be const-ref1125 PassType.PassBy = HoverInfo::PassType::ConstRef;1126 } else { // Unknown implicit node, assume type conversion.1127 PassType.PassBy = HoverInfo::PassType::Value;1128 PassType.Converted = true;1129 }1130 }1131 1132 HI.CallPassType.emplace(PassType);1133}1134 1135const NamedDecl *pickDeclToUse(llvm::ArrayRef<const NamedDecl *> Candidates) {1136 if (Candidates.empty())1137 return nullptr;1138 1139 // This is e.g the case for1140 // namespace ns { void foo(); }1141 // void bar() { using ns::foo; f^oo(); }1142 // One declaration in Candidates will refer to the using declaration,1143 // which isn't really useful for Hover. So use the other one,1144 // which in this example would be the actual declaration of foo.1145 if (Candidates.size() <= 2) {1146 if (llvm::isa<UsingDecl>(Candidates.front()))1147 return Candidates.back();1148 return Candidates.front();1149 }1150 1151 // For something like1152 // namespace ns { void foo(int); void foo(char); }1153 // using ns::foo;1154 // template <typename T> void bar() { fo^o(T{}); }1155 // we actually want to show the using declaration,1156 // it's not clear which declaration to pick otherwise.1157 auto BaseDecls = llvm::make_filter_range(1158 Candidates, [](const NamedDecl *D) { return llvm::isa<UsingDecl>(D); });1159 if (std::distance(BaseDecls.begin(), BaseDecls.end()) == 1)1160 return *BaseDecls.begin();1161 1162 return Candidates.front();1163}1164 1165void maybeAddSymbolProviders(ParsedAST &AST, HoverInfo &HI,1166 include_cleaner::Symbol Sym) {1167 trace::Span Tracer("Hover::maybeAddSymbolProviders");1168 1169 llvm::SmallVector<include_cleaner::Header> RankedProviders =1170 include_cleaner::headersForSymbol(Sym, AST.getPreprocessor(),1171 &AST.getPragmaIncludes());1172 if (RankedProviders.empty())1173 return;1174 1175 const SourceManager &SM = AST.getSourceManager();1176 std::string Result;1177 include_cleaner::Includes ConvertedIncludes = convertIncludes(AST);1178 for (const auto &P : RankedProviders) {1179 if (P.kind() == include_cleaner::Header::Physical &&1180 P.physical() == SM.getFileEntryForID(SM.getMainFileID()))1181 // Main file ranked higher than any #include'd file1182 break;1183 1184 // Pick the best-ranked #include'd provider1185 auto Matches = ConvertedIncludes.match(P);1186 if (!Matches.empty()) {1187 Result = Matches[0]->quote();1188 break;1189 }1190 }1191 1192 if (!Result.empty()) {1193 HI.Provider = std::move(Result);1194 return;1195 }1196 1197 // Pick the best-ranked non-#include'd provider1198 const auto &H = RankedProviders.front();1199 if (H.kind() == include_cleaner::Header::Physical &&1200 H.physical() == SM.getFileEntryForID(SM.getMainFileID()))1201 // Do not show main file as provider, otherwise we'll show provider info1202 // on local variables, etc.1203 return;1204 1205 HI.Provider = include_cleaner::spellHeader(1206 {H, AST.getPreprocessor().getHeaderSearchInfo(),1207 SM.getFileEntryForID(SM.getMainFileID())});1208}1209 1210// FIXME: similar functions are present in FindHeaders.cpp (symbolName)1211// and IncludeCleaner.cpp (getSymbolName). Introduce a name() method into1212// include_cleaner::Symbol instead.1213std::string getSymbolName(include_cleaner::Symbol Sym) {1214 std::string Name;1215 switch (Sym.kind()) {1216 case include_cleaner::Symbol::Declaration:1217 if (const auto *ND = llvm::dyn_cast<NamedDecl>(&Sym.declaration()))1218 Name = ND->getDeclName().getAsString();1219 break;1220 case include_cleaner::Symbol::Macro:1221 Name = Sym.macro().Name->getName();1222 break;1223 }1224 return Name;1225}1226 1227void maybeAddUsedSymbols(ParsedAST &AST, HoverInfo &HI, const Inclusion &Inc) {1228 auto Converted = convertIncludes(AST);1229 llvm::DenseSet<include_cleaner::Symbol> UsedSymbols;1230 include_cleaner::walkUsed(1231 AST.getLocalTopLevelDecls(), collectMacroReferences(AST),1232 &AST.getPragmaIncludes(), AST.getPreprocessor(),1233 [&](const include_cleaner::SymbolReference &Ref,1234 llvm::ArrayRef<include_cleaner::Header> Providers) {1235 if (Ref.RT != include_cleaner::RefType::Explicit ||1236 UsedSymbols.contains(Ref.Target))1237 return;1238 1239 if (isPreferredProvider(Inc, Converted, Providers))1240 UsedSymbols.insert(Ref.Target);1241 });1242 1243 for (const auto &UsedSymbolDecl : UsedSymbols)1244 HI.UsedSymbolNames.push_back(getSymbolName(UsedSymbolDecl));1245 llvm::sort(HI.UsedSymbolNames);1246 HI.UsedSymbolNames.erase(llvm::unique(HI.UsedSymbolNames),1247 HI.UsedSymbolNames.end());1248}1249 1250} // namespace1251 1252std::optional<HoverInfo> getHover(ParsedAST &AST, Position Pos,1253 const format::FormatStyle &Style,1254 const SymbolIndex *Index) {1255 static constexpr trace::Metric HoverCountMetric(1256 "hover", trace::Metric::Counter, "case");1257 PrintingPolicy PP =1258 getPrintingPolicy(AST.getASTContext().getPrintingPolicy());1259 const SourceManager &SM = AST.getSourceManager();1260 auto CurLoc = sourceLocationInMainFile(SM, Pos);1261 if (!CurLoc) {1262 llvm::consumeError(CurLoc.takeError());1263 return std::nullopt;1264 }1265 const auto &TB = AST.getTokens();1266 auto TokensTouchingCursor = syntax::spelledTokensTouching(*CurLoc, TB);1267 // Early exit if there were no tokens around the cursor.1268 if (TokensTouchingCursor.empty())1269 return std::nullopt;1270 1271 // Show full header file path if cursor is on include directive.1272 for (const auto &Inc : AST.getIncludeStructure().MainFileIncludes) {1273 if (Inc.Resolved.empty() || Inc.HashLine != Pos.line)1274 continue;1275 HoverCountMetric.record(1, "include");1276 HoverInfo HI;1277 HI.Name = std::string(llvm::sys::path::filename(Inc.Resolved));1278 HI.Definition =1279 URIForFile::canonicalize(Inc.Resolved, AST.tuPath()).file().str();1280 HI.DefinitionLanguage = "";1281 HI.Kind = index::SymbolKind::IncludeDirective;1282 maybeAddUsedSymbols(AST, HI, Inc);1283 return HI;1284 }1285 1286 // To be used as a backup for highlighting the selected token, we use back as1287 // it aligns better with biases elsewhere (editors tend to send the position1288 // for the left of the hovered token).1289 CharSourceRange HighlightRange =1290 TokensTouchingCursor.back().range(SM).toCharRange(SM);1291 std::optional<HoverInfo> HI;1292 // Macros and deducedtype only works on identifiers and auto/decltype keywords1293 // respectively. Therefore they are only trggered on whichever works for them,1294 // similar to SelectionTree::create().1295 for (const auto &Tok : TokensTouchingCursor) {1296 if (Tok.kind() == tok::identifier) {1297 // Prefer the identifier token as a fallback highlighting range.1298 HighlightRange = Tok.range(SM).toCharRange(SM);1299 if (auto M = locateMacroAt(Tok, AST.getPreprocessor())) {1300 HoverCountMetric.record(1, "macro");1301 HI = getHoverContents(*M, Tok, AST);1302 if (auto DefLoc = M->Info->getDefinitionLoc(); DefLoc.isValid()) {1303 include_cleaner::Macro IncludeCleanerMacro{1304 AST.getPreprocessor().getIdentifierInfo(Tok.text(SM)), DefLoc};1305 maybeAddSymbolProviders(AST, *HI,1306 include_cleaner::Symbol{IncludeCleanerMacro});1307 }1308 break;1309 }1310 } else if (Tok.kind() == tok::kw_auto || Tok.kind() == tok::kw_decltype) {1311 HoverCountMetric.record(1, "keyword");1312 if (auto Deduced =1313 getDeducedType(AST.getASTContext(), AST.getHeuristicResolver(),1314 Tok.location())) {1315 HI = getDeducedTypeHoverContents(*Deduced, Tok, AST.getASTContext(), PP,1316 Index);1317 HighlightRange = Tok.range(SM).toCharRange(SM);1318 break;1319 }1320 1321 // If we can't find interesting hover information for this1322 // auto/decltype keyword, return nothing to avoid showing1323 // irrelevant or incorrect informations.1324 return std::nullopt;1325 }1326 }1327 1328 // If it wasn't auto/decltype or macro, look for decls and expressions.1329 if (!HI) {1330 auto Offset = SM.getFileOffset(*CurLoc);1331 // Editors send the position on the left of the hovered character.1332 // So our selection tree should be biased right. (Tested with VSCode).1333 SelectionTree ST =1334 SelectionTree::createRight(AST.getASTContext(), TB, Offset, Offset);1335 if (const SelectionTree::Node *N = ST.commonAncestor()) {1336 // FIXME: Fill in HighlightRange with range coming from N->ASTNode.1337 auto Decls = explicitReferenceTargets(N->ASTNode, DeclRelation::Alias,1338 AST.getHeuristicResolver());1339 if (const auto *DeclToUse = pickDeclToUse(Decls)) {1340 HoverCountMetric.record(1, "decl");1341 HI = getHoverContents(DeclToUse, PP, Index, TB);1342 // Layout info only shown when hovering on the field/class itself.1343 if (DeclToUse == N->ASTNode.get<Decl>())1344 addLayoutInfo(*DeclToUse, *HI);1345 // Look for a close enclosing expression to show the value of.1346 if (!HI->Value)1347 HI->Value = printExprValue(N, AST.getASTContext()).PrintedValue;1348 maybeAddCalleeArgInfo(N, *HI, PP);1349 1350 if (!isa<NamespaceDecl>(DeclToUse))1351 maybeAddSymbolProviders(AST, *HI,1352 include_cleaner::Symbol{*DeclToUse});1353 } else if (const Expr *E = N->ASTNode.get<Expr>()) {1354 HoverCountMetric.record(1, "expr");1355 HI = getHoverContents(N, E, AST, PP, Index);1356 } else if (const Attr *A = N->ASTNode.get<Attr>()) {1357 HoverCountMetric.record(1, "attribute");1358 HI = getHoverContents(A, AST);1359 }1360 // FIXME: support hovers for other nodes?1361 // - built-in types1362 }1363 }1364 1365 if (!HI)1366 return std::nullopt;1367 1368 // Reformat Definition1369 if (!HI->Definition.empty()) {1370 auto Replacements = format::reformat(1371 Style, HI->Definition, tooling::Range(0, HI->Definition.size()));1372 if (auto Formatted =1373 tooling::applyAllReplacements(HI->Definition, Replacements))1374 HI->Definition = *Formatted;1375 }1376 1377 HI->DefinitionLanguage = getMarkdownLanguage(AST.getASTContext());1378 HI->SymRange = halfOpenToRange(SM, HighlightRange);1379 1380 return HI;1381}1382 1383// Sizes (and padding) are shown in bytes if possible, otherwise in bits.1384static std::string formatSize(uint64_t SizeInBits) {1385 uint64_t Value = SizeInBits % 8 == 0 ? SizeInBits / 8 : SizeInBits;1386 const char *Unit = Value != 0 && Value == SizeInBits ? "bit" : "byte";1387 return llvm::formatv("{0} {1}{2}", Value, Unit, Value == 1 ? "" : "s").str();1388}1389 1390// Offsets are shown in bytes + bits, so offsets of different fields1391// can always be easily compared.1392static std::string formatOffset(uint64_t OffsetInBits) {1393 const auto Bytes = OffsetInBits / 8;1394 const auto Bits = OffsetInBits % 8;1395 auto Offset = formatSize(Bytes * 8);1396 if (Bits != 0)1397 Offset += " and " + formatSize(Bits);1398 return Offset;1399}1400 1401void HoverInfo::calleeArgInfoToMarkupParagraph(markup::Paragraph &P) const {1402 assert(CallPassType);1403 std::string Buffer;1404 llvm::raw_string_ostream OS(Buffer);1405 OS << "Passed ";1406 if (CallPassType->PassBy != HoverInfo::PassType::Value) {1407 OS << "by ";1408 if (CallPassType->PassBy == HoverInfo::PassType::ConstRef)1409 OS << "const ";1410 OS << "reference ";1411 }1412 if (CalleeArgInfo->Name)1413 OS << "as " << CalleeArgInfo->Name;1414 else if (CallPassType->PassBy == HoverInfo::PassType::Value)1415 OS << "by value";1416 if (CallPassType->Converted && CalleeArgInfo->Type)1417 OS << " (converted to " << CalleeArgInfo->Type->Type << ")";1418 P.appendText(OS.str());1419}1420 1421void HoverInfo::usedSymbolNamesToMarkup(markup::Document &Output) const {1422 markup::Paragraph &P = Output.addParagraph();1423 P.appendText("provides ");1424 1425 const std::vector<std::string>::size_type SymbolNamesLimit = 5;1426 auto Front = llvm::ArrayRef(UsedSymbolNames).take_front(SymbolNamesLimit);1427 1428 llvm::interleave(1429 Front, [&](llvm::StringRef Sym) { P.appendCode(Sym); },1430 [&] { P.appendText(", "); });1431 if (UsedSymbolNames.size() > Front.size()) {1432 P.appendText(" and ");1433 P.appendText(std::to_string(UsedSymbolNames.size() - Front.size()));1434 P.appendText(" more");1435 }1436}1437 1438void HoverInfo::providerToMarkupParagraph(markup::Document &Output) const {1439 markup::Paragraph &DI = Output.addParagraph();1440 DI.appendText("provided by");1441 DI.appendSpace();1442 DI.appendCode(Provider);1443}1444 1445void HoverInfo::definitionScopeToMarkup(markup::Document &Output) const {1446 std::string Buffer;1447 1448 // Append scope comment, dropping trailing "::".1449 // Note that we don't print anything for global namespace, to not annoy1450 // non-c++ projects or projects that are not making use of namespaces.1451 if (!LocalScope.empty()) {1452 // Container name, e.g. class, method, function.1453 // We might want to propagate some info about container type to print1454 // function foo, class X, method X::bar, etc.1455 Buffer += "// In " + llvm::StringRef(LocalScope).rtrim(':').str() + '\n';1456 } else if (NamespaceScope && !NamespaceScope->empty()) {1457 Buffer += "// In namespace " +1458 llvm::StringRef(*NamespaceScope).rtrim(':').str() + '\n';1459 }1460 1461 if (!AccessSpecifier.empty()) {1462 Buffer += AccessSpecifier + ": ";1463 }1464 1465 Buffer += Definition;1466 1467 Output.addCodeBlock(Buffer, DefinitionLanguage);1468}1469 1470void HoverInfo::valueToMarkupParagraph(markup::Paragraph &P) const {1471 P.appendText("Value = ");1472 P.appendCode(*Value);1473}1474 1475void HoverInfo::offsetToMarkupParagraph(markup::Paragraph &P) const {1476 P.appendText("Offset: " + formatOffset(*Offset));1477}1478 1479void HoverInfo::sizeToMarkupParagraph(markup::Paragraph &P) const {1480 P.appendText("Size: " + formatSize(*Size));1481 if (Padding && *Padding != 0) {1482 P.appendText(llvm::formatv(" (+{0} padding)", formatSize(*Padding)).str());1483 }1484 if (Align)1485 P.appendText(", alignment " + formatSize(*Align));1486}1487 1488markup::Document HoverInfo::presentDoxygen() const {1489 1490 markup::Document Output;1491 // Header contains a text of the form:1492 // variable `var`1493 //1494 // class `X`1495 //1496 // function `foo`1497 //1498 // expression1499 //1500 // Note that we are making use of a level-3 heading because VSCode renders1501 // level 1 and 2 headers in a huge font, see1502 // https://github.com/microsoft/vscode/issues/88417 for details.1503 markup::Paragraph &Header = Output.addHeading(3);1504 if (Kind != index::SymbolKind::Unknown &&1505 Kind != index::SymbolKind::IncludeDirective)1506 Header.appendText(index::getSymbolKindString(Kind)).appendSpace();1507 assert(!Name.empty() && "hover triggered on a nameless symbol");1508 1509 if (Kind == index::SymbolKind::IncludeDirective) {1510 Header.appendCode(Name);1511 1512 if (!Definition.empty())1513 Output.addParagraph().appendCode(Definition);1514 1515 if (!UsedSymbolNames.empty()) {1516 Output.addRuler();1517 usedSymbolNamesToMarkup(Output);1518 }1519 1520 return Output;1521 }1522 1523 if (!Definition.empty()) {1524 Output.addRuler();1525 definitionScopeToMarkup(Output);1526 } else {1527 Header.appendCode(Name);1528 }1529 1530 if (!Provider.empty()) {1531 providerToMarkupParagraph(Output);1532 }1533 1534 // Put a linebreak after header to increase readability.1535 Output.addRuler();1536 1537 SymbolDocCommentVisitor SymbolDoc(Documentation, CommentOpts);1538 1539 if (SymbolDoc.hasBriefCommand()) {1540 if (Kind != index::SymbolKind::Parameter &&1541 Kind != index::SymbolKind::TemplateTypeParm)1542 // Only add a "Brief" heading if we are not documenting a parameter.1543 // Parameters only have a brief section and adding the brief header would1544 // be redundant.1545 Output.addHeading(3).appendText("Brief");1546 SymbolDoc.briefToMarkup(Output.addParagraph());1547 Output.addRuler();1548 }1549 1550 // For functions we display signature in a list form, e.g.:1551 // Template Parameters:1552 // - `typename T` - description1553 // Parameters:1554 // - `bool param1` - description1555 // - `int param2 = 5` - description1556 // Returns1557 // `type` - description1558 if (TemplateParameters && !TemplateParameters->empty()) {1559 Output.addHeading(3).appendText("Template Parameters");1560 markup::BulletList &L = Output.addBulletList();1561 for (const auto &Param : *TemplateParameters) {1562 markup::Paragraph &P = L.addItem().addParagraph();1563 P.appendCode(llvm::to_string(Param));1564 if (SymbolDoc.isTemplateTypeParmDocumented(llvm::to_string(Param.Name))) {1565 P.appendText(" - ");1566 SymbolDoc.templateTypeParmDocToMarkup(llvm::to_string(Param.Name), P);1567 }1568 }1569 Output.addRuler();1570 }1571 1572 if (Parameters && !Parameters->empty()) {1573 Output.addHeading(3).appendText("Parameters");1574 markup::BulletList &L = Output.addBulletList();1575 for (const auto &Param : *Parameters) {1576 markup::Paragraph &P = L.addItem().addParagraph();1577 P.appendCode(llvm::to_string(Param));1578 1579 if (SymbolDoc.isParameterDocumented(llvm::to_string(Param.Name))) {1580 P.appendText(" - ");1581 SymbolDoc.parameterDocToMarkup(llvm::to_string(Param.Name), P);1582 }1583 }1584 Output.addRuler();1585 }1586 1587 // Print Types on their own lines to reduce chances of getting line-wrapped by1588 // editor, as they might be long.1589 if (ReturnType &&1590 ((ReturnType->Type != "void" && !ReturnType->AKA.has_value()) ||1591 (ReturnType->AKA.has_value() && ReturnType->AKA != "void"))) {1592 Output.addHeading(3).appendText("Returns");1593 markup::Paragraph &P = Output.addParagraph();1594 P.appendCode(llvm::to_string(*ReturnType));1595 1596 if (SymbolDoc.hasReturnCommand()) {1597 P.appendText(" - ");1598 SymbolDoc.returnToMarkup(P);1599 }1600 1601 SymbolDoc.retvalsToMarkup(Output);1602 Output.addRuler();1603 }1604 1605 if (SymbolDoc.hasDetailedDoc()) {1606 Output.addHeading(3).appendText("Details");1607 SymbolDoc.detailedDocToMarkup(Output);1608 }1609 1610 Output.addRuler();1611 1612 // Don't print Type after Parameters or ReturnType as this will just duplicate1613 // the information1614 if (Type && !ReturnType && !Parameters)1615 Output.addParagraph().appendText("Type: ").appendCode(1616 llvm::to_string(*Type));1617 1618 if (Value) {1619 valueToMarkupParagraph(Output.addParagraph());1620 }1621 1622 if (Offset)1623 offsetToMarkupParagraph(Output.addParagraph());1624 if (Size) {1625 sizeToMarkupParagraph(Output.addParagraph());1626 }1627 1628 if (CalleeArgInfo) {1629 calleeArgInfoToMarkupParagraph(Output.addParagraph());1630 }1631 1632 if (!UsedSymbolNames.empty()) {1633 Output.addRuler();1634 usedSymbolNamesToMarkup(Output);1635 }1636 1637 return Output;1638}1639 1640markup::Document HoverInfo::presentDefault() const {1641 markup::Document Output;1642 // Header contains a text of the form:1643 // variable `var`1644 //1645 // class `X`1646 //1647 // function `foo`1648 //1649 // expression1650 //1651 // Note that we are making use of a level-3 heading because VSCode renders1652 // level 1 and 2 headers in a huge font, see1653 // https://github.com/microsoft/vscode/issues/88417 for details.1654 markup::Paragraph &Header = Output.addHeading(3);1655 if (Kind != index::SymbolKind::Unknown &&1656 Kind != index::SymbolKind::IncludeDirective)1657 Header.appendText(index::getSymbolKindString(Kind)).appendSpace();1658 assert(!Name.empty() && "hover triggered on a nameless symbol");1659 Header.appendCode(Name);1660 1661 if (!Provider.empty()) {1662 providerToMarkupParagraph(Output);1663 }1664 1665 // Put a linebreak after header to increase readability.1666 Output.addRuler();1667 // Print Types on their own lines to reduce chances of getting line-wrapped by1668 // editor, as they might be long.1669 if (ReturnType) {1670 // For functions we display signature in a list form, e.g.:1671 // → `x`1672 // Parameters:1673 // - `bool param1`1674 // - `int param2 = 5`1675 Output.addParagraph().appendText("→ ").appendCode(1676 llvm::to_string(*ReturnType));1677 }1678 1679 if (Parameters && !Parameters->empty()) {1680 Output.addParagraph().appendText("Parameters:");1681 markup::BulletList &L = Output.addBulletList();1682 for (const auto &Param : *Parameters)1683 L.addItem().addParagraph().appendCode(llvm::to_string(Param));1684 }1685 1686 // Don't print Type after Parameters or ReturnType as this will just duplicate1687 // the information1688 if (Type && !ReturnType && !Parameters)1689 Output.addParagraph().appendText("Type: ").appendCode(1690 llvm::to_string(*Type));1691 1692 if (Value) {1693 valueToMarkupParagraph(Output.addParagraph());1694 }1695 1696 if (Offset)1697 offsetToMarkupParagraph(Output.addParagraph());1698 if (Size) {1699 sizeToMarkupParagraph(Output.addParagraph());1700 }1701 1702 if (CalleeArgInfo) {1703 calleeArgInfoToMarkupParagraph(Output.addParagraph());1704 }1705 1706 if (!Documentation.empty())1707 parseDocumentation(Documentation, Output);1708 1709 if (!Definition.empty()) {1710 Output.addRuler();1711 definitionScopeToMarkup(Output);1712 }1713 1714 if (!UsedSymbolNames.empty()) {1715 Output.addRuler();1716 usedSymbolNamesToMarkup(Output);1717 }1718 1719 return Output;1720}1721 1722std::string HoverInfo::present(MarkupKind Kind) const {1723 if (Kind == MarkupKind::Markdown) {1724 const Config &Cfg = Config::current();1725 if (Cfg.Documentation.CommentFormat ==1726 Config::CommentFormatPolicy::Markdown)1727 return presentDefault().asMarkdown();1728 if (Cfg.Documentation.CommentFormat == Config::CommentFormatPolicy::Doxygen)1729 return presentDoxygen().asMarkdown();1730 if (Cfg.Documentation.CommentFormat ==1731 Config::CommentFormatPolicy::PlainText)1732 // If the user prefers plain text, we use the present() method to generate1733 // the plain text output.1734 return presentDefault().asEscapedMarkdown();1735 }1736 1737 return presentDefault().asPlainText();1738}1739 1740// If the backtick at `Offset` starts a probable quoted range, return the range1741// (including the quotes).1742std::optional<llvm::StringRef> getBacktickQuoteRange(llvm::StringRef Line,1743 unsigned Offset) {1744 assert(Line[Offset] == '`');1745 1746 // The open-quote is usually preceded by whitespace.1747 llvm::StringRef Prefix = Line.substr(0, Offset);1748 constexpr llvm::StringLiteral BeforeStartChars = " \t(=";1749 if (!Prefix.empty() && !BeforeStartChars.contains(Prefix.back()))1750 return std::nullopt;1751 1752 // The quoted string must be nonempty and usually has no leading/trailing ws.1753 auto Next = Line.find_first_of("`\n", Offset + 1);1754 if (Next == llvm::StringRef::npos)1755 return std::nullopt;1756 1757 // There should be no newline in the quoted string.1758 if (Line[Next] == '\n')1759 return std::nullopt;1760 1761 llvm::StringRef Contents = Line.slice(Offset + 1, Next);1762 if (Contents.empty() || isWhitespace(Contents.front()) ||1763 isWhitespace(Contents.back()))1764 return std::nullopt;1765 1766 // The close-quote is usually followed by whitespace or punctuation.1767 llvm::StringRef Suffix = Line.substr(Next + 1);1768 constexpr llvm::StringLiteral AfterEndChars = " \t)=.,;:";1769 if (!Suffix.empty() && !AfterEndChars.contains(Suffix.front()))1770 return std::nullopt;1771 1772 return Line.slice(Offset, Next + 1);1773}1774 1775void parseDocumentationParagraph(llvm::StringRef Text, markup::Paragraph &Out) {1776 // Probably this is appendText(Line), but scan for something interesting.1777 for (unsigned I = 0; I < Text.size(); ++I) {1778 switch (Text[I]) {1779 case '`':1780 if (auto Range = getBacktickQuoteRange(Text, I)) {1781 Out.appendText(Text.substr(0, I));1782 Out.appendCode(Range->trim("`"), /*Preserve=*/true);1783 return parseDocumentationParagraph(Text.substr(I + Range->size()), Out);1784 }1785 break;1786 }1787 }1788 Out.appendText(Text);1789}1790 1791void parseDocumentation(llvm::StringRef Input, markup::Document &Output) {1792 // A documentation string is treated as a sequence of paragraphs,1793 // where the paragraphs are seperated by at least one empty line1794 // (meaning 2 consecutive newline characters).1795 // Possible leading empty lines (introduced by an odd number > 1 of1796 // empty lines between 2 paragraphs) will be removed later in the Markup1797 // renderer.1798 llvm::StringRef Paragraph, Rest;1799 for (std::tie(Paragraph, Rest) = Input.split("\n\n");1800 !(Paragraph.empty() && Rest.empty());1801 std::tie(Paragraph, Rest) = Rest.split("\n\n")) {1802 1803 // The Paragraph will be empty if there is an even number of newline1804 // characters between two paragraphs, so we skip it.1805 if (!Paragraph.empty())1806 parseDocumentationParagraph(Paragraph, Output.addParagraph());1807 }1808}1809llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,1810 const HoverInfo::PrintedType &T) {1811 OS << T.Type;1812 if (T.AKA)1813 OS << " (aka " << *T.AKA << ")";1814 return OS;1815}1816 1817llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,1818 const HoverInfo::Param &P) {1819 if (P.Type)1820 OS << P.Type->Type;1821 if (P.Name)1822 OS << " " << *P.Name;1823 if (P.Default)1824 OS << " = " << *P.Default;1825 if (P.Type && P.Type->AKA)1826 OS << " (aka " << *P.Type->AKA << ")";1827 return OS;1828}1829 1830} // namespace clangd1831} // namespace clang1832