@@ -3,14 +3,14 @@ use clippy_utils::higher;
33use clippy_utils:: source:: snippet_with_applicability;
44use clippy_utils:: sugg:: Sugg ;
55use clippy_utils:: ty:: is_type_diagnostic_item;
6- use clippy_utils:: { eq_expr_value, is_lang_ctor, path_to_local, path_to_local_id , peel_blocks, peel_blocks_with_stmt} ;
6+ use clippy_utils:: { eq_expr_value, is_lang_ctor, path_to_local, peel_blocks, peel_blocks_with_stmt} ;
77use if_chain:: if_chain;
88use rustc_errors:: Applicability ;
9- use rustc_hir:: LangItem :: { OptionNone , OptionSome , ResultOk } ;
10- use rustc_hir:: { BindingAnnotation , Expr , ExprKind , PatKind } ;
9+ use rustc_hir:: LangItem :: { OptionNone , OptionSome , ResultErr , ResultOk } ;
10+ use rustc_hir:: { BindingAnnotation , Expr , ExprKind , PatKind , QPath } ;
1111use rustc_lint:: { LateContext , LateLintPass } ;
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
@@ -61,21 +61,21 @@ impl QuestionMark {
6161 if let ExprKind :: MethodCall ( segment, _, args, _) = & cond. kind;
6262 if let Some ( subject) = args. get( 0 ) ;
6363 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) ) ;
64+ ( Self :: result_check_and_early_return( cx, subject, then, None ) && segment. ident. name == sym!( is_err) ) ;
6565 then {
6666 let mut applicability = Applicability :: MachineApplicable ;
67- let receiver_str = & Sugg :: hir_with_applicability( cx, subject, ".." , & mut applicability) ;
67+ let suggestion = & Sugg :: hir_with_applicability( cx, subject, ".." , & mut applicability) ;
6868 let mut replacement: Option <String > = None ;
6969 if let Some ( else_inner) = r#else {
7070 if eq_expr_value( cx, subject, peel_blocks( else_inner) ) {
71- replacement = Some ( format!( "Some({}?)" , receiver_str ) ) ;
71+ replacement = Some ( format!( "Some({}?)" , suggestion ) ) ;
7272 }
7373 } else if Self :: moves_by_default( cx, subject)
7474 && !matches!( subject. kind, ExprKind :: Call ( ..) | ExprKind :: MethodCall ( ..) )
7575 {
76- replacement = Some ( format!( "{}.as_ref()?;" , receiver_str ) ) ;
76+ replacement = Some ( format!( "{}.as_ref()?;" , suggestion ) ) ;
7777 } else {
78- replacement = Some ( format!( "{}?;" , receiver_str ) ) ;
78+ replacement = Some ( format!( "{}?;" , suggestion ) ) ;
7979 }
8080
8181 if let Some ( replacement_str) = replacement {
@@ -95,19 +95,20 @@ impl QuestionMark {
9595
9696 fn check_if_let_some_or_err_and_early_return ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) {
9797 if_chain ! {
98- if let Some ( higher:: IfLet { let_pat, let_expr, if_then, if_else: Some ( if_else ) } )
98+ if let Some ( higher:: IfLet { let_pat, let_expr, if_then, if_else } )
9999 = higher:: IfLet :: hir( cx, expr) ;
100100 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 ) ) ;
103-
104- if let PatKind :: Binding ( annot, bind_id, _, _) = fields[ 0 ] . kind;
101+ // Only check one of the blocks
102+ let nested_expr = if_else. unwrap_or( if_then) ;
103+ if Self :: check_lang_items( cx, path1, & [ OptionSome , ResultOk , ResultErr ] ) ;
104+ if let PatKind :: Binding ( annot, _, ident, _) = fields[ 0 ] . kind;
105+ if Self :: result_check_and_early_return( cx, let_expr, nested_expr, Some ( ident. name) )
106+ || Self :: option_check_and_early_return( cx, let_expr, nested_expr) ;
105107 let by_ref = matches!( annot, BindingAnnotation :: Ref | BindingAnnotation :: RefMut ) ;
106- if path_to_local_id( peel_blocks( if_then) , bind_id) ;
107108 then {
108109 let mut applicability = Applicability :: MachineApplicable ;
109110 let receiver_str = snippet_with_applicability( cx, let_expr. span, ".." , & mut applicability) ;
110- let replacement = format!( "{}{}?" , receiver_str, if by_ref { ".as_ref()" } else { "" } , ) ;
111+ let replacement = format!( "{}{}?; " , receiver_str, if by_ref { ".as_ref()" } else { "" } , ) ;
111112
112113 span_lint_and_sugg(
113114 cx,
@@ -122,8 +123,22 @@ impl QuestionMark {
122123 }
123124 }
124125
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)
126+ fn check_lang_items ( cx : & LateContext < ' _ > , qpath : & QPath < ' _ > , items : & [ rustc_hir:: LangItem ] ) -> bool {
127+ for lang_item in items {
128+ if is_lang_ctor ( cx, qpath, * lang_item) {
129+ return true ;
130+ }
131+ }
132+ false
133+ }
134+
135+ fn result_check_and_early_return (
136+ cx : & LateContext < ' _ > ,
137+ expr : & Expr < ' _ > ,
138+ nested_expr : & Expr < ' _ > ,
139+ symbol : Option < Symbol > ,
140+ ) -> bool {
141+ Self :: is_result ( cx, expr) && Self :: expression_returns_unmodified_err ( cx, nested_expr, expr, symbol)
127142 }
128143
129144 fn option_check_and_early_return ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > , nested_expr : & Expr < ' _ > ) -> bool {
@@ -156,10 +171,23 @@ impl QuestionMark {
156171 }
157172 }
158173
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) ,
174+ fn expression_returns_unmodified_err (
175+ cx : & LateContext < ' _ > ,
176+ expr : & Expr < ' _ > ,
177+ cond_expr : & Expr < ' _ > ,
178+ symbol : Option < Symbol > ,
179+ ) -> bool {
180+ match & peel_blocks_with_stmt ( expr) . kind {
181+ ExprKind :: Ret ( Some ( ret_expr) ) => Self :: expression_returns_unmodified_err ( cx, ret_expr, cond_expr, symbol) ,
162182 ExprKind :: Path ( _) => path_to_local ( expr) . is_some ( ) && path_to_local ( expr) == path_to_local ( cond_expr) ,
183+ ExprKind :: Call ( _, args_expr) => {
184+ if let Some ( arg) = args_expr. first ( ) {
185+ if let Some ( name) = symbol {
186+ return clippy_utils:: contains_name ( name, arg) ;
187+ }
188+ }
189+ false
190+ } ,
163191 _ => false ,
164192 }
165193 }
0 commit comments