@@ -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 ;
@@ -49,35 +50,18 @@ fn is_option_filter_map<'tcx>(cx: &LateContext<'tcx>, filter_arg: &hir::Expr<'_>
4950 is_method ( cx, map_arg, sym:: unwrap) && is_method ( cx, filter_arg, sym ! ( is_some) )
5051}
5152
52- /// lint use of `filter() .map()` for `Iterators `
53- fn lint_filter_some_map_unwrap (
53+ /// is `filter(|x| x.is_some()) .map(|x| x.unwrap()) `
54+ fn is_filter_some_map_unwrap (
5455 cx : & LateContext < ' _ > ,
5556 expr : & hir:: Expr < ' _ > ,
5657 filter_recv : & hir:: Expr < ' _ > ,
5758 filter_arg : & hir:: Expr < ' _ > ,
5859 map_arg : & hir:: Expr < ' _ > ,
59- target_span : Span ,
60- methods_span : Span ,
61- ) {
60+ ) -> bool {
6261 let iterator = is_trait_method ( cx, expr, sym:: Iterator ) ;
6362 let option = is_type_diagnostic_item ( cx, cx. typeck_results ( ) . expr_ty ( filter_recv) , sym:: Option ) ;
64- if ( iterator || option) && is_option_filter_map ( cx, filter_arg, map_arg) {
65- let msg = "`filter` for `Some` followed by `unwrap`" ;
66- let help = "consider using `flatten` instead" ;
67- let sugg = format ! (
68- "{}" ,
69- reindent_multiline( Cow :: Borrowed ( "flatten()" ) , true , indent_of( cx, target_span) , )
70- ) ;
71- span_lint_and_sugg (
72- cx,
73- OPTION_FILTER_MAP ,
74- methods_span,
75- msg,
76- help,
77- sugg,
78- Applicability :: MachineApplicable ,
79- ) ;
80- }
63+
64+ ( iterator || option) && is_option_filter_map ( cx, filter_arg, map_arg)
8165}
8266
8367/// lint use of `filter().map()` or `find().map()` for `Iterators`
@@ -93,15 +77,20 @@ pub(super) fn check<'tcx>(
9377 map_span : Span ,
9478 is_find : bool ,
9579) {
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- ) ;
80+ if is_filter_some_map_unwrap ( cx, expr, filter_recv, filter_arg, map_arg) {
81+ span_lint_and_sugg (
82+ cx,
83+ OPTION_FILTER_MAP ,
84+ filter_span. with_hi ( expr. span . hi ( ) ) ,
85+ "`filter` for `Some` followed by `unwrap`" ,
86+ "consider using `flatten` instead" ,
87+ reindent_multiline ( Cow :: Borrowed ( "flatten()" ) , true , indent_of ( cx, map_span) ) . into_owned ( ) ,
88+ Applicability :: MachineApplicable ,
89+ ) ;
90+
91+ return ;
92+ }
93+
10594 if_chain ! {
10695 if is_trait_method( cx, map_recv, sym:: Iterator ) ;
10796
@@ -118,7 +107,7 @@ pub(super) fn check<'tcx>(
118107 // closure ends with is_some() or is_ok()
119108 if let PatKind :: Binding ( _, filter_param_id, _, None ) = filter_pat. kind;
120109 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( ) ;
110+ if let Some ( opt_ty) = cx. typeck_results( ) . expr_ty( filter_arg) . peel_refs ( ) . ty_adt_def( ) ;
122111 if let Some ( is_result) = if cx. tcx. is_diagnostic_item( sym:: Option , opt_ty. did( ) ) {
123112 Some ( false )
124113 } else if cx. tcx. is_diagnostic_item( sym:: Result , opt_ty. did( ) ) {
@@ -137,6 +126,19 @@ pub(super) fn check<'tcx>(
137126 if let ExprKind :: MethodCall ( seg, [ map_arg, ..] , _) = map_body. value. kind;
138127 if matches!( seg. ident. name, sym:: expect | sym:: unwrap | sym:: unwrap_or) ;
139128
129+ // .filter(..).map(|y| f(y).copied().unwrap())
130+ // ~~~~
131+ let map_arg_peeled = match map_arg. kind {
132+ ExprKind :: MethodCall ( method, [ original_arg] , _) if acceptable_methods( method) => {
133+ original_arg
134+ } ,
135+ _ => map_arg,
136+ } ;
137+
138+ // .filter(|x| x.is_some()).map(|y| y[.acceptable_method()].unwrap())
139+ let simple_equal = path_to_local_id( filter_arg, filter_param_id)
140+ && path_to_local_id( map_arg_peeled, map_param_id) ;
141+
140142 let eq_fallback = |a: & Expr <' _>, b: & Expr <' _>| {
141143 // in `filter(|x| ..)`, replace `*x` with `x`
142144 let a_path = if_chain! {
@@ -145,36 +147,35 @@ pub(super) fn check<'tcx>(
145147 then { expr_path } else { a }
146148 } ;
147149 // 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)
150+ path_to_local_id( a_path, filter_param_id)
151+ && path_to_local_id( b, map_param_id)
152+ && cx. typeck_results( ) . expr_ty_adjusted( a) == cx. typeck_results( ) . expr_ty_adjusted( b)
165153 } ;
166154
155+ if simple_equal || SpanlessEq :: new( cx) . expr_fallback( eq_fallback) . eq_expr( filter_arg, map_arg_peeled) ;
167156 then {
168157 let span = filter_span. with_hi( expr. span. hi( ) ) ;
169158 let ( filter_name, lint) = if is_find {
170159 ( "find" , MANUAL_FIND_MAP )
171160 } else {
172161 ( "filter" , MANUAL_FILTER_MAP )
173162 } ;
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) ;
163+ let msg = format!( "`{filter_name}(..).map(..)` can be simplified as `{filter_name}_map(..)`" ) ;
164+ let ( to_opt, deref) = if is_result {
165+ ( ".ok()" , String :: new( ) )
166+ } else {
167+ let derefs = cx. typeck_results( )
168+ . expr_adjustments( map_arg)
169+ . iter( )
170+ . filter( |adj| matches!( adj. kind, Adjust :: Deref ( _) ) )
171+ . count( ) ;
172+
173+ ( "" , "*" . repeat( derefs) )
174+ } ;
175+ let sugg = format!(
176+ "{filter_name}_map(|{map_param_ident}| {deref}{}{to_opt})" ,
177+ snippet( cx, map_arg. span, ".." ) ,
178+ ) ;
178179 span_lint_and_sugg( cx, lint, span, & msg, "try" , sugg, Applicability :: MachineApplicable ) ;
179180 }
180181 }
0 commit comments