@@ -2,12 +2,14 @@ use crate::utils;
22use if_chain:: if_chain;
33use rustc_errors:: Applicability ;
44use rustc_hir:: { def, Arm , Expr , ExprKind , PatKind , QPath } ;
5+ use rustc_lint:: LintContext ;
56use rustc_lint:: { LateContext , LateLintPass } ;
7+ use rustc_middle:: lint:: in_external_macro;
68use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
79
810declare_clippy_lint ! {
911 /// **What it does:**
10- /// Finds patterns that can be encoded more concisely with `Option::unwrap_or`.
12+ /// Finds patterns that reimplement `Option::unwrap_or`.
1113 ///
1214 /// **Why is this bad?**
1315 /// Concise code helps focusing on behavior instead of boilerplate.
@@ -16,47 +18,43 @@ declare_clippy_lint! {
1618 ///
1719 /// **Example:**
1820 /// ```rust
19- /// match int_optional {
21+ /// match int_option {
2022 /// Some(v) => v,
2123 /// None => 1,
2224 /// }
2325 /// ```
2426 ///
2527 /// Use instead:
2628 /// ```rust
27- /// int_optional .unwrap_or(1)
29+ /// int_option .unwrap_or(1)
2830 /// ```
29- pub LESS_CONCISE_THAN_OPTION_UNWRAP_OR ,
30- pedantic ,
31- "finds patterns that can be encoded more concisely with `Option::unwrap_or`"
31+ pub MANUAL_UNWRAP_OR ,
32+ complexity ,
33+ "finds patterns that can be encoded more concisely with `Option::unwrap_or(_else) `"
3234}
3335
34- declare_lint_pass ! ( LessConciseThan => [ LESS_CONCISE_THAN_OPTION_UNWRAP_OR ] ) ;
36+ declare_lint_pass ! ( ManualUnwrapOr => [ MANUAL_UNWRAP_OR ] ) ;
3537
36- impl LateLintPass < ' _ > for LessConciseThan {
38+ impl LateLintPass < ' _ > for ManualUnwrapOr {
3739 fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > ) {
38- if utils:: in_macro ( expr. span ) {
39- return ;
40- }
41- if lint_option_unwrap_or_case ( cx, expr) {
40+ if in_external_macro ( cx. sess ( ) , expr. span ) {
4241 return ;
4342 }
43+ lint_option_unwrap_or_case ( cx, expr) ;
4444 }
4545}
4646
4747fn lint_option_unwrap_or_case < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > ) -> bool {
48- #[ allow( clippy:: needless_bool) ]
4948 fn applicable_none_arm < ' a > ( arms : & ' a [ Arm < ' a > ] ) -> Option < & ' a Arm < ' a > > {
5049 if_chain ! {
5150 if arms. len( ) == 2 ;
5251 if arms. iter( ) . all( |arm| arm. guard. is_none( ) ) ;
5352 if let Some ( ( idx, none_arm) ) = arms. iter( ) . enumerate( ) . find( |( _, arm) |
54- if_chain! {
55- if let PatKind :: Path ( ref qpath) = arm. pat. kind;
56- if utils:: match_qpath( qpath, & utils:: paths:: OPTION_NONE ) ;
57- then { true }
58- else { false }
59- }
53+ if let PatKind :: Path ( ref qpath) = arm. pat. kind {
54+ utils:: match_qpath( qpath, & utils:: paths:: OPTION_NONE )
55+ } else {
56+ false
57+ }
6058 ) ;
6159 let some_arm = & arms[ 1 - idx] ;
6260 if let PatKind :: TupleStruct ( ref some_qpath, & [ some_binding] , _) = some_arm. pat. kind;
@@ -65,43 +63,50 @@ fn lint_option_unwrap_or_case<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tc
6563 if let ExprKind :: Path ( QPath :: Resolved ( _, body_path) ) = some_arm. body. kind;
6664 if let def:: Res :: Local ( body_path_hir_id) = body_path. res;
6765 if body_path_hir_id == binding_hir_id;
68- then { Some ( none_arm) }
69- else { None }
66+ if !utils:: usage:: contains_return_break_continue_macro( none_arm. body) ;
67+ then {
68+ Some ( none_arm)
69+ }
70+ else {
71+ None
72+ }
7073 }
7174 }
75+
7276 if_chain ! {
73- if !utils:: usage:: contains_return_break_continue_macro( expr) ;
74- if let ExprKind :: Match ( match_expr, match_arms, _) = expr. kind;
75- let ty = cx. typeck_results( ) . expr_ty( match_expr) ;
76- if utils:: is_type_diagnostic_item( cx, ty, sym!( option_type) ) ;
77- if let Some ( none_arm) = applicable_none_arm( match_arms) ;
78- if let Some ( match_expr_snippet) = utils:: snippet_opt( cx, match_expr. span) ;
79- if let Some ( none_body_snippet) = utils:: snippet_opt( cx, none_arm. body. span) ;
80- if let Some ( indent) = utils:: indent_of( cx, expr. span) ;
81- then {
82- let reindented_none_body =
83- utils:: reindent_multiline( none_body_snippet. into( ) , true , Some ( indent) ) ;
84- let eager_eval = utils:: eager_or_lazy:: is_eagerness_candidate( cx, none_arm. body) ;
85- let method = if eager_eval {
86- "unwrap_or"
87- } else {
88- "unwrap_or_else"
89- } ;
90- utils:: span_lint_and_sugg(
91- cx,
92- LESS_CONCISE_THAN_OPTION_UNWRAP_OR , expr. span,
93- "this pattern can be more concisely encoded with `Option::unwrap_or`" ,
94- "replace with" ,
95- format!(
96- "{}.{}({}{})" ,
97- match_expr_snippet,
98- method,
99- if eager_eval { "" } else { "|| " } ,
100- reindented_none_body
101- ) ,
102- Applicability :: MachineApplicable ,
103- ) ;
104- true
105- } else { false }
77+ if let ExprKind :: Match ( scrutinee, match_arms, _) = expr. kind;
78+ let ty = cx. typeck_results( ) . expr_ty( scrutinee) ;
79+ if utils:: is_type_diagnostic_item( cx, ty, sym!( option_type) ) ;
80+ if let Some ( none_arm) = applicable_none_arm( match_arms) ;
81+ if let Some ( scrutinee_snippet) = utils:: snippet_opt( cx, scrutinee. span) ;
82+ if let Some ( none_body_snippet) = utils:: snippet_opt( cx, none_arm. body. span) ;
83+ if let Some ( indent) = utils:: indent_of( cx, expr. span) ;
84+ then {
85+ let reindented_none_body =
86+ utils:: reindent_multiline( none_body_snippet. into( ) , true , Some ( indent) ) ;
87+ let eager_eval = utils:: eager_or_lazy:: is_eagerness_candidate( cx, none_arm. body) ;
88+ let method = if eager_eval {
89+ "unwrap_or"
90+ } else {
91+ "unwrap_or_else"
92+ } ;
93+ utils:: span_lint_and_sugg(
94+ cx,
95+ MANUAL_UNWRAP_OR , expr. span,
96+ & format!( "this pattern reimplements `Option::{}`" , & method) ,
97+ "replace with" ,
98+ format!(
99+ "{}.{}({}{})" ,
100+ scrutinee_snippet,
101+ method,
102+ if eager_eval { "" } else { "|| " } ,
103+ reindented_none_body
104+ ) ,
105+ Applicability :: MachineApplicable ,
106+ ) ;
107+ true
108+ } else {
109+ false
110+ }
106111 }
107112}
0 commit comments