@@ -11,8 +11,8 @@ use syntax::ast::LitKind;
11
11
use syntax:: ast:: NodeId ;
12
12
use syntax:: codemap:: Span ;
13
13
use utils:: paths;
14
- use utils:: { expr_block, in_external_macro, is_allowed, is_expn_of, match_type , remove_blocks , snippet ,
15
- span_lint_and_sugg, span_lint_and_then, span_note_and_lint, walk_ptrs_ty} ;
14
+ use utils:: { expr_block, in_external_macro, is_allowed, is_expn_of, match_qpath , match_type , remove_blocks ,
15
+ snippet , span_lint_and_sugg, span_lint_and_then, span_note_and_lint, walk_ptrs_ty} ;
16
16
use utils:: sugg:: Sugg ;
17
17
18
18
/// **What it does:** Checks for matches with a single arm where an `if let`
@@ -145,6 +145,27 @@ declare_lint! {
145
145
"a match with `Err(_)` arm and take drastic actions"
146
146
}
147
147
148
+ /// **What it does:** Checks for match which is used to add a reference to an
149
+ /// `Option` value.
150
+ ///
151
+ /// **Why is this bad?** Using `as_ref()` or `as_mut()` instead is shorter.
152
+ ///
153
+ /// **Known problems:** None.
154
+ ///
155
+ /// **Example:**
156
+ /// ```rust
157
+ /// let x: Option<()> = None;
158
+ /// let r: Option<&()> = match x {
159
+ /// None => None,
160
+ /// Some(ref v) => Some(v),
161
+ /// };
162
+ /// ```
163
+ declare_lint ! {
164
+ pub MATCH_AS_REF ,
165
+ Warn ,
166
+ "a match on an Option value instead of using `as_ref()` or `as_mut`"
167
+ }
168
+
148
169
#[ allow( missing_copy_implementations) ]
149
170
pub struct MatchPass ;
150
171
@@ -156,7 +177,8 @@ impl LintPass for MatchPass {
156
177
MATCH_BOOL ,
157
178
SINGLE_MATCH_ELSE ,
158
179
MATCH_OVERLAPPING_ARM ,
159
- MATCH_WILD_ERR_ARM
180
+ MATCH_WILD_ERR_ARM ,
181
+ MATCH_AS_REF
160
182
)
161
183
}
162
184
}
@@ -171,6 +193,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MatchPass {
171
193
check_match_bool ( cx, ex, arms, expr) ;
172
194
check_overlapping_arms ( cx, ex, arms) ;
173
195
check_wild_err_arm ( cx, ex, arms) ;
196
+ check_match_as_ref ( cx, ex, arms, expr) ;
174
197
}
175
198
if let ExprMatch ( ref ex, ref arms, source) = expr. node {
176
199
check_match_ref_pats ( cx, ex, arms, source, expr) ;
@@ -411,6 +434,31 @@ fn check_match_ref_pats(cx: &LateContext, ex: &Expr, arms: &[Arm], source: Match
411
434
}
412
435
}
413
436
437
+ fn check_match_as_ref ( cx : & LateContext , ex : & Expr , arms : & [ Arm ] , expr : & Expr ) {
438
+ if arms. len ( ) == 2 &&
439
+ arms[ 0 ] . pats . len ( ) == 1 && arms[ 0 ] . guard . is_none ( ) &&
440
+ arms[ 1 ] . pats . len ( ) == 1 && arms[ 1 ] . guard . is_none ( ) {
441
+ let arm_ref: Option < BindingAnnotation > = if is_none_arm ( & arms[ 0 ] ) {
442
+ is_ref_some_arm ( & arms[ 1 ] )
443
+ } else if is_none_arm ( & arms[ 1 ] ) {
444
+ is_ref_some_arm ( & arms[ 0 ] )
445
+ } else {
446
+ None
447
+ } ;
448
+ if let Some ( rb) = arm_ref {
449
+ let suggestion = if rb == BindingAnnotation :: Ref { "as_ref" } else { "as_mut" } ;
450
+ span_lint_and_sugg (
451
+ cx,
452
+ MATCH_AS_REF ,
453
+ expr. span ,
454
+ & format ! ( "use {}() instead" , suggestion) ,
455
+ "try this" ,
456
+ format ! ( "{}.{}()" , snippet( cx, ex. span, "_" ) , suggestion)
457
+ )
458
+ }
459
+ }
460
+ }
461
+
414
462
/// Get all arms that are unbounded `PatRange`s.
415
463
fn all_ranges < ' a , ' tcx > (
416
464
cx : & LateContext < ' a , ' tcx > ,
@@ -524,6 +572,34 @@ fn is_unit_expr(expr: &Expr) -> bool {
524
572
}
525
573
}
526
574
575
+ // Checks if arm has the form `None => None`
576
+ fn is_none_arm ( arm : & Arm ) -> bool {
577
+ match arm. pats [ 0 ] . node {
578
+ PatKind :: Path ( ref path) if match_qpath ( path, & paths:: OPTION_NONE ) => true ,
579
+ _ => false ,
580
+ }
581
+ }
582
+
583
+ // Checks if arm has the form `Some(ref v) => Some(v)` (checks for `ref` and `ref mut`)
584
+ fn is_ref_some_arm ( arm : & Arm ) -> Option < BindingAnnotation > {
585
+ if_chain ! {
586
+ if let PatKind :: TupleStruct ( ref path, ref pats, _) = arm. pats[ 0 ] . node;
587
+ if pats. len( ) == 1 && match_qpath( path, & paths:: OPTION_SOME ) ;
588
+ if let PatKind :: Binding ( rb, _, ref ident, _) = pats[ 0 ] . node;
589
+ if rb == BindingAnnotation :: Ref || rb == BindingAnnotation :: RefMut ;
590
+ if let ExprCall ( ref e, ref args) = remove_blocks( & arm. body) . node;
591
+ if let ExprPath ( ref some_path) = e. node;
592
+ if match_qpath( some_path, & paths:: OPTION_SOME ) && args. len( ) == 1 ;
593
+ if let ExprPath ( ref qpath) = args[ 0 ] . node;
594
+ if let & QPath :: Resolved ( _, ref path2) = qpath;
595
+ if path2. segments. len( ) == 1 && ident. node == path2. segments[ 0 ] . name;
596
+ then {
597
+ return Some ( rb)
598
+ }
599
+ }
600
+ None
601
+ }
602
+
527
603
fn has_only_ref_pats ( arms : & [ Arm ] ) -> bool {
528
604
let mapped = arms. iter ( )
529
605
. flat_map ( |a| & a. pats )
0 commit comments