mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-26 04:53:34 +00:00
Semantical call info
This commit is contained in:
parent
9210fcc076
commit
ff0312fa32
12 changed files with 313 additions and 261 deletions
|
@ -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,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
|
||||
#[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,41 @@
|
|||
//! FIXME: write short doc here
|
||||
use hir::Semantics;
|
||||
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 +45,127 @@ 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 {
|
||||
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 +181,6 @@ impl FnCallNode {
|
|||
}
|
||||
Some(FnCallNode::MethodCallExpr(it))
|
||||
},
|
||||
ast::MacroCall(it) => Some(FnCallNode::MacroCallExpr(it)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -150,7 +192,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 +207,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 +214,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 +230,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 +322,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 +336,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 +346,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 +372,8 @@ fn bar() {
|
|||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
pub fn do_it(&self)
|
||||
(&self)
|
||||
fn do_it(&self)
|
||||
()
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
@ -365,8 +391,8 @@ fn bar() {
|
|||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
pub fn do_it(&self, x: i32)
|
||||
(&self, <x: i32>)
|
||||
fn do_it(&self, x: i32)
|
||||
(<x: i32>)
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
@ -425,7 +451,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 +493,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 +531,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 +565,7 @@ fn main() {
|
|||
"#,
|
||||
expect![[r#"
|
||||
fn bar(&self, _: u32)
|
||||
(&self, <_: u32>)
|
||||
(<_: u32>)
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
@ -549,15 +575,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 +593,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 +625,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 +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]
|
||||
fn fn_signature_for_call_in_macro() {
|
||||
check(
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -149,27 +149,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 {
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -553,21 +553,12 @@ 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);
|
||||
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,30 @@ pub(crate) fn completion_item(
|
|||
res
|
||||
}
|
||||
|
||||
pub(crate) fn signature_information(
|
||||
signature: FunctionSignature,
|
||||
concise: bool,
|
||||
) -> lsp_types::SignatureInformation {
|
||||
let (label, documentation, params) = if concise {
|
||||
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),
|
||||
pub(crate) fn signature_help(call_info: CallInfo, concise: bool) -> lsp_types::SignatureHelp {
|
||||
let parameters = call_info
|
||||
.parameter_labels()
|
||||
.map(|label| lsp_types::ParameterInformation {
|
||||
label: lsp_types::ParameterLabel::Simple(label.to_string()),
|
||||
documentation: None,
|
||||
})
|
||||
.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 {
|
||||
|
|
Loading…
Reference in a new issue