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