diff --git a/clang-tools-extra/clangd/Hover.h b/clang-tools-extra/clangd/Hover.h --- a/clang-tools-extra/clangd/Hover.h +++ b/clang-tools-extra/clangd/Hover.h @@ -77,6 +77,9 @@ llvm::Optional Size; /// Contains the offset of fields within the enclosing class. llvm::Optional Offset; + // Set when symbol is inside function call. Contains information extracted + // from the callee definition about the argument this is passed as. + llvm::Optional CalleeArgInfo; /// Produce a user-readable information. markup::Document present() const; diff --git a/clang-tools-extra/clangd/Hover.cpp b/clang-tools-extra/clangd/Hover.cpp --- a/clang-tools-extra/clangd/Hover.cpp +++ b/clang-tools-extra/clangd/Hover.cpp @@ -289,6 +289,26 @@ : PVD->getDefaultArg(); } +void fillParam(const ParmVarDecl *PVD, HoverInfo::Param &Out, + const PrintingPolicy &Policy) { + if (!PVD->getType().isNull()) { + Out.Type = printType(PVD->getType(), Policy); + } else { + std::string Param; + llvm::raw_string_ostream OS(Param); + PVD->dump(OS); + OS.flush(); + elog("Got param with null type: {0}", Param); + } + if (!PVD->getName().empty()) + Out.Name = PVD->getNameAsString(); + if (const Expr *DefArg = getDefaultArg(PVD)) { + Out.Default.emplace(); + llvm::raw_string_ostream OS(*Out.Default); + DefArg->printPretty(OS, nullptr, Policy); + } +} + // Populates Type, ReturnType, and Parameters for function-like decls. void fillFunctionTypeAndParams(HoverInfo &HI, const Decl *D, const FunctionDecl *FD, @@ -296,23 +316,7 @@ HI.Parameters.emplace(); for (const ParmVarDecl *PVD : FD->parameters()) { HI.Parameters->emplace_back(); - auto &P = HI.Parameters->back(); - if (!PVD->getType().isNull()) { - P.Type = printType(PVD->getType(), Policy); - } else { - std::string Param; - llvm::raw_string_ostream OS(Param); - PVD->dump(OS); - OS.flush(); - elog("Got param with null type: {0}", Param); - } - if (!PVD->getName().empty()) - P.Name = PVD->getNameAsString(); - if (const Expr *DefArg = getDefaultArg(PVD)) { - P.Default.emplace(); - llvm::raw_string_ostream Out(*P.Default); - DefArg->printPretty(Out, nullptr, Policy); - } + fillParam(PVD, HI.Parameters->back(), Policy); } // We don't want any type info, if name already contains it. This is true for @@ -518,6 +522,10 @@ HI.Value = ECD->getInitVal().toString(10); } + if (const auto *Var = dyn_cast(D)) { + // Check if we are inside CallExpr + } + HI.Definition = printDefinition(D); return HI; } @@ -685,6 +693,37 @@ } // namespace +// If N is passed as argument to a function, fill HI.CalleeArgInfo with +// information about that argument. +void maybeAddCalleeArgInfo(const SelectionTree::Node *N, HoverInfo &HI, + const PrintingPolicy &Policy) { + if (!N || !N->outerImplicit().Parent) + return; + auto *CE = N->outerImplicit().Parent->ASTNode.get(); + if (!CE) + return; + + // Find argument index for N. + unsigned I = 0; + for (; I < CE->getNumArgs(); ++I) { + auto *Arg = CE->getArg(I); + if (Arg == N->outerImplicit().ASTNode.get()) + break; + } + if (I >= CE->getNumArgs()) + return; + // Extract matching argument from function declaration. + if (const FunctionDecl *FD = CE->getDirectCallee()) { + if (FD->isOverloadedOperator() || FD->isVariadic() || + FD->getNumParams() <= I) + return; + if (const ParmVarDecl *PVD = FD->getParamDecl(I)) { + HI.CalleeArgInfo.emplace(); + fillParam(PVD, *HI.CalleeArgInfo, Policy); + } + } +} + llvm::Optional getHover(ParsedAST &AST, Position Pos, format::FormatStyle Style, const SymbolIndex *Index) { @@ -745,6 +784,7 @@ // Look for a close enclosing expression to show the value of. if (!HI->Value) HI->Value = printExprValue(N, AST.getASTContext()); + maybeAddCalleeArgInfo(N, *HI, AST.getASTContext().getPrintingPolicy()); } else if (const Expr *E = N->ASTNode.get()) { HI = getHoverContents(E, AST); } @@ -849,6 +889,15 @@ // non-c++ projects or projects that are not making use of namespaces. Output.addCodeBlock(ScopeComment + DefinitionWithAccess); } + + if (CalleeArgInfo) { + Output.addRuler(); + std::string Buffer; + llvm::raw_string_ostream OS(Buffer); + OS << "Passed as " << *CalleeArgInfo; + Output.addParagraph().appendText(OS.str()); + } + return Output; } diff --git a/clang-tools-extra/clangd/unittests/HoverTests.cpp b/clang-tools-extra/clangd/unittests/HoverTests.cpp --- a/clang-tools-extra/clangd/unittests/HoverTests.cpp +++ b/clang-tools-extra/clangd/unittests/HoverTests.cpp @@ -696,6 +696,51 @@ HI.Parameters->back().Name = "v"; HI.AccessSpecifier = "public"; }}, + {// Extra info for function call. + R"cpp( + void fun(int arg_a, int &arg_b) {}; + void code() { + int a = 1, b = 2; + fun(a, [[^b]]); + } + )cpp", + [](HoverInfo &HI) { + HI.Name = "b"; + HI.Kind = index::SymbolKind::Variable; + HI.NamespaceScope = ""; + HI.Definition = "int b = 2"; + HI.LocalScope = "code::"; + HI.Value = "2"; + HI.Type = "int"; + HI.CalleeArgInfo.emplace(); + HI.CalleeArgInfo->Name = "arg_b"; + HI.CalleeArgInfo->Type = "int &"; + }}, + {// Extra info for method call. + R"cpp( + class C { + public: + void fun(int arg_a = 3, int arg_b = 4) {} + }; + void code() { + int a = 1, b = 2; + C c; + c.fun([[^a]], b); + } + )cpp", + [](HoverInfo &HI) { + HI.Name = "a"; + HI.Kind = index::SymbolKind::Variable; + HI.NamespaceScope = ""; + HI.Definition = "int a = 1"; + HI.LocalScope = "code::"; + HI.Value = "1"; + HI.Type = "int"; + HI.CalleeArgInfo.emplace(); + HI.CalleeArgInfo->Name = "arg_a"; + HI.CalleeArgInfo->Type = "int"; + HI.CalleeArgInfo->Default = "3"; + }}, }; for (const auto &Case : Cases) { SCOPED_TRACE(Case.Code); @@ -729,6 +774,7 @@ EXPECT_EQ(H->Size, Expected.Size); EXPECT_EQ(H->Offset, Expected.Offset); EXPECT_EQ(H->AccessSpecifier, Expected.AccessSpecifier); + EXPECT_EQ(H->CalleeArgInfo, Expected.CalleeArgInfo); } } @@ -2022,6 +2068,29 @@ // In namespace ns1 private: union foo {})", + }, + { + [](HoverInfo &HI) { + HI.Kind = index::SymbolKind::Variable; + HI.Name = "foo"; + HI.Definition = "int foo = 3"; + HI.LocalScope = "test::Bar::"; + HI.Value = "3"; + HI.Type = "int"; + HI.CalleeArgInfo.emplace(); + HI.CalleeArgInfo->Name = "arg_a"; + HI.CalleeArgInfo->Type = "int"; + HI.CalleeArgInfo->Default = "7"; + }, + R"(variable foo + +Type: int +Value = 3 + +// In test::Bar +int foo = 3 + +Passed as int arg_a = 7)", }}; for (const auto &C : Cases) {