diff options
Diffstat (limited to 'crates/ra_ide/src/display/function_signature.rs')
-rw-r--r-- | crates/ra_ide/src/display/function_signature.rs | 334 |
1 files changed, 0 insertions, 334 deletions
diff --git a/crates/ra_ide/src/display/function_signature.rs b/crates/ra_ide/src/display/function_signature.rs deleted file mode 100644 index a98264fb3..000000000 --- a/crates/ra_ide/src/display/function_signature.rs +++ /dev/null | |||
@@ -1,334 +0,0 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | |||
3 | // FIXME: this modules relies on strings and AST way too much, and it should be | ||
4 | // rewritten (matklad 2020-05-07) | ||
5 | use std::{ | ||
6 | convert::From, | ||
7 | fmt::{self, Display}, | ||
8 | }; | ||
9 | |||
10 | use hir::{Docs, Documentation, HasSource, HirDisplay}; | ||
11 | use ra_ide_db::RootDatabase; | ||
12 | use ra_syntax::ast::{self, AstNode, NameOwner, VisibilityOwner}; | ||
13 | use stdx::{split_delim, SepBy}; | ||
14 | |||
15 | use crate::display::{generic_parameters, where_predicates}; | ||
16 | |||
17 | #[derive(Debug)] | ||
18 | pub enum CallableKind { | ||
19 | Function, | ||
20 | StructConstructor, | ||
21 | VariantConstructor, | ||
22 | Macro, | ||
23 | } | ||
24 | |||
25 | /// Contains information about a function signature | ||
26 | #[derive(Debug)] | ||
27 | pub struct FunctionSignature { | ||
28 | pub kind: CallableKind, | ||
29 | /// Optional visibility | ||
30 | pub visibility: Option<String>, | ||
31 | /// Qualifiers like `async`, `unsafe`, ... | ||
32 | pub qualifier: FunctionQualifier, | ||
33 | /// Name of the function | ||
34 | pub name: Option<String>, | ||
35 | /// Documentation for the function | ||
36 | pub doc: Option<Documentation>, | ||
37 | /// Generic parameters | ||
38 | pub generic_parameters: Vec<String>, | ||
39 | /// Parameters of the function | ||
40 | pub parameters: Vec<String>, | ||
41 | /// Parameter names of the function | ||
42 | pub parameter_names: Vec<String>, | ||
43 | /// Parameter types of the function | ||
44 | pub parameter_types: Vec<String>, | ||
45 | /// Optional return type | ||
46 | pub ret_type: Option<String>, | ||
47 | /// Where predicates | ||
48 | pub where_predicates: Vec<String>, | ||
49 | /// Self param presence | ||
50 | pub has_self_param: bool, | ||
51 | } | ||
52 | |||
53 | #[derive(Debug, Default)] | ||
54 | pub struct FunctionQualifier { | ||
55 | // `async` and `const` are mutually exclusive. Do we need to enforcing it here? | ||
56 | pub is_async: bool, | ||
57 | pub is_const: bool, | ||
58 | pub is_unsafe: bool, | ||
59 | /// The string `extern ".."` | ||
60 | pub extern_abi: Option<String>, | ||
61 | } | ||
62 | |||
63 | impl FunctionSignature { | ||
64 | pub(crate) fn with_doc_opt(mut self, doc: Option<Documentation>) -> Self { | ||
65 | self.doc = doc; | ||
66 | self | ||
67 | } | ||
68 | |||
69 | pub(crate) fn from_hir(db: &RootDatabase, function: hir::Function) -> Self { | ||
70 | let doc = function.docs(db); | ||
71 | let ast_node = function.source(db).value; | ||
72 | FunctionSignature::from(&ast_node).with_doc_opt(doc) | ||
73 | } | ||
74 | |||
75 | pub(crate) fn from_struct(db: &RootDatabase, st: hir::Struct) -> Option<Self> { | ||
76 | let node: ast::StructDef = st.source(db).value; | ||
77 | if let ast::StructKind::Record(_) = node.kind() { | ||
78 | return None; | ||
79 | }; | ||
80 | |||
81 | let mut params = vec![]; | ||
82 | let mut parameter_types = vec![]; | ||
83 | for field in st.fields(db).into_iter() { | ||
84 | let ty = field.signature_ty(db); | ||
85 | let raw_param = format!("{}", ty.display(db)); | ||
86 | |||
87 | if let Some(param_type) = raw_param.split(':').nth(1).and_then(|it| it.get(1..)) { | ||
88 | parameter_types.push(param_type.to_string()); | ||
89 | } else { | ||
90 | // useful when you have tuple struct | ||
91 | parameter_types.push(raw_param.clone()); | ||
92 | } | ||
93 | params.push(raw_param); | ||
94 | } | ||
95 | |||
96 | Some( | ||
97 | FunctionSignature { | ||
98 | kind: CallableKind::StructConstructor, | ||
99 | visibility: node.visibility().map(|n| n.syntax().text().to_string()), | ||
100 | // Do we need `const`? | ||
101 | qualifier: Default::default(), | ||
102 | name: node.name().map(|n| n.text().to_string()), | ||
103 | ret_type: node.name().map(|n| n.text().to_string()), | ||
104 | parameters: params, | ||
105 | parameter_names: vec![], | ||
106 | parameter_types, | ||
107 | generic_parameters: generic_parameters(&node), | ||
108 | where_predicates: where_predicates(&node), | ||
109 | doc: None, | ||
110 | has_self_param: false, | ||
111 | } | ||
112 | .with_doc_opt(st.docs(db)), | ||
113 | ) | ||
114 | } | ||
115 | |||
116 | pub(crate) fn from_enum_variant(db: &RootDatabase, variant: hir::EnumVariant) -> Option<Self> { | ||
117 | let node: ast::EnumVariant = variant.source(db).value; | ||
118 | match node.kind() { | ||
119 | ast::StructKind::Record(_) | ast::StructKind::Unit => return None, | ||
120 | _ => (), | ||
121 | }; | ||
122 | |||
123 | let parent_name = variant.parent_enum(db).name(db).to_string(); | ||
124 | |||
125 | let name = format!("{}::{}", parent_name, variant.name(db)); | ||
126 | |||
127 | let mut params = vec![]; | ||
128 | let mut parameter_types = vec![]; | ||
129 | for field in variant.fields(db).into_iter() { | ||
130 | let ty = field.signature_ty(db); | ||
131 | let raw_param = format!("{}", ty.display(db)); | ||
132 | if let Some(param_type) = raw_param.split(':').nth(1).and_then(|it| it.get(1..)) { | ||
133 | parameter_types.push(param_type.to_string()); | ||
134 | } else { | ||
135 | // The unwrap_or_else is useful when you have tuple | ||
136 | parameter_types.push(raw_param); | ||
137 | } | ||
138 | let name = field.name(db); | ||
139 | |||
140 | params.push(format!("{}: {}", name, ty.display(db))); | ||
141 | } | ||
142 | |||
143 | Some( | ||
144 | FunctionSignature { | ||
145 | kind: CallableKind::VariantConstructor, | ||
146 | visibility: None, | ||
147 | // Do we need `const`? | ||
148 | qualifier: Default::default(), | ||
149 | name: Some(name), | ||
150 | ret_type: None, | ||
151 | parameters: params, | ||
152 | parameter_names: vec![], | ||
153 | parameter_types, | ||
154 | generic_parameters: vec![], | ||
155 | where_predicates: vec![], | ||
156 | doc: None, | ||
157 | has_self_param: false, | ||
158 | } | ||
159 | .with_doc_opt(variant.docs(db)), | ||
160 | ) | ||
161 | } | ||
162 | |||
163 | pub(crate) fn from_macro(db: &RootDatabase, macro_def: hir::MacroDef) -> Option<Self> { | ||
164 | let node: ast::MacroCall = macro_def.source(db).value; | ||
165 | |||
166 | let params = vec![]; | ||
167 | |||
168 | Some( | ||
169 | FunctionSignature { | ||
170 | kind: CallableKind::Macro, | ||
171 | visibility: None, | ||
172 | qualifier: Default::default(), | ||
173 | name: node.name().map(|n| n.text().to_string()), | ||
174 | ret_type: None, | ||
175 | parameters: params, | ||
176 | parameter_names: vec![], | ||
177 | parameter_types: vec![], | ||
178 | generic_parameters: vec![], | ||
179 | where_predicates: vec![], | ||
180 | doc: None, | ||
181 | has_self_param: false, | ||
182 | } | ||
183 | .with_doc_opt(macro_def.docs(db)), | ||
184 | ) | ||
185 | } | ||
186 | } | ||
187 | |||
188 | impl From<&'_ ast::FnDef> for FunctionSignature { | ||
189 | fn from(node: &ast::FnDef) -> FunctionSignature { | ||
190 | fn param_list(node: &ast::FnDef) -> (bool, Vec<String>, Vec<String>) { | ||
191 | let mut res = vec![]; | ||
192 | let mut res_types = vec![]; | ||
193 | let mut has_self_param = false; | ||
194 | if let Some(param_list) = node.param_list() { | ||
195 | if let Some(self_param) = param_list.self_param() { | ||
196 | has_self_param = true; | ||
197 | let raw_param = self_param.syntax().text().to_string(); | ||
198 | |||
199 | res_types.push( | ||
200 | raw_param | ||
201 | .split(':') | ||
202 | .nth(1) | ||
203 | .and_then(|it| it.get(1..)) | ||
204 | .unwrap_or_else(|| "Self") | ||
205 | .to_string(), | ||
206 | ); | ||
207 | res.push(raw_param); | ||
208 | } | ||
209 | |||
210 | // macro-generated functions are missing whitespace | ||
211 | fn fmt_param(param: ast::Param) -> String { | ||
212 | let text = param.syntax().text().to_string(); | ||
213 | match split_delim(&text, ':') { | ||
214 | Some((left, right)) => format!("{}: {}", left.trim(), right.trim()), | ||
215 | _ => text, | ||
216 | } | ||
217 | } | ||
218 | |||
219 | res.extend(param_list.params().map(fmt_param)); | ||
220 | res_types.extend(param_list.params().map(|param| { | ||
221 | let param_text = param.syntax().text().to_string(); | ||
222 | match param_text.split(':').nth(1).and_then(|it| it.get(1..)) { | ||
223 | Some(it) => it.to_string(), | ||
224 | None => param_text, | ||
225 | } | ||
226 | })); | ||
227 | } | ||
228 | (has_self_param, res, res_types) | ||
229 | } | ||
230 | |||
231 | fn param_name_list(node: &ast::FnDef) -> Vec<String> { | ||
232 | let mut res = vec![]; | ||
233 | if let Some(param_list) = node.param_list() { | ||
234 | if let Some(self_param) = param_list.self_param() { | ||
235 | res.push(self_param.syntax().text().to_string()) | ||
236 | } | ||
237 | |||
238 | res.extend( | ||
239 | param_list | ||
240 | .params() | ||
241 | .map(|param| { | ||
242 | Some( | ||
243 | param | ||
244 | .pat()? | ||
245 | .syntax() | ||
246 | .descendants() | ||
247 | .find_map(ast::Name::cast)? | ||
248 | .text() | ||
249 | .to_string(), | ||
250 | ) | ||
251 | }) | ||
252 | .map(|param| param.unwrap_or_default()), | ||
253 | ); | ||
254 | } | ||
255 | res | ||
256 | } | ||
257 | |||
258 | let (has_self_param, parameters, parameter_types) = param_list(node); | ||
259 | |||
260 | FunctionSignature { | ||
261 | kind: CallableKind::Function, | ||
262 | visibility: node.visibility().map(|n| n.syntax().text().to_string()), | ||
263 | qualifier: FunctionQualifier { | ||
264 | is_async: node.async_token().is_some(), | ||
265 | is_const: node.const_token().is_some(), | ||
266 | is_unsafe: node.unsafe_token().is_some(), | ||
267 | extern_abi: node.abi().map(|n| n.to_string()), | ||
268 | }, | ||
269 | name: node.name().map(|n| n.text().to_string()), | ||
270 | ret_type: node | ||
271 | .ret_type() | ||
272 | .and_then(|r| r.type_ref()) | ||
273 | .map(|n| n.syntax().text().to_string()), | ||
274 | parameters, | ||
275 | parameter_names: param_name_list(node), | ||
276 | parameter_types, | ||
277 | generic_parameters: generic_parameters(node), | ||
278 | where_predicates: where_predicates(node), | ||
279 | // docs are processed separately | ||
280 | doc: None, | ||
281 | has_self_param, | ||
282 | } | ||
283 | } | ||
284 | } | ||
285 | |||
286 | impl Display for FunctionSignature { | ||
287 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
288 | if let Some(t) = &self.visibility { | ||
289 | write!(f, "{} ", t)?; | ||
290 | } | ||
291 | |||
292 | if self.qualifier.is_async { | ||
293 | write!(f, "async ")?; | ||
294 | } | ||
295 | |||
296 | if self.qualifier.is_const { | ||
297 | write!(f, "const ")?; | ||
298 | } | ||
299 | |||
300 | if self.qualifier.is_unsafe { | ||
301 | write!(f, "unsafe ")?; | ||
302 | } | ||
303 | |||
304 | if let Some(extern_abi) = &self.qualifier.extern_abi { | ||
305 | // Keyword `extern` is included in the string. | ||
306 | write!(f, "{} ", extern_abi)?; | ||
307 | } | ||
308 | |||
309 | if let Some(name) = &self.name { | ||
310 | match self.kind { | ||
311 | CallableKind::Function => write!(f, "fn {}", name)?, | ||
312 | CallableKind::StructConstructor => write!(f, "struct {}", name)?, | ||
313 | CallableKind::VariantConstructor => write!(f, "{}", name)?, | ||
314 | CallableKind::Macro => write!(f, "{}!", name)?, | ||
315 | } | ||
316 | } | ||
317 | |||
318 | if !self.generic_parameters.is_empty() { | ||
319 | write!(f, "{}", self.generic_parameters.iter().sep_by(", ").surround_with("<", ">"))?; | ||
320 | } | ||
321 | |||
322 | write!(f, "{}", self.parameters.iter().sep_by(", ").surround_with("(", ")"))?; | ||
323 | |||
324 | if let Some(t) = &self.ret_type { | ||
325 | write!(f, " -> {}", t)?; | ||
326 | } | ||
327 | |||
328 | if !self.where_predicates.is_empty() { | ||
329 | write!(f, "\nwhere {}", self.where_predicates.iter().sep_by(",\n "))?; | ||
330 | } | ||
331 | |||
332 | Ok(()) | ||
333 | } | ||
334 | } | ||