@@ -29,6 +29,29 @@ const WRAPPER_TYPES: &[&str] = &["Box", "Option", "Result"];
2929/// `args.into_config()` -> `config`
3030/// `bytes.to_vec()` -> `vec`
3131const 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+ ] ;
3255
3356/// Suggest name of variable for given expression
3457///
@@ -49,10 +72,39 @@ const USELESS_METHOD_PREFIXES: &[&str] = &["into_", "as_", "to_"];
4972///
5073/// Currently it sticks to the first name found.
5174pub ( 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 ( ) )
75+ // `from_param` does not benifit from stripping
76+ // it need the largest context possible
77+ // so we check firstmost
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 ( )
56108}
57109
58110fn normalize ( name : & str ) -> Option < String > {
@@ -76,6 +128,16 @@ fn is_valid_name(name: &str) -> bool {
76128 }
77129}
78130
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+
79141fn from_call ( expr : & ast:: Expr ) -> Option < String > {
80142 from_func_call ( expr) . or_else ( || from_method_call ( expr) )
81143}
@@ -99,15 +161,20 @@ fn from_method_call(expr: &ast::Expr) -> Option<String> {
99161 _ => return None ,
100162 } ;
101163 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+ }
103169
104170 for prefix in USELESS_METHOD_PREFIXES {
105171 if let Some ( suffix) = name. strip_prefix ( prefix) {
106- return Some ( suffix. to_string ( ) ) ;
172+ name = suffix;
173+ break ;
107174 }
108175 }
109176
110- Some ( name)
177+ normalize ( & name)
111178}
112179
113180fn from_param ( expr : & ast:: Expr , sema : & Semantics < ' _ , RootDatabase > ) -> Option < String > {
@@ -767,4 +834,44 @@ mod tests {
767834 ) ;
768835 }
769836 }
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+ }
770877}
0 commit comments