diff options
author | Vladyslav Katasonov <[email protected]> | 2021-02-16 20:48:15 +0000 |
---|---|---|
committer | Aleksey Kladov <[email protected]> | 2021-03-02 13:25:22 +0000 |
commit | afc68277f69572944fd81d61b126732ab29b5d17 (patch) | |
tree | 2ce079a2d48f83cd9520a48c3d6d1082626008fa /crates/ide_assists/src | |
parent | f915ab79fa1b11982b8e82e2db5b2486a893bed4 (diff) |
pull out suggest_name::* to utils; enchance heuristics
Diffstat (limited to 'crates/ide_assists/src')
-rw-r--r-- | crates/ide_assists/src/handlers/extract_variable.rs | 92 | ||||
-rw-r--r-- | crates/ide_assists/src/tests.rs | 46 | ||||
-rw-r--r-- | crates/ide_assists/src/utils.rs | 2 | ||||
-rw-r--r-- | crates/ide_assists/src/utils/suggest_name.rs | 770 |
4 files changed, 821 insertions, 89 deletions
diff --git a/crates/ide_assists/src/handlers/extract_variable.rs b/crates/ide_assists/src/handlers/extract_variable.rs index 2c47be987..312ac7ac4 100644 --- a/crates/ide_assists/src/handlers/extract_variable.rs +++ b/crates/ide_assists/src/handlers/extract_variable.rs | |||
@@ -1,7 +1,6 @@ | |||
1 | use itertools::Itertools; | 1 | use stdx::format_to; |
2 | use stdx::{format_to, to_lower_snake_case}; | ||
3 | use syntax::{ | 2 | use syntax::{ |
4 | ast::{self, AstNode, NameOwner}, | 3 | ast::{self, AstNode}, |
5 | SyntaxKind::{ | 4 | SyntaxKind::{ |
6 | BLOCK_EXPR, BREAK_EXPR, CLOSURE_EXPR, COMMENT, LOOP_EXPR, MATCH_ARM, PATH_EXPR, RETURN_EXPR, | 5 | BLOCK_EXPR, BREAK_EXPR, CLOSURE_EXPR, COMMENT, LOOP_EXPR, MATCH_ARM, PATH_EXPR, RETURN_EXPR, |
7 | }, | 6 | }, |
@@ -9,7 +8,7 @@ use syntax::{ | |||
9 | }; | 8 | }; |
10 | use test_utils::mark; | 9 | use test_utils::mark; |
11 | 10 | ||
12 | use crate::{AssistContext, AssistId, AssistKind, Assists}; | 11 | use crate::{utils::suggest_name, AssistContext, AssistId, AssistKind, Assists}; |
13 | 12 | ||
14 | // Assist: extract_variable | 13 | // Assist: extract_variable |
15 | // | 14 | // |
@@ -55,7 +54,7 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext) -> Option | |||
55 | 54 | ||
56 | let var_name = match &field_shorthand { | 55 | let var_name = match &field_shorthand { |
57 | Some(it) => it.to_string(), | 56 | Some(it) => it.to_string(), |
58 | None => suggest_variable_name(ctx, &to_extract), | 57 | None => suggest_name::variable(&to_extract, &ctx.sema), |
59 | }; | 58 | }; |
60 | let expr_range = match &field_shorthand { | 59 | let expr_range = match &field_shorthand { |
61 | Some(it) => it.syntax().text_range().cover(to_extract.syntax().text_range()), | 60 | Some(it) => it.syntax().text_range().cover(to_extract.syntax().text_range()), |
@@ -174,89 +173,6 @@ impl Anchor { | |||
174 | } | 173 | } |
175 | } | 174 | } |
176 | 175 | ||
177 | fn suggest_variable_name(ctx: &AssistContext, expr: &ast::Expr) -> String { | ||
178 | // FIXME: account for existing names in the scope | ||
179 | suggest_name_from_param(ctx, expr) | ||
180 | .or_else(|| suggest_name_from_func(expr)) | ||
181 | .or_else(|| suggest_name_from_method(expr)) | ||
182 | .or_else(|| suggest_name_by_type(ctx, expr)) | ||
183 | .unwrap_or_else(|| "var_name".to_string()) | ||
184 | } | ||
185 | |||
186 | fn normalize_name(name: &str) -> Option<String> { | ||
187 | let name = to_lower_snake_case(name); | ||
188 | |||
189 | let useless_names = ["new", "default", "some", "none", "ok", "err"]; | ||
190 | if useless_names.contains(&name.as_str()) { | ||
191 | return None; | ||
192 | } | ||
193 | |||
194 | Some(name) | ||
195 | } | ||
196 | |||
197 | fn suggest_name_from_func(expr: &ast::Expr) -> Option<String> { | ||
198 | let call = match expr { | ||
199 | ast::Expr::CallExpr(call) => call, | ||
200 | _ => return None, | ||
201 | }; | ||
202 | let func = match call.expr()? { | ||
203 | ast::Expr::PathExpr(path) => path, | ||
204 | _ => return None, | ||
205 | }; | ||
206 | let ident = func.path()?.segment()?.name_ref()?.ident_token()?; | ||
207 | normalize_name(ident.text()) | ||
208 | } | ||
209 | |||
210 | fn suggest_name_from_method(expr: &ast::Expr) -> Option<String> { | ||
211 | let method = match expr { | ||
212 | ast::Expr::MethodCallExpr(call) => call, | ||
213 | _ => return None, | ||
214 | }; | ||
215 | let ident = method.name_ref()?.ident_token()?; | ||
216 | normalize_name(ident.text()) | ||
217 | } | ||
218 | |||
219 | fn suggest_name_from_param(ctx: &AssistContext, expr: &ast::Expr) -> Option<String> { | ||
220 | let arg_list = expr.syntax().parent().and_then(ast::ArgList::cast)?; | ||
221 | let args_parent = arg_list.syntax().parent()?; | ||
222 | let func = if let Some(call) = ast::CallExpr::cast(args_parent.clone()) { | ||
223 | let func = call.expr()?; | ||
224 | let func_ty = ctx.sema.type_of_expr(&func)?; | ||
225 | func_ty.as_callable(ctx.db())? | ||
226 | } else if let Some(method) = ast::MethodCallExpr::cast(args_parent) { | ||
227 | ctx.sema.resolve_method_call_as_callable(&method)? | ||
228 | } else { | ||
229 | return None; | ||
230 | }; | ||
231 | |||
232 | let (idx, _) = arg_list.args().find_position(|it| it == expr).unwrap(); | ||
233 | let (pat, _) = func.params(ctx.db()).into_iter().nth(idx)?; | ||
234 | let param = match pat? { | ||
235 | either::Either::Right(ast::Pat::IdentPat(param)) => param, | ||
236 | _ => return None, | ||
237 | }; | ||
238 | let name = param.name()?; | ||
239 | normalize_name(&name.to_string()) | ||
240 | } | ||
241 | |||
242 | fn suggest_name_by_type(ctx: &AssistContext, expr: &ast::Expr) -> Option<String> { | ||
243 | let ty = ctx.sema.type_of_expr(expr)?; | ||
244 | let ty = ty.remove_ref().unwrap_or(ty); | ||
245 | |||
246 | name_from_type(ty, ctx) | ||
247 | } | ||
248 | |||
249 | fn name_from_type(ty: hir::Type, ctx: &AssistContext) -> Option<String> { | ||
250 | let name = if let Some(adt) = ty.as_adt() { | ||
251 | adt.name(ctx.db()).to_string() | ||
252 | } else if let Some(trait_) = ty.as_dyn_trait() { | ||
253 | trait_.name(ctx.db()).to_string() | ||
254 | } else { | ||
255 | return None; | ||
256 | }; | ||
257 | normalize_name(&name) | ||
258 | } | ||
259 | |||
260 | #[cfg(test)] | 176 | #[cfg(test)] |
261 | mod tests { | 177 | mod tests { |
262 | use test_utils::mark; | 178 | use test_utils::mark; |
diff --git a/crates/ide_assists/src/tests.rs b/crates/ide_assists/src/tests.rs index b7f616760..66820058b 100644 --- a/crates/ide_assists/src/tests.rs +++ b/crates/ide_assists/src/tests.rs | |||
@@ -12,7 +12,7 @@ use ide_db::{ | |||
12 | RootDatabase, | 12 | RootDatabase, |
13 | }; | 13 | }; |
14 | use stdx::{format_to, trim_indent}; | 14 | use stdx::{format_to, trim_indent}; |
15 | use syntax::TextRange; | 15 | use syntax::{ast, AstNode, TextRange}; |
16 | use test_utils::{assert_eq_text, extract_offset}; | 16 | use test_utils::{assert_eq_text, extract_offset}; |
17 | 17 | ||
18 | use crate::{handlers::Handler, Assist, AssistConfig, AssistContext, AssistKind, Assists}; | 18 | use crate::{handlers::Handler, Assist, AssistConfig, AssistContext, AssistKind, Assists}; |
@@ -180,6 +180,50 @@ fn labels(assists: &[Assist]) -> String { | |||
180 | labels.into_iter().collect::<String>() | 180 | labels.into_iter().collect::<String>() |
181 | } | 181 | } |
182 | 182 | ||
183 | pub(crate) type NameSuggestion = fn(&ast::Expr, &Semantics<'_, RootDatabase>) -> Option<String>; | ||
184 | |||
185 | #[track_caller] | ||
186 | pub(crate) fn check_name_suggestion( | ||
187 | suggestion: NameSuggestion, | ||
188 | ra_fixture: &str, | ||
189 | suggested_name: &str, | ||
190 | ) { | ||
191 | check_name(suggestion, ra_fixture, Some(suggested_name)); | ||
192 | } | ||
193 | |||
194 | #[track_caller] | ||
195 | pub(crate) fn check_name_suggestion_not_applicable(suggestion: NameSuggestion, ra_fixture: &str) { | ||
196 | check_name(suggestion, ra_fixture, None); | ||
197 | } | ||
198 | |||
199 | #[track_caller] | ||
200 | fn check_name(suggestion: NameSuggestion, ra_fixture: &str, expected: Option<&str>) { | ||
201 | let (db, file_with_carret_id, range_or_offset) = RootDatabase::with_range_or_offset(ra_fixture); | ||
202 | let frange = FileRange { file_id: file_with_carret_id, range: range_or_offset.into() }; | ||
203 | |||
204 | let sema = Semantics::new(&db); | ||
205 | let source_file = sema.parse(frange.file_id); | ||
206 | let element = source_file.syntax().covering_element(frange.range); | ||
207 | let expr = | ||
208 | element.ancestors().find_map(ast::Expr::cast).expect("selection is not an expression"); | ||
209 | assert_eq!( | ||
210 | expr.syntax().text_range(), | ||
211 | frange.range, | ||
212 | "selection is not an expression(yet contained in one)" | ||
213 | ); | ||
214 | |||
215 | let name = suggestion(&expr, &sema); | ||
216 | |||
217 | match (name, expected) { | ||
218 | (Some(name), Some(expected_name)) => { | ||
219 | assert_eq_text!(&name, expected_name); | ||
220 | } | ||
221 | (Some(_), None) => panic!("name suggestion should not be applicable"), | ||
222 | (None, Some(_)) => panic!("name suggestion is not applicable"), | ||
223 | (None, None) => (), | ||
224 | } | ||
225 | } | ||
226 | |||
183 | #[test] | 227 | #[test] |
184 | fn assist_order_field_struct() { | 228 | fn assist_order_field_struct() { |
185 | let before = "struct Foo { $0bar: u32 }"; | 229 | let before = "struct Foo { $0bar: u32 }"; |
diff --git a/crates/ide_assists/src/utils.rs b/crates/ide_assists/src/utils.rs index 880ab6fe3..62f959082 100644 --- a/crates/ide_assists/src/utils.rs +++ b/crates/ide_assists/src/utils.rs | |||
@@ -1,5 +1,7 @@ | |||
1 | //! Assorted functions shared by several assists. | 1 | //! Assorted functions shared by several assists. |
2 | 2 | ||
3 | pub(crate) mod suggest_name; | ||
4 | |||
3 | use std::ops; | 5 | use std::ops; |
4 | 6 | ||
5 | use ast::TypeBoundsOwner; | 7 | use ast::TypeBoundsOwner; |
diff --git a/crates/ide_assists/src/utils/suggest_name.rs b/crates/ide_assists/src/utils/suggest_name.rs new file mode 100644 index 000000000..345e9af40 --- /dev/null +++ b/crates/ide_assists/src/utils/suggest_name.rs | |||
@@ -0,0 +1,770 @@ | |||
1 | //! This module contains functions to suggest names for expressions, functions and other items | ||
2 | |||
3 | use hir::Semantics; | ||
4 | use ide_db::RootDatabase; | ||
5 | use itertools::Itertools; | ||
6 | use stdx::to_lower_snake_case; | ||
7 | use syntax::{ | ||
8 | ast::{self, NameOwner}, | ||
9 | match_ast, AstNode, | ||
10 | }; | ||
11 | |||
12 | /// Trait names, that will be ignored when in `impl Trait` and `dyn Trait` | ||
13 | const USELESS_TRAITS: &[&str] = &["Send", "Sync", "Copy", "Clone", "Eq", "PartialEq"]; | ||
14 | /// Identifier names that won't be suggested, ever | ||
15 | /// | ||
16 | /// **NOTE**: they all must be snake lower case | ||
17 | const USELESS_NAMES: &[&str] = | ||
18 | &["new", "default", "option", "some", "none", "ok", "err", "str", "string"]; | ||
19 | /// Generic types replaced by their first argument | ||
20 | /// | ||
21 | /// # Examples | ||
22 | /// `Option<Name>` -> `Name` | ||
23 | /// `Result<User, Error>` -> `User` | ||
24 | const WRAPPER_TYPES: &[&str] = &["Box", "Option", "Result"]; | ||
25 | /// Prefixes to strip from methods names | ||
26 | /// | ||
27 | /// # Examples | ||
28 | /// `vec.as_slice()` -> `slice` | ||
29 | /// `args.into_config()` -> `config` | ||
30 | /// `bytes.to_vec()` -> `vec` | ||
31 | const USELESS_METHOD_PREFIXES: &[&str] = &["into_", "as_", "to_"]; | ||
32 | |||
33 | /// Suggest name of variable for given expression | ||
34 | /// | ||
35 | /// **NOTE**: it is caller's responsibility to guarantee uniqueness of the name. | ||
36 | /// I.e. it doesn't look for names in scope. | ||
37 | /// | ||
38 | /// # Current implementation | ||
39 | /// | ||
40 | /// In current implementation, the function tries to get the name from | ||
41 | /// the following sources: | ||
42 | /// | ||
43 | /// * if expr is an argument to function/method, use paramter name | ||
44 | /// * if expr is a function/method call, use function name | ||
45 | /// * expression type name if it exists (E.g. `()`, `fn() -> ()` or `!` do not have names) | ||
46 | /// * fallback: `var_name` | ||
47 | /// | ||
48 | /// It also applies heuristics to filter out less informative names | ||
49 | /// | ||
50 | /// Currently it sticks to the first name found. | ||
51 | pub(crate) fn variable(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> String { | ||
52 | from_param(expr, sema) | ||
53 | .or_else(|| from_call(expr)) | ||
54 | .or_else(|| from_type(expr, sema)) | ||
55 | .unwrap_or_else(|| "var_name".to_string()) | ||
56 | } | ||
57 | |||
58 | fn normalize(name: &str) -> Option<String> { | ||
59 | let name = to_lower_snake_case(name); | ||
60 | |||
61 | if USELESS_NAMES.contains(&name.as_str()) { | ||
62 | return None; | ||
63 | } | ||
64 | |||
65 | if !is_valid_name(&name) { | ||
66 | return None; | ||
67 | } | ||
68 | |||
69 | Some(name) | ||
70 | } | ||
71 | |||
72 | fn is_valid_name(name: &str) -> bool { | ||
73 | match syntax::lex_single_syntax_kind(name) { | ||
74 | Some((syntax::SyntaxKind::IDENT, _error)) => true, | ||
75 | _ => false, | ||
76 | } | ||
77 | } | ||
78 | |||
79 | fn from_call(expr: &ast::Expr) -> Option<String> { | ||
80 | from_func_call(expr).or_else(|| from_method_call(expr)) | ||
81 | } | ||
82 | |||
83 | fn from_func_call(expr: &ast::Expr) -> Option<String> { | ||
84 | let call = match expr { | ||
85 | ast::Expr::CallExpr(call) => call, | ||
86 | _ => return None, | ||
87 | }; | ||
88 | let func = match call.expr()? { | ||
89 | ast::Expr::PathExpr(path) => path, | ||
90 | _ => return None, | ||
91 | }; | ||
92 | let ident = func.path()?.segment()?.name_ref()?.ident_token()?; | ||
93 | normalize(ident.text()) | ||
94 | } | ||
95 | |||
96 | fn from_method_call(expr: &ast::Expr) -> Option<String> { | ||
97 | let method = match expr { | ||
98 | ast::Expr::MethodCallExpr(call) => call, | ||
99 | _ => return None, | ||
100 | }; | ||
101 | let ident = method.name_ref()?.ident_token()?; | ||
102 | let name = normalize(ident.text())?; | ||
103 | |||
104 | for prefix in USELESS_METHOD_PREFIXES { | ||
105 | if let Some(suffix) = name.strip_prefix(prefix) { | ||
106 | return Some(suffix.to_string()); | ||
107 | } | ||
108 | } | ||
109 | |||
110 | Some(name) | ||
111 | } | ||
112 | |||
113 | fn from_param(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> Option<String> { | ||
114 | let arg_list = expr.syntax().parent().and_then(ast::ArgList::cast)?; | ||
115 | let args_parent = arg_list.syntax().parent()?; | ||
116 | let func = match_ast! { | ||
117 | match args_parent { | ||
118 | ast::CallExpr(call) => { | ||
119 | let func = call.expr()?; | ||
120 | let func_ty = sema.type_of_expr(&func)?; | ||
121 | func_ty.as_callable(sema.db)? | ||
122 | }, | ||
123 | ast::MethodCallExpr(method) => sema.resolve_method_call_as_callable(&method)?, | ||
124 | _ => return None, | ||
125 | } | ||
126 | }; | ||
127 | |||
128 | let (idx, _) = arg_list.args().find_position(|it| it == expr).unwrap(); | ||
129 | let (pat, _) = func.params(sema.db).into_iter().nth(idx)?; | ||
130 | let pat = match pat? { | ||
131 | either::Either::Right(pat) => pat, | ||
132 | _ => return None, | ||
133 | }; | ||
134 | let name = var_name_from_pat(&pat)?; | ||
135 | normalize(&name.to_string()) | ||
136 | } | ||
137 | |||
138 | fn var_name_from_pat(pat: &ast::Pat) -> Option<ast::Name> { | ||
139 | match pat { | ||
140 | ast::Pat::IdentPat(var) => var.name(), | ||
141 | ast::Pat::RefPat(ref_pat) => var_name_from_pat(&ref_pat.pat()?), | ||
142 | ast::Pat::BoxPat(box_pat) => var_name_from_pat(&box_pat.pat()?), | ||
143 | _ => None, | ||
144 | } | ||
145 | } | ||
146 | |||
147 | fn from_type(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> Option<String> { | ||
148 | let ty = sema.type_of_expr(expr)?; | ||
149 | let ty = ty.remove_ref().unwrap_or(ty); | ||
150 | |||
151 | name_of_type(&ty, sema.db) | ||
152 | } | ||
153 | |||
154 | fn name_of_type(ty: &hir::Type, db: &RootDatabase) -> Option<String> { | ||
155 | let name = if let Some(adt) = ty.as_adt() { | ||
156 | let name = adt.name(db).to_string(); | ||
157 | |||
158 | if WRAPPER_TYPES.contains(&name.as_str()) { | ||
159 | let inner_ty = ty.type_parameters().next()?; | ||
160 | return name_of_type(&inner_ty, db); | ||
161 | } | ||
162 | |||
163 | name | ||
164 | } else if let Some(trait_) = ty.as_dyn_trait() { | ||
165 | trait_name(&trait_, db)? | ||
166 | } else if let Some(traits) = ty.as_impl_traits(db) { | ||
167 | let mut iter = traits.into_iter().filter_map(|t| trait_name(&t, db)); | ||
168 | let name = iter.next()?; | ||
169 | if iter.next().is_some() { | ||
170 | return None; | ||
171 | } | ||
172 | name | ||
173 | } else { | ||
174 | return None; | ||
175 | }; | ||
176 | normalize(&name) | ||
177 | } | ||
178 | |||
179 | fn trait_name(trait_: &hir::Trait, db: &RootDatabase) -> Option<String> { | ||
180 | let name = trait_.name(db).to_string(); | ||
181 | if USELESS_TRAITS.contains(&name.as_str()) { | ||
182 | return None; | ||
183 | } | ||
184 | Some(name) | ||
185 | } | ||
186 | |||
187 | #[cfg(test)] | ||
188 | mod tests { | ||
189 | use super::*; | ||
190 | |||
191 | use crate::tests::check_name_suggestion; | ||
192 | |||
193 | mod from_func_call { | ||
194 | use super::*; | ||
195 | |||
196 | #[test] | ||
197 | fn no_args() { | ||
198 | check_name_suggestion( | ||
199 | |e, _| from_func_call(e), | ||
200 | r#" | ||
201 | fn foo() { | ||
202 | $0bar()$0 | ||
203 | }"#, | ||
204 | "bar", | ||
205 | ); | ||
206 | } | ||
207 | |||
208 | #[test] | ||
209 | fn single_arg() { | ||
210 | check_name_suggestion( | ||
211 | |e, _| from_func_call(e), | ||
212 | r#" | ||
213 | fn foo() { | ||
214 | $0bar(1)$0 | ||
215 | }"#, | ||
216 | "bar", | ||
217 | ); | ||
218 | } | ||
219 | |||
220 | #[test] | ||
221 | fn many_args() { | ||
222 | check_name_suggestion( | ||
223 | |e, _| from_func_call(e), | ||
224 | r#" | ||
225 | fn foo() { | ||
226 | $0bar(1, 2, 3)$0 | ||
227 | }"#, | ||
228 | "bar", | ||
229 | ); | ||
230 | } | ||
231 | |||
232 | #[test] | ||
233 | fn path() { | ||
234 | check_name_suggestion( | ||
235 | |e, _| from_func_call(e), | ||
236 | r#" | ||
237 | fn foo() { | ||
238 | $0i32::bar(1, 2, 3)$0 | ||
239 | }"#, | ||
240 | "bar", | ||
241 | ); | ||
242 | } | ||
243 | |||
244 | #[test] | ||
245 | fn generic_params() { | ||
246 | check_name_suggestion( | ||
247 | |e, _| from_func_call(e), | ||
248 | r#" | ||
249 | fn foo() { | ||
250 | $0bar::<i32>(1, 2, 3)$0 | ||
251 | }"#, | ||
252 | "bar", | ||
253 | ); | ||
254 | } | ||
255 | } | ||
256 | |||
257 | mod from_method_call { | ||
258 | use super::*; | ||
259 | |||
260 | #[test] | ||
261 | fn no_args() { | ||
262 | check_name_suggestion( | ||
263 | |e, _| from_method_call(e), | ||
264 | r#" | ||
265 | fn foo() { | ||
266 | $0bar.frobnicate()$0 | ||
267 | }"#, | ||
268 | "frobnicate", | ||
269 | ); | ||
270 | } | ||
271 | |||
272 | #[test] | ||
273 | fn generic_params() { | ||
274 | check_name_suggestion( | ||
275 | |e, _| from_method_call(e), | ||
276 | r#" | ||
277 | fn foo() { | ||
278 | $0bar.frobnicate::<i32, u32>()$0 | ||
279 | }"#, | ||
280 | "frobnicate", | ||
281 | ); | ||
282 | } | ||
283 | |||
284 | #[test] | ||
285 | fn to_name() { | ||
286 | check_name_suggestion( | ||
287 | |e, _| from_method_call(e), | ||
288 | r#" | ||
289 | struct Args; | ||
290 | struct Config; | ||
291 | impl Args { | ||
292 | fn to_config(&self) -> Config {} | ||
293 | } | ||
294 | fn foo() { | ||
295 | $0Args.to_config()$0; | ||
296 | }"#, | ||
297 | "config", | ||
298 | ); | ||
299 | } | ||
300 | } | ||
301 | |||
302 | mod from_param { | ||
303 | use crate::tests::check_name_suggestion_not_applicable; | ||
304 | |||
305 | use super::*; | ||
306 | |||
307 | #[test] | ||
308 | fn plain_func() { | ||
309 | check_name_suggestion( | ||
310 | from_param, | ||
311 | r#" | ||
312 | fn bar(n: i32, m: u32); | ||
313 | fn foo() { | ||
314 | bar($01$0, 2) | ||
315 | }"#, | ||
316 | "n", | ||
317 | ); | ||
318 | } | ||
319 | |||
320 | #[test] | ||
321 | fn mut_param() { | ||
322 | check_name_suggestion( | ||
323 | from_param, | ||
324 | r#" | ||
325 | fn bar(mut n: i32, m: u32); | ||
326 | fn foo() { | ||
327 | bar($01$0, 2) | ||
328 | }"#, | ||
329 | "n", | ||
330 | ); | ||
331 | } | ||
332 | |||
333 | #[test] | ||
334 | fn func_does_not_exist() { | ||
335 | check_name_suggestion_not_applicable( | ||
336 | from_param, | ||
337 | r#" | ||
338 | fn foo() { | ||
339 | bar($01$0, 2) | ||
340 | }"#, | ||
341 | ); | ||
342 | } | ||
343 | |||
344 | #[test] | ||
345 | fn unnamed_param() { | ||
346 | check_name_suggestion_not_applicable( | ||
347 | from_param, | ||
348 | r#" | ||
349 | fn bar(_: i32, m: u32); | ||
350 | fn foo() { | ||
351 | bar($01$0, 2) | ||
352 | }"#, | ||
353 | ); | ||
354 | } | ||
355 | |||
356 | #[test] | ||
357 | fn tuple_pat() { | ||
358 | check_name_suggestion_not_applicable( | ||
359 | from_param, | ||
360 | r#" | ||
361 | fn bar((n, k): (i32, i32), m: u32); | ||
362 | fn foo() { | ||
363 | bar($0(1, 2)$0, 3) | ||
364 | }"#, | ||
365 | ); | ||
366 | } | ||
367 | |||
368 | #[test] | ||
369 | fn ref_pat() { | ||
370 | check_name_suggestion( | ||
371 | from_param, | ||
372 | r#" | ||
373 | fn bar(&n: &i32, m: u32); | ||
374 | fn foo() { | ||
375 | bar($0&1$0, 3) | ||
376 | }"#, | ||
377 | "n", | ||
378 | ); | ||
379 | } | ||
380 | |||
381 | #[test] | ||
382 | fn box_pat() { | ||
383 | check_name_suggestion( | ||
384 | from_param, | ||
385 | r#" | ||
386 | fn bar(box n: &i32, m: u32); | ||
387 | fn foo() { | ||
388 | bar($01$0, 3) | ||
389 | }"#, | ||
390 | "n", | ||
391 | ); | ||
392 | } | ||
393 | |||
394 | #[test] | ||
395 | fn param_out_of_index() { | ||
396 | check_name_suggestion_not_applicable( | ||
397 | from_param, | ||
398 | r#" | ||
399 | fn bar(n: i32, m: u32); | ||
400 | fn foo() { | ||
401 | bar(1, 2, $03$0) | ||
402 | }"#, | ||
403 | ); | ||
404 | } | ||
405 | |||
406 | #[test] | ||
407 | fn generic_param_resolved() { | ||
408 | check_name_suggestion( | ||
409 | from_param, | ||
410 | r#" | ||
411 | fn bar<T>(n: T, m: u32); | ||
412 | fn foo() { | ||
413 | bar($01$0, 2) | ||
414 | }"#, | ||
415 | "n", | ||
416 | ); | ||
417 | } | ||
418 | |||
419 | #[test] | ||
420 | fn generic_param_unresolved() { | ||
421 | check_name_suggestion( | ||
422 | from_param, | ||
423 | r#" | ||
424 | fn bar<T>(n: T, m: u32); | ||
425 | fn foo<T>(x: T) { | ||
426 | bar($0x$0, 2) | ||
427 | }"#, | ||
428 | "n", | ||
429 | ); | ||
430 | } | ||
431 | |||
432 | #[test] | ||
433 | fn method() { | ||
434 | check_name_suggestion( | ||
435 | from_param, | ||
436 | r#" | ||
437 | struct S; | ||
438 | impl S { | ||
439 | fn bar(&self, n: i32, m: u32); | ||
440 | } | ||
441 | fn foo() { | ||
442 | S.bar($01$0, 2) | ||
443 | }"#, | ||
444 | "n", | ||
445 | ); | ||
446 | } | ||
447 | |||
448 | #[test] | ||
449 | fn method_ufcs() { | ||
450 | check_name_suggestion( | ||
451 | from_param, | ||
452 | r#" | ||
453 | struct S; | ||
454 | impl S { | ||
455 | fn bar(&self, n: i32, m: u32); | ||
456 | } | ||
457 | fn foo() { | ||
458 | S::bar(&S, $01$0, 2) | ||
459 | }"#, | ||
460 | "n", | ||
461 | ); | ||
462 | } | ||
463 | |||
464 | #[test] | ||
465 | fn method_self() { | ||
466 | check_name_suggestion_not_applicable( | ||
467 | from_param, | ||
468 | r#" | ||
469 | struct S; | ||
470 | impl S { | ||
471 | fn bar(&self, n: i32, m: u32); | ||
472 | } | ||
473 | fn foo() { | ||
474 | S::bar($0&S$0, 1, 2) | ||
475 | }"#, | ||
476 | ); | ||
477 | } | ||
478 | |||
479 | #[test] | ||
480 | fn method_self_named() { | ||
481 | check_name_suggestion( | ||
482 | from_param, | ||
483 | r#" | ||
484 | struct S; | ||
485 | impl S { | ||
486 | fn bar(strukt: &Self, n: i32, m: u32); | ||
487 | } | ||
488 | fn foo() { | ||
489 | S::bar($0&S$0, 1, 2) | ||
490 | }"#, | ||
491 | "strukt", | ||
492 | ); | ||
493 | } | ||
494 | } | ||
495 | |||
496 | mod from_type { | ||
497 | use crate::tests::check_name_suggestion_not_applicable; | ||
498 | |||
499 | use super::*; | ||
500 | |||
501 | #[test] | ||
502 | fn i32() { | ||
503 | check_name_suggestion_not_applicable( | ||
504 | from_type, | ||
505 | r#" | ||
506 | fn foo() { | ||
507 | let _: i32 = $01$0; | ||
508 | }"#, | ||
509 | ); | ||
510 | } | ||
511 | |||
512 | #[test] | ||
513 | fn u64() { | ||
514 | check_name_suggestion_not_applicable( | ||
515 | from_type, | ||
516 | r#" | ||
517 | fn foo() { | ||
518 | let _: u64 = $01$0; | ||
519 | }"#, | ||
520 | ); | ||
521 | } | ||
522 | |||
523 | #[test] | ||
524 | fn bool() { | ||
525 | check_name_suggestion_not_applicable( | ||
526 | from_type, | ||
527 | r#" | ||
528 | fn foo() { | ||
529 | let _: bool = $0true$0; | ||
530 | }"#, | ||
531 | ); | ||
532 | } | ||
533 | |||
534 | #[test] | ||
535 | fn struct_unit() { | ||
536 | check_name_suggestion( | ||
537 | from_type, | ||
538 | r#" | ||
539 | struct Seed; | ||
540 | fn foo() { | ||
541 | let _ = $0Seed$0; | ||
542 | }"#, | ||
543 | "seed", | ||
544 | ); | ||
545 | } | ||
546 | |||
547 | #[test] | ||
548 | fn struct_unit_to_snake() { | ||
549 | check_name_suggestion( | ||
550 | from_type, | ||
551 | r#" | ||
552 | struct SeedState; | ||
553 | fn foo() { | ||
554 | let _ = $0SeedState$0; | ||
555 | }"#, | ||
556 | "seed_state", | ||
557 | ); | ||
558 | } | ||
559 | |||
560 | #[test] | ||
561 | fn struct_single_arg() { | ||
562 | check_name_suggestion( | ||
563 | from_type, | ||
564 | r#" | ||
565 | struct Seed(u32); | ||
566 | fn foo() { | ||
567 | let _ = $0Seed(0)$0; | ||
568 | }"#, | ||
569 | "seed", | ||
570 | ); | ||
571 | } | ||
572 | |||
573 | #[test] | ||
574 | fn struct_with_fields() { | ||
575 | check_name_suggestion( | ||
576 | from_type, | ||
577 | r#" | ||
578 | struct Seed { value: u32 } | ||
579 | fn foo() { | ||
580 | let _ = $0Seed { value: 0 }$0; | ||
581 | }"#, | ||
582 | "seed", | ||
583 | ); | ||
584 | } | ||
585 | |||
586 | #[test] | ||
587 | fn enum_() { | ||
588 | check_name_suggestion( | ||
589 | from_type, | ||
590 | r#" | ||
591 | enum Kind { A, B } | ||
592 | fn foo() { | ||
593 | let _ = $0Kind::A$0; | ||
594 | }"#, | ||
595 | "kind", | ||
596 | ); | ||
597 | } | ||
598 | |||
599 | #[test] | ||
600 | fn enum_generic_resolved() { | ||
601 | check_name_suggestion( | ||
602 | from_type, | ||
603 | r#" | ||
604 | enum Kind<T> { A(T), B } | ||
605 | fn foo() { | ||
606 | let _ = $0Kind::A(1)$0; | ||
607 | }"#, | ||
608 | "kind", | ||
609 | ); | ||
610 | } | ||
611 | |||
612 | #[test] | ||
613 | fn enum_generic_unresolved() { | ||
614 | check_name_suggestion( | ||
615 | from_type, | ||
616 | r#" | ||
617 | enum Kind<T> { A(T), B } | ||
618 | fn foo<T>(x: T) { | ||
619 | let _ = $0Kind::A(x)$0; | ||
620 | }"#, | ||
621 | "kind", | ||
622 | ); | ||
623 | } | ||
624 | |||
625 | #[test] | ||
626 | fn dyn_trait() { | ||
627 | check_name_suggestion( | ||
628 | from_type, | ||
629 | r#" | ||
630 | trait DynHandler {} | ||
631 | fn bar() -> dyn DynHandler {} | ||
632 | fn foo() { | ||
633 | $0bar()$0; | ||
634 | }"#, | ||
635 | "dyn_handler", | ||
636 | ); | ||
637 | } | ||
638 | |||
639 | #[test] | ||
640 | fn impl_trait() { | ||
641 | check_name_suggestion( | ||
642 | from_type, | ||
643 | r#" | ||
644 | trait StaticHandler {} | ||
645 | fn bar() -> impl StaticHandler {} | ||
646 | fn foo() { | ||
647 | $0bar()$0; | ||
648 | }"#, | ||
649 | "static_handler", | ||
650 | ); | ||
651 | } | ||
652 | |||
653 | #[test] | ||
654 | fn impl_trait_plus_clone() { | ||
655 | check_name_suggestion( | ||
656 | from_type, | ||
657 | r#" | ||
658 | trait StaticHandler {} | ||
659 | trait Clone {} | ||
660 | fn bar() -> impl StaticHandler + Clone {} | ||
661 | fn foo() { | ||
662 | $0bar()$0; | ||
663 | }"#, | ||
664 | "static_handler", | ||
665 | ); | ||
666 | } | ||
667 | |||
668 | #[test] | ||
669 | fn impl_trait_plus_lifetime() { | ||
670 | check_name_suggestion( | ||
671 | from_type, | ||
672 | r#" | ||
673 | trait StaticHandler {} | ||
674 | trait Clone {} | ||
675 | fn bar<'a>(&'a i32) -> impl StaticHandler + 'a {} | ||
676 | fn foo() { | ||
677 | $0bar(&1)$0; | ||
678 | }"#, | ||
679 | "static_handler", | ||
680 | ); | ||
681 | } | ||
682 | |||
683 | #[test] | ||
684 | fn impl_trait_plus_trait() { | ||
685 | check_name_suggestion_not_applicable( | ||
686 | from_type, | ||
687 | r#" | ||
688 | trait Handler {} | ||
689 | trait StaticHandler {} | ||
690 | fn bar() -> impl StaticHandler + Handler {} | ||
691 | fn foo() { | ||
692 | $0bar()$0; | ||
693 | }"#, | ||
694 | ); | ||
695 | } | ||
696 | |||
697 | #[test] | ||
698 | fn ref_value() { | ||
699 | check_name_suggestion( | ||
700 | from_type, | ||
701 | r#" | ||
702 | struct Seed; | ||
703 | fn bar() -> &Seed {} | ||
704 | fn foo() { | ||
705 | $0bar()$0; | ||
706 | }"#, | ||
707 | "seed", | ||
708 | ); | ||
709 | } | ||
710 | |||
711 | #[test] | ||
712 | fn box_value() { | ||
713 | check_name_suggestion( | ||
714 | from_type, | ||
715 | r#" | ||
716 | struct Box<T>(*const T); | ||
717 | struct Seed; | ||
718 | fn bar() -> Box<Seed> {} | ||
719 | fn foo() { | ||
720 | $0bar()$0; | ||
721 | }"#, | ||
722 | "seed", | ||
723 | ); | ||
724 | } | ||
725 | |||
726 | #[test] | ||
727 | fn box_generic() { | ||
728 | check_name_suggestion_not_applicable( | ||
729 | from_type, | ||
730 | r#" | ||
731 | struct Box<T>(*const T); | ||
732 | fn bar<T>() -> Box<T> {} | ||
733 | fn foo<T>() { | ||
734 | $0bar::<T>()$0; | ||
735 | }"#, | ||
736 | ); | ||
737 | } | ||
738 | |||
739 | #[test] | ||
740 | fn option_value() { | ||
741 | check_name_suggestion( | ||
742 | from_type, | ||
743 | r#" | ||
744 | enum Option<T> { Some(T) } | ||
745 | struct Seed; | ||
746 | fn bar() -> Option<Seed> {} | ||
747 | fn foo() { | ||
748 | $0bar()$0; | ||
749 | }"#, | ||
750 | "seed", | ||
751 | ); | ||
752 | } | ||
753 | |||
754 | #[test] | ||
755 | fn result_value() { | ||
756 | check_name_suggestion( | ||
757 | from_type, | ||
758 | r#" | ||
759 | enum Result<T, E> { Ok(T), Err(E) } | ||
760 | struct Seed; | ||
761 | struct Error; | ||
762 | fn bar() -> Result<Seed, Error> {} | ||
763 | fn foo() { | ||
764 | $0bar()$0; | ||
765 | }"#, | ||
766 | "seed", | ||
767 | ); | ||
768 | } | ||
769 | } | ||
770 | } | ||