//! FIXME: write short doc here // FIXME: this modules relies on strings and AST way too much, and it should be // rewritten (matklad 2020-05-07) use std::convert::From; use hir::{Docs, Documentation, HasSource, HirDisplay}; use ra_ide_db::RootDatabase; use ra_syntax::ast::{self, AstNode, NameOwner, VisibilityOwner}; use stdx::split_delim; use crate::display::{generic_parameters, where_predicates}; #[derive(Debug)] pub(crate) enum CallableKind { Function, StructConstructor, VariantConstructor, } /// Contains information about a function signature #[derive(Debug)] pub(crate) struct FunctionSignature { pub(crate) kind: CallableKind, /// Optional visibility pub(crate) visibility: Option, /// Qualifiers like `async`, `unsafe`, ... pub(crate) qualifier: FunctionQualifier, /// Name of the function pub(crate) name: Option, /// Documentation for the function pub(crate) doc: Option, /// Generic parameters pub(crate) generic_parameters: Vec, /// Parameters of the function pub(crate) parameters: Vec, /// Parameter names of the function pub(crate) parameter_names: Vec, /// Parameter types of the function pub(crate) parameter_types: Vec, /// Optional return type pub(crate) ret_type: Option, /// Where predicates pub(crate) where_predicates: Vec, /// Self param presence pub(crate) has_self_param: bool, } #[derive(Debug, Default)] pub(crate) struct FunctionQualifier { // `async` and `const` are mutually exclusive. Do we need to enforcing it here? pub(crate) is_async: bool, pub(crate) is_const: bool, pub(crate) is_unsafe: bool, /// The string `extern ".."` 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) { let mut res = vec![]; let mut res_types = vec![]; let mut has_self_param = false; if let Some(param_list) = node.param_list() { if let Some(self_param) = param_list.self_param() { has_self_param = true; let raw_param = self_param.syntax().text().to_string(); res_types.push( raw_param .split(':') .nth(1) .and_then(|it| it.get(1..)) .unwrap_or_else(|| "Self") .to_string(), ); res.push(raw_param); } // macro-generated functions are missing whitespace fn fmt_param(param: ast::Param) -> String { let text = param.syntax().text().to_string(); match split_delim(&text, ':') { Some((left, right)) => format!("{}: {}", left.trim(), right.trim()), _ => text, } } res.extend(param_list.params().map(fmt_param)); res_types.extend(param_list.params().map(|param| { let param_text = param.syntax().text().to_string(); match param_text.split(':').nth(1).and_then(|it| it.get(1..)) { Some(it) => it.to_string(), None => param_text, } })); } (has_self_param, res, res_types) } fn param_name_list(node: &ast::FnDef) -> Vec { let mut res = vec![]; if let Some(param_list) = node.param_list() { if let Some(self_param) = param_list.self_param() { res.push(self_param.syntax().text().to_string()) } res.extend( param_list .params() .map(|param| { Some( param .pat()? .syntax() .descendants() .find_map(ast::Name::cast)? .text() .to_string(), ) }) .map(|param| param.unwrap_or_default()), ); } res } let (has_self_param, parameters, parameter_types) = param_list(node); FunctionSignature { kind: CallableKind::Function, visibility: node.visibility().map(|n| n.syntax().text().to_string()), qualifier: FunctionQualifier { is_async: node.async_token().is_some(), is_const: node.const_token().is_some(), is_unsafe: node.unsafe_token().is_some(), extern_abi: node.abi().map(|n| n.to_string()), }, name: node.name().map(|n| n.text().to_string()), ret_type: node .ret_type() .and_then(|r| r.type_ref()) .map(|n| n.syntax().text().to_string()), parameters, parameter_names: param_name_list(node), parameter_types, generic_parameters: generic_parameters(node), where_predicates: where_predicates(node), // docs are processed separately doc: None, has_self_param, } } }