aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide/src')
-rw-r--r--crates/ra_ide/src/completion/complete_trait_impl.rs9
-rw-r--r--crates/ra_ide/src/completion/presentation.rs16
-rw-r--r--crates/ra_ide/src/display.rs62
-rw-r--r--crates/ra_ide/src/display/function_signature.rs298
-rw-r--r--crates/ra_ide/src/display/short_label.rs2
-rw-r--r--crates/ra_ide/src/inlay_hints.rs76
6 files changed, 84 insertions, 379 deletions
diff --git a/crates/ra_ide/src/completion/complete_trait_impl.rs b/crates/ra_ide/src/completion/complete_trait_impl.rs
index 90f5b1c25..cf716540f 100644
--- a/crates/ra_ide/src/completion/complete_trait_impl.rs
+++ b/crates/ra_ide/src/completion/complete_trait_impl.rs
@@ -43,7 +43,7 @@ use crate::{
43 completion::{ 43 completion::{
44 CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions, 44 CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions,
45 }, 45 },
46 display::function_signature::FunctionSignature, 46 display::function_declaration,
47}; 47};
48 48
49pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext) { 49pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext) {
@@ -125,8 +125,6 @@ fn add_function_impl(
125 ctx: &CompletionContext, 125 ctx: &CompletionContext,
126 func: hir::Function, 126 func: hir::Function,
127) { 127) {
128 let signature = FunctionSignature::from_hir(ctx.db, func);
129
130 let fn_name = func.name(ctx.db).to_string(); 128 let fn_name = func.name(ctx.db).to_string();
131 129
132 let label = if !func.params(ctx.db).is_empty() { 130 let label = if !func.params(ctx.db).is_empty() {
@@ -146,13 +144,14 @@ fn add_function_impl(
146 }; 144 };
147 let range = TextRange::new(fn_def_node.text_range().start(), ctx.source_range().end()); 145 let range = TextRange::new(fn_def_node.text_range().start(), ctx.source_range().end());
148 146
147 let function_decl = function_declaration(&func.source(ctx.db).value);
149 match ctx.config.snippet_cap { 148 match ctx.config.snippet_cap {
150 Some(cap) => { 149 Some(cap) => {
151 let snippet = format!("{} {{\n $0\n}}", signature); 150 let snippet = format!("{} {{\n $0\n}}", function_decl);
152 builder.snippet_edit(cap, TextEdit::replace(range, snippet)) 151 builder.snippet_edit(cap, TextEdit::replace(range, snippet))
153 } 152 }
154 None => { 153 None => {
155 let header = format!("{} {{", signature); 154 let header = format!("{} {{", function_decl);
156 builder.text_edit(TextEdit::replace(range, header)) 155 builder.text_edit(TextEdit::replace(range, header))
157 } 156 }
158 } 157 }
diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs
index e29b82017..c7b74e635 100644
--- a/crates/ra_ide/src/completion/presentation.rs
+++ b/crates/ra_ide/src/completion/presentation.rs
@@ -11,7 +11,7 @@ use crate::{
11 completion_item::Builder, CompletionContext, CompletionItem, CompletionItemKind, 11 completion_item::Builder, CompletionContext, CompletionItem, CompletionItemKind,
12 CompletionKind, Completions, 12 CompletionKind, Completions,
13 }, 13 },
14 display::{const_label, function_signature::FunctionSignature, macro_label, type_label}, 14 display::{const_label, function_declaration, macro_label, type_label},
15 CompletionScore, RootDatabase, 15 CompletionScore, RootDatabase,
16}; 16};
17 17
@@ -195,7 +195,6 @@ impl Completions {
195 195
196 let name = local_name.unwrap_or_else(|| func.name(ctx.db).to_string()); 196 let name = local_name.unwrap_or_else(|| func.name(ctx.db).to_string());
197 let ast_node = func.source(ctx.db).value; 197 let ast_node = func.source(ctx.db).value;
198 let function_signature = FunctionSignature::from(&ast_node);
199 198
200 let mut builder = 199 let mut builder =
201 CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.clone()) 200 CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.clone())
@@ -206,13 +205,14 @@ impl Completions {
206 }) 205 })
207 .set_documentation(func.docs(ctx.db)) 206 .set_documentation(func.docs(ctx.db))
208 .set_deprecated(is_deprecated(func, ctx.db)) 207 .set_deprecated(is_deprecated(func, ctx.db))
209 .detail(function_signature.to_string()); 208 .detail(function_declaration(&ast_node));
210 209
211 let params = function_signature 210 let params = ast_node
212 .parameter_names 211 .param_list()
213 .iter() 212 .into_iter()
214 .skip(if function_signature.has_self_param { 1 } else { 0 }) 213 .flat_map(|it| it.params())
215 .map(|name| name.trim_start_matches('_').into()) 214 .flat_map(|it| it.pat())
215 .map(|pat| pat.to_string().trim_start_matches('_').into())
216 .collect(); 216 .collect();
217 217
218 builder = builder.add_call_parens(ctx, name, Params::Named(params)); 218 builder = builder.add_call_parens(ctx, name, Params::Named(params));
diff --git a/crates/ra_ide/src/display.rs b/crates/ra_ide/src/display.rs
index 1ec946369..6d4151dd8 100644
--- a/crates/ra_ide/src/display.rs
+++ b/crates/ra_ide/src/display.rs
@@ -1,7 +1,6 @@
1//! This module contains utilities for turning SyntaxNodes and HIR types 1//! This module contains utilities for turning SyntaxNodes and HIR types
2//! into types that may be used to render in a UI. 2//! into types that may be used to render in a UI.
3 3
4pub(crate) mod function_signature;
5mod navigation_target; 4mod navigation_target;
6mod short_label; 5mod short_label;
7 6
@@ -10,13 +9,49 @@ use ra_syntax::{
10 SyntaxKind::{ATTR, COMMENT}, 9 SyntaxKind::{ATTR, COMMENT},
11}; 10};
12 11
13pub(crate) use navigation_target::{ToNav, TryToNav}; 12use ast::VisibilityOwner;
14pub(crate) use short_label::ShortLabel; 13use stdx::format_to;
15 14
16pub use navigation_target::NavigationTarget; 15pub use navigation_target::NavigationTarget;
16pub(crate) use navigation_target::{ToNav, TryToNav};
17pub(crate) use short_label::ShortLabel;
17 18
18pub(crate) fn function_label(node: &ast::FnDef) -> String { 19pub(crate) fn function_declaration(node: &ast::FnDef) -> String {
19 function_signature::FunctionSignature::from(node).to_string() 20 let mut buf = String::new();
21 if let Some(vis) = node.visibility() {
22 format_to!(buf, "{} ", vis);
23 }
24 if node.async_token().is_some() {
25 format_to!(buf, "async ");
26 }
27 if node.const_token().is_some() {
28 format_to!(buf, "const ");
29 }
30 if node.unsafe_token().is_some() {
31 format_to!(buf, "unsafe ");
32 }
33 if let Some(abi) = node.abi() {
34 // Keyword `extern` is included in the string.
35 format_to!(buf, "{} ", abi);
36 }
37 if let Some(name) = node.name() {
38 format_to!(buf, "fn {}", name)
39 }
40 if let Some(type_params) = node.type_param_list() {
41 format_to!(buf, "{}", type_params);
42 }
43 if let Some(param_list) = node.param_list() {
44 format_to!(buf, "{}", param_list);
45 }
46 if let Some(ret_type) = node.ret_type() {
47 if ret_type.type_ref().is_some() {
48 format_to!(buf, " {}", ret_type);
49 }
50 }
51 if let Some(where_clause) = node.where_clause() {
52 format_to!(buf, "\n{}", where_clause);
53 }
54 buf
20} 55}
21 56
22pub(crate) fn const_label(node: &ast::ConstDef) -> String { 57pub(crate) fn const_label(node: &ast::ConstDef) -> String {
@@ -41,23 +76,6 @@ pub(crate) fn type_label(node: &ast::TypeAliasDef) -> String {
41 label.trim().to_owned() 76 label.trim().to_owned()
42} 77}
43 78
44pub(crate) fn generic_parameters<N: TypeParamsOwner>(node: &N) -> Vec<String> {
45 let mut res = vec![];
46 if let Some(type_params) = node.type_param_list() {
47 res.extend(type_params.lifetime_params().map(|p| p.syntax().text().to_string()));
48 res.extend(type_params.type_params().map(|p| p.syntax().text().to_string()));
49 }
50 res
51}
52
53pub(crate) fn where_predicates<N: TypeParamsOwner>(node: &N) -> Vec<String> {
54 let mut res = vec![];
55 if let Some(clause) = node.where_clause() {
56 res.extend(clause.predicates().map(|p| p.syntax().text().to_string()));
57 }
58 res
59}
60
61pub(crate) fn macro_label(node: &ast::MacroCall) -> String { 79pub(crate) fn macro_label(node: &ast::MacroCall) -> String {
62 let name = node.name().map(|name| name.syntax().text().to_string()).unwrap_or_default(); 80 let name = node.name().map(|name| name.syntax().text().to_string()).unwrap_or_default();
63 let vis = if node.has_atom_attr("macro_export") { "#[macro_export]\n" } else { "" }; 81 let vis = if node.has_atom_attr("macro_export") { "#[macro_export]\n" } else { "" };
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 9b7220d1f..000000000
--- a/crates/ra_ide/src/display/function_signature.rs
+++ /dev/null
@@ -1,298 +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)
5use std::{
6 convert::From,
7 fmt::{self, Display},
8};
9
10use hir::{Docs, Documentation, HasSource, HirDisplay};
11use ra_ide_db::RootDatabase;
12use ra_syntax::ast::{self, AstNode, NameOwner, VisibilityOwner};
13use stdx::{split_delim, SepBy};
14
15use crate::display::{generic_parameters, where_predicates};
16
17#[derive(Debug)]
18pub(crate) enum CallableKind {
19 Function,
20 StructConstructor,
21 VariantConstructor,
22}
23
24/// Contains information about a function signature
25#[derive(Debug)]
26pub(crate) struct FunctionSignature {
27 pub(crate) kind: CallableKind,
28 /// Optional visibility
29 pub(crate) visibility: Option<String>,
30 /// Qualifiers like `async`, `unsafe`, ...
31 pub(crate) qualifier: FunctionQualifier,
32 /// Name of the function
33 pub(crate) name: Option<String>,
34 /// Documentation for the function
35 pub(crate) doc: Option<Documentation>,
36 /// Generic parameters
37 pub(crate) generic_parameters: Vec<String>,
38 /// Parameters of the function
39 pub(crate) parameters: Vec<String>,
40 /// Parameter names of the function
41 pub(crate) parameter_names: Vec<String>,
42 /// Parameter types of the function
43 pub(crate) parameter_types: Vec<String>,
44 /// Optional return type
45 pub(crate) ret_type: Option<String>,
46 /// Where predicates
47 pub(crate) where_predicates: Vec<String>,
48 /// Self param presence
49 pub(crate) has_self_param: bool,
50}
51
52#[derive(Debug, Default)]
53pub(crate) struct FunctionQualifier {
54 // `async` and `const` are mutually exclusive. Do we need to enforcing it here?
55 pub(crate) is_async: bool,
56 pub(crate) is_const: bool,
57 pub(crate) is_unsafe: bool,
58 /// The string `extern ".."`
59 pub(crate) extern_abi: Option<String>,
60}
61
62impl FunctionSignature {
63 pub(crate) fn from_hir(db: &RootDatabase, function: hir::Function) -> Self {
64 let ast_node = function.source(db).value;
65 let mut res = FunctionSignature::from(&ast_node);
66 res.doc = function.docs(db);
67 res
68 }
69
70 pub(crate) fn from_struct(db: &RootDatabase, st: hir::Struct) -> Option<Self> {
71 let node: ast::StructDef = st.source(db).value;
72 if let ast::StructKind::Record(_) = node.kind() {
73 return None;
74 };
75
76 let mut params = vec![];
77 let mut parameter_types = vec![];
78 for field in st.fields(db).into_iter() {
79 let ty = field.signature_ty(db);
80 let raw_param = format!("{}", ty.display(db));
81
82 if let Some(param_type) = raw_param.split(':').nth(1).and_then(|it| it.get(1..)) {
83 parameter_types.push(param_type.to_string());
84 } else {
85 // useful when you have tuple struct
86 parameter_types.push(raw_param.clone());
87 }
88 params.push(raw_param);
89 }
90
91 Some(FunctionSignature {
92 kind: CallableKind::StructConstructor,
93 visibility: node.visibility().map(|n| n.syntax().text().to_string()),
94 // Do we need `const`?
95 qualifier: Default::default(),
96 name: node.name().map(|n| n.text().to_string()),
97 ret_type: node.name().map(|n| n.text().to_string()),
98 parameters: params,
99 parameter_names: vec![],
100 parameter_types,
101 generic_parameters: generic_parameters(&node),
102 where_predicates: where_predicates(&node),
103 doc: st.docs(db),
104 has_self_param: false,
105 })
106 }
107
108 pub(crate) fn from_enum_variant(db: &RootDatabase, variant: hir::EnumVariant) -> Option<Self> {
109 let node: ast::EnumVariant = variant.source(db).value;
110 match node.kind() {
111 ast::StructKind::Record(_) | ast::StructKind::Unit => return None,
112 _ => (),
113 };
114
115 let parent_name = variant.parent_enum(db).name(db).to_string();
116
117 let name = format!("{}::{}", parent_name, variant.name(db));
118
119 let mut params = vec![];
120 let mut parameter_types = vec![];
121 for field in variant.fields(db).into_iter() {
122 let ty = field.signature_ty(db);
123 let raw_param = format!("{}", ty.display(db));
124 if let Some(param_type) = raw_param.split(':').nth(1).and_then(|it| it.get(1..)) {
125 parameter_types.push(param_type.to_string());
126 } else {
127 // The unwrap_or_else is useful when you have tuple
128 parameter_types.push(raw_param);
129 }
130 let name = field.name(db);
131
132 params.push(format!("{}: {}", name, ty.display(db)));
133 }
134
135 Some(FunctionSignature {
136 kind: CallableKind::VariantConstructor,
137 visibility: None,
138 // Do we need `const`?
139 qualifier: Default::default(),
140 name: Some(name),
141 ret_type: None,
142 parameters: params,
143 parameter_names: vec![],
144 parameter_types,
145 generic_parameters: vec![],
146 where_predicates: vec![],
147 doc: variant.docs(db),
148 has_self_param: false,
149 })
150 }
151}
152
153impl From<&'_ ast::FnDef> for FunctionSignature {
154 fn from(node: &ast::FnDef) -> FunctionSignature {
155 fn param_list(node: &ast::FnDef) -> (bool, Vec<String>, Vec<String>) {
156 let mut res = vec![];
157 let mut res_types = vec![];
158 let mut has_self_param = false;
159 if let Some(param_list) = node.param_list() {
160 if let Some(self_param) = param_list.self_param() {
161 has_self_param = true;
162 let raw_param = self_param.syntax().text().to_string();
163
164 res_types.push(
165 raw_param
166 .split(':')
167 .nth(1)
168 .and_then(|it| it.get(1..))
169 .unwrap_or_else(|| "Self")
170 .to_string(),
171 );
172 res.push(raw_param);
173 }
174
175 // macro-generated functions are missing whitespace
176 fn fmt_param(param: ast::Param) -> String {
177 let text = param.syntax().text().to_string();
178 match split_delim(&text, ':') {
179 Some((left, right)) => format!("{}: {}", left.trim(), right.trim()),
180 _ => text,
181 }
182 }
183
184 res.extend(param_list.params().map(fmt_param));
185 res_types.extend(param_list.params().map(|param| {
186 let param_text = param.syntax().text().to_string();
187 match param_text.split(':').nth(1).and_then(|it| it.get(1..)) {
188 Some(it) => it.to_string(),
189 None => param_text,
190 }
191 }));
192 }
193 (has_self_param, res, res_types)
194 }
195
196 fn param_name_list(node: &ast::FnDef) -> Vec<String> {
197 let mut res = vec![];
198 if let Some(param_list) = node.param_list() {
199 if let Some(self_param) = param_list.self_param() {
200 res.push(self_param.syntax().text().to_string())
201 }
202
203 res.extend(
204 param_list
205 .params()
206 .map(|param| {
207 Some(
208 param
209 .pat()?
210 .syntax()
211 .descendants()
212 .find_map(ast::Name::cast)?
213 .text()
214 .to_string(),
215 )
216 })
217 .map(|param| param.unwrap_or_default()),
218 );
219 }
220 res
221 }
222
223 let (has_self_param, parameters, parameter_types) = param_list(node);
224
225 FunctionSignature {
226 kind: CallableKind::Function,
227 visibility: node.visibility().map(|n| n.syntax().text().to_string()),
228 qualifier: FunctionQualifier {
229 is_async: node.async_token().is_some(),
230 is_const: node.const_token().is_some(),
231 is_unsafe: node.unsafe_token().is_some(),
232 extern_abi: node.abi().map(|n| n.to_string()),
233 },
234 name: node.name().map(|n| n.text().to_string()),
235 ret_type: node
236 .ret_type()
237 .and_then(|r| r.type_ref())
238 .map(|n| n.syntax().text().to_string()),
239 parameters,
240 parameter_names: param_name_list(node),
241 parameter_types,
242 generic_parameters: generic_parameters(node),
243 where_predicates: where_predicates(node),
244 // docs are processed separately
245 doc: None,
246 has_self_param,
247 }
248 }
249}
250
251impl Display for FunctionSignature {
252 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
253 if let Some(t) = &self.visibility {
254 write!(f, "{} ", t)?;
255 }
256
257 if self.qualifier.is_async {
258 write!(f, "async ")?;
259 }
260
261 if self.qualifier.is_const {
262 write!(f, "const ")?;
263 }
264
265 if self.qualifier.is_unsafe {
266 write!(f, "unsafe ")?;
267 }
268
269 if let Some(extern_abi) = &self.qualifier.extern_abi {
270 // Keyword `extern` is included in the string.
271 write!(f, "{} ", extern_abi)?;
272 }
273
274 if let Some(name) = &self.name {
275 match self.kind {
276 CallableKind::Function => write!(f, "fn {}", name)?,
277 CallableKind::StructConstructor => write!(f, "struct {}", name)?,
278 CallableKind::VariantConstructor => write!(f, "{}", name)?,
279 }
280 }
281
282 if !self.generic_parameters.is_empty() {
283 write!(f, "{}", self.generic_parameters.iter().sep_by(", ").surround_with("<", ">"))?;
284 }
285
286 write!(f, "{}", self.parameters.iter().sep_by(", ").surround_with("(", ")"))?;
287
288 if let Some(t) = &self.ret_type {
289 write!(f, " -> {}", t)?;
290 }
291
292 if !self.where_predicates.is_empty() {
293 write!(f, "\nwhere {}", self.where_predicates.iter().sep_by(",\n "))?;
294 }
295
296 Ok(())
297 }
298}
diff --git a/crates/ra_ide/src/display/short_label.rs b/crates/ra_ide/src/display/short_label.rs
index d37260e96..5588130a1 100644
--- a/crates/ra_ide/src/display/short_label.rs
+++ b/crates/ra_ide/src/display/short_label.rs
@@ -9,7 +9,7 @@ pub(crate) trait ShortLabel {
9 9
10impl ShortLabel for ast::FnDef { 10impl ShortLabel for ast::FnDef {
11 fn short_label(&self) -> Option<String> { 11 fn short_label(&self) -> Option<String> {
12 Some(crate::display::function_label(self)) 12 Some(crate::display::function_declaration(self))
13 } 13 }
14} 14}
15 15
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 @@
1use hir::{Adt, HirDisplay, Semantics, Type}; 1use hir::{Adt, Callable, HirDisplay, Semantics, Type};
2use ra_ide_db::RootDatabase; 2use ra_ide_db::RootDatabase;
3use ra_prof::profile; 3use ra_prof::profile;
4use ra_syntax::{ 4use ra_syntax::{
@@ -7,7 +7,9 @@ use ra_syntax::{
7}; 7};
8use stdx::to_lower_snake_case; 8use stdx::to_lower_snake_case;
9 9
10use crate::{display::function_signature::FunctionSignature, FileId}; 10use crate::FileId;
11use ast::NameOwner;
12use either::Either;
11 13
12#[derive(Clone, Debug, PartialEq, Eq)] 14#[derive(Clone, Debug, PartialEq, Eq)]
13pub struct InlayHintsConfig { 15pub struct InlayHintsConfig {
@@ -150,23 +152,26 @@ fn get_param_name_hints(
150 _ => return None, 152 _ => return None,
151 }; 153 };
152 154
153 let fn_signature = get_fn_signature(sema, &expr)?; 155 let callable = get_callable(sema, &expr)?;
154 let n_params_to_skip = 156 let hints = callable
155 if fn_signature.has_self_param && matches!(&expr, ast::Expr::MethodCallExpr(_)) { 157 .params(sema.db)
156 1 158 .into_iter()
157 } else {
158 0
159 };
160 let hints = fn_signature
161 .parameter_names
162 .iter()
163 .skip(n_params_to_skip)
164 .zip(args) 159 .zip(args)
165 .filter(|(param, arg)| should_show_param_name_hint(sema, &fn_signature, param, &arg)) 160 .filter_map(|((param, _ty), arg)| match param? {
161 Either::Left(self_param) => Some((self_param.to_string(), arg)),
162 Either::Right(pat) => {
163 let param_name = match pat {
164 ast::Pat::BindPat(it) => it.name()?.to_string(),
165 it => it.to_string(),
166 };
167 Some((param_name, arg))
168 }
169 })
170 .filter(|(param_name, arg)| should_show_param_name_hint(sema, &callable, &param_name, &arg))
166 .map(|(param_name, arg)| InlayHint { 171 .map(|(param_name, arg)| InlayHint {
167 range: arg.syntax().text_range(), 172 range: arg.syntax().text_range(),
168 kind: InlayKind::ParameterHint, 173 kind: InlayKind::ParameterHint,
169 label: param_name.into(), 174 label: param_name.to_string().into(),
170 }); 175 });
171 176
172 acc.extend(hints); 177 acc.extend(hints);
@@ -250,28 +255,26 @@ fn should_not_display_type_hint(db: &RootDatabase, bind_pat: &ast::BindPat, pat_
250 255
251fn should_show_param_name_hint( 256fn should_show_param_name_hint(
252 sema: &Semantics<RootDatabase>, 257 sema: &Semantics<RootDatabase>,
253 fn_signature: &FunctionSignature, 258 callable: &Callable,
254 param_name: &str, 259 param_name: &str,
255 argument: &ast::Expr, 260 argument: &ast::Expr,
256) -> bool { 261) -> bool {
257 let param_name = param_name.trim_start_matches('_'); 262 let param_name = param_name.trim_start_matches('_');
263 let fn_name = match callable.kind() {
264 hir::CallableKind::Function(it) => Some(it.name(sema.db).to_string()),
265 hir::CallableKind::TupleStruct(_) | hir::CallableKind::TupleEnumVariant(_) => None,
266 };
258 if param_name.is_empty() 267 if param_name.is_empty()
259 || Some(param_name) == fn_signature.name.as_ref().map(|s| s.trim_start_matches('_')) 268 || Some(param_name) == fn_name.as_ref().map(|s| s.trim_start_matches('_'))
260 || is_argument_similar_to_param_name(sema, argument, param_name) 269 || is_argument_similar_to_param_name(sema, argument, param_name)
261 || param_name.starts_with("ra_fixture") 270 || param_name.starts_with("ra_fixture")
262 { 271 {
263 return false; 272 return false;
264 } 273 }
265 274
266 let parameters_len = if fn_signature.has_self_param {
267 fn_signature.parameters.len() - 1
268 } else {
269 fn_signature.parameters.len()
270 };
271
272 // avoid displaying hints for common functions like map, filter, etc. 275 // avoid displaying hints for common functions like map, filter, etc.
273 // or other obvious words used in std 276 // or other obvious words used in std
274 !(parameters_len == 1 && is_obvious_param(param_name)) 277 !(callable.n_params() == 1 && is_obvious_param(param_name))
275} 278}
276 279
277fn is_argument_similar_to_param_name( 280fn is_argument_similar_to_param_name(
@@ -318,27 +321,10 @@ fn is_obvious_param(param_name: &str) -> bool {
318 param_name.len() == 1 || is_obvious_param_name 321 param_name.len() == 1 || is_obvious_param_name
319} 322}
320 323
321fn get_fn_signature(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<FunctionSignature> { 324fn get_callable(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<Callable> {
322 match expr { 325 match expr {
323 ast::Expr::CallExpr(expr) => { 326 ast::Expr::CallExpr(expr) => sema.type_of_expr(&expr.expr()?)?.as_callable(sema.db),
324 // FIXME: Type::as_callable is broken for closures 327 ast::Expr::MethodCallExpr(expr) => sema.resolve_method_call_as_callable(expr),
325 let callable = sema.type_of_expr(&expr.expr()?)?.as_callable(sema.db)?;
326 match callable.kind() {
327 hir::CallableKind::Function(it) => {
328 Some(FunctionSignature::from_hir(sema.db, it.into()))
329 }
330 hir::CallableKind::TupleStruct(it) => {
331 FunctionSignature::from_struct(sema.db, it.into())
332 }
333 hir::CallableKind::TupleEnumVariant(it) => {
334 FunctionSignature::from_enum_variant(sema.db, it.into())
335 }
336 }
337 }
338 ast::Expr::MethodCallExpr(expr) => {
339 let fn_def = sema.resolve_method_call(&expr)?;
340 Some(FunctionSignature::from_hir(sema.db, fn_def))
341 }
342 _ => None, 328 _ => None,
343 } 329 }
344} 330}
@@ -360,7 +346,7 @@ mod tests {
360 let inlay_hints = analysis.inlay_hints(file_id, &config).unwrap(); 346 let inlay_hints = analysis.inlay_hints(file_id, &config).unwrap();
361 let actual = 347 let actual =
362 inlay_hints.into_iter().map(|it| (it.range, it.label.to_string())).collect::<Vec<_>>(); 348 inlay_hints.into_iter().map(|it| (it.range, it.label.to_string())).collect::<Vec<_>>();
363 assert_eq!(expected, actual); 349 assert_eq!(expected, actual, "\nExpected:\n{:#?}\n\nActual:\n{:#?}", expected, actual);
364 } 350 }
365 351
366 fn check_expect(config: InlayHintsConfig, ra_fixture: &str, expect: Expect) { 352 fn check_expect(config: InlayHintsConfig, ra_fixture: &str, expect: Expect) {