diff options
Diffstat (limited to 'crates/ide_assists')
-rw-r--r-- | crates/ide_assists/src/utils/suggest_name.rs | 121 |
1 files changed, 114 insertions, 7 deletions
diff --git a/crates/ide_assists/src/utils/suggest_name.rs b/crates/ide_assists/src/utils/suggest_name.rs index 345e9af40..d37c62642 100644 --- a/crates/ide_assists/src/utils/suggest_name.rs +++ b/crates/ide_assists/src/utils/suggest_name.rs | |||
@@ -29,6 +29,29 @@ const WRAPPER_TYPES: &[&str] = &["Box", "Option", "Result"]; | |||
29 | /// `args.into_config()` -> `config` | 29 | /// `args.into_config()` -> `config` |
30 | /// `bytes.to_vec()` -> `vec` | 30 | /// `bytes.to_vec()` -> `vec` |
31 | const USELESS_METHOD_PREFIXES: &[&str] = &["into_", "as_", "to_"]; | 31 | const USELESS_METHOD_PREFIXES: &[&str] = &["into_", "as_", "to_"]; |
32 | /// Useless methods that are stripped from expression | ||
33 | /// | ||
34 | /// # Examples | ||
35 | /// `var.name().to_string()` -> `var.name()` | ||
36 | const USELESS_METHODS: &[&str] = &[ | ||
37 | "to_string", | ||
38 | "as_str", | ||
39 | "to_owned", | ||
40 | "as_ref", | ||
41 | "clone", | ||
42 | "cloned", | ||
43 | "expect", | ||
44 | "expect_none", | ||
45 | "unwrap", | ||
46 | "unwrap_none", | ||
47 | "unwrap_or", | ||
48 | "unwrap_or_default", | ||
49 | "unwrap_or_else", | ||
50 | "unwrap_unchecked", | ||
51 | "iter", | ||
52 | "into_iter", | ||
53 | "iter_mut", | ||
54 | ]; | ||
32 | 55 | ||
33 | /// Suggest name of variable for given expression | 56 | /// Suggest name of variable for given expression |
34 | /// | 57 | /// |
@@ -49,10 +72,39 @@ const USELESS_METHOD_PREFIXES: &[&str] = &["into_", "as_", "to_"]; | |||
49 | /// | 72 | /// |
50 | /// Currently it sticks to the first name found. | 73 | /// Currently it sticks to the first name found. |
51 | pub(crate) fn variable(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> String { | 74 | pub(crate) fn variable(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> String { |
52 | from_param(expr, sema) | 75 | // `from_param` does not benifit from stripping |
53 | .or_else(|| from_call(expr)) | 76 | // it need the largest context possible |
54 | .or_else(|| from_type(expr, sema)) | 77 | // so we check firstmost |
55 | .unwrap_or_else(|| "var_name".to_string()) | 78 | if let Some(name) = from_param(expr, sema) { |
79 | return name; | ||
80 | } | ||
81 | |||
82 | let mut next_expr = Some(expr.clone()); | ||
83 | while let Some(expr) = next_expr { | ||
84 | let name = from_call(&expr).or_else(|| from_type(&expr, sema)); | ||
85 | if let Some(name) = name { | ||
86 | return name; | ||
87 | } | ||
88 | |||
89 | match expr { | ||
90 | ast::Expr::RefExpr(inner) => next_expr = inner.expr(), | ||
91 | ast::Expr::BoxExpr(inner) => next_expr = inner.expr(), | ||
92 | ast::Expr::AwaitExpr(inner) => next_expr = inner.expr(), | ||
93 | // ast::Expr::BlockExpr(block) => expr = block.tail_expr(), | ||
94 | ast::Expr::CastExpr(inner) => next_expr = inner.expr(), | ||
95 | ast::Expr::MethodCallExpr(method) if is_useless_method(&method) => { | ||
96 | next_expr = method.receiver(); | ||
97 | } | ||
98 | ast::Expr::ParenExpr(inner) => next_expr = inner.expr(), | ||
99 | ast::Expr::TryExpr(inner) => next_expr = inner.expr(), | ||
100 | ast::Expr::PrefixExpr(prefix) if prefix.op_kind() == Some(ast::PrefixOp::Deref) => { | ||
101 | next_expr = prefix.expr() | ||
102 | } | ||
103 | _ => break, | ||
104 | } | ||
105 | } | ||
106 | |||
107 | "var_name".to_string() | ||
56 | } | 108 | } |
57 | 109 | ||
58 | fn normalize(name: &str) -> Option<String> { | 110 | fn normalize(name: &str) -> Option<String> { |
@@ -76,6 +128,16 @@ fn is_valid_name(name: &str) -> bool { | |||
76 | } | 128 | } |
77 | } | 129 | } |
78 | 130 | ||
131 | fn is_useless_method(method: &ast::MethodCallExpr) -> bool { | ||
132 | let ident = method.name_ref().and_then(|it| it.ident_token()); | ||
133 | |||
134 | if let Some(ident) = ident { | ||
135 | USELESS_METHODS.contains(&ident.text()) | ||
136 | } else { | ||
137 | false | ||
138 | } | ||
139 | } | ||
140 | |||
79 | fn from_call(expr: &ast::Expr) -> Option<String> { | 141 | fn from_call(expr: &ast::Expr) -> Option<String> { |
80 | from_func_call(expr).or_else(|| from_method_call(expr)) | 142 | from_func_call(expr).or_else(|| from_method_call(expr)) |
81 | } | 143 | } |
@@ -99,15 +161,20 @@ fn from_method_call(expr: &ast::Expr) -> Option<String> { | |||
99 | _ => return None, | 161 | _ => return None, |
100 | }; | 162 | }; |
101 | let ident = method.name_ref()?.ident_token()?; | 163 | let ident = method.name_ref()?.ident_token()?; |
102 | let name = normalize(ident.text())?; | 164 | let mut name = ident.text(); |
165 | |||
166 | if USELESS_METHODS.contains(&name) { | ||
167 | return None; | ||
168 | } | ||
103 | 169 | ||
104 | for prefix in USELESS_METHOD_PREFIXES { | 170 | for prefix in USELESS_METHOD_PREFIXES { |
105 | if let Some(suffix) = name.strip_prefix(prefix) { | 171 | if let Some(suffix) = name.strip_prefix(prefix) { |
106 | return Some(suffix.to_string()); | 172 | name = suffix; |
173 | break; | ||
107 | } | 174 | } |
108 | } | 175 | } |
109 | 176 | ||
110 | Some(name) | 177 | normalize(&name) |
111 | } | 178 | } |
112 | 179 | ||
113 | fn from_param(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> Option<String> { | 180 | fn from_param(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> Option<String> { |
@@ -767,4 +834,44 @@ mod tests { | |||
767 | ); | 834 | ); |
768 | } | 835 | } |
769 | } | 836 | } |
837 | |||
838 | mod variable { | ||
839 | use super::*; | ||
840 | |||
841 | #[test] | ||
842 | fn ref_call() { | ||
843 | check_name_suggestion( | ||
844 | |e, c| Some(variable(e, c)), | ||
845 | r#" | ||
846 | fn foo() { | ||
847 | $0&bar(1, 3)$0 | ||
848 | }"#, | ||
849 | "bar", | ||
850 | ); | ||
851 | } | ||
852 | |||
853 | #[test] | ||
854 | fn name_to_string() { | ||
855 | check_name_suggestion( | ||
856 | |e, c| Some(variable(e, c)), | ||
857 | r#" | ||
858 | fn foo() { | ||
859 | $0function.name().to_string()$0 | ||
860 | }"#, | ||
861 | "name", | ||
862 | ); | ||
863 | } | ||
864 | |||
865 | #[test] | ||
866 | fn nested_useless_method() { | ||
867 | check_name_suggestion( | ||
868 | |e, c| Some(variable(e, c)), | ||
869 | r#" | ||
870 | fn foo() { | ||
871 | $0function.name().as_ref().unwrap().to_string()$0 | ||
872 | }"#, | ||
873 | "name", | ||
874 | ); | ||
875 | } | ||
876 | } | ||
770 | } | 877 | } |