From a5ae8b8b92748e1b876002799d160136a7836212 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 16 Jul 2020 21:51:44 +0200 Subject: Inlay hints use callables --- crates/ra_ide/src/display/function_signature.rs | 96 +------------------------ crates/ra_ide/src/inlay_hints.rs | 76 ++++++++------------ 2 files changed, 32 insertions(+), 140 deletions(-) (limited to 'crates/ra_ide') diff --git a/crates/ra_ide/src/display/function_signature.rs b/crates/ra_ide/src/display/function_signature.rs index 77551117b..f6e11357f 100644 --- a/crates/ra_ide/src/display/function_signature.rs +++ b/crates/ra_ide/src/display/function_signature.rs @@ -4,8 +4,7 @@ // rewritten (matklad 2020-05-07) use std::convert::From; -use hir::{Docs, Documentation, HasSource, HirDisplay}; -use ra_ide_db::RootDatabase; +use hir::Documentation; use ra_syntax::ast::{self, AstNode, NameOwner, VisibilityOwner}; use stdx::split_delim; @@ -14,8 +13,6 @@ use crate::display::{generic_parameters, where_predicates}; #[derive(Debug)] pub(crate) enum CallableKind { Function, - StructConstructor, - VariantConstructor, } /// Contains information about a function signature @@ -56,97 +53,6 @@ pub(crate) struct FunctionQualifier { pub(crate) extern_abi: Option, } -impl FunctionSignature { - pub(crate) fn from_hir(db: &RootDatabase, function: hir::Function) -> Self { - let ast_node = function.source(db).value; - let mut res = FunctionSignature::from(&ast_node); - res.doc = function.docs(db); - res - } - - pub(crate) fn from_struct(db: &RootDatabase, st: hir::Struct) -> Option { - let node: ast::StructDef = st.source(db).value; - if let ast::StructKind::Record(_) = node.kind() { - return None; - }; - - let mut params = vec![]; - let mut parameter_types = vec![]; - for field in st.fields(db).into_iter() { - let ty = field.signature_ty(db); - let raw_param = format!("{}", ty.display(db)); - - if let Some(param_type) = raw_param.split(':').nth(1).and_then(|it| it.get(1..)) { - parameter_types.push(param_type.to_string()); - } else { - // useful when you have tuple struct - parameter_types.push(raw_param.clone()); - } - params.push(raw_param); - } - - Some(FunctionSignature { - kind: CallableKind::StructConstructor, - visibility: node.visibility().map(|n| n.syntax().text().to_string()), - // Do we need `const`? - qualifier: Default::default(), - name: node.name().map(|n| n.text().to_string()), - ret_type: node.name().map(|n| n.text().to_string()), - parameters: params, - parameter_names: vec![], - parameter_types, - generic_parameters: generic_parameters(&node), - where_predicates: where_predicates(&node), - doc: st.docs(db), - has_self_param: false, - }) - } - - pub(crate) fn from_enum_variant(db: &RootDatabase, variant: hir::EnumVariant) -> Option { - let node: ast::EnumVariant = variant.source(db).value; - match node.kind() { - ast::StructKind::Record(_) | ast::StructKind::Unit => return None, - _ => (), - }; - - let parent_name = variant.parent_enum(db).name(db).to_string(); - - let name = format!("{}::{}", parent_name, variant.name(db)); - - let mut params = vec![]; - let mut parameter_types = vec![]; - for field in variant.fields(db).into_iter() { - let ty = field.signature_ty(db); - let raw_param = format!("{}", ty.display(db)); - if let Some(param_type) = raw_param.split(':').nth(1).and_then(|it| it.get(1..)) { - parameter_types.push(param_type.to_string()); - } else { - // The unwrap_or_else is useful when you have tuple - parameter_types.push(raw_param); - } - let name = field.name(db); - - params.push(format!("{}: {}", name, ty.display(db))); - } - - Some(FunctionSignature { - kind: CallableKind::VariantConstructor, - visibility: None, - // Do we need `const`? - qualifier: Default::default(), - name: Some(name), - ret_type: None, - parameters: params, - parameter_names: vec![], - parameter_types, - generic_parameters: vec![], - where_predicates: vec![], - doc: variant.docs(db), - has_self_param: false, - }) - } -} - impl From<&'_ ast::FnDef> for FunctionSignature { fn from(node: &ast::FnDef) -> FunctionSignature { fn param_list(node: &ast::FnDef) -> (bool, Vec, Vec) { diff --git a/crates/ra_ide/src/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs index ae5695f61..cec3b04e8 100644 --- a/crates/ra_ide/src/inlay_hints.rs +++ b/crates/ra_ide/src/inlay_hints.rs @@ -1,4 +1,4 @@ -use hir::{Adt, HirDisplay, Semantics, Type}; +use hir::{Adt, Callable, HirDisplay, Semantics, Type}; use ra_ide_db::RootDatabase; use ra_prof::profile; use ra_syntax::{ @@ -7,7 +7,9 @@ use ra_syntax::{ }; use stdx::to_lower_snake_case; -use crate::{display::function_signature::FunctionSignature, FileId}; +use crate::FileId; +use ast::NameOwner; +use either::Either; #[derive(Clone, Debug, PartialEq, Eq)] pub struct InlayHintsConfig { @@ -150,23 +152,26 @@ fn get_param_name_hints( _ => return None, }; - let fn_signature = get_fn_signature(sema, &expr)?; - let n_params_to_skip = - if fn_signature.has_self_param && matches!(&expr, ast::Expr::MethodCallExpr(_)) { - 1 - } else { - 0 - }; - let hints = fn_signature - .parameter_names - .iter() - .skip(n_params_to_skip) + let callable = get_callable(sema, &expr)?; + let hints = callable + .params(sema.db) + .into_iter() .zip(args) - .filter(|(param, arg)| should_show_param_name_hint(sema, &fn_signature, param, &arg)) + .filter_map(|((param, _ty), arg)| match param? { + Either::Left(self_param) => Some((self_param.to_string(), arg)), + Either::Right(pat) => { + let param_name = match pat { + ast::Pat::BindPat(it) => it.name()?.to_string(), + it => it.to_string(), + }; + Some((param_name, arg)) + } + }) + .filter(|(param_name, arg)| should_show_param_name_hint(sema, &callable, ¶m_name, &arg)) .map(|(param_name, arg)| InlayHint { range: arg.syntax().text_range(), kind: InlayKind::ParameterHint, - label: param_name.into(), + label: param_name.to_string().into(), }); acc.extend(hints); @@ -250,28 +255,26 @@ fn should_not_display_type_hint(db: &RootDatabase, bind_pat: &ast::BindPat, pat_ fn should_show_param_name_hint( sema: &Semantics, - fn_signature: &FunctionSignature, + callable: &Callable, param_name: &str, argument: &ast::Expr, ) -> bool { let param_name = param_name.trim_start_matches('_'); + let fn_name = match callable.kind() { + hir::CallableKind::Function(it) => Some(it.name(sema.db).to_string()), + hir::CallableKind::TupleStruct(_) | hir::CallableKind::TupleEnumVariant(_) => None, + }; if param_name.is_empty() - || Some(param_name) == fn_signature.name.as_ref().map(|s| s.trim_start_matches('_')) + || Some(param_name) == fn_name.as_ref().map(|s| s.trim_start_matches('_')) || is_argument_similar_to_param_name(sema, argument, param_name) || param_name.starts_with("ra_fixture") { return false; } - let parameters_len = if fn_signature.has_self_param { - fn_signature.parameters.len() - 1 - } else { - fn_signature.parameters.len() - }; - // avoid displaying hints for common functions like map, filter, etc. // or other obvious words used in std - !(parameters_len == 1 && is_obvious_param(param_name)) + !(callable.n_params() == 1 && is_obvious_param(param_name)) } fn is_argument_similar_to_param_name( @@ -318,27 +321,10 @@ fn is_obvious_param(param_name: &str) -> bool { param_name.len() == 1 || is_obvious_param_name } -fn get_fn_signature(sema: &Semantics, expr: &ast::Expr) -> Option { +fn get_callable(sema: &Semantics, expr: &ast::Expr) -> Option { match expr { - ast::Expr::CallExpr(expr) => { - // FIXME: Type::as_callable is broken for closures - 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::CallableKind::TupleStruct(it) => { - FunctionSignature::from_struct(sema.db, it.into()) - } - hir::CallableKind::TupleEnumVariant(it) => { - FunctionSignature::from_enum_variant(sema.db, it.into()) - } - } - } - ast::Expr::MethodCallExpr(expr) => { - let fn_def = sema.resolve_method_call(&expr)?; - Some(FunctionSignature::from_hir(sema.db, fn_def)) - } + ast::Expr::CallExpr(expr) => sema.type_of_expr(&expr.expr()?)?.as_callable(sema.db), + ast::Expr::MethodCallExpr(expr) => sema.resolve_method_call_as_callable(expr), _ => None, } } @@ -360,7 +346,7 @@ mod tests { let inlay_hints = analysis.inlay_hints(file_id, &config).unwrap(); let actual = inlay_hints.into_iter().map(|it| (it.range, it.label.to_string())).collect::>(); - assert_eq!(expected, actual); + assert_eq!(expected, actual, "\nExpected:\n{:#?}\n\nActual:\n{:#?}", expected, actual); } fn check_expect(config: InlayHintsConfig, ra_fixture: &str, expect: Expect) { -- cgit v1.2.3