@@ -8,6 +8,7 @@ use rustc_hir as hir;
88use rustc_hir:: def:: Res ;
99use rustc_hir:: { Expr , ExprKind , PatKind , PathSegment , QPath , UnOp } ;
1010use rustc_lint:: LateContext ;
11+ use rustc_middle:: ty:: adjustment:: Adjust ;
1112use rustc_span:: source_map:: Span ;
1213use rustc_span:: symbol:: { sym, Symbol } ;
1314use std:: borrow:: Cow ;
@@ -58,15 +59,15 @@ fn lint_filter_some_map_unwrap(
5859 map_arg : & hir:: Expr < ' _ > ,
5960 target_span : Span ,
6061 methods_span : Span ,
61- ) {
62+ ) -> bool {
6263 let iterator = is_trait_method ( cx, expr, sym:: Iterator ) ;
6364 let option = is_type_diagnostic_item ( cx, cx. typeck_results ( ) . expr_ty ( filter_recv) , sym:: Option ) ;
6465 if ( iterator || option) && is_option_filter_map ( cx, filter_arg, map_arg) {
6566 let msg = "`filter` for `Some` followed by `unwrap`" ;
6667 let help = "consider using `flatten` instead" ;
6768 let sugg = format ! (
6869 "{}" ,
69- reindent_multiline( Cow :: Borrowed ( "flatten()" ) , true , indent_of( cx, target_span) , )
70+ reindent_multiline( Cow :: Borrowed ( "flatten()" ) , true , indent_of( cx, target_span) ) ,
7071 ) ;
7172 span_lint_and_sugg (
7273 cx,
@@ -77,6 +78,10 @@ fn lint_filter_some_map_unwrap(
7778 sugg,
7879 Applicability :: MachineApplicable ,
7980 ) ;
81+
82+ true
83+ } else {
84+ false
8085 }
8186}
8287
@@ -93,16 +98,17 @@ pub(super) fn check<'tcx>(
9398 map_span : Span ,
9499 is_find : bool ,
95100) {
96- lint_filter_some_map_unwrap (
97- cx,
98- expr,
99- filter_recv,
100- filter_arg,
101- map_arg,
102- map_span,
103- filter_span. with_hi ( expr. span . hi ( ) ) ,
104- ) ;
105101 if_chain ! {
102+ if !lint_filter_some_map_unwrap(
103+ cx,
104+ expr,
105+ filter_recv,
106+ filter_arg,
107+ map_arg,
108+ map_span,
109+ filter_span. with_hi( expr. span. hi( ) ) ,
110+ ) ;
111+
106112 if is_trait_method( cx, map_recv, sym:: Iterator ) ;
107113
108114 // filter(|x| ...is_some())...
@@ -118,7 +124,7 @@ pub(super) fn check<'tcx>(
118124 // closure ends with is_some() or is_ok()
119125 if let PatKind :: Binding ( _, filter_param_id, _, None ) = filter_pat. kind;
120126 if let ExprKind :: MethodCall ( path, [ filter_arg] , _) = filter_body. value. kind;
121- if let Some ( opt_ty) = cx. typeck_results( ) . expr_ty( filter_arg) . ty_adt_def( ) ;
127+ if let Some ( opt_ty) = cx. typeck_results( ) . expr_ty( filter_arg) . peel_refs ( ) . ty_adt_def( ) ;
122128 if let Some ( is_result) = if cx. tcx. is_diagnostic_item( sym:: Option , opt_ty. did( ) ) {
123129 Some ( false )
124130 } else if cx. tcx. is_diagnostic_item( sym:: Result , opt_ty. did( ) ) {
@@ -137,6 +143,19 @@ pub(super) fn check<'tcx>(
137143 if let ExprKind :: MethodCall ( seg, [ map_arg, ..] , _) = map_body. value. kind;
138144 if matches!( seg. ident. name, sym:: expect | sym:: unwrap | sym:: unwrap_or) ;
139145
146+ // .filter(..).map(|y| f(y).copied().unwrap())
147+ // ~~~~
148+ let map_arg_peeled = match map_arg. kind {
149+ ExprKind :: MethodCall ( method, [ original_arg] , _) if acceptable_methods( method) => {
150+ original_arg
151+ } ,
152+ _ => map_arg,
153+ } ;
154+
155+ // .filter(|x| x.is_some()).map(|y| y[.acceptable_method()].unwrap())
156+ let simple_equal = path_to_local_id( filter_arg, filter_param_id)
157+ && path_to_local_id( map_arg_peeled, map_param_id) ;
158+
140159 let eq_fallback = |a: & Expr <' _>, b: & Expr <' _>| {
141160 // in `filter(|x| ..)`, replace `*x` with `x`
142161 let a_path = if_chain! {
@@ -145,36 +164,35 @@ pub(super) fn check<'tcx>(
145164 then { expr_path } else { a }
146165 } ;
147166 // let the filter closure arg and the map closure arg be equal
148- if_chain! {
149- if path_to_local_id( a_path, filter_param_id) ;
150- if path_to_local_id( b, map_param_id) ;
151- if cx. typeck_results( ) . expr_ty_adjusted( a) == cx. typeck_results( ) . expr_ty_adjusted( b) ;
152- then {
153- return true ;
154- }
155- }
156- false
157- } ;
158-
159- if match map_arg. kind {
160- ExprKind :: MethodCall ( method, [ original_arg] , _) => {
161- acceptable_methods( method)
162- && SpanlessEq :: new( cx) . expr_fallback( eq_fallback) . eq_expr( filter_arg, original_arg)
163- } ,
164- _ => SpanlessEq :: new( cx) . expr_fallback( eq_fallback) . eq_expr( filter_arg, map_arg)
167+ path_to_local_id( a_path, filter_param_id)
168+ && path_to_local_id( b, map_param_id)
169+ && cx. typeck_results( ) . expr_ty_adjusted( a) == cx. typeck_results( ) . expr_ty_adjusted( b)
165170 } ;
166171
172+ if simple_equal || SpanlessEq :: new( cx) . expr_fallback( eq_fallback) . eq_expr( filter_arg, map_arg_peeled) ;
167173 then {
168174 let span = filter_span. with_hi( expr. span. hi( ) ) ;
169175 let ( filter_name, lint) = if is_find {
170176 ( "find" , MANUAL_FIND_MAP )
171177 } else {
172178 ( "filter" , MANUAL_FILTER_MAP )
173179 } ;
174- let msg = format!( "`{}(..).map(..)` can be simplified as `{0}_map(..)`" , filter_name) ;
175- let to_opt = if is_result { ".ok()" } else { "" } ;
176- let sugg = format!( "{}_map(|{}| {}{})" , filter_name, map_param_ident,
177- snippet( cx, map_arg. span, ".." ) , to_opt) ;
180+ let msg = format!( "`{filter_name}(..).map(..)` can be simplified as `{filter_name}_map(..)`" ) ;
181+ let ( to_opt, deref) = if is_result {
182+ ( ".ok()" , String :: new( ) )
183+ } else {
184+ let derefs = cx. typeck_results( )
185+ . expr_adjustments( map_arg)
186+ . iter( )
187+ . filter( |adj| matches!( adj. kind, Adjust :: Deref ( _) ) )
188+ . count( ) ;
189+
190+ ( "" , "*" . repeat( derefs) )
191+ } ;
192+ let sugg = format!(
193+ "{filter_name}_map(|{map_param_ident}| {deref}{}{to_opt})" ,
194+ snippet( cx, map_arg. span, ".." ) ,
195+ ) ;
178196 span_lint_and_sugg( cx, lint, span, & msg, "try" , sugg, Applicability :: MachineApplicable ) ;
179197 }
180198 }
0 commit comments