@@ -3,7 +3,8 @@ use crate::{
3
3
matches:: MATCH_AS_REF ,
4
4
utils:: {
5
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, span_lint_and_sugg,
6
+ peel_hir_expr_refs, peel_mid_ty_refs_is_mutable, snippet_with_applicability, snippet_with_context,
7
+ span_lint_and_sugg,
7
8
} ,
8
9
} ;
9
10
use rustc_ast:: util:: parser:: PREC_POSTFIX ;
@@ -16,7 +17,10 @@ use rustc_hir::{
16
17
use rustc_lint:: { LateContext , LateLintPass , LintContext } ;
17
18
use rustc_middle:: lint:: in_external_macro;
18
19
use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
19
- use rustc_span:: symbol:: { sym, Ident } ;
20
+ use rustc_span:: {
21
+ symbol:: { sym, Ident } ,
22
+ SyntaxContext ,
23
+ } ;
20
24
21
25
declare_clippy_lint ! {
22
26
/// **What it does:** Checks for usages of `match` which could be implemented using `map`
@@ -56,43 +60,46 @@ impl LateLintPass<'_> for ManualMap {
56
60
{
57
61
let ( scrutinee_ty, ty_ref_count, ty_mutability) =
58
62
peel_mid_ty_refs_is_mutable ( cx. typeck_results ( ) . expr_ty ( scrutinee) ) ;
59
- if !is_type_diagnostic_item ( cx, scrutinee_ty, sym:: option_type)
60
- || ! 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) )
61
65
{
62
66
return ;
63
67
}
64
68
65
- let ( some_expr, some_pat, pat_ref_count, is_wild_none) =
66
- match ( try_parse_pattern ( cx, arm1. pat ) , try_parse_pattern ( cx, arm2. pat ) ) {
67
- ( Some ( OptionPat :: Wild ) , Some ( OptionPat :: Some { pattern, ref_count } ) )
68
- if is_none_expr ( cx, arm1. body ) =>
69
- {
70
- ( arm2. body , pattern, ref_count, true )
71
- } ,
72
- ( Some ( OptionPat :: None ) , Some ( OptionPat :: Some { pattern, ref_count } ) )
73
- if is_none_expr ( cx, arm1. body ) =>
74
- {
75
- ( arm2. body , pattern, ref_count, false )
76
- } ,
77
- ( Some ( OptionPat :: Some { pattern, ref_count } ) , Some ( OptionPat :: Wild ) )
78
- if is_none_expr ( cx, arm2. body ) =>
79
- {
80
- ( arm1. body , pattern, ref_count, true )
81
- } ,
82
- ( Some ( OptionPat :: Some { pattern, ref_count } ) , Some ( OptionPat :: None ) )
83
- if is_none_expr ( cx, arm2. body ) =>
84
- {
85
- ( arm1. body , pattern, ref_count, false )
86
- } ,
87
- _ => return ,
88
- } ;
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
+ } ;
89
96
90
97
// Top level or patterns aren't allowed in closures.
91
98
if matches ! ( some_pat. kind, PatKind :: Or ( _) ) {
92
99
return ;
93
100
}
94
101
95
- let some_expr = match get_some_expr ( cx, some_expr) {
102
+ let some_expr = match get_some_expr ( cx, some_expr, expr_ctxt ) {
96
103
Some ( expr) => expr,
97
104
None => return ,
98
105
} ;
@@ -119,47 +126,50 @@ impl LateLintPass<'_> for ManualMap {
119
126
120
127
let mut app = Applicability :: MachineApplicable ;
121
128
122
- // Remove address-of expressions from the scrutinee. `as_ref` will be called,
123
- // 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.
124
131
let scrutinee = peel_hir_expr_refs ( scrutinee) . 0 ;
125
- let scrutinee_str = snippet_with_applicability ( cx, scrutinee. span , "_ ", & mut app) ;
126
- let scrutinee_str = if expr . precedence ( ) . order ( ) < PREC_POSTFIX {
127
- // Parens are needed to chain method calls.
128
- format ! ( "({})" , scrutinee_str)
129
- } else {
130
- scrutinee_str. into ( )
131
- } ;
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
+ } ;
132
139
133
140
let body_str = if let PatKind :: Binding ( annotation, _, some_binding, None ) = some_pat. kind {
134
- if let Some ( func) = can_pass_as_func ( cx, some_binding, some_expr) {
135
- snippet_with_applicability ( cx, func. span , ".." , & mut app) . into_owned ( )
136
- } else {
137
- if match_var ( some_expr, some_binding. name )
138
- && !is_allowed ( cx, MATCH_AS_REF , expr. hir_id )
139
- && binding_ref. is_some ( )
140
- {
141
- return ;
142
- }
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
+ }
143
152
144
- // `ref` and `ref mut` annotations were handled earlier.
145
- let annotation = if matches ! ( annotation, BindingAnnotation :: Mutable ) {
146
- "mut "
147
- } else {
148
- ""
149
- } ;
150
- format ! (
151
- "|{}{}| {}" ,
152
- annotation,
153
- some_binding,
154
- snippet_with_applicability( cx, some_expr. span, ".." , & mut app)
155
- )
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
+ } ,
156
166
}
157
167
} else if !is_wild_none && explicit_ref. is_none ( ) {
158
168
// TODO: handle explicit reference annotations.
159
169
format ! (
160
170
"|{}| {}" ,
161
- snippet_with_applicability ( cx, some_pat. span, ".." , & mut app) ,
162
- 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)
163
173
)
164
174
} else {
165
175
// Refutable bindings and mixed reference annotations can't be handled by `map`.
@@ -246,11 +256,11 @@ enum OptionPat<'a> {
246
256
247
257
// Try to parse into a recognized `Option` pattern.
248
258
// i.e. `_`, `None`, `Some(..)`, or a reference to any of those.
249
- fn try_parse_pattern ( cx : & LateContext < ' tcx > , pat : & ' tcx Pat < ' _ > ) -> Option < OptionPat < ' tcx > > {
250
- fn f ( cx : & LateContext < ' tcx > , pat : & ' tcx Pat < ' _ > , ref_count : usize ) -> Option < OptionPat < ' tcx > > {
259
+ fn try_parse_pattern ( cx : & LateContext < ' tcx > , pat : & ' tcx Pat < ' _ > , ctxt : SyntaxContext ) -> Option < OptionPat < ' tcx > > {
260
+ fn f ( cx : & LateContext < ' tcx > , pat : & ' tcx Pat < ' _ > , ref_count : usize , ctxt : SyntaxContext ) -> Option < OptionPat < ' tcx > > {
251
261
match pat. kind {
252
262
PatKind :: Wild => Some ( OptionPat :: Wild ) ,
253
- PatKind :: Ref ( pat, _) => f ( cx, pat, ref_count + 1 ) ,
263
+ PatKind :: Ref ( pat, _) => f ( cx, pat, ref_count + 1 , ctxt ) ,
254
264
PatKind :: Path ( QPath :: Resolved ( None , path) )
255
265
if path
256
266
. res
@@ -263,18 +273,19 @@ fn try_parse_pattern(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) -> Option<Optio
263
273
if path
264
274
. res
265
275
. opt_def_id ( )
266
- . map_or ( false , |id| match_def_path ( cx, id, & paths:: OPTION_SOME ) ) =>
276
+ . map_or ( false , |id| match_def_path ( cx, id, & paths:: OPTION_SOME ) )
277
+ && pat. span . ctxt ( ) == ctxt =>
267
278
{
268
279
Some ( OptionPat :: Some { pattern, ref_count } )
269
280
} ,
270
281
_ => None ,
271
282
}
272
283
}
273
- f ( cx, pat, 0 )
284
+ f ( cx, pat, 0 , ctxt )
274
285
}
275
286
276
287
// Checks for an expression wrapped by the `Some` constructor. Returns the contained expression.
277
- fn get_some_expr ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) -> Option < & ' tcx Expr < ' tcx > > {
288
+ fn get_some_expr ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > , ctxt : SyntaxContext ) -> Option < & ' tcx Expr < ' tcx > > {
278
289
// TODO: Allow more complex expressions.
279
290
match expr. kind {
280
291
ExprKind :: Call (
@@ -283,7 +294,7 @@ fn get_some_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx E
283
294
..
284
295
} ,
285
296
[ arg] ,
286
- ) => {
297
+ ) if ctxt == expr . span . ctxt ( ) => {
287
298
if match_def_path ( cx, path. res . opt_def_id ( ) ?, & paths:: OPTION_SOME ) {
288
299
Some ( arg)
289
300
} else {
@@ -297,7 +308,7 @@ fn get_some_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx E
297
308
..
298
309
} ,
299
310
_,
300
- ) => get_some_expr ( cx, expr) ,
311
+ ) => get_some_expr ( cx, expr, ctxt ) ,
301
312
_ => None ,
302
313
}
303
314
}
0 commit comments