11use crate :: consts:: constant_simple;
22use crate :: utils;
3+ use crate :: utils:: sugg;
34use if_chain:: if_chain;
45use rustc_errors:: Applicability ;
5- use rustc_hir:: { def, Arm , Expr , ExprKind , PatKind , QPath } ;
6+ use rustc_hir:: { def, Arm , Expr , ExprKind , Pat , PatKind , QPath } ;
67use rustc_lint:: LintContext ;
78use rustc_lint:: { LateContext , LateLintPass } ;
89use rustc_middle:: lint:: in_external_macro;
910use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
1011
1112declare_clippy_lint ! {
1213 /// **What it does:**
13- /// Finds patterns that reimplement `Option::unwrap_or`.
14+ /// Finds patterns that reimplement `Option::unwrap_or` or `Result::unwrap_or` .
1415 ///
1516 /// **Why is this bad?**
1617 /// Concise code helps focusing on behavior instead of boilerplate.
@@ -33,7 +34,7 @@ declare_clippy_lint! {
3334 /// ```
3435 pub MANUAL_UNWRAP_OR ,
3536 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` "
3738}
3839
3940declare_lint_pass ! ( ManualUnwrapOr => [ MANUAL_UNWRAP_OR ] ) ;
@@ -43,32 +44,50 @@ impl LateLintPass<'_> for ManualUnwrapOr {
4344 if in_external_macro ( cx. sess ( ) , expr. span ) {
4445 return ;
4546 }
46- lint_option_unwrap_or_case ( cx, expr) ;
47+ lint_manual_unwrap_or ( cx, expr) ;
4748 }
4849}
4950
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 > > {
5268 if_chain ! {
5369 if arms. len( ) == 2 ;
5470 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 ,
6078 }
6179 ) ;
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;
6786 if let def:: Res :: Local ( body_path_hir_id) = body_path. res;
6887 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) ;
7089 then {
71- Some ( none_arm )
90+ Some ( or_arm )
7291 } else {
7392 None
7493 }
@@ -78,24 +97,29 @@ fn lint_option_unwrap_or_case<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tc
7897 if_chain ! {
7998 if let ExprKind :: Match ( scrutinee, match_arms, _) = expr. kind;
8099 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) ;
85109 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( ) ;
87111 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) ) ;
90114 utils:: span_lint_and_sugg(
91115 cx,
92116 MANUAL_UNWRAP_OR , expr. span,
93- "this pattern reimplements `Option::unwrap_or`" ,
117+ & format! ( "this pattern reimplements `{}`" , case . unwrap_fn_path ( ) ) ,
94118 "replace with" ,
95119 format!(
96120 "{}.unwrap_or({})" ,
97- scrutinee_snippet ,
98- reindented_none_body ,
121+ sugg :: Sugg :: hir ( cx , scrutinee , ".." ) . maybe_par ( ) ,
122+ reindented_or_body ,
99123 ) ,
100124 Applicability :: MachineApplicable ,
101125 ) ;
0 commit comments