@@ -2,15 +2,15 @@ use crate::consts::constant_simple;
22use crate :: utils;
33use if_chain:: if_chain;
44use rustc_errors:: Applicability ;
5- use rustc_hir:: { def, Arm , Expr , ExprKind , PatKind , QPath } ;
5+ use rustc_hir:: { def, Arm , Expr , ExprKind , Pat , PatKind , QPath } ;
66use rustc_lint:: LintContext ;
77use rustc_lint:: { LateContext , LateLintPass } ;
88use rustc_middle:: lint:: in_external_macro;
99use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
1010
1111declare_clippy_lint ! {
1212 /// **What it does:**
13- /// Finds patterns that reimplement `Option::unwrap_or`.
13+ /// Finds patterns that reimplement `Option::unwrap_or` or `Result::unwrap_or` .
1414 ///
1515 /// **Why is this bad?**
1616 /// Concise code helps focusing on behavior instead of boilerplate.
@@ -33,7 +33,7 @@ declare_clippy_lint! {
3333 /// ```
3434 pub MANUAL_UNWRAP_OR ,
3535 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` "
3737}
3838
3939declare_lint_pass ! ( ManualUnwrapOr => [ MANUAL_UNWRAP_OR ] ) ;
@@ -43,32 +43,50 @@ impl LateLintPass<'_> for ManualUnwrapOr {
4343 if in_external_macro ( cx. sess ( ) , expr. span ) {
4444 return ;
4545 }
46- lint_option_unwrap_or_case ( cx, expr) ;
46+ lint_manual_unwrap_or ( cx, expr) ;
4747 }
4848}
4949
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 > > {
5267 if_chain ! {
5368 if arms. len( ) == 2 ;
5469 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 ,
6077 }
6178 ) ;
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;
6785 if let def:: Res :: Local ( body_path_hir_id) = body_path. res;
6886 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) ;
7088 then {
71- Some ( none_arm )
89+ Some ( or_arm )
7290 } else {
7391 None
7492 }
@@ -78,24 +96,35 @@ fn lint_option_unwrap_or_case<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tc
7896 if_chain ! {
7997 if let ExprKind :: Match ( scrutinee, match_arms, _) = expr. kind;
8098 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) ;
83107 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) ;
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) ) ;
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 { "" } ;
90117 utils:: span_lint_and_sugg(
91118 cx,
92119 MANUAL_UNWRAP_OR , expr. span,
93- "this pattern reimplements `Option::unwrap_or`" ,
120+ & format! ( "this pattern reimplements `{}`" , case . unwrap_fn_path ( ) ) ,
94121 "replace with" ,
95122 format!(
96- "{}.unwrap_or({})" ,
123+ "{}{}{}.unwrap_or({})" ,
124+ l_paren,
97125 scrutinee_snippet,
98- reindented_none_body,
126+ r_paren,
127+ reindented_or_body,
99128 ) ,
100129 Applicability :: MachineApplicable ,
101130 ) ;
0 commit comments