@@ -2,24 +2,32 @@ use crate::{
22 map_unit_fn:: OPTION_MAP_UNIT_FN ,
33 matches:: MATCH_AS_REF ,
44 utils:: {
5- is_allowed, is_type_diagnostic_item, match_def_path, match_var, paths, peel_hir_expr_refs,
6- peel_mid_ty_refs_is_mutable, snippet_with_applicability, span_lint_and_sugg,
5+ can_partially_move_ty, is_allowed, is_type_diagnostic_item, match_def_path, match_var, paths,
6+ peel_hir_expr_refs, peel_mid_ty_refs_is_mutable, snippet_with_applicability, snippet_with_context,
7+ span_lint_and_sugg,
78 } ,
89} ;
910use rustc_ast:: util:: parser:: PREC_POSTFIX ;
1011use rustc_errors:: Applicability ;
11- use rustc_hir:: { Arm , BindingAnnotation , Block , Expr , ExprKind , Mutability , Pat , PatKind , QPath } ;
12+ use rustc_hir:: {
13+ def:: Res ,
14+ intravisit:: { walk_expr, ErasedMap , NestedVisitorMap , Visitor } ,
15+ Arm , BindingAnnotation , Block , Expr , ExprKind , Mutability , Pat , PatKind , Path , QPath ,
16+ } ;
1217use rustc_lint:: { LateContext , LateLintPass , LintContext } ;
1318use rustc_middle:: lint:: in_external_macro;
1419use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
15- use rustc_span:: symbol:: { sym, Ident } ;
20+ use rustc_span:: {
21+ symbol:: { sym, Ident } ,
22+ SyntaxContext ,
23+ } ;
1624
1725declare_clippy_lint ! {
1826 /// **What it does:** Checks for usages of `match` which could be implemented using `map`
1927 ///
2028 /// **Why is this bad?** Using the `map` method is clearer and more concise.
2129 ///
22- /// **Known problems:** `map` is not capable of representing some control flow which works fine in `match` .
30+ /// **Known problems:** None .
2331 ///
2432 /// **Example:**
2533 ///
@@ -34,7 +42,7 @@ declare_clippy_lint! {
3442 /// Some(0).map(|x| x + 1);
3543 /// ```
3644 pub MANUAL_MAP ,
37- nursery ,
45+ style ,
3846 "reimplementation of `map`"
3947}
4048
@@ -52,43 +60,46 @@ impl LateLintPass<'_> for ManualMap {
5260 {
5361 let ( scrutinee_ty, ty_ref_count, ty_mutability) =
5462 peel_mid_ty_refs_is_mutable ( cx. typeck_results ( ) . expr_ty ( scrutinee) ) ;
55- if !is_type_diagnostic_item ( cx, scrutinee_ty, sym:: option_type)
56- || ! is_type_diagnostic_item ( cx, cx. typeck_results ( ) . expr_ty ( expr) , sym:: option_type)
63+ if !( is_type_diagnostic_item ( cx, scrutinee_ty, sym:: option_type)
64+ && is_type_diagnostic_item ( cx, cx. typeck_results ( ) . expr_ty ( expr) , sym:: option_type) )
5765 {
5866 return ;
5967 }
6068
61- let ( some_expr, some_pat, pat_ref_count, is_wild_none) =
62- match ( try_parse_pattern ( cx, arm1. pat ) , try_parse_pattern ( cx, arm2. pat ) ) {
63- ( Some ( OptionPat :: Wild ) , Some ( OptionPat :: Some { pattern, ref_count } ) )
64- if is_none_expr ( cx, arm1. body ) =>
65- {
66- ( arm2. body , pattern, ref_count, true )
67- } ,
68- ( Some ( OptionPat :: None ) , Some ( OptionPat :: Some { pattern, ref_count } ) )
69- if is_none_expr ( cx, arm1. body ) =>
70- {
71- ( arm2. body , pattern, ref_count, false )
72- } ,
73- ( Some ( OptionPat :: Some { pattern, ref_count } ) , Some ( OptionPat :: Wild ) )
74- if is_none_expr ( cx, arm2. body ) =>
75- {
76- ( arm1. body , pattern, ref_count, true )
77- } ,
78- ( Some ( OptionPat :: Some { pattern, ref_count } ) , Some ( OptionPat :: None ) )
79- if is_none_expr ( cx, arm2. body ) =>
80- {
81- ( arm1. body , pattern, ref_count, false )
82- } ,
83- _ => return ,
84- } ;
69+ let expr_ctxt = expr. span . ctxt ( ) ;
70+ let ( some_expr, some_pat, pat_ref_count, is_wild_none) = match (
71+ try_parse_pattern ( cx, arm1. pat , expr_ctxt) ,
72+ try_parse_pattern ( cx, arm2. pat , expr_ctxt) ,
73+ ) {
74+ ( Some ( OptionPat :: Wild ) , Some ( OptionPat :: Some { pattern, ref_count } ) )
75+ if is_none_expr ( cx, arm1. body ) =>
76+ {
77+ ( arm2. body , pattern, ref_count, true )
78+ } ,
79+ ( Some ( OptionPat :: None ) , Some ( OptionPat :: Some { pattern, ref_count } ) )
80+ if is_none_expr ( cx, arm1. body ) =>
81+ {
82+ ( arm2. body , pattern, ref_count, false )
83+ } ,
84+ ( Some ( OptionPat :: Some { pattern, ref_count } ) , Some ( OptionPat :: Wild ) )
85+ if is_none_expr ( cx, arm2. body ) =>
86+ {
87+ ( arm1. body , pattern, ref_count, true )
88+ } ,
89+ ( Some ( OptionPat :: Some { pattern, ref_count } ) , Some ( OptionPat :: None ) )
90+ if is_none_expr ( cx, arm2. body ) =>
91+ {
92+ ( arm1. body , pattern, ref_count, false )
93+ } ,
94+ _ => return ,
95+ } ;
8596
8697 // Top level or patterns aren't allowed in closures.
8798 if matches ! ( some_pat. kind, PatKind :: Or ( _) ) {
8899 return ;
89100 }
90101
91- let some_expr = match get_some_expr ( cx, some_expr) {
102+ let some_expr = match get_some_expr ( cx, some_expr, expr_ctxt ) {
92103 Some ( expr) => expr,
93104 None => return ,
94105 } ;
@@ -99,6 +110,10 @@ impl LateLintPass<'_> for ManualMap {
99110 return ;
100111 }
101112
113+ if !can_move_expr_to_closure ( cx, some_expr) {
114+ return ;
115+ }
116+
102117 // Determine which binding mode to use.
103118 let explicit_ref = some_pat. contains_explicit_ref_binding ( ) ;
104119 let binding_ref = explicit_ref. or_else ( || ( ty_ref_count != pat_ref_count) . then ( || ty_mutability) ) ;
@@ -111,47 +126,50 @@ impl LateLintPass<'_> for ManualMap {
111126
112127 let mut app = Applicability :: MachineApplicable ;
113128
114- // Remove address-of expressions from the scrutinee. `as_ref` will be called,
115- // the type is copyable, or the option is being passed by value.
129+ // Remove address-of expressions from the scrutinee. Either `as_ref` will be called, or
130+ // it's being passed by value.
116131 let scrutinee = peel_hir_expr_refs ( scrutinee) . 0 ;
117- let scrutinee_str = snippet_with_applicability ( cx, scrutinee. span , "_ ", & mut app) ;
118- let scrutinee_str = if expr . precedence ( ) . order ( ) < PREC_POSTFIX {
119- // Parens are needed to chain method calls.
120- format ! ( "({})" , scrutinee_str)
121- } else {
122- scrutinee_str. into ( )
123- } ;
132+ let scrutinee_str = snippet_with_context ( cx, scrutinee. span , expr_ctxt , ".. ", & mut app) ;
133+ let scrutinee_str =
134+ if scrutinee . span . ctxt ( ) == expr . span . ctxt ( ) && scrutinee . precedence ( ) . order ( ) < PREC_POSTFIX {
135+ format ! ( "({})" , scrutinee_str)
136+ } else {
137+ scrutinee_str. into ( )
138+ } ;
124139
125140 let body_str = if let PatKind :: Binding ( annotation, _, some_binding, None ) = some_pat. kind {
126- if let Some ( func) = can_pass_as_func ( cx, some_binding, some_expr) {
127- snippet_with_applicability ( cx, func. span , ".." , & mut app) . into_owned ( )
128- } else {
129- if match_var ( some_expr, some_binding. name )
130- && !is_allowed ( cx, MATCH_AS_REF , expr. hir_id )
131- && binding_ref. is_some ( )
132- {
133- return ;
134- }
141+ match can_pass_as_func ( cx, some_binding, some_expr) {
142+ Some ( func) if func. span . ctxt ( ) == some_expr. span . ctxt ( ) => {
143+ snippet_with_applicability ( cx, func. span , ".." , & mut app) . into_owned ( )
144+ } ,
145+ _ => {
146+ if match_var ( some_expr, some_binding. name )
147+ && !is_allowed ( cx, MATCH_AS_REF , expr. hir_id )
148+ && binding_ref. is_some ( )
149+ {
150+ return ;
151+ }
135152
136- // `ref` and `ref mut` annotations were handled earlier.
137- let annotation = if matches ! ( annotation, BindingAnnotation :: Mutable ) {
138- "mut "
139- } else {
140- ""
141- } ;
142- format ! (
143- "|{}{}| {}" ,
144- annotation,
145- some_binding,
146- snippet_with_applicability( cx, some_expr. span, ".." , & mut app)
147- )
153+ // `ref` and `ref mut` annotations were handled earlier.
154+ let annotation = if matches ! ( annotation, BindingAnnotation :: Mutable ) {
155+ "mut "
156+ } else {
157+ ""
158+ } ;
159+ format ! (
160+ "|{}{}| {}" ,
161+ annotation,
162+ some_binding,
163+ snippet_with_context( cx, some_expr. span, expr_ctxt, ".." , & mut app)
164+ )
165+ } ,
148166 }
149167 } else if !is_wild_none && explicit_ref. is_none ( ) {
150168 // TODO: handle explicit reference annotations.
151169 format ! (
152170 "|{}| {}" ,
153- snippet_with_applicability ( cx, some_pat. span, ".." , & mut app) ,
154- snippet_with_applicability ( cx, some_expr. span, ".." , & mut app)
171+ snippet_with_context ( cx, some_pat. span, expr_ctxt , ".." , & mut app) ,
172+ snippet_with_context ( cx, some_expr. span, expr_ctxt , ".." , & mut app)
155173 )
156174 } else {
157175 // Refutable bindings and mixed reference annotations can't be handled by `map`.
@@ -171,6 +189,51 @@ impl LateLintPass<'_> for ManualMap {
171189 }
172190}
173191
192+ // Checks if the expression can be moved into a closure as is.
193+ fn can_move_expr_to_closure ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) -> bool {
194+ struct V < ' cx , ' tcx > {
195+ cx : & ' cx LateContext < ' tcx > ,
196+ make_closure : bool ,
197+ }
198+ impl Visitor < ' tcx > for V < ' _ , ' tcx > {
199+ type Map = ErasedMap < ' tcx > ;
200+ fn nested_visit_map ( & mut self ) -> NestedVisitorMap < Self :: Map > {
201+ NestedVisitorMap :: None
202+ }
203+
204+ fn visit_expr ( & mut self , e : & ' tcx Expr < ' _ > ) {
205+ match e. kind {
206+ ExprKind :: Break ( ..)
207+ | ExprKind :: Continue ( _)
208+ | ExprKind :: Ret ( _)
209+ | ExprKind :: Yield ( ..)
210+ | ExprKind :: InlineAsm ( _)
211+ | ExprKind :: LlvmInlineAsm ( _) => {
212+ self . make_closure = false ;
213+ } ,
214+ // Accessing a field of a local value can only be done if the type isn't
215+ // partially moved.
216+ ExprKind :: Field ( base_expr, _)
217+ if matches ! (
218+ base_expr. kind,
219+ ExprKind :: Path ( QPath :: Resolved ( _, Path { res: Res :: Local ( _) , .. } ) )
220+ ) && can_partially_move_ty ( self . cx , self . cx . typeck_results ( ) . expr_ty ( base_expr) ) =>
221+ {
222+ // TODO: check if the local has been partially moved. Assume it has for now.
223+ self . make_closure = false ;
224+ return ;
225+ }
226+ _ => ( ) ,
227+ } ;
228+ walk_expr ( self , e) ;
229+ }
230+ }
231+
232+ let mut v = V { cx, make_closure : true } ;
233+ v. visit_expr ( expr) ;
234+ v. make_closure
235+ }
236+
174237// Checks whether the expression could be passed as a function, or whether a closure is needed.
175238// Returns the function to be passed to `map` if it exists.
176239fn can_pass_as_func ( cx : & LateContext < ' tcx > , binding : Ident , expr : & ' tcx Expr < ' _ > ) -> Option < & ' tcx Expr < ' tcx > > {
@@ -198,11 +261,11 @@ enum OptionPat<'a> {
198261
199262// Try to parse into a recognized `Option` pattern.
200263// i.e. `_`, `None`, `Some(..)`, or a reference to any of those.
201- fn try_parse_pattern ( cx : & LateContext < ' tcx > , pat : & ' tcx Pat < ' _ > ) -> Option < OptionPat < ' tcx > > {
202- fn f ( cx : & LateContext < ' tcx > , pat : & ' tcx Pat < ' _ > , ref_count : usize ) -> Option < OptionPat < ' tcx > > {
264+ fn try_parse_pattern ( cx : & LateContext < ' tcx > , pat : & ' tcx Pat < ' _ > , ctxt : SyntaxContext ) -> Option < OptionPat < ' tcx > > {
265+ fn f ( cx : & LateContext < ' tcx > , pat : & ' tcx Pat < ' _ > , ref_count : usize , ctxt : SyntaxContext ) -> Option < OptionPat < ' tcx > > {
203266 match pat. kind {
204267 PatKind :: Wild => Some ( OptionPat :: Wild ) ,
205- PatKind :: Ref ( pat, _) => f ( cx, pat, ref_count + 1 ) ,
268+ PatKind :: Ref ( pat, _) => f ( cx, pat, ref_count + 1 , ctxt ) ,
206269 PatKind :: Path ( QPath :: Resolved ( None , path) )
207270 if path
208271 . res
@@ -215,18 +278,19 @@ fn try_parse_pattern(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) -> Option<Optio
215278 if path
216279 . res
217280 . opt_def_id ( )
218- . map_or ( false , |id| match_def_path ( cx, id, & paths:: OPTION_SOME ) ) =>
281+ . map_or ( false , |id| match_def_path ( cx, id, & paths:: OPTION_SOME ) )
282+ && pat. span . ctxt ( ) == ctxt =>
219283 {
220284 Some ( OptionPat :: Some { pattern, ref_count } )
221285 } ,
222286 _ => None ,
223287 }
224288 }
225- f ( cx, pat, 0 )
289+ f ( cx, pat, 0 , ctxt )
226290}
227291
228292// Checks for an expression wrapped by the `Some` constructor. Returns the contained expression.
229- fn get_some_expr ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) -> Option < & ' tcx Expr < ' tcx > > {
293+ fn get_some_expr ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > , ctxt : SyntaxContext ) -> Option < & ' tcx Expr < ' tcx > > {
230294 // TODO: Allow more complex expressions.
231295 match expr. kind {
232296 ExprKind :: Call (
@@ -235,7 +299,7 @@ fn get_some_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx E
235299 ..
236300 } ,
237301 [ arg] ,
238- ) => {
302+ ) if ctxt == expr . span . ctxt ( ) => {
239303 if match_def_path ( cx, path. res . opt_def_id ( ) ?, & paths:: OPTION_SOME ) {
240304 Some ( arg)
241305 } else {
@@ -249,7 +313,7 @@ fn get_some_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx E
249313 ..
250314 } ,
251315 _,
252- ) => get_some_expr ( cx, expr) ,
316+ ) => get_some_expr ( cx, expr, ctxt ) ,
253317 _ => None ,
254318 }
255319}
0 commit comments