diff options
| author | dswij <dswijj@gmail.com> | 2021-10-19 18:26:50 +0800 |
|---|---|---|
| committer | dswij <dswijj@gmail.com> | 2021-10-19 18:50:49 +0800 |
| commit | 687f3925da6481d938be0d787808328f8a93fdee (patch) | |
| tree | de1fed00edb74b6fd3f7c64e1d342e34b2ca653c | |
| parent | 63d715209eaf241114f87db247b435df5272e8e4 (diff) | |
| download | rust-687f3925da6481d938be0d787808328f8a93fdee.tar.gz rust-687f3925da6481d938be0d787808328f8a93fdee.zip | |
Cover `Result` for `question_mark`
| -rw-r--r-- | clippy_lints/src/question_mark.rs | 76 |
1 files changed, 55 insertions, 21 deletions
diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index aa6d254e7a5..c1ee20c370a 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -4,10 +4,10 @@ use clippy_utils::is_lang_ctor; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{eq_expr_value, path_to_local_id}; +use clippy_utils::{eq_expr_value, path_to_local, path_to_local_id}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::LangItem::{OptionNone, OptionSome}; +use rustc_hir::LangItem::{OptionNone, OptionSome, ResultOk}; use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, PatKind, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -48,16 +48,25 @@ impl QuestionMark { /// } /// ``` /// + /// ```ignore + /// if result.is_err() { + /// return result; + /// } + /// ``` + /// /// If it matches, it will suggest to use the question mark operator instead - fn check_is_none_and_early_return_none(cx: &LateContext<'_>, expr: &Expr<'_>) { + fn check_is_none_or_err_and_early_return(cx: &LateContext<'_>, expr: &Expr<'_>) { if_chain! { if let Some(higher::If { cond, then, r#else }) = higher::If::hir(expr); if let ExprKind::MethodCall(segment, _, args, _) = &cond.kind; - if segment.ident.name == sym!(is_none); - if Self::expression_returns_none(cx, then); if let Some(subject) = args.get(0); - if Self::is_option(cx, subject); - + if (Self::is_option(cx, subject) + && Self::expression_returns_none(cx, then) + && segment.ident.name == sym!(is_none)) + || + (Self::is_result(cx, subject) + && Self::expression_returns_unmodified_err(cx, then, subject) + && segment.ident.name == sym!(is_err)); then { let mut applicability = Applicability::MachineApplicable; let receiver_str = &Sugg::hir_with_applicability(cx, subject, "..", &mut applicability); @@ -95,31 +104,29 @@ impl QuestionMark { } } - fn check_if_let_some_and_early_return_none(cx: &LateContext<'_>, expr: &Expr<'_>) { + fn check_if_let_some_or_err_and_early_return(cx: &LateContext<'_>, expr: &Expr<'_>) { if_chain! { if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else: Some(if_else) }) = higher::IfLet::hir(cx, expr); - if Self::is_option(cx, let_expr); - if let PatKind::TupleStruct(ref path1, fields, None) = let_pat.kind; - if is_lang_ctor(cx, path1, OptionSome); + if (Self::is_option(cx, let_expr) + && Self::expression_returns_none(cx, if_else) + && is_lang_ctor(cx, path1, OptionSome)) + || + (Self::is_result(cx, let_expr) + && Self::expression_returns_unmodified_err(cx, if_else, let_expr) + && is_lang_ctor(cx, path1, ResultOk)); + if let PatKind::Binding(annot, bind_id, _, _) = fields[0].kind; let by_ref = matches!(annot, BindingAnnotation::Ref | BindingAnnotation::RefMut); - if let ExprKind::Block(block, None) = if_then.kind; if block.stmts.is_empty(); if let Some(trailing_expr) = &block.expr; if path_to_local_id(trailing_expr, bind_id); - - if Self::expression_returns_none(cx, if_else); then { let mut applicability = Applicability::MachineApplicable; let receiver_str = snippet_with_applicability(cx, let_expr.span, "..", &mut applicability); - let replacement = format!( - "{}{}?", - receiver_str, - if by_ref { ".as_ref()" } else { "" }, - ); + let replacement = format!("{}{}?", receiver_str, if by_ref { ".as_ref()" } else { "" },); span_lint_and_sugg( cx, @@ -146,6 +153,12 @@ impl QuestionMark { is_type_diagnostic_item(cx, expr_ty, sym::Option) } + fn is_result(cx: &LateContext<'_>, expression: &Expr<'_>) -> bool { + let expr_ty = cx.typeck_results().expr_ty(expression); + + is_type_diagnostic_item(cx, expr_ty, sym::Result) + } + fn expression_returns_none(cx: &LateContext<'_>, expression: &Expr<'_>) -> bool { match expression.kind { ExprKind::Block(block, _) => { @@ -161,6 +174,27 @@ impl QuestionMark { } } + fn expression_returns_unmodified_err( + cx: &LateContext<'_>, + expression: &Expr<'_>, + origin_hir_id: &Expr<'_>, + ) -> bool { + match expression.kind { + ExprKind::Block(block, _) => { + if let Some(return_expression) = Self::return_expression(block) { + return Self::expression_returns_unmodified_err(cx, return_expression, origin_hir_id); + } + + false + }, + ExprKind::Ret(Some(expr)) | ExprKind::Call(expr, _) => { + Self::expression_returns_unmodified_err(cx, expr, origin_hir_id) + }, + ExprKind::Path(_) => path_to_local(expression) == path_to_local(origin_hir_id), + _ => false, + } + } + fn return_expression<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<'tcx>> { // Check if last expression is a return statement. Then, return the expression if_chain! { @@ -189,7 +223,7 @@ impl QuestionMark { impl<'tcx> LateLintPass<'tcx> for QuestionMark { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - Self::check_is_none_and_early_return_none(cx, expr); - Self::check_if_let_some_and_early_return_none(cx, expr); + Self::check_is_none_or_err_and_early_return(cx, expr); + Self::check_if_let_some_or_err_and_early_return(cx, expr); } } |
