11use clippy_utils:: diagnostics:: span_lint_and_sugg;
22use clippy_utils:: higher;
33use clippy_utils:: source:: snippet_with_applicability;
4- use clippy_utils:: sugg:: Sugg ;
54use clippy_utils:: ty:: is_type_diagnostic_item;
65use clippy_utils:: { eq_expr_value, is_lang_ctor, path_to_local, path_to_local_id, peel_blocks, peel_blocks_with_stmt} ;
76use if_chain:: if_chain;
87use rustc_errors:: Applicability ;
98use rustc_hir:: LangItem :: { OptionNone , OptionSome , ResultOk } ;
109use rustc_hir:: { BindingAnnotation , Expr , ExprKind , PatKind } ;
1110use rustc_lint:: { LateContext , LateLintPass } ;
11+ use rustc_middle:: ty:: Ty ;
1212use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
13- use rustc_span:: sym;
13+ use rustc_span:: { sym, symbol :: Symbol } ;
1414
1515declare_clippy_lint ! {
1616 /// ### What it does
@@ -60,35 +60,25 @@ impl QuestionMark {
6060 if let Some ( higher:: If { cond, then, r#else } ) = higher:: If :: hir( expr) ;
6161 if let ExprKind :: MethodCall ( segment, args, _) = & cond. kind;
6262 if let Some ( subject) = args. get( 0 ) ;
63- if ( Self :: option_check_and_early_return( cx, subject, then) && segment. ident. name == sym!( is_none) ) ||
64- ( Self :: result_check_and_early_return( cx, subject, then) && segment. ident. name == sym!( is_err) ) ;
63+ let subject_ty = cx. typeck_results( ) . expr_ty( subject) ;
64+ if ( Self :: check_early_return( cx, subject, then, subject_ty, sym:: Option ) &&
65+ segment. ident. name == sym!( is_none) ) ||
66+ ( Self :: check_early_return( cx, subject, then, subject_ty, sym:: Result ) &&
67+ segment. ident. name == sym!( is_err) ) ;
6568 then {
6669 let mut applicability = Applicability :: MachineApplicable ;
67- let receiver_str = & Sugg :: hir_with_applicability( cx, subject, ".." , & mut applicability) ;
68- let mut replacement: Option <String > = None ;
70+ let receiver_str = snippet_with_applicability( cx, subject. span, ".." , & mut applicability) ;
71+ let by_ref = !subject_ty. is_copy_modulo_regions( cx. tcx. at( subject. span) , cx. param_env) &&
72+ !matches!( subject. kind, ExprKind :: Call ( ..) | ExprKind :: MethodCall ( ..) ) ;
73+ let mut suggestion = String :: new( ) ;
6974 if let Some ( else_inner) = r#else {
7075 if eq_expr_value( cx, subject, peel_blocks( else_inner) ) {
71- replacement = Some ( format!( "Some({}?)" , receiver_str) ) ;
76+ suggestion = format!( "Some({}?)" , receiver_str) ;
7277 }
73- } else if Self :: moves_by_default( cx, subject)
74- && !matches!( subject. kind, ExprKind :: Call ( ..) | ExprKind :: MethodCall ( ..) )
75- {
76- replacement = Some ( format!( "{}.as_ref()?;" , receiver_str) ) ;
7778 } else {
78- replacement = Some ( format!( "{}?;" , receiver_str) ) ;
79- }
80-
81- if let Some ( replacement_str) = replacement {
82- span_lint_and_sugg(
83- cx,
84- QUESTION_MARK ,
85- expr. span,
86- "this block may be rewritten with the `?` operator" ,
87- "replace it with" ,
88- replacement_str,
89- applicability,
90- ) ;
79+ suggestion = format!( "{}{}?;" , receiver_str, if by_ref { ".as_ref()" } else { "" } ) ;
9180 }
81+ Self :: offer_suggestion( cx, expr, suggestion, applicability) ;
9282 }
9383 }
9484 }
@@ -98,8 +88,11 @@ impl QuestionMark {
9888 if let Some ( higher:: IfLet { let_pat, let_expr, if_then, if_else: Some ( if_else) } )
9989 = higher:: IfLet :: hir( cx, expr) ;
10090 if let PatKind :: TupleStruct ( ref path1, fields, None ) = let_pat. kind;
101- if ( Self :: option_check_and_early_return( cx, let_expr, if_else) && is_lang_ctor( cx, path1, OptionSome ) ) ||
102- ( Self :: result_check_and_early_return( cx, let_expr, if_else) && is_lang_ctor( cx, path1, ResultOk ) ) ;
91+ let let_expr_ty = cx. typeck_results( ) . expr_ty( let_expr) ;
92+ if ( Self :: check_early_return( cx, let_expr, if_else, let_expr_ty, sym:: Option ) &&
93+ is_lang_ctor( cx, path1, OptionSome ) ) ||
94+ ( Self :: check_early_return( cx, let_expr, if_else, let_expr_ty, sym:: Result ) &&
95+ is_lang_ctor( cx, path1, ResultOk ) ) ;
10396
10497 if let PatKind :: Binding ( annot, bind_id, _, _) = fields[ 0 ] . kind;
10598 let by_ref = matches!( annot, BindingAnnotation :: Ref | BindingAnnotation :: RefMut ) ;
@@ -108,59 +101,52 @@ impl QuestionMark {
108101 let mut applicability = Applicability :: MachineApplicable ;
109102 let receiver_str = snippet_with_applicability( cx, let_expr. span, ".." , & mut applicability) ;
110103 let replacement = format!( "{}{}?" , receiver_str, if by_ref { ".as_ref()" } else { "" } , ) ;
111-
112- span_lint_and_sugg(
113- cx,
114- QUESTION_MARK ,
115- expr. span,
116- "this if-let-else may be rewritten with the `?` operator" ,
117- "replace it with" ,
118- replacement,
119- applicability,
120- ) ;
104+ Self :: offer_suggestion( cx, expr, replacement, applicability) ;
121105 }
122106 }
123107 }
124108
125- fn result_check_and_early_return ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > , nested_expr : & Expr < ' _ > ) -> bool {
126- Self :: is_result ( cx, expr) && Self :: expression_returns_unmodified_err ( cx, nested_expr, expr)
127- }
128-
129- fn option_check_and_early_return ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > , nested_expr : & Expr < ' _ > ) -> bool {
130- Self :: is_option ( cx, expr) && Self :: expression_returns_none ( cx, nested_expr)
131- }
132-
133- fn moves_by_default ( cx : & LateContext < ' _ > , expression : & Expr < ' _ > ) -> bool {
134- let expr_ty = cx. typeck_results ( ) . expr_ty ( expression) ;
135-
136- !expr_ty. is_copy_modulo_regions ( cx. tcx . at ( expression. span ) , cx. param_env )
109+ fn check_early_return (
110+ cx : & LateContext < ' _ > ,
111+ cond_expr : & Expr < ' _ > ,
112+ nested_expr : & Expr < ' _ > ,
113+ cond_ty : Ty < ' _ > ,
114+ ret_sym : Symbol ,
115+ ) -> bool {
116+ is_type_diagnostic_item ( cx, cond_ty, ret_sym)
117+ && Self :: expr_return_none_or_err ( cx, cond_expr, nested_expr, ret_sym)
137118 }
138119
139- fn is_option ( cx : & LateContext < ' _ > , expression : & Expr < ' _ > ) -> bool {
140- let expr_ty = cx. typeck_results ( ) . expr_ty ( expression) ;
141-
142- is_type_diagnostic_item ( cx, expr_ty, sym:: Option )
143- }
144-
145- fn is_result ( cx : & LateContext < ' _ > , expression : & Expr < ' _ > ) -> bool {
146- let expr_ty = cx. typeck_results ( ) . expr_ty ( expression) ;
147-
148- is_type_diagnostic_item ( cx, expr_ty, sym:: Result )
149- }
150-
151- fn expression_returns_none ( cx : & LateContext < ' _ > , expression : & Expr < ' _ > ) -> bool {
152- match peel_blocks_with_stmt ( expression) . kind {
153- ExprKind :: Ret ( Some ( expr) ) => Self :: expression_returns_none ( cx, expr) ,
154- ExprKind :: Path ( ref qpath) => is_lang_ctor ( cx, qpath, OptionNone ) ,
120+ fn expr_return_none_or_err (
121+ cx : & LateContext < ' _ > ,
122+ cond_expr : & Expr < ' _ > ,
123+ nested_expr : & Expr < ' _ > ,
124+ ret_sym : Symbol ,
125+ ) -> bool {
126+ match peel_blocks_with_stmt ( nested_expr) . kind {
127+ ExprKind :: Ret ( Some ( ret_expr) ) => Self :: expr_return_none_or_err ( cx, cond_expr, ret_expr, ret_sym) ,
128+ ExprKind :: Path ( ref qpath) => match ret_sym {
129+ sym:: Option => is_lang_ctor ( cx, qpath, OptionNone ) ,
130+ sym:: Result => {
131+ path_to_local ( nested_expr) . is_some ( ) && path_to_local ( nested_expr) == path_to_local ( cond_expr)
132+ } ,
133+ _ => false ,
134+ } ,
155135 _ => false ,
156136 }
157137 }
158138
159- fn expression_returns_unmodified_err ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > , cond_expr : & Expr < ' _ > ) -> bool {
160- match peel_blocks_with_stmt ( expr) . kind {
161- ExprKind :: Ret ( Some ( ret_expr) ) => Self :: expression_returns_unmodified_err ( cx, ret_expr, cond_expr) ,
162- ExprKind :: Path ( _) => path_to_local ( expr) . is_some ( ) && path_to_local ( expr) == path_to_local ( cond_expr) ,
163- _ => false ,
139+ fn offer_suggestion ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > , suggestion : String , applicability : Applicability ) {
140+ if !suggestion. is_empty ( ) {
141+ span_lint_and_sugg (
142+ cx,
143+ QUESTION_MARK ,
144+ expr. span ,
145+ "this block may be rewritten with the `?` operator" ,
146+ "replace it with" ,
147+ suggestion,
148+ applicability,
149+ ) ;
164150 }
165151 }
166152}
0 commit comments