//! 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, fmt::{self, Display}, }; use hir::{Docs, Documentation, HasSource, HirDisplay}; use ra_ide_db::RootDatabase; use ra_syntax::ast::{self, AstNode, NameOwner, VisibilityOwner}; use stdx::{split1, SepBy}; use crate::display::{generic_parameters, where_predicates}; #[derive(Debug)] pub enum CallableKind { Function, StructConstructor, VariantConstructor, Macro, } /// Contains information about a function signature #[derive(Debug)] pub struct FunctionSignature { pub kind: CallableKind, /// Optional visibility pub visibility: Option, /// Qualifiers like `async`, `unsafe`, ... pub qualifier: FunctionQualifier, /// Name of the function pub name: Option, /// Documentation for the function pub doc: Option, /// Generic parameters pub generic_parameters: Vec, /// Parameters of the function pub parameters: Vec, /// Parameter names of the function pub parameter_names: Vec, /// Parameter types of the function pub parameter_types: Vec, /// Optional return type pub ret_type: Option, /// Where predicates pub where_predicates: Vec, /// Self param presence pub has_self_param: bool, } #[derive(Debug, Default)] pub 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, /// The string `extern ".."` pub extern_abi: Option, } impl FunctionSignature { pub(crate) fn with_doc_opt(mut self, doc: Option) -> Self { self.doc = doc; self } pub(crate) fn from_hir(db: &RootDatabase, function: hir::Function) -> Self { let doc = function.docs(db); let ast_node = function.source(db).value; FunctionSignature::from(&ast_node).with_doc_opt(doc) } 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: None, has_self_param: false, } .with_doc_opt(st.docs(db)), ) } 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: None, has_self_param: false, } .with_doc_opt(variant.docs(db)), ) } pub(crate) fn from_macro(db: &RootDatabase, macro_def: hir::MacroDef) -> Option { 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: None, has_self_param: false, } .with_doc_opt(macro_def.docs(db)), ) } } 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 split1(&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, } } } impl Display for FunctionSignature { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { if let Some(t) = &self.visibility { write!(f, "{} ", t)?; } if self.qualifier.is_async { write!(f, "async ")?; } if self.qualifier.is_const { write!(f, "const ")?; } if self.qualifier.is_unsafe { write!(f, "unsafe ")?; } if let Some(extern_abi) = &self.qualifier.extern_abi { // Keyword `extern` is included in the string. write!(f, "{} ", extern_abi)?; } if let Some(name) = &self.name { match self.kind { CallableKind::Function => write!(f, "fn {}", name)?, CallableKind::StructConstructor => write!(f, "struct {}", name)?, CallableKind::VariantConstructor => write!(f, "{}", name)?, CallableKind::Macro => write!(f, "{}!", name)?, } } if !self.generic_parameters.is_empty() { write!(f, "{}", self.generic_parameters.iter().sep_by(", ").surround_with("<", ">"))?; } write!(f, "{}", self.parameters.iter().sep_by(", ").surround_with("(", ")"))?; if let Some(t) = &self.ret_type { write!(f, " -> {}", t)?; } if !self.where_predicates.is_empty() { write!(f, "\nwhere {}", self.where_predicates.iter().sep_by(",\n "))?; } Ok(()) } }