mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-25 08:13:41 +00:00
Merge #5413
5413: Semantical call info r=matklad a=matklad Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com>
This commit is contained in:
commit
081596dd58
@ -1,5 +1,5 @@
|
||||
//! FIXME: write short doc here
|
||||
use std::sync::Arc;
|
||||
use std::{iter, sync::Arc};
|
||||
|
||||
use arrayvec::ArrayVec;
|
||||
use either::Either;
|
||||
@ -12,6 +12,7 @@ use hir_def::{
|
||||
import_map,
|
||||
per_ns::PerNs,
|
||||
resolver::{HasResolver, Resolver},
|
||||
src::HasSource as _,
|
||||
type_ref::{Mutability, TypeRef},
|
||||
AdtId, AssocContainerId, ConstId, DefWithBodyId, EnumId, FunctionId, GenericDefId, HasModule,
|
||||
ImplId, LocalEnumVariantId, LocalFieldId, LocalModuleId, Lookup, ModuleId, StaticId, StructId,
|
||||
@ -25,8 +26,8 @@ use hir_expand::{
|
||||
use hir_ty::{
|
||||
autoderef,
|
||||
display::{HirDisplayError, HirFormatter},
|
||||
method_resolution, ApplicationTy, Canonical, GenericPredicate, InEnvironment, Substs,
|
||||
TraitEnvironment, Ty, TyDefId, TypeCtor,
|
||||
method_resolution, ApplicationTy, CallableDefId, Canonical, FnSig, GenericPredicate,
|
||||
InEnvironment, Substs, TraitEnvironment, Ty, TyDefId, TypeCtor,
|
||||
};
|
||||
use ra_db::{CrateId, Edition, FileId};
|
||||
use ra_prof::profile;
|
||||
@ -40,7 +41,7 @@ use stdx::impl_from;
|
||||
use crate::{
|
||||
db::{DefDatabase, HirDatabase},
|
||||
has_source::HasSource,
|
||||
CallableDefId, HirDisplay, InFile, Name,
|
||||
HirDisplay, InFile, Name,
|
||||
};
|
||||
|
||||
/// hir::Crate describes a single crate. It's the main interface with which
|
||||
@ -1168,6 +1169,12 @@ impl Type {
|
||||
Type::new(db, krate, def, ty)
|
||||
}
|
||||
|
||||
pub fn is_unit(&self) -> bool {
|
||||
matches!(
|
||||
self.ty.value,
|
||||
Ty::Apply(ApplicationTy { ctor: TypeCtor::Tuple { cardinality: 0 }, .. })
|
||||
)
|
||||
}
|
||||
pub fn is_bool(&self) -> bool {
|
||||
matches!(self.ty.value, Ty::Apply(ApplicationTy { ctor: TypeCtor::Bool, .. }))
|
||||
}
|
||||
@ -1225,9 +1232,10 @@ impl Type {
|
||||
db.trait_solve(self.krate, goal).is_some()
|
||||
}
|
||||
|
||||
// FIXME: this method is broken, as it doesn't take closures into account.
|
||||
pub fn as_callable(&self) -> Option<CallableDefId> {
|
||||
Some(self.ty.value.as_callable()?.0)
|
||||
pub fn as_callable(&self, db: &dyn HirDatabase) -> Option<Callable> {
|
||||
let (id, substs) = self.ty.value.as_callable()?;
|
||||
let sig = db.callable_item_signature(id).subst(substs);
|
||||
Some(Callable { ty: self.clone(), sig, id, is_bound_method: false })
|
||||
}
|
||||
|
||||
pub fn is_closure(&self) -> bool {
|
||||
@ -1512,6 +1520,70 @@ impl HirDisplay for Type {
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: closures
|
||||
#[derive(Debug)]
|
||||
pub struct Callable {
|
||||
ty: Type,
|
||||
sig: FnSig,
|
||||
id: CallableDefId,
|
||||
pub(crate) is_bound_method: bool,
|
||||
}
|
||||
|
||||
pub enum CallableKind {
|
||||
Function(Function),
|
||||
TupleStruct(Struct),
|
||||
TupleEnumVariant(EnumVariant),
|
||||
}
|
||||
|
||||
impl Callable {
|
||||
pub fn kind(&self) -> CallableKind {
|
||||
match self.id {
|
||||
CallableDefId::FunctionId(it) => CallableKind::Function(it.into()),
|
||||
CallableDefId::StructId(it) => CallableKind::TupleStruct(it.into()),
|
||||
CallableDefId::EnumVariantId(it) => CallableKind::TupleEnumVariant(it.into()),
|
||||
}
|
||||
}
|
||||
pub fn receiver_param(&self, db: &dyn HirDatabase) -> Option<ast::SelfParam> {
|
||||
let func = match self.id {
|
||||
CallableDefId::FunctionId(it) if self.is_bound_method => it,
|
||||
_ => return None,
|
||||
};
|
||||
let src = func.lookup(db.upcast()).source(db.upcast());
|
||||
let param_list = src.value.param_list()?;
|
||||
param_list.self_param()
|
||||
}
|
||||
pub fn params(
|
||||
&self,
|
||||
db: &dyn HirDatabase,
|
||||
) -> Vec<(Option<Either<ast::SelfParam, ast::Pat>>, Type)> {
|
||||
let types = self
|
||||
.sig
|
||||
.params()
|
||||
.iter()
|
||||
.skip(if self.is_bound_method { 1 } else { 0 })
|
||||
.map(|ty| self.ty.derived(ty.clone()));
|
||||
let patterns = match self.id {
|
||||
CallableDefId::FunctionId(func) => {
|
||||
let src = func.lookup(db.upcast()).source(db.upcast());
|
||||
src.value.param_list().map(|param_list| {
|
||||
param_list
|
||||
.self_param()
|
||||
.map(|it| Some(Either::Left(it)))
|
||||
.filter(|_| !self.is_bound_method)
|
||||
.into_iter()
|
||||
.chain(param_list.params().map(|it| it.pat().map(Either::Right)))
|
||||
})
|
||||
}
|
||||
CallableDefId::StructId(_) => None,
|
||||
CallableDefId::EnumVariantId(_) => None,
|
||||
};
|
||||
patterns.into_iter().flatten().chain(iter::repeat(None)).zip(types).collect()
|
||||
}
|
||||
pub fn return_type(&self) -> Type {
|
||||
self.ty.derived(self.sig.ret().clone())
|
||||
}
|
||||
}
|
||||
|
||||
/// For IDE only
|
||||
#[derive(Debug)]
|
||||
pub enum ScopeDef {
|
||||
|
@ -32,10 +32,10 @@ mod has_source;
|
||||
|
||||
pub use crate::{
|
||||
code_model::{
|
||||
Adt, AsAssocItem, AssocItem, AssocItemContainer, AttrDef, Const, Crate, CrateDependency,
|
||||
DefWithBody, Docs, Enum, EnumVariant, Field, FieldSource, Function, GenericDef, HasAttrs,
|
||||
HasVisibility, ImplDef, Local, MacroDef, Module, ModuleDef, ScopeDef, Static, Struct,
|
||||
Trait, Type, TypeAlias, TypeParam, Union, VariantDef, Visibility,
|
||||
Adt, AsAssocItem, AssocItem, AssocItemContainer, AttrDef, Callable, CallableKind, Const,
|
||||
Crate, CrateDependency, DefWithBody, Docs, Enum, EnumVariant, Field, FieldSource, Function,
|
||||
GenericDef, HasAttrs, HasVisibility, ImplDef, Local, MacroDef, Module, ModuleDef, ScopeDef,
|
||||
Static, Struct, Trait, Type, TypeAlias, TypeParam, Union, VariantDef, Visibility,
|
||||
},
|
||||
has_source::HasSource,
|
||||
semantics::{original_range, PathResolution, Semantics, SemanticsScope},
|
||||
@ -52,7 +52,8 @@ pub use hir_def::{
|
||||
type_ref::Mutability,
|
||||
};
|
||||
pub use hir_expand::{
|
||||
hygiene::Hygiene, name::Name, HirFileId, InFile, MacroCallId, MacroCallLoc, MacroDefId,
|
||||
hygiene::Hygiene, name::Name, HirFileId, InFile, MacroCallId, MacroCallLoc,
|
||||
MacroDefId, /* FIXME */
|
||||
MacroFile, Origin,
|
||||
};
|
||||
pub use hir_ty::{display::HirDisplay, CallableDefId};
|
||||
pub use hir_ty::display::HirDisplay;
|
||||
|
@ -6,7 +6,7 @@ use std::{cell::RefCell, fmt, iter::successors};
|
||||
|
||||
use hir_def::{
|
||||
resolver::{self, HasResolver, Resolver},
|
||||
AsMacroCall, TraitId, VariantId,
|
||||
AsMacroCall, FunctionId, TraitId, VariantId,
|
||||
};
|
||||
use hir_expand::{diagnostics::AstDiagnostic, hygiene::Hygiene, ExpansionInfo};
|
||||
use hir_ty::associated_type_shorthand_candidates;
|
||||
@ -24,8 +24,8 @@ use crate::{
|
||||
diagnostics::Diagnostic,
|
||||
semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx},
|
||||
source_analyzer::{resolve_hir_path, resolve_hir_path_qualifier, SourceAnalyzer},
|
||||
AssocItem, Field, Function, HirFileId, ImplDef, InFile, Local, MacroDef, Module, ModuleDef,
|
||||
Name, Origin, Path, ScopeDef, Trait, Type, TypeAlias, TypeParam, VariantDef,
|
||||
AssocItem, Callable, Field, Function, HirFileId, ImplDef, InFile, Local, MacroDef, Module,
|
||||
ModuleDef, Name, Origin, Path, ScopeDef, Trait, Type, TypeAlias, TypeParam, VariantDef,
|
||||
};
|
||||
use resolver::TypeNs;
|
||||
|
||||
@ -197,7 +197,11 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
|
||||
}
|
||||
|
||||
pub fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<Function> {
|
||||
self.imp.resolve_method_call(call)
|
||||
self.imp.resolve_method_call(call).map(Function::from)
|
||||
}
|
||||
|
||||
pub fn resolve_method_call_as_callable(&self, call: &ast::MethodCallExpr) -> Option<Callable> {
|
||||
self.imp.resolve_method_call_as_callable(call)
|
||||
}
|
||||
|
||||
pub fn resolve_field(&self, field: &ast::FieldExpr) -> Option<Field> {
|
||||
@ -385,10 +389,21 @@ impl<'db> SemanticsImpl<'db> {
|
||||
self.analyze(param.syntax()).type_of_self(self.db, ¶m)
|
||||
}
|
||||
|
||||
fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<Function> {
|
||||
fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<FunctionId> {
|
||||
self.analyze(call.syntax()).resolve_method_call(self.db, call)
|
||||
}
|
||||
|
||||
fn resolve_method_call_as_callable(&self, call: &ast::MethodCallExpr) -> Option<Callable> {
|
||||
// FIXME: this erases Substs
|
||||
let func = self.resolve_method_call(call)?;
|
||||
let ty = self.db.value_ty(func.into());
|
||||
let resolver = self.analyze(call.syntax()).resolver;
|
||||
let ty = Type::new_with_resolver(self.db, &resolver, ty.value)?;
|
||||
let mut res = ty.as_callable(self.db)?;
|
||||
res.is_bound_method = true;
|
||||
Some(res)
|
||||
}
|
||||
|
||||
fn resolve_field(&self, field: &ast::FieldExpr) -> Option<Field> {
|
||||
self.analyze(field.syntax()).resolve_field(self.db, field)
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ use hir_def::{
|
||||
},
|
||||
expr::{ExprId, Pat, PatId},
|
||||
resolver::{resolver_for_scope, Resolver, TypeNs, ValueNs},
|
||||
AsMacroCall, DefWithBodyId, FieldId, LocalFieldId, VariantId,
|
||||
AsMacroCall, DefWithBodyId, FieldId, FunctionId, LocalFieldId, VariantId,
|
||||
};
|
||||
use hir_expand::{hygiene::Hygiene, name::AsName, HirFileId, InFile};
|
||||
use hir_ty::{
|
||||
@ -142,9 +142,9 @@ impl SourceAnalyzer {
|
||||
&self,
|
||||
db: &dyn HirDatabase,
|
||||
call: &ast::MethodCallExpr,
|
||||
) -> Option<Function> {
|
||||
) -> Option<FunctionId> {
|
||||
let expr_id = self.expr_id(db, &call.clone().into())?;
|
||||
self.infer.as_ref()?.method_resolution(expr_id).map(Function::from)
|
||||
self.infer.as_ref()?.method_resolution(expr_id)
|
||||
}
|
||||
|
||||
pub(crate) fn resolve_field(
|
||||
|
@ -226,7 +226,15 @@ impl CrateDefMap {
|
||||
match enum_data.variant(&segment) {
|
||||
Some(local_id) => {
|
||||
let variant = EnumVariantId { parent: e, local_id };
|
||||
PerNs::both(variant.into(), variant.into(), Visibility::Public)
|
||||
match &*enum_data.variants[local_id].variant_data {
|
||||
crate::adt::VariantData::Record(_) => {
|
||||
PerNs::types(variant.into(), Visibility::Public)
|
||||
}
|
||||
crate::adt::VariantData::Tuple(_)
|
||||
| crate::adt::VariantData::Unit => {
|
||||
PerNs::both(variant.into(), variant.into(), Visibility::Public)
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {
|
||||
return ResolvePathResult::with(
|
||||
|
@ -95,9 +95,9 @@ pub(crate) fn outgoing_calls(db: &RootDatabase, position: FilePosition) -> Optio
|
||||
if let Some(func_target) = match &call_node {
|
||||
FnCallNode::CallExpr(expr) => {
|
||||
//FIXME: Type::as_callable is broken
|
||||
let callable_def = sema.type_of_expr(&expr.expr()?)?.as_callable()?;
|
||||
match callable_def {
|
||||
hir::CallableDefId::FunctionId(it) => {
|
||||
let callable = sema.type_of_expr(&expr.expr()?)?.as_callable(db)?;
|
||||
match callable.kind() {
|
||||
hir::CallableKind::Function(it) => {
|
||||
let fn_def: hir::Function = it.into();
|
||||
let nav = fn_def.to_nav(db);
|
||||
Some(nav)
|
||||
@ -109,10 +109,6 @@ pub(crate) fn outgoing_calls(db: &RootDatabase, position: FilePosition) -> Optio
|
||||
let function = sema.resolve_method_call(&expr)?;
|
||||
Some(function.to_nav(db))
|
||||
}
|
||||
FnCallNode::MacroCallExpr(macro_call) => {
|
||||
let macro_def = sema.resolve_macro_call(¯o_call)?;
|
||||
Some(macro_def.to_nav(db))
|
||||
}
|
||||
} {
|
||||
Some((func_target, name_ref.syntax().text_range()))
|
||||
} else {
|
||||
|
@ -1,20 +1,42 @@
|
||||
//! FIXME: write short doc here
|
||||
use hir::Semantics;
|
||||
use either::Either;
|
||||
use hir::{Docs, HirDisplay, Semantics, Type};
|
||||
use ra_ide_db::RootDatabase;
|
||||
use ra_syntax::{
|
||||
ast::{self, ArgListOwner},
|
||||
match_ast, AstNode, SyntaxNode, SyntaxToken,
|
||||
match_ast, AstNode, SyntaxNode, SyntaxToken, TextRange, TextSize,
|
||||
};
|
||||
use stdx::format_to;
|
||||
use test_utils::mark;
|
||||
|
||||
use crate::{FilePosition, FunctionSignature};
|
||||
use crate::FilePosition;
|
||||
|
||||
/// Contains information about a call site. Specifically the
|
||||
/// `FunctionSignature`and current parameter.
|
||||
#[derive(Debug)]
|
||||
pub struct CallInfo {
|
||||
pub signature: FunctionSignature,
|
||||
pub doc: Option<String>,
|
||||
pub signature: String,
|
||||
pub active_parameter: Option<usize>,
|
||||
parameters: Vec<TextRange>,
|
||||
}
|
||||
|
||||
impl CallInfo {
|
||||
pub fn parameter_labels(&self) -> impl Iterator<Item = &str> + '_ {
|
||||
self.parameters.iter().map(move |&it| &self.signature[it])
|
||||
}
|
||||
pub fn parameter_ranges(&self) -> &[TextRange] {
|
||||
&self.parameters
|
||||
}
|
||||
fn push_param(&mut self, param: &str) {
|
||||
if !self.signature.ends_with('(') {
|
||||
self.signature.push_str(", ");
|
||||
}
|
||||
let start = TextSize::of(&self.signature);
|
||||
self.signature.push_str(param);
|
||||
let end = TextSize::of(&self.signature);
|
||||
self.parameters.push(TextRange::new(start, end))
|
||||
}
|
||||
}
|
||||
|
||||
/// Computes parameter information for the given call expression.
|
||||
@ -24,105 +46,130 @@ pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<Cal
|
||||
let file = file.syntax();
|
||||
let token = file.token_at_offset(position.offset).next()?;
|
||||
let token = sema.descend_into_macros(token);
|
||||
call_info_for_token(&sema, token)
|
||||
|
||||
let (callable, active_parameter) = call_info_impl(&sema, token)?;
|
||||
|
||||
let mut res =
|
||||
CallInfo { doc: None, signature: String::new(), parameters: vec![], active_parameter };
|
||||
|
||||
match callable.kind() {
|
||||
hir::CallableKind::Function(func) => {
|
||||
res.doc = func.docs(db).map(|it| it.as_str().to_string());
|
||||
format_to!(res.signature, "fn {}", func.name(db));
|
||||
}
|
||||
hir::CallableKind::TupleStruct(strukt) => {
|
||||
res.doc = strukt.docs(db).map(|it| it.as_str().to_string());
|
||||
format_to!(res.signature, "struct {}", strukt.name(db));
|
||||
}
|
||||
hir::CallableKind::TupleEnumVariant(variant) => {
|
||||
res.doc = variant.docs(db).map(|it| it.as_str().to_string());
|
||||
format_to!(
|
||||
res.signature,
|
||||
"enum {}::{}",
|
||||
variant.parent_enum(db).name(db),
|
||||
variant.name(db)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
res.signature.push('(');
|
||||
{
|
||||
if let Some(self_param) = callable.receiver_param(db) {
|
||||
format_to!(res.signature, "{}", self_param)
|
||||
}
|
||||
let mut buf = String::new();
|
||||
for (pat, ty) in callable.params(db) {
|
||||
buf.clear();
|
||||
if let Some(pat) = pat {
|
||||
match pat {
|
||||
Either::Left(_self) => format_to!(buf, "self: "),
|
||||
Either::Right(pat) => format_to!(buf, "{}: ", pat),
|
||||
}
|
||||
}
|
||||
format_to!(buf, "{}", ty.display(db));
|
||||
res.push_param(&buf);
|
||||
}
|
||||
}
|
||||
res.signature.push(')');
|
||||
|
||||
match callable.kind() {
|
||||
hir::CallableKind::Function(_) => {
|
||||
let ret_type = callable.return_type();
|
||||
if !ret_type.is_unit() {
|
||||
format_to!(res.signature, " -> {}", ret_type.display(db));
|
||||
}
|
||||
}
|
||||
hir::CallableKind::TupleStruct(_) | hir::CallableKind::TupleEnumVariant(_) => {}
|
||||
}
|
||||
Some(res)
|
||||
}
|
||||
|
||||
fn call_info_impl(
|
||||
sema: &Semantics<RootDatabase>,
|
||||
token: SyntaxToken,
|
||||
) -> Option<(hir::Callable, Option<usize>)> {
|
||||
// Find the calling expression and it's NameRef
|
||||
let calling_node = FnCallNode::with_node(&token.parent())?;
|
||||
|
||||
let callable = match &calling_node {
|
||||
FnCallNode::CallExpr(call) => sema.type_of_expr(&call.expr()?)?.as_callable(sema.db)?,
|
||||
FnCallNode::MethodCallExpr(call) => sema.resolve_method_call_as_callable(call)?,
|
||||
};
|
||||
let active_param = if let Some(arg_list) = calling_node.arg_list() {
|
||||
// Number of arguments specified at the call site
|
||||
let num_args_at_callsite = arg_list.args().count();
|
||||
|
||||
let arg_list_range = arg_list.syntax().text_range();
|
||||
if !arg_list_range.contains_inclusive(token.text_range().start()) {
|
||||
mark::hit!(call_info_bad_offset);
|
||||
return None;
|
||||
}
|
||||
let param = std::cmp::min(
|
||||
num_args_at_callsite,
|
||||
arg_list
|
||||
.args()
|
||||
.take_while(|arg| arg.syntax().text_range().end() <= token.text_range().start())
|
||||
.count(),
|
||||
);
|
||||
|
||||
Some(param)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Some((callable, active_param))
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct ActiveParameter {
|
||||
/// FIXME: should be `Type` and `Name
|
||||
pub(crate) ty: String,
|
||||
pub(crate) ty: Type,
|
||||
pub(crate) name: String,
|
||||
}
|
||||
|
||||
impl ActiveParameter {
|
||||
pub(crate) fn at(db: &RootDatabase, position: FilePosition) -> Option<Self> {
|
||||
call_info(db, position)?.into_active_parameter()
|
||||
let sema = Semantics::new(db);
|
||||
let file = sema.parse(position.file_id);
|
||||
let file = file.syntax();
|
||||
let token = file.token_at_offset(position.offset).next()?;
|
||||
let token = sema.descend_into_macros(token);
|
||||
Self::at_token(&sema, token)
|
||||
}
|
||||
|
||||
pub(crate) fn at_token(sema: &Semantics<RootDatabase>, token: SyntaxToken) -> Option<Self> {
|
||||
call_info_for_token(sema, token)?.into_active_parameter()
|
||||
let (signature, active_parameter) = call_info_impl(&sema, token)?;
|
||||
|
||||
let idx = active_parameter?;
|
||||
let mut params = signature.params(sema.db);
|
||||
let (pat, ty) = params.swap_remove(idx);
|
||||
let name = pat?.to_string();
|
||||
Some(ActiveParameter { ty, name })
|
||||
}
|
||||
}
|
||||
|
||||
fn call_info_for_token(sema: &Semantics<RootDatabase>, token: SyntaxToken) -> Option<CallInfo> {
|
||||
// Find the calling expression and it's NameRef
|
||||
let calling_node = FnCallNode::with_node(&token.parent())?;
|
||||
|
||||
let signature = match &calling_node {
|
||||
FnCallNode::CallExpr(call) => {
|
||||
//FIXME: Type::as_callable is broken
|
||||
let callable_def = sema.type_of_expr(&call.expr()?)?.as_callable()?;
|
||||
match callable_def {
|
||||
hir::CallableDefId::FunctionId(it) => {
|
||||
let fn_def = it.into();
|
||||
FunctionSignature::from_hir(sema.db, fn_def)
|
||||
}
|
||||
hir::CallableDefId::StructId(it) => {
|
||||
FunctionSignature::from_struct(sema.db, it.into())?
|
||||
}
|
||||
hir::CallableDefId::EnumVariantId(it) => {
|
||||
FunctionSignature::from_enum_variant(sema.db, it.into())?
|
||||
}
|
||||
}
|
||||
}
|
||||
FnCallNode::MethodCallExpr(method_call) => {
|
||||
let function = sema.resolve_method_call(&method_call)?;
|
||||
FunctionSignature::from_hir(sema.db, function)
|
||||
}
|
||||
FnCallNode::MacroCallExpr(macro_call) => {
|
||||
let macro_def = sema.resolve_macro_call(¯o_call)?;
|
||||
FunctionSignature::from_macro(sema.db, macro_def)?
|
||||
}
|
||||
};
|
||||
|
||||
// If we have a calling expression let's find which argument we are on
|
||||
let num_params = signature.parameters.len();
|
||||
|
||||
let active_parameter = match num_params {
|
||||
0 => None,
|
||||
1 if signature.has_self_param => None,
|
||||
1 => Some(0),
|
||||
_ => {
|
||||
if let Some(arg_list) = calling_node.arg_list() {
|
||||
// Number of arguments specified at the call site
|
||||
let num_args_at_callsite = arg_list.args().count();
|
||||
|
||||
let arg_list_range = arg_list.syntax().text_range();
|
||||
if !arg_list_range.contains_inclusive(token.text_range().start()) {
|
||||
mark::hit!(call_info_bad_offset);
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut param = std::cmp::min(
|
||||
num_args_at_callsite,
|
||||
arg_list
|
||||
.args()
|
||||
.take_while(|arg| {
|
||||
arg.syntax().text_range().end() <= token.text_range().start()
|
||||
})
|
||||
.count(),
|
||||
);
|
||||
|
||||
// If we are in a method account for `self`
|
||||
if signature.has_self_param {
|
||||
param += 1;
|
||||
}
|
||||
|
||||
Some(param)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Some(CallInfo { signature, active_parameter })
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum FnCallNode {
|
||||
CallExpr(ast::CallExpr),
|
||||
MethodCallExpr(ast::MethodCallExpr),
|
||||
MacroCallExpr(ast::MacroCall),
|
||||
}
|
||||
|
||||
impl FnCallNode {
|
||||
@ -138,7 +185,6 @@ impl FnCallNode {
|
||||
}
|
||||
Some(FnCallNode::MethodCallExpr(it))
|
||||
},
|
||||
ast::MacroCall(it) => Some(FnCallNode::MacroCallExpr(it)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@ -150,7 +196,6 @@ impl FnCallNode {
|
||||
match node {
|
||||
ast::CallExpr(it) => Some(FnCallNode::CallExpr(it)),
|
||||
ast::MethodCallExpr(it) => Some(FnCallNode::MethodCallExpr(it)),
|
||||
ast::MacroCall(it) => Some(FnCallNode::MacroCallExpr(it)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@ -166,8 +211,6 @@ impl FnCallNode {
|
||||
FnCallNode::MethodCallExpr(call_expr) => {
|
||||
call_expr.syntax().children().filter_map(ast::NameRef::cast).next()
|
||||
}
|
||||
|
||||
FnCallNode::MacroCallExpr(call_expr) => call_expr.path()?.segment()?.name_ref(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -175,21 +218,10 @@ impl FnCallNode {
|
||||
match self {
|
||||
FnCallNode::CallExpr(expr) => expr.arg_list(),
|
||||
FnCallNode::MethodCallExpr(expr) => expr.arg_list(),
|
||||
FnCallNode::MacroCallExpr(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CallInfo {
|
||||
fn into_active_parameter(self) -> Option<ActiveParameter> {
|
||||
let idx = self.active_parameter?;
|
||||
let ty = self.signature.parameter_types.get(idx)?.clone();
|
||||
let name = self.signature.parameter_names.get(idx)?.clone();
|
||||
let res = ActiveParameter { ty, name };
|
||||
Some(res)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use expect::{expect, Expect};
|
||||
@ -202,20 +234,18 @@ mod tests {
|
||||
let call_info = analysis.call_info(position).unwrap();
|
||||
let actual = match call_info {
|
||||
Some(call_info) => {
|
||||
let docs = match &call_info.signature.doc {
|
||||
let docs = match &call_info.doc {
|
||||
None => "".to_string(),
|
||||
Some(docs) => format!("{}\n------\n", docs.as_str()),
|
||||
};
|
||||
let params = call_info
|
||||
.signature
|
||||
.parameters
|
||||
.iter()
|
||||
.parameter_labels()
|
||||
.enumerate()
|
||||
.map(|(i, param)| {
|
||||
if Some(i) == call_info.active_parameter {
|
||||
format!("<{}>", param)
|
||||
} else {
|
||||
param.clone()
|
||||
param.to_string()
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
@ -296,10 +326,8 @@ fn foo<T, U: Copy + Display>(x: T, y: U) -> u32
|
||||
fn bar() { foo(<|>3, ); }
|
||||
"#,
|
||||
expect![[r#"
|
||||
fn foo<T, U: Copy + Display>(x: T, y: U) -> u32
|
||||
where T: Copy + Display,
|
||||
U: Debug
|
||||
(<x: T>, y: U)
|
||||
fn foo(x: i32, y: {unknown}) -> u32
|
||||
(<x: i32>, y: {unknown})
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
@ -312,8 +340,7 @@ fn foo<T>() -> T where T: Copy + Display {}
|
||||
fn bar() { foo(<|>); }
|
||||
"#,
|
||||
expect![[r#"
|
||||
fn foo<T>() -> T
|
||||
where T: Copy + Display
|
||||
fn foo() -> {unknown}
|
||||
()
|
||||
"#]],
|
||||
);
|
||||
@ -323,11 +350,14 @@ fn bar() { foo(<|>); }
|
||||
fn test_fn_signature_for_impl() {
|
||||
check(
|
||||
r#"
|
||||
struct F; impl F { pub fn new() { F{}} }
|
||||
fn bar() {let _ : F = F::new(<|>);}
|
||||
struct F;
|
||||
impl F { pub fn new() { } }
|
||||
fn bar() {
|
||||
let _ : F = F::new(<|>);
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
pub fn new()
|
||||
fn new()
|
||||
()
|
||||
"#]],
|
||||
);
|
||||
@ -346,8 +376,8 @@ fn bar() {
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
pub fn do_it(&self)
|
||||
(&self)
|
||||
fn do_it(&self)
|
||||
()
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
@ -357,16 +387,33 @@ fn bar() {
|
||||
check(
|
||||
r#"
|
||||
struct S;
|
||||
impl S { pub fn do_it(&self, x: i32) {} }
|
||||
|
||||
fn bar() {
|
||||
let s: S = S;
|
||||
s.do_it(<|>);
|
||||
impl S {
|
||||
fn foo(&self, x: i32) {}
|
||||
}
|
||||
|
||||
fn main() { S.foo(<|>); }
|
||||
"#,
|
||||
expect![[r#"
|
||||
pub fn do_it(&self, x: i32)
|
||||
(&self, <x: i32>)
|
||||
fn foo(&self, x: i32)
|
||||
(<x: i32>)
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fn_signature_for_method_with_arg_as_assoc_fn() {
|
||||
check(
|
||||
r#"
|
||||
struct S;
|
||||
impl S {
|
||||
fn foo(&self, x: i32) {}
|
||||
}
|
||||
|
||||
fn main() { S::foo(<|>); }
|
||||
"#,
|
||||
expect![[r#"
|
||||
fn foo(self: &S, x: i32)
|
||||
(<self: &S>, x: i32)
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
@ -425,7 +472,7 @@ pub fn do() {
|
||||
assert_eq!(6, my_crate::add_one(5));
|
||||
```
|
||||
------
|
||||
pub fn add_one(x: i32) -> i32
|
||||
fn add_one(x: i32) -> i32
|
||||
(<x: i32>)
|
||||
"##]],
|
||||
);
|
||||
@ -467,7 +514,7 @@ pub fn do_it() {
|
||||
assert_eq!(6, my_crate::add_one(5));
|
||||
```
|
||||
------
|
||||
pub fn add_one(x: i32) -> i32
|
||||
fn add_one(x: i32) -> i32
|
||||
(<x: i32>)
|
||||
"##]],
|
||||
);
|
||||
@ -505,8 +552,8 @@ pub fn foo(mut r: WriteHandler<()>) {
|
||||
|
||||
By default this method stops actor's `Context`.
|
||||
------
|
||||
fn finished(&mut self, ctx: &mut Self::Context)
|
||||
(&mut self, <ctx: &mut Self::Context>)
|
||||
fn finished(&mut self, ctx: &mut {unknown})
|
||||
(<ctx: &mut {unknown}>)
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
@ -539,7 +586,7 @@ fn main() {
|
||||
"#,
|
||||
expect![[r#"
|
||||
fn bar(&self, _: u32)
|
||||
(&self, <_: u32>)
|
||||
(<_: u32>)
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
@ -549,15 +596,15 @@ fn main() {
|
||||
check(
|
||||
r#"
|
||||
/// A cool tuple struct
|
||||
struct TS(u32, i32);
|
||||
struct S(u32, i32);
|
||||
fn main() {
|
||||
let s = TS(0, <|>);
|
||||
let s = S(0, <|>);
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
A cool tuple struct
|
||||
------
|
||||
struct TS(u32, i32) -> TS
|
||||
struct S(u32, i32)
|
||||
(u32, <i32>)
|
||||
"#]],
|
||||
);
|
||||
@ -567,31 +614,18 @@ fn main() {
|
||||
fn generic_struct() {
|
||||
check(
|
||||
r#"
|
||||
struct TS<T>(T);
|
||||
struct S<T>(T);
|
||||
fn main() {
|
||||
let s = TS(<|>);
|
||||
let s = S(<|>);
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
struct TS<T>(T) -> TS
|
||||
(<T>)
|
||||
struct S({unknown})
|
||||
(<{unknown}>)
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cant_call_named_structs() {
|
||||
check(
|
||||
r#"
|
||||
struct TS { x: u32, y: i32 }
|
||||
fn main() {
|
||||
let s = TS(<|>);
|
||||
}
|
||||
"#,
|
||||
expect![[""]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn works_for_enum_variants() {
|
||||
check(
|
||||
@ -612,14 +646,27 @@ fn main() {
|
||||
expect![[r#"
|
||||
A Variant
|
||||
------
|
||||
E::A(0: i32)
|
||||
(<0: i32>)
|
||||
enum E::A(i32)
|
||||
(<i32>)
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cant_call_enum_records() {
|
||||
fn cant_call_struct_record() {
|
||||
check(
|
||||
r#"
|
||||
struct S { x: u32, y: i32 }
|
||||
fn main() {
|
||||
let s = S(<|>);
|
||||
}
|
||||
"#,
|
||||
expect![[""]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cant_call_enum_record() {
|
||||
check(
|
||||
r#"
|
||||
enum E {
|
||||
@ -639,28 +686,6 @@ fn main() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fn_signature_for_macro() {
|
||||
check(
|
||||
r#"
|
||||
/// empty macro
|
||||
macro_rules! foo {
|
||||
() => {}
|
||||
}
|
||||
|
||||
fn f() {
|
||||
foo!(<|>);
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
empty macro
|
||||
------
|
||||
foo!()
|
||||
()
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fn_signature_for_call_in_macro() {
|
||||
check(
|
||||
|
@ -43,7 +43,7 @@ use crate::{
|
||||
completion::{
|
||||
CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions,
|
||||
},
|
||||
display::FunctionSignature,
|
||||
display::function_signature::FunctionSignature,
|
||||
};
|
||||
|
||||
pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext) {
|
||||
|
@ -11,7 +11,7 @@ use crate::{
|
||||
completion_item::Builder, CompletionContext, CompletionItem, CompletionItemKind,
|
||||
CompletionKind, Completions,
|
||||
},
|
||||
display::{const_label, macro_label, type_label, FunctionSignature},
|
||||
display::{const_label, function_signature::FunctionSignature, macro_label, type_label},
|
||||
CompletionScore, RootDatabase,
|
||||
};
|
||||
|
||||
@ -329,15 +329,10 @@ pub(crate) fn compute_score(
|
||||
ty: &Type,
|
||||
name: &str,
|
||||
) -> Option<CompletionScore> {
|
||||
// FIXME: this should not fall back to string equality.
|
||||
let ty = &ty.display(ctx.db).to_string();
|
||||
let (active_name, active_type) = if let Some(record_field) = &ctx.record_field_syntax {
|
||||
mark::hit!(record_field_type_match);
|
||||
let (struct_field, _local) = ctx.sema.resolve_record_field(record_field)?;
|
||||
(
|
||||
struct_field.name(ctx.db).to_string(),
|
||||
struct_field.signature_ty(ctx.db).display(ctx.db).to_string(),
|
||||
)
|
||||
(struct_field.name(ctx.db).to_string(), struct_field.signature_ty(ctx.db))
|
||||
} else if let Some(active_parameter) = &ctx.active_parameter {
|
||||
mark::hit!(active_param_type_match);
|
||||
(active_parameter.name.clone(), active_parameter.ty.clone())
|
||||
|
@ -1,9 +1,8 @@
|
||||
//! This module contains utilities for turning SyntaxNodes and HIR types
|
||||
//! into types that may be used to render in a UI.
|
||||
|
||||
mod function_signature;
|
||||
pub(crate) mod function_signature;
|
||||
mod navigation_target;
|
||||
mod structure;
|
||||
mod short_label;
|
||||
|
||||
use ra_syntax::{
|
||||
@ -11,15 +10,13 @@ use ra_syntax::{
|
||||
SyntaxKind::{ATTR, COMMENT},
|
||||
};
|
||||
|
||||
pub use function_signature::FunctionSignature;
|
||||
pub use navigation_target::NavigationTarget;
|
||||
pub use structure::{file_structure, StructureNode};
|
||||
|
||||
pub(crate) use navigation_target::{ToNav, TryToNav};
|
||||
pub(crate) use short_label::ShortLabel;
|
||||
|
||||
pub use navigation_target::NavigationTarget;
|
||||
|
||||
pub(crate) fn function_label(node: &ast::FnDef) -> String {
|
||||
FunctionSignature::from(node).to_string()
|
||||
function_signature::FunctionSignature::from(node).to_string()
|
||||
}
|
||||
|
||||
pub(crate) fn const_label(node: &ast::ConstDef) -> String {
|
||||
|
@ -15,49 +15,48 @@ use stdx::{split_delim, SepBy};
|
||||
use crate::display::{generic_parameters, where_predicates};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum CallableKind {
|
||||
pub(crate) enum CallableKind {
|
||||
Function,
|
||||
StructConstructor,
|
||||
VariantConstructor,
|
||||
Macro,
|
||||
}
|
||||
|
||||
/// Contains information about a function signature
|
||||
#[derive(Debug)]
|
||||
pub struct FunctionSignature {
|
||||
pub kind: CallableKind,
|
||||
pub(crate) struct FunctionSignature {
|
||||
pub(crate) kind: CallableKind,
|
||||
/// Optional visibility
|
||||
pub visibility: Option<String>,
|
||||
pub(crate) visibility: Option<String>,
|
||||
/// Qualifiers like `async`, `unsafe`, ...
|
||||
pub qualifier: FunctionQualifier,
|
||||
pub(crate) qualifier: FunctionQualifier,
|
||||
/// Name of the function
|
||||
pub name: Option<String>,
|
||||
pub(crate) name: Option<String>,
|
||||
/// Documentation for the function
|
||||
pub doc: Option<Documentation>,
|
||||
pub(crate) doc: Option<Documentation>,
|
||||
/// Generic parameters
|
||||
pub generic_parameters: Vec<String>,
|
||||
pub(crate) generic_parameters: Vec<String>,
|
||||
/// Parameters of the function
|
||||
pub parameters: Vec<String>,
|
||||
pub(crate) parameters: Vec<String>,
|
||||
/// Parameter names of the function
|
||||
pub parameter_names: Vec<String>,
|
||||
pub(crate) parameter_names: Vec<String>,
|
||||
/// Parameter types of the function
|
||||
pub parameter_types: Vec<String>,
|
||||
pub(crate) parameter_types: Vec<String>,
|
||||
/// Optional return type
|
||||
pub ret_type: Option<String>,
|
||||
pub(crate) ret_type: Option<String>,
|
||||
/// Where predicates
|
||||
pub where_predicates: Vec<String>,
|
||||
pub(crate) where_predicates: Vec<String>,
|
||||
/// Self param presence
|
||||
pub has_self_param: bool,
|
||||
pub(crate) has_self_param: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct FunctionQualifier {
|
||||
pub(crate) struct FunctionQualifier {
|
||||
// `async` and `const` are mutually exclusive. Do we need to enforcing it here?
|
||||
pub is_async: bool,
|
||||
pub is_const: bool,
|
||||
pub is_unsafe: bool,
|
||||
pub(crate) is_async: bool,
|
||||
pub(crate) is_const: bool,
|
||||
pub(crate) is_unsafe: bool,
|
||||
/// The string `extern ".."`
|
||||
pub extern_abi: Option<String>,
|
||||
pub(crate) extern_abi: Option<String>,
|
||||
}
|
||||
|
||||
impl FunctionSignature {
|
||||
@ -149,27 +148,6 @@ impl FunctionSignature {
|
||||
has_self_param: false,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn from_macro(db: &RootDatabase, macro_def: hir::MacroDef) -> Option<Self> {
|
||||
let node: ast::MacroCall = macro_def.source(db).value;
|
||||
|
||||
let params = vec![];
|
||||
|
||||
Some(FunctionSignature {
|
||||
kind: CallableKind::Macro,
|
||||
visibility: None,
|
||||
qualifier: Default::default(),
|
||||
name: node.name().map(|n| n.text().to_string()),
|
||||
ret_type: None,
|
||||
parameters: params,
|
||||
parameter_names: vec![],
|
||||
parameter_types: vec![],
|
||||
generic_parameters: vec![],
|
||||
where_predicates: vec![],
|
||||
doc: macro_def.docs(db),
|
||||
has_self_param: false,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&'_ ast::FnDef> for FunctionSignature {
|
||||
@ -298,7 +276,6 @@ impl Display for FunctionSignature {
|
||||
CallableKind::Function => write!(f, "fn {}", name)?,
|
||||
CallableKind::StructConstructor => write!(f, "struct {}", name)?,
|
||||
CallableKind::VariantConstructor => write!(f, "{}", name)?,
|
||||
CallableKind::Macro => write!(f, "{}!", name)?,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,10 +5,10 @@ use ra_syntax::{
|
||||
ast::{self, ArgListOwner, AstNode, TypeAscriptionOwner},
|
||||
match_ast, Direction, NodeOrToken, SmolStr, SyntaxKind, TextRange, T,
|
||||
};
|
||||
|
||||
use crate::{FileId, FunctionSignature};
|
||||
use stdx::to_lower_snake_case;
|
||||
|
||||
use crate::{display::function_signature::FunctionSignature, FileId};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct InlayHintsConfig {
|
||||
pub type_hints: bool,
|
||||
@ -322,15 +322,15 @@ fn get_fn_signature(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<
|
||||
match expr {
|
||||
ast::Expr::CallExpr(expr) => {
|
||||
// FIXME: Type::as_callable is broken for closures
|
||||
let callable_def = sema.type_of_expr(&expr.expr()?)?.as_callable()?;
|
||||
match callable_def {
|
||||
hir::CallableDefId::FunctionId(it) => {
|
||||
let callable = sema.type_of_expr(&expr.expr()?)?.as_callable(sema.db)?;
|
||||
match callable.kind() {
|
||||
hir::CallableKind::Function(it) => {
|
||||
Some(FunctionSignature::from_hir(sema.db, it.into()))
|
||||
}
|
||||
hir::CallableDefId::StructId(it) => {
|
||||
hir::CallableKind::TupleStruct(it) => {
|
||||
FunctionSignature::from_struct(sema.db, it.into())
|
||||
}
|
||||
hir::CallableDefId::EnumVariantId(it) => {
|
||||
hir::CallableKind::TupleEnumVariant(it) => {
|
||||
FunctionSignature::from_enum_variant(sema.db, it.into())
|
||||
}
|
||||
}
|
||||
|
@ -19,29 +19,31 @@ pub mod mock_analysis;
|
||||
|
||||
mod markup;
|
||||
mod prime_caches;
|
||||
mod status;
|
||||
mod completion;
|
||||
mod runnables;
|
||||
mod goto_definition;
|
||||
mod goto_type_definition;
|
||||
mod goto_implementation;
|
||||
mod extend_selection;
|
||||
mod hover;
|
||||
mod display;
|
||||
|
||||
mod call_hierarchy;
|
||||
mod call_info;
|
||||
mod syntax_highlighting;
|
||||
mod completion;
|
||||
mod diagnostics;
|
||||
mod expand_macro;
|
||||
mod extend_selection;
|
||||
mod file_structure;
|
||||
mod folding_ranges;
|
||||
mod goto_definition;
|
||||
mod goto_implementation;
|
||||
mod goto_type_definition;
|
||||
mod hover;
|
||||
mod inlay_hints;
|
||||
mod join_lines;
|
||||
mod matching_brace;
|
||||
mod parent_module;
|
||||
mod references;
|
||||
mod diagnostics;
|
||||
mod syntax_tree;
|
||||
mod folding_ranges;
|
||||
mod join_lines;
|
||||
mod typing;
|
||||
mod matching_brace;
|
||||
mod display;
|
||||
mod inlay_hints;
|
||||
mod expand_macro;
|
||||
mod runnables;
|
||||
mod ssr;
|
||||
mod status;
|
||||
mod syntax_highlighting;
|
||||
mod syntax_tree;
|
||||
mod typing;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
@ -65,8 +67,9 @@ pub use crate::{
|
||||
CompletionConfig, CompletionItem, CompletionItemKind, CompletionScore, InsertTextFormat,
|
||||
},
|
||||
diagnostics::Severity,
|
||||
display::{file_structure, FunctionSignature, NavigationTarget, StructureNode},
|
||||
display::NavigationTarget,
|
||||
expand_macro::ExpandedMacro,
|
||||
file_structure::StructureNode,
|
||||
folding_ranges::{Fold, FoldKind},
|
||||
hover::{HoverAction, HoverConfig, HoverGotoTypeData, HoverResult},
|
||||
inlay_hints::{InlayHint, InlayHintsConfig, InlayKind},
|
||||
@ -323,7 +326,7 @@ impl Analysis {
|
||||
/// Returns a tree representation of symbols in the file. Useful to draw a
|
||||
/// file outline.
|
||||
pub fn file_structure(&self, file_id: FileId) -> Cancelable<Vec<StructureNode>> {
|
||||
self.with_db(|db| file_structure(&db.parse(file_id).tree()))
|
||||
self.with_db(|db| file_structure::file_structure(&db.parse(file_id).tree()))
|
||||
}
|
||||
|
||||
/// Returns a list of the places in the file where type hints can be displayed.
|
||||
|
@ -10,7 +10,7 @@ mod ssr;
|
||||
use std::io::Read;
|
||||
|
||||
use anyhow::Result;
|
||||
use ra_ide::{file_structure, Analysis};
|
||||
use ra_ide::Analysis;
|
||||
use ra_prof::profile;
|
||||
use ra_syntax::{AstNode, SourceFile};
|
||||
|
||||
@ -48,8 +48,10 @@ pub fn parse(no_dump: bool) -> Result<()> {
|
||||
}
|
||||
|
||||
pub fn symbols() -> Result<()> {
|
||||
let file = file()?;
|
||||
for s in file_structure(&file) {
|
||||
let text = read_stdin()?;
|
||||
let (analysis, file_id) = Analysis::from_single_file(text);
|
||||
let structure = analysis.file_structure(file_id).unwrap();
|
||||
for s in structure {
|
||||
println!("{:?}", s);
|
||||
}
|
||||
Ok(())
|
||||
|
@ -127,6 +127,7 @@ pub struct ClientCapsConfig {
|
||||
pub resolve_code_action: bool,
|
||||
pub hover_actions: bool,
|
||||
pub status_notification: bool,
|
||||
pub signature_help_label_offsets: bool,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
@ -302,6 +303,15 @@ impl Config {
|
||||
{
|
||||
self.client_caps.code_action_literals = value;
|
||||
}
|
||||
if let Some(value) = doc_caps
|
||||
.signature_help
|
||||
.as_ref()
|
||||
.and_then(|it| it.signature_information.as_ref())
|
||||
.and_then(|it| it.parameter_information.as_ref())
|
||||
.and_then(|it| it.label_offset_support)
|
||||
{
|
||||
self.client_caps.signature_help_label_offsets = value;
|
||||
}
|
||||
|
||||
self.completion.allow_snippets(false);
|
||||
if let Some(completion) = &doc_caps.completion {
|
||||
|
@ -553,21 +553,16 @@ pub(crate) fn handle_signature_help(
|
||||
let _p = profile("handle_signature_help");
|
||||
let position = from_proto::file_position(&snap, params.text_document_position_params)?;
|
||||
let call_info = match snap.analysis.call_info(position)? {
|
||||
None => return Ok(None),
|
||||
Some(it) => it,
|
||||
None => return Ok(None),
|
||||
};
|
||||
let concise = !snap.config.call_info_full;
|
||||
let mut active_parameter = call_info.active_parameter.map(|it| it as i64);
|
||||
if concise && call_info.signature.has_self_param {
|
||||
active_parameter = active_parameter.map(|it| it.saturating_sub(1));
|
||||
}
|
||||
let sig_info = to_proto::signature_information(call_info.signature, concise);
|
||||
|
||||
Ok(Some(lsp_types::SignatureHelp {
|
||||
signatures: vec![sig_info],
|
||||
active_signature: Some(0),
|
||||
active_parameter,
|
||||
}))
|
||||
let res = to_proto::signature_help(
|
||||
call_info,
|
||||
concise,
|
||||
snap.config.client_caps.signature_help_label_offsets,
|
||||
);
|
||||
Ok(Some(res))
|
||||
}
|
||||
|
||||
pub(crate) fn handle_hover(
|
||||
|
@ -4,8 +4,8 @@ use std::path::{self, Path};
|
||||
use itertools::Itertools;
|
||||
use ra_db::{FileId, FileRange};
|
||||
use ra_ide::{
|
||||
Assist, AssistKind, CompletionItem, CompletionItemKind, Documentation, FileSystemEdit, Fold,
|
||||
FoldKind, FunctionSignature, Highlight, HighlightModifier, HighlightTag, HighlightedRange,
|
||||
Assist, AssistKind, CallInfo, CompletionItem, CompletionItemKind, Documentation,
|
||||
FileSystemEdit, Fold, FoldKind, Highlight, HighlightModifier, HighlightTag, HighlightedRange,
|
||||
Indel, InlayHint, InlayKind, InsertTextFormat, LineIndex, Markup, NavigationTarget,
|
||||
ReferenceAccess, ResolvedAssist, Runnable, Severity, SourceChange, SourceFileEdit, TextEdit,
|
||||
};
|
||||
@ -219,29 +219,72 @@ pub(crate) fn completion_item(
|
||||
res
|
||||
}
|
||||
|
||||
pub(crate) fn signature_information(
|
||||
signature: FunctionSignature,
|
||||
pub(crate) fn signature_help(
|
||||
call_info: CallInfo,
|
||||
concise: bool,
|
||||
) -> lsp_types::SignatureInformation {
|
||||
let (label, documentation, params) = if concise {
|
||||
let mut params = signature.parameters;
|
||||
if signature.has_self_param {
|
||||
params.remove(0);
|
||||
label_offsets: bool,
|
||||
) -> lsp_types::SignatureHelp {
|
||||
let (label, parameters) = match (concise, label_offsets) {
|
||||
(_, false) => {
|
||||
let params = call_info
|
||||
.parameter_labels()
|
||||
.map(|label| lsp_types::ParameterInformation {
|
||||
label: lsp_types::ParameterLabel::Simple(label.to_string()),
|
||||
documentation: None,
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let label =
|
||||
if concise { call_info.parameter_labels().join(", ") } else { call_info.signature };
|
||||
(label, params)
|
||||
}
|
||||
(false, true) => {
|
||||
let params = call_info
|
||||
.parameter_ranges()
|
||||
.iter()
|
||||
.map(|it| [u32::from(it.start()).into(), u32::from(it.end()).into()])
|
||||
.map(|label_offsets| lsp_types::ParameterInformation {
|
||||
label: lsp_types::ParameterLabel::LabelOffsets(label_offsets),
|
||||
documentation: None,
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
(call_info.signature, params)
|
||||
}
|
||||
(true, true) => {
|
||||
let mut params = Vec::new();
|
||||
let mut label = String::new();
|
||||
let mut first = true;
|
||||
for param in call_info.parameter_labels() {
|
||||
if !first {
|
||||
label.push_str(", ");
|
||||
}
|
||||
first = false;
|
||||
let start = label.len() as u64;
|
||||
label.push_str(param);
|
||||
let end = label.len() as u64;
|
||||
params.push(lsp_types::ParameterInformation {
|
||||
label: lsp_types::ParameterLabel::LabelOffsets([start, end]),
|
||||
documentation: None,
|
||||
});
|
||||
}
|
||||
|
||||
(label, params)
|
||||
}
|
||||
(params.join(", "), None, params)
|
||||
} else {
|
||||
(signature.to_string(), signature.doc.map(documentation), signature.parameters)
|
||||
};
|
||||
|
||||
let parameters: Vec<lsp_types::ParameterInformation> = params
|
||||
.into_iter()
|
||||
.map(|param| lsp_types::ParameterInformation {
|
||||
label: lsp_types::ParameterLabel::Simple(param),
|
||||
documentation: None,
|
||||
let documentation = call_info.doc.map(|doc| {
|
||||
lsp_types::Documentation::MarkupContent(lsp_types::MarkupContent {
|
||||
kind: lsp_types::MarkupKind::Markdown,
|
||||
value: doc,
|
||||
})
|
||||
.collect();
|
||||
});
|
||||
|
||||
lsp_types::SignatureInformation { label, documentation, parameters: Some(parameters) }
|
||||
let signature =
|
||||
lsp_types::SignatureInformation { label, documentation, parameters: Some(parameters) };
|
||||
lsp_types::SignatureHelp {
|
||||
signatures: vec![signature],
|
||||
active_signature: None,
|
||||
active_parameter: call_info.active_parameter.map(|it| it as i64),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn inlay_int(line_index: &LineIndex, inlay_hint: InlayHint) -> lsp_ext::InlayHint {
|
||||
|
Loading…
Reference in New Issue
Block a user