use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::LangItem::{OptionSome, ResultOk};
-use rustc_hir::{Body, Expr, ExprKind, LangItem, MatchSource, QPath};
+use rustc_hir::{AsyncGeneratorKind, Block, Body, Expr, ExprKind, GeneratorKind, LangItem, MatchSource, QPath};
use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty::TyS;
use rustc_session::{declare_lint_pass, declare_tool_lint};
declare_clippy_lint! {
- /// **What it does:**
+ /// ### What it does
/// Suggests alternatives for useless applications of `?` in terminating expressions
///
- /// **Why is this bad?** There's no reason to use `?` to short-circuit when execution of the body will end there anyway.
- ///
- /// **Known problems:** None.
- ///
- /// **Example:**
+ /// ### Why is this bad?
+ /// There's no reason to use `?` to short-circuit when execution of the body will end there anyway.
///
+ /// ### Example
/// ```rust
/// struct TO {
/// magic: Option<usize>,
/// tr.and_then(|t| t.magic)
/// }
/// ```
+ #[clippy::version = "1.51.0"]
pub NEEDLESS_QUESTION_MARK,
complexity,
"Suggest `value.inner_option` instead of `Some(value.inner_option?)`. The same goes for `Result<T, E>`."
}
fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) {
- check(cx, body.value.peel_blocks());
+ if let Some(GeneratorKind::Async(AsyncGeneratorKind::Fn)) = body.generator_kind {
+ if let ExprKind::Block(
+ Block {
+ expr:
+ Some(Expr {
+ kind: ExprKind::DropTemps(async_body),
+ ..
+ }),
+ ..
+ },
+ _,
+ ) = body.value.kind
+ {
+ if let ExprKind::Block(Block { expr: Some(expr), .. }, ..) = async_body.kind {
+ check(cx, expr);
+ }
+ }
+ } else {
+ check(cx, body.value.peel_blocks());
+ }
}
}
fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
- let inner_expr = if_chain! {
+ if_chain! {
if let ExprKind::Call(path, [arg]) = &expr.kind;
if let ExprKind::Path(ref qpath) = &path.kind;
- if is_lang_ctor(cx, qpath, OptionSome) || is_lang_ctor(cx, qpath, ResultOk);
+ let sugg_remove = if is_lang_ctor(cx, qpath, OptionSome) {
+ "Some()"
+ } else if is_lang_ctor(cx, qpath, ResultOk) {
+ "Ok()"
+ } else {
+ return;
+ };
if let ExprKind::Match(inner_expr_with_q, _, MatchSource::TryDesugar) = &arg.kind;
if let ExprKind::Call(called, [inner_expr]) = &inner_expr_with_q.kind;
- if let ExprKind::Path(QPath::LangItem(LangItem::TryTraitBranch, _)) = &called.kind;
+ if let ExprKind::Path(QPath::LangItem(LangItem::TryTraitBranch, ..)) = &called.kind;
if expr.span.ctxt() == inner_expr.span.ctxt();
let expr_ty = cx.typeck_results().expr_ty(expr);
let inner_ty = cx.typeck_results().expr_ty(inner_expr);
- if TyS::same_type(expr_ty, inner_ty);
- then { inner_expr } else { return; }
- };
- span_lint_and_sugg(
- cx,
- NEEDLESS_QUESTION_MARK,
- expr.span,
- "question mark operator is useless here",
- "try",
- format!("{}", snippet(cx, inner_expr.span, r#""...""#)),
- Applicability::MachineApplicable,
- );
+ if expr_ty == inner_ty;
+ then {
+ span_lint_and_sugg(
+ cx,
+ NEEDLESS_QUESTION_MARK,
+ expr.span,
+ "question mark operator is useless here",
+ &format!("try removing question mark and `{sugg_remove}`"),
+ format!("{}", snippet(cx, inner_expr.span, r#""...""#)),
+ Applicability::MachineApplicable,
+ );
+ }
+ }
}