@@ -2,15 +2,15 @@ use crate::consts::constant_simple;
2
2
use crate :: utils;
3
3
use if_chain:: if_chain;
4
4
use rustc_errors:: Applicability ;
5
- use rustc_hir:: { def, Arm , Expr , ExprKind , PatKind , QPath } ;
5
+ use rustc_hir:: { def, Arm , Expr , ExprKind , Pat , PatKind , QPath } ;
6
6
use rustc_lint:: LintContext ;
7
7
use rustc_lint:: { LateContext , LateLintPass } ;
8
8
use rustc_middle:: lint:: in_external_macro;
9
9
use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
10
10
11
11
declare_clippy_lint ! {
12
12
/// **What it does:**
13
- /// Finds patterns that reimplement `Option::unwrap_or`.
13
+ /// Finds patterns that reimplement `Option::unwrap_or` or `Result::unwrap_or` .
14
14
///
15
15
/// **Why is this bad?**
16
16
/// Concise code helps focusing on behavior instead of boilerplate.
@@ -33,7 +33,7 @@ declare_clippy_lint! {
33
33
/// ```
34
34
pub MANUAL_UNWRAP_OR ,
35
35
complexity,
36
- "finds patterns that can be encoded more concisely with `Option::unwrap_or`"
36
+ "finds patterns that can be encoded more concisely with `Option::unwrap_or` or `Result::unwrap_or` "
37
37
}
38
38
39
39
declare_lint_pass ! ( ManualUnwrapOr => [ MANUAL_UNWRAP_OR ] ) ;
@@ -43,32 +43,50 @@ impl LateLintPass<'_> for ManualUnwrapOr {
43
43
if in_external_macro ( cx. sess ( ) , expr. span ) {
44
44
return ;
45
45
}
46
- lint_option_unwrap_or_case ( cx, expr) ;
46
+ lint_manual_unwrap_or ( cx, expr) ;
47
47
}
48
48
}
49
49
50
- fn lint_option_unwrap_or_case < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > ) {
51
- fn applicable_none_arm < ' a > ( arms : & ' a [ Arm < ' a > ] ) -> Option < & ' a Arm < ' a > > {
50
+ #[ derive( Copy , Clone ) ]
51
+ enum Case {
52
+ Option ,
53
+ Result ,
54
+ }
55
+
56
+ impl Case {
57
+ fn unwrap_fn_path ( & self ) -> & str {
58
+ match self {
59
+ Case :: Option => "Option::unwrap_or" ,
60
+ Case :: Result => "Result::unwrap_or" ,
61
+ }
62
+ }
63
+ }
64
+
65
+ fn lint_manual_unwrap_or < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > ) {
66
+ fn applicable_or_arm < ' a > ( arms : & ' a [ Arm < ' a > ] ) -> Option < & ' a Arm < ' a > > {
52
67
if_chain ! {
53
68
if arms. len( ) == 2 ;
54
69
if arms. iter( ) . all( |arm| arm. guard. is_none( ) ) ;
55
- if let Some ( ( idx, none_arm) ) = arms. iter( ) . enumerate( ) . find( |( _, arm) |
56
- if let PatKind :: Path ( ref qpath) = arm. pat. kind {
57
- utils:: match_qpath( qpath, & utils:: paths:: OPTION_NONE )
58
- } else {
59
- false
70
+ if let Some ( ( idx, or_arm) ) = arms. iter( ) . enumerate( ) . find( |( _, arm) |
71
+ match arm. pat. kind {
72
+ PatKind :: Path ( ref some_qpath) =>
73
+ utils:: match_qpath( some_qpath, & utils:: paths:: OPTION_NONE ) ,
74
+ PatKind :: TupleStruct ( ref err_qpath, & [ Pat { kind: PatKind :: Wild , .. } ] , _) =>
75
+ utils:: match_qpath( err_qpath, & utils:: paths:: RESULT_ERR ) ,
76
+ _ => false ,
60
77
}
61
78
) ;
62
- let some_arm = & arms[ 1 - idx] ;
63
- if let PatKind :: TupleStruct ( ref some_qpath, & [ some_binding] , _) = some_arm. pat. kind;
64
- if utils:: match_qpath( some_qpath, & utils:: paths:: OPTION_SOME ) ;
65
- if let PatKind :: Binding ( _, binding_hir_id, ..) = some_binding. kind;
66
- if let ExprKind :: Path ( QPath :: Resolved ( _, body_path) ) = some_arm. body. kind;
79
+ let unwrap_arm = & arms[ 1 - idx] ;
80
+ if let PatKind :: TupleStruct ( ref unwrap_qpath, & [ unwrap_pat] , _) = unwrap_arm. pat. kind;
81
+ if utils:: match_qpath( unwrap_qpath, & utils:: paths:: OPTION_SOME )
82
+ || utils:: match_qpath( unwrap_qpath, & utils:: paths:: RESULT_OK ) ;
83
+ if let PatKind :: Binding ( _, binding_hir_id, ..) = unwrap_pat. kind;
84
+ if let ExprKind :: Path ( QPath :: Resolved ( _, body_path) ) = unwrap_arm. body. kind;
67
85
if let def:: Res :: Local ( body_path_hir_id) = body_path. res;
68
86
if body_path_hir_id == binding_hir_id;
69
- if !utils:: usage:: contains_return_break_continue_macro( none_arm . body) ;
87
+ if !utils:: usage:: contains_return_break_continue_macro( or_arm . body) ;
70
88
then {
71
- Some ( none_arm )
89
+ Some ( or_arm )
72
90
} else {
73
91
None
74
92
}
@@ -78,24 +96,35 @@ fn lint_option_unwrap_or_case<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tc
78
96
if_chain ! {
79
97
if let ExprKind :: Match ( scrutinee, match_arms, _) = expr. kind;
80
98
let ty = cx. typeck_results( ) . expr_ty( scrutinee) ;
81
- if utils:: is_type_diagnostic_item( cx, ty, sym!( option_type) ) ;
82
- if let Some ( none_arm) = applicable_none_arm( match_arms) ;
99
+ if let Some ( case) = if utils:: is_type_diagnostic_item( cx, ty, sym!( option_type) ) {
100
+ Some ( Case :: Option )
101
+ } else if utils:: is_type_diagnostic_item( cx, ty, sym!( result_type) ) {
102
+ Some ( Case :: Result )
103
+ } else {
104
+ None
105
+ } ;
106
+ if let Some ( or_arm) = applicable_or_arm( match_arms) ;
83
107
if let Some ( scrutinee_snippet) = utils:: snippet_opt( cx, scrutinee. span) ;
84
- if let Some ( none_body_snippet ) = utils:: snippet_opt( cx, none_arm . body. span) ;
108
+ if let Some ( or_body_snippet ) = utils:: snippet_opt( cx, or_arm . body. span) ;
85
109
if let Some ( indent) = utils:: indent_of( cx, expr. span) ;
86
- if constant_simple( cx, cx. typeck_results( ) , none_arm . body) . is_some( ) ;
110
+ if constant_simple( cx, cx. typeck_results( ) , or_arm . body) . is_some( ) ;
87
111
then {
88
- let reindented_none_body =
89
- utils:: reindent_multiline( none_body_snippet. into( ) , true , Some ( indent) ) ;
112
+ let reindented_or_body =
113
+ utils:: reindent_multiline( or_body_snippet. into( ) , true , Some ( indent) ) ;
114
+ let wrap_in_parens = !matches!( scrutinee, Expr { kind: ExprKind :: Call ( ..) , .. } ) ;
115
+ let l_paren = if wrap_in_parens { "(" } else { "" } ;
116
+ let r_paren = if wrap_in_parens { ")" } else { "" } ;
90
117
utils:: span_lint_and_sugg(
91
118
cx,
92
119
MANUAL_UNWRAP_OR , expr. span,
93
- "this pattern reimplements `Option::unwrap_or`" ,
120
+ & format! ( "this pattern reimplements `{}`" , case . unwrap_fn_path ( ) ) ,
94
121
"replace with" ,
95
122
format!(
96
- "{}.unwrap_or({})" ,
123
+ "{}{}{}.unwrap_or({})" ,
124
+ l_paren,
97
125
scrutinee_snippet,
98
- reindented_none_body,
126
+ r_paren,
127
+ reindented_or_body,
99
128
) ,
100
129
Applicability :: MachineApplicable ,
101
130
) ;
0 commit comments