Semantical call info

This commit is contained in:
Aleksey Kladov 2020-07-16 13:00:56 +02:00
parent 9210fcc076
commit ff0312fa32
12 changed files with 313 additions and 261 deletions

View file

@ -1,5 +1,5 @@
//! FIXME: write short doc here //! FIXME: write short doc here
use std::sync::Arc; use std::{iter, sync::Arc};
use arrayvec::ArrayVec; use arrayvec::ArrayVec;
use either::Either; use either::Either;
@ -12,6 +12,7 @@ use hir_def::{
import_map, import_map,
per_ns::PerNs, per_ns::PerNs,
resolver::{HasResolver, Resolver}, resolver::{HasResolver, Resolver},
src::HasSource as _,
type_ref::{Mutability, TypeRef}, type_ref::{Mutability, TypeRef},
AdtId, AssocContainerId, ConstId, DefWithBodyId, EnumId, FunctionId, GenericDefId, HasModule, AdtId, AssocContainerId, ConstId, DefWithBodyId, EnumId, FunctionId, GenericDefId, HasModule,
ImplId, LocalEnumVariantId, LocalFieldId, LocalModuleId, Lookup, ModuleId, StaticId, StructId, ImplId, LocalEnumVariantId, LocalFieldId, LocalModuleId, Lookup, ModuleId, StaticId, StructId,
@ -25,8 +26,8 @@ use hir_expand::{
use hir_ty::{ use hir_ty::{
autoderef, autoderef,
display::{HirDisplayError, HirFormatter}, display::{HirDisplayError, HirFormatter},
method_resolution, ApplicationTy, Canonical, GenericPredicate, InEnvironment, Substs, method_resolution, ApplicationTy, CallableDefId, Canonical, FnSig, GenericPredicate,
TraitEnvironment, Ty, TyDefId, TypeCtor, InEnvironment, Substs, TraitEnvironment, Ty, TyDefId, TypeCtor,
}; };
use ra_db::{CrateId, Edition, FileId}; use ra_db::{CrateId, Edition, FileId};
use ra_prof::profile; use ra_prof::profile;
@ -40,7 +41,7 @@ use stdx::impl_from;
use crate::{ use crate::{
db::{DefDatabase, HirDatabase}, db::{DefDatabase, HirDatabase},
has_source::HasSource, has_source::HasSource,
CallableDefId, HirDisplay, InFile, Name, HirDisplay, InFile, Name,
}; };
/// hir::Crate describes a single crate. It's the main interface with which /// 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) 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 { pub fn is_bool(&self) -> bool {
matches!(self.ty.value, Ty::Apply(ApplicationTy { ctor: TypeCtor::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() 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, db: &dyn HirDatabase) -> Option<Callable> {
pub fn as_callable(&self) -> Option<CallableDefId> { let (id, substs) = self.ty.value.as_callable()?;
Some(self.ty.value.as_callable()?.0) 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 { pub fn is_closure(&self) -> bool {
@ -1512,6 +1520,60 @@ 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<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(|it| it.params().map(|it| it.pat()))
}
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 /// For IDE only
#[derive(Debug)] #[derive(Debug)]
pub enum ScopeDef { pub enum ScopeDef {

View file

@ -32,10 +32,10 @@ mod has_source;
pub use crate::{ pub use crate::{
code_model::{ code_model::{
Adt, AsAssocItem, AssocItem, AssocItemContainer, AttrDef, Const, Crate, CrateDependency, Adt, AsAssocItem, AssocItem, AssocItemContainer, AttrDef, Callable, CallableKind, Const,
DefWithBody, Docs, Enum, EnumVariant, Field, FieldSource, Function, GenericDef, HasAttrs, Crate, CrateDependency, DefWithBody, Docs, Enum, EnumVariant, Field, FieldSource, Function,
HasVisibility, ImplDef, Local, MacroDef, Module, ModuleDef, ScopeDef, Static, Struct, GenericDef, HasAttrs, HasVisibility, ImplDef, Local, MacroDef, Module, ModuleDef, ScopeDef,
Trait, Type, TypeAlias, TypeParam, Union, VariantDef, Visibility, Static, Struct, Trait, Type, TypeAlias, TypeParam, Union, VariantDef, Visibility,
}, },
has_source::HasSource, has_source::HasSource,
semantics::{original_range, PathResolution, Semantics, SemanticsScope}, semantics::{original_range, PathResolution, Semantics, SemanticsScope},
@ -52,7 +52,8 @@ pub use hir_def::{
type_ref::Mutability, type_ref::Mutability,
}; };
pub use hir_expand::{ 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, MacroFile, Origin,
}; };
pub use hir_ty::{display::HirDisplay, CallableDefId}; pub use hir_ty::display::HirDisplay;

View file

@ -6,7 +6,7 @@ use std::{cell::RefCell, fmt, iter::successors};
use hir_def::{ use hir_def::{
resolver::{self, HasResolver, Resolver}, resolver::{self, HasResolver, Resolver},
AsMacroCall, TraitId, VariantId, AsMacroCall, FunctionId, TraitId, VariantId,
}; };
use hir_expand::{diagnostics::AstDiagnostic, hygiene::Hygiene, ExpansionInfo}; use hir_expand::{diagnostics::AstDiagnostic, hygiene::Hygiene, ExpansionInfo};
use hir_ty::associated_type_shorthand_candidates; use hir_ty::associated_type_shorthand_candidates;
@ -24,8 +24,8 @@ use crate::{
diagnostics::Diagnostic, diagnostics::Diagnostic,
semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx}, semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx},
source_analyzer::{resolve_hir_path, resolve_hir_path_qualifier, SourceAnalyzer}, source_analyzer::{resolve_hir_path, resolve_hir_path_qualifier, SourceAnalyzer},
AssocItem, Field, Function, HirFileId, ImplDef, InFile, Local, MacroDef, Module, ModuleDef, AssocItem, Callable, Field, Function, HirFileId, ImplDef, InFile, Local, MacroDef, Module,
Name, Origin, Path, ScopeDef, Trait, Type, TypeAlias, TypeParam, VariantDef, ModuleDef, Name, Origin, Path, ScopeDef, Trait, Type, TypeAlias, TypeParam, VariantDef,
}; };
use resolver::TypeNs; 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> { 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> { 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, &param) self.analyze(param.syntax()).type_of_self(self.db, &param)
} }
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) 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> { fn resolve_field(&self, field: &ast::FieldExpr) -> Option<Field> {
self.analyze(field.syntax()).resolve_field(self.db, field) self.analyze(field.syntax()).resolve_field(self.db, field)
} }

View file

@ -14,7 +14,7 @@ use hir_def::{
}, },
expr::{ExprId, Pat, PatId}, expr::{ExprId, Pat, PatId},
resolver::{resolver_for_scope, Resolver, TypeNs, ValueNs}, 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_expand::{hygiene::Hygiene, name::AsName, HirFileId, InFile};
use hir_ty::{ use hir_ty::{
@ -142,9 +142,9 @@ impl SourceAnalyzer {
&self, &self,
db: &dyn HirDatabase, db: &dyn HirDatabase,
call: &ast::MethodCallExpr, call: &ast::MethodCallExpr,
) -> Option<Function> { ) -> Option<FunctionId> {
let expr_id = self.expr_id(db, &call.clone().into())?; 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( pub(crate) fn resolve_field(

View file

@ -226,7 +226,15 @@ impl CrateDefMap {
match enum_data.variant(&segment) { match enum_data.variant(&segment) {
Some(local_id) => { Some(local_id) => {
let variant = EnumVariantId { parent: e, 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 => { None => {
return ResolvePathResult::with( return ResolvePathResult::with(

View file

@ -95,9 +95,9 @@ pub(crate) fn outgoing_calls(db: &RootDatabase, position: FilePosition) -> Optio
if let Some(func_target) = match &call_node { if let Some(func_target) = match &call_node {
FnCallNode::CallExpr(expr) => { FnCallNode::CallExpr(expr) => {
//FIXME: Type::as_callable is broken //FIXME: Type::as_callable is broken
let callable_def = sema.type_of_expr(&expr.expr()?)?.as_callable()?; let callable = sema.type_of_expr(&expr.expr()?)?.as_callable(db)?;
match callable_def { match callable.kind() {
hir::CallableDefId::FunctionId(it) => { hir::CallableKind::Function(it) => {
let fn_def: hir::Function = it.into(); let fn_def: hir::Function = it.into();
let nav = fn_def.to_nav(db); let nav = fn_def.to_nav(db);
Some(nav) Some(nav)
@ -109,10 +109,6 @@ pub(crate) fn outgoing_calls(db: &RootDatabase, position: FilePosition) -> Optio
let function = sema.resolve_method_call(&expr)?; let function = sema.resolve_method_call(&expr)?;
Some(function.to_nav(db)) Some(function.to_nav(db))
} }
FnCallNode::MacroCallExpr(macro_call) => {
let macro_def = sema.resolve_macro_call(&macro_call)?;
Some(macro_def.to_nav(db))
}
} { } {
Some((func_target, name_ref.syntax().text_range())) Some((func_target, name_ref.syntax().text_range()))
} else { } else {

View file

@ -1,20 +1,41 @@
//! FIXME: write short doc here //! FIXME: write short doc here
use hir::Semantics; use hir::{Docs, HirDisplay, Semantics, Type};
use ra_ide_db::RootDatabase; use ra_ide_db::RootDatabase;
use ra_syntax::{ use ra_syntax::{
ast::{self, ArgListOwner}, ast::{self, ArgListOwner},
match_ast, AstNode, SyntaxNode, SyntaxToken, match_ast, AstNode, SyntaxNode, SyntaxToken, TextRange, TextSize,
}; };
use stdx::format_to;
use test_utils::mark; use test_utils::mark;
use crate::{FilePosition, FunctionSignature}; use crate::FilePosition;
/// Contains information about a call site. Specifically the /// Contains information about a call site. Specifically the
/// `FunctionSignature`and current parameter. /// `FunctionSignature`and current parameter.
#[derive(Debug)] #[derive(Debug)]
pub struct CallInfo { pub struct CallInfo {
pub signature: FunctionSignature, pub doc: Option<String>,
pub signature: String,
pub active_parameter: Option<usize>, 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. /// Computes parameter information for the given call expression.
@ -24,105 +45,127 @@ pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<Cal
let file = file.syntax(); let file = file.syntax();
let token = file.token_at_offset(position.offset).next()?; let token = file.token_at_offset(position.offset).next()?;
let token = sema.descend_into_macros(token); 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 {
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)] #[derive(Debug)]
pub(crate) struct ActiveParameter { pub(crate) struct ActiveParameter {
/// FIXME: should be `Type` and `Name pub(crate) ty: Type,
pub(crate) ty: String,
pub(crate) name: String, pub(crate) name: String,
} }
impl ActiveParameter { impl ActiveParameter {
pub(crate) fn at(db: &RootDatabase, position: FilePosition) -> Option<Self> { 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> { 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(&macro_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)] #[derive(Debug)]
pub(crate) enum FnCallNode { pub(crate) enum FnCallNode {
CallExpr(ast::CallExpr), CallExpr(ast::CallExpr),
MethodCallExpr(ast::MethodCallExpr), MethodCallExpr(ast::MethodCallExpr),
MacroCallExpr(ast::MacroCall),
} }
impl FnCallNode { impl FnCallNode {
@ -138,7 +181,6 @@ impl FnCallNode {
} }
Some(FnCallNode::MethodCallExpr(it)) Some(FnCallNode::MethodCallExpr(it))
}, },
ast::MacroCall(it) => Some(FnCallNode::MacroCallExpr(it)),
_ => None, _ => None,
} }
} }
@ -150,7 +192,6 @@ impl FnCallNode {
match node { match node {
ast::CallExpr(it) => Some(FnCallNode::CallExpr(it)), ast::CallExpr(it) => Some(FnCallNode::CallExpr(it)),
ast::MethodCallExpr(it) => Some(FnCallNode::MethodCallExpr(it)), ast::MethodCallExpr(it) => Some(FnCallNode::MethodCallExpr(it)),
ast::MacroCall(it) => Some(FnCallNode::MacroCallExpr(it)),
_ => None, _ => None,
} }
} }
@ -166,8 +207,6 @@ impl FnCallNode {
FnCallNode::MethodCallExpr(call_expr) => { FnCallNode::MethodCallExpr(call_expr) => {
call_expr.syntax().children().filter_map(ast::NameRef::cast).next() call_expr.syntax().children().filter_map(ast::NameRef::cast).next()
} }
FnCallNode::MacroCallExpr(call_expr) => call_expr.path()?.segment()?.name_ref(),
} }
} }
@ -175,21 +214,10 @@ impl FnCallNode {
match self { match self {
FnCallNode::CallExpr(expr) => expr.arg_list(), FnCallNode::CallExpr(expr) => expr.arg_list(),
FnCallNode::MethodCallExpr(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)] #[cfg(test)]
mod tests { mod tests {
use expect::{expect, Expect}; use expect::{expect, Expect};
@ -202,20 +230,18 @@ mod tests {
let call_info = analysis.call_info(position).unwrap(); let call_info = analysis.call_info(position).unwrap();
let actual = match call_info { let actual = match call_info {
Some(call_info) => { Some(call_info) => {
let docs = match &call_info.signature.doc { let docs = match &call_info.doc {
None => "".to_string(), None => "".to_string(),
Some(docs) => format!("{}\n------\n", docs.as_str()), Some(docs) => format!("{}\n------\n", docs.as_str()),
}; };
let params = call_info let params = call_info
.signature .parameter_labels()
.parameters
.iter()
.enumerate() .enumerate()
.map(|(i, param)| { .map(|(i, param)| {
if Some(i) == call_info.active_parameter { if Some(i) == call_info.active_parameter {
format!("<{}>", param) format!("<{}>", param)
} else { } else {
param.clone() param.to_string()
} }
}) })
.collect::<Vec<_>>() .collect::<Vec<_>>()
@ -296,10 +322,8 @@ fn foo<T, U: Copy + Display>(x: T, y: U) -> u32
fn bar() { foo(<|>3, ); } fn bar() { foo(<|>3, ); }
"#, "#,
expect![[r#" expect![[r#"
fn foo<T, U: Copy + Display>(x: T, y: U) -> u32 fn foo(x: i32, y: {unknown}) -> u32
where T: Copy + Display, (<x: i32>, y: {unknown})
U: Debug
(<x: T>, y: U)
"#]], "#]],
); );
} }
@ -312,8 +336,7 @@ fn foo<T>() -> T where T: Copy + Display {}
fn bar() { foo(<|>); } fn bar() { foo(<|>); }
"#, "#,
expect![[r#" expect![[r#"
fn foo<T>() -> T fn foo() -> {unknown}
where T: Copy + Display
() ()
"#]], "#]],
); );
@ -323,11 +346,14 @@ fn bar() { foo(<|>); }
fn test_fn_signature_for_impl() { fn test_fn_signature_for_impl() {
check( check(
r#" r#"
struct F; impl F { pub fn new() { F{}} } struct F;
fn bar() {let _ : F = F::new(<|>);} impl F { pub fn new() { } }
fn bar() {
let _ : F = F::new(<|>);
}
"#, "#,
expect![[r#" expect![[r#"
pub fn new() fn new()
() ()
"#]], "#]],
); );
@ -346,8 +372,8 @@ fn bar() {
} }
"#, "#,
expect![[r#" expect![[r#"
pub fn do_it(&self) fn do_it(&self)
(&self) ()
"#]], "#]],
); );
} }
@ -365,8 +391,8 @@ fn bar() {
} }
"#, "#,
expect![[r#" expect![[r#"
pub fn do_it(&self, x: i32) fn do_it(&self, x: i32)
(&self, <x: i32>) (<x: i32>)
"#]], "#]],
); );
} }
@ -425,7 +451,7 @@ pub fn do() {
assert_eq!(6, my_crate::add_one(5)); assert_eq!(6, my_crate::add_one(5));
``` ```
------ ------
pub fn add_one(x: i32) -> i32 fn add_one(x: i32) -> i32
(<x: i32>) (<x: i32>)
"##]], "##]],
); );
@ -467,7 +493,7 @@ pub fn do_it() {
assert_eq!(6, my_crate::add_one(5)); assert_eq!(6, my_crate::add_one(5));
``` ```
------ ------
pub fn add_one(x: i32) -> i32 fn add_one(x: i32) -> i32
(<x: i32>) (<x: i32>)
"##]], "##]],
); );
@ -505,8 +531,8 @@ pub fn foo(mut r: WriteHandler<()>) {
By default this method stops actor's `Context`. By default this method stops actor's `Context`.
------ ------
fn finished(&mut self, ctx: &mut Self::Context) fn finished(&mut self, ctx: &mut {unknown})
(&mut self, <ctx: &mut Self::Context>) (<ctx: &mut {unknown}>)
"#]], "#]],
); );
} }
@ -539,7 +565,7 @@ fn main() {
"#, "#,
expect![[r#" expect![[r#"
fn bar(&self, _: u32) fn bar(&self, _: u32)
(&self, <_: u32>) (<_: u32>)
"#]], "#]],
); );
} }
@ -549,15 +575,15 @@ fn main() {
check( check(
r#" r#"
/// A cool tuple struct /// A cool tuple struct
struct TS(u32, i32); struct S(u32, i32);
fn main() { fn main() {
let s = TS(0, <|>); let s = S(0, <|>);
} }
"#, "#,
expect![[r#" expect![[r#"
A cool tuple struct A cool tuple struct
------ ------
struct TS(u32, i32) -> TS struct S(u32, i32)
(u32, <i32>) (u32, <i32>)
"#]], "#]],
); );
@ -567,31 +593,18 @@ fn main() {
fn generic_struct() { fn generic_struct() {
check( check(
r#" r#"
struct TS<T>(T); struct S<T>(T);
fn main() { fn main() {
let s = TS(<|>); let s = S(<|>);
} }
"#, "#,
expect![[r#" expect![[r#"
struct TS<T>(T) -> TS struct S({unknown})
(<T>) (<{unknown}>)
"#]], "#]],
); );
} }
#[test]
fn cant_call_named_structs() {
check(
r#"
struct TS { x: u32, y: i32 }
fn main() {
let s = TS(<|>);
}
"#,
expect![[""]],
);
}
#[test] #[test]
fn works_for_enum_variants() { fn works_for_enum_variants() {
check( check(
@ -612,14 +625,27 @@ fn main() {
expect![[r#" expect![[r#"
A Variant A Variant
------ ------
E::A(0: i32) enum E::A(i32)
(<0: i32>) (<i32>)
"#]], "#]],
); );
} }
#[test] #[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( check(
r#" r#"
enum E { enum E {
@ -639,28 +665,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] #[test]
fn fn_signature_for_call_in_macro() { fn fn_signature_for_call_in_macro() {
check( check(

View file

@ -329,15 +329,10 @@ pub(crate) fn compute_score(
ty: &Type, ty: &Type,
name: &str, name: &str,
) -> Option<CompletionScore> { ) -> 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 { let (active_name, active_type) = if let Some(record_field) = &ctx.record_field_syntax {
mark::hit!(record_field_type_match); mark::hit!(record_field_type_match);
let (struct_field, _local) = ctx.sema.resolve_record_field(record_field)?; let (struct_field, _local) = ctx.sema.resolve_record_field(record_field)?;
( (struct_field.name(ctx.db).to_string(), struct_field.signature_ty(ctx.db))
struct_field.name(ctx.db).to_string(),
struct_field.signature_ty(ctx.db).display(ctx.db).to_string(),
)
} else if let Some(active_parameter) = &ctx.active_parameter { } else if let Some(active_parameter) = &ctx.active_parameter {
mark::hit!(active_param_type_match); mark::hit!(active_param_type_match);
(active_parameter.name.clone(), active_parameter.ty.clone()) (active_parameter.name.clone(), active_parameter.ty.clone())

View file

@ -149,27 +149,6 @@ impl FunctionSignature {
has_self_param: false, 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 { impl From<&'_ ast::FnDef> for FunctionSignature {

View file

@ -322,15 +322,15 @@ fn get_fn_signature(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<
match expr { match expr {
ast::Expr::CallExpr(expr) => { ast::Expr::CallExpr(expr) => {
// FIXME: Type::as_callable is broken for closures // FIXME: Type::as_callable is broken for closures
let callable_def = sema.type_of_expr(&expr.expr()?)?.as_callable()?; let callable = sema.type_of_expr(&expr.expr()?)?.as_callable(sema.db)?;
match callable_def { match callable.kind() {
hir::CallableDefId::FunctionId(it) => { hir::CallableKind::Function(it) => {
Some(FunctionSignature::from_hir(sema.db, it.into())) Some(FunctionSignature::from_hir(sema.db, it.into()))
} }
hir::CallableDefId::StructId(it) => { hir::CallableKind::TupleStruct(it) => {
FunctionSignature::from_struct(sema.db, it.into()) FunctionSignature::from_struct(sema.db, it.into())
} }
hir::CallableDefId::EnumVariantId(it) => { hir::CallableKind::TupleEnumVariant(it) => {
FunctionSignature::from_enum_variant(sema.db, it.into()) FunctionSignature::from_enum_variant(sema.db, it.into())
} }
} }

View file

@ -553,21 +553,12 @@ pub(crate) fn handle_signature_help(
let _p = profile("handle_signature_help"); let _p = profile("handle_signature_help");
let position = from_proto::file_position(&snap, params.text_document_position_params)?; let position = from_proto::file_position(&snap, params.text_document_position_params)?;
let call_info = match snap.analysis.call_info(position)? { let call_info = match snap.analysis.call_info(position)? {
None => return Ok(None),
Some(it) => it, Some(it) => it,
None => return Ok(None),
}; };
let concise = !snap.config.call_info_full; let concise = !snap.config.call_info_full;
let mut active_parameter = call_info.active_parameter.map(|it| it as i64); let res = to_proto::signature_help(call_info, concise);
if concise && call_info.signature.has_self_param { Ok(Some(res))
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,
}))
} }
pub(crate) fn handle_hover( pub(crate) fn handle_hover(

View file

@ -4,8 +4,8 @@ use std::path::{self, Path};
use itertools::Itertools; use itertools::Itertools;
use ra_db::{FileId, FileRange}; use ra_db::{FileId, FileRange};
use ra_ide::{ use ra_ide::{
Assist, AssistKind, CompletionItem, CompletionItemKind, Documentation, FileSystemEdit, Fold, Assist, AssistKind, CallInfo, CompletionItem, CompletionItemKind, Documentation,
FoldKind, FunctionSignature, Highlight, HighlightModifier, HighlightTag, HighlightedRange, FileSystemEdit, Fold, FoldKind, Highlight, HighlightModifier, HighlightTag, HighlightedRange,
Indel, InlayHint, InlayKind, InsertTextFormat, LineIndex, Markup, NavigationTarget, Indel, InlayHint, InlayKind, InsertTextFormat, LineIndex, Markup, NavigationTarget,
ReferenceAccess, ResolvedAssist, Runnable, Severity, SourceChange, SourceFileEdit, TextEdit, ReferenceAccess, ResolvedAssist, Runnable, Severity, SourceChange, SourceFileEdit, TextEdit,
}; };
@ -219,29 +219,30 @@ pub(crate) fn completion_item(
res res
} }
pub(crate) fn signature_information( pub(crate) fn signature_help(call_info: CallInfo, concise: bool) -> lsp_types::SignatureHelp {
signature: FunctionSignature, let parameters = call_info
concise: bool, .parameter_labels()
) -> lsp_types::SignatureInformation { .map(|label| lsp_types::ParameterInformation {
let (label, documentation, params) = if concise { label: lsp_types::ParameterLabel::Simple(label.to_string()),
let mut params = signature.parameters;
if signature.has_self_param {
params.remove(0);
}
(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, documentation: None,
}) })
.collect(); .collect();
lsp_types::SignatureInformation { label, documentation, parameters: Some(parameters) } let label = if concise { call_info.parameter_labels().join(", ") } else { call_info.signature };
let documentation = call_info.doc.map(|doc| {
lsp_types::Documentation::MarkupContent(lsp_types::MarkupContent {
kind: lsp_types::MarkupKind::Markdown,
value: doc,
})
});
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 { pub(crate) fn inlay_int(line_index: &LineIndex, inlay_hint: InlayHint) -> lsp_ext::InlayHint {