1 use clippy_utils::diagnostics::span_lint_and_sugg;
2 use clippy_utils::is_lang_ctor;
3 use clippy_utils::source::snippet;
4 use if_chain::if_chain;
5 use rustc_errors::Applicability;
6 use rustc_hir::LangItem::{OptionSome, ResultOk};
7 use rustc_hir::{AsyncGeneratorKind, Block, Body, Expr, ExprKind, GeneratorKind, LangItem, MatchSource, QPath};
8 use rustc_lint::{LateContext, LateLintPass};
9 use rustc_middle::ty::TyS;
10 use rustc_session::{declare_lint_pass, declare_tool_lint};
12 declare_clippy_lint! {
14 /// Suggests alternatives for useless applications of `?` in terminating expressions
16 /// ### Why is this bad?
17 /// There's no reason to use `?` to short-circuit when execution of the body will end there anyway.
22 /// magic: Option<usize>,
25 /// fn f(to: TO) -> Option<usize> {
30 /// magic: Result<usize, bool>,
33 /// fn g(tr: Result<TR, bool>) -> Result<usize, bool> {
34 /// tr.and_then(|t| Ok(t.magic?))
41 /// magic: Option<usize>,
44 /// fn f(to: TO) -> Option<usize> {
49 /// magic: Result<usize, bool>,
52 /// fn g(tr: Result<TR, bool>) -> Result<usize, bool> {
53 /// tr.and_then(|t| t.magic)
56 #[clippy::version = "1.51.0"]
57 pub NEEDLESS_QUESTION_MARK,
59 "Suggest `value.inner_option` instead of `Some(value.inner_option?)`. The same goes for `Result<T, E>`."
62 declare_lint_pass!(NeedlessQuestionMark => [NEEDLESS_QUESTION_MARK]);
64 impl LateLintPass<'_> for NeedlessQuestionMark {
66 * The question mark operator is compatible with both Result<T, E> and Option<T>,
67 * from Rust 1.13 and 1.22 respectively.
72 * Expressions that look like this:
73 * Some(option?), Ok(result?)
76 * Last expression of a body
78 * A body's value (single line closure)
80 * What do we not match:
81 * Implicit calls to `from(..)` on the error value
84 fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) {
85 if let ExprKind::Ret(Some(e)) = expr.kind {
90 fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) {
91 if let Some(GeneratorKind::Async(AsyncGeneratorKind::Fn)) = body.generator_kind {
92 if let ExprKind::Block(
96 kind: ExprKind::DropTemps(async_body),
104 if let ExprKind::Block(Block { expr: Some(expr), .. }, ..) = async_body.kind {
109 check(cx, body.value.peel_blocks());
114 fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
116 if let ExprKind::Call(path, [arg]) = &expr.kind;
117 if let ExprKind::Path(ref qpath) = &path.kind;
118 let sugg_remove = if is_lang_ctor(cx, qpath, OptionSome) {
120 } else if is_lang_ctor(cx, qpath, ResultOk) {
125 if let ExprKind::Match(inner_expr_with_q, _, MatchSource::TryDesugar) = &arg.kind;
126 if let ExprKind::Call(called, [inner_expr]) = &inner_expr_with_q.kind;
127 if let ExprKind::Path(QPath::LangItem(LangItem::TryTraitBranch, ..)) = &called.kind;
128 if expr.span.ctxt() == inner_expr.span.ctxt();
129 let expr_ty = cx.typeck_results().expr_ty(expr);
130 let inner_ty = cx.typeck_results().expr_ty(inner_expr);
131 if TyS::same_type(expr_ty, inner_ty);
135 NEEDLESS_QUESTION_MARK,
137 "question mark operator is useless here",
138 &format!("try removing question mark and `{}`", sugg_remove),
139 format!("{}", snippet(cx, inner_expr.span, r#""...""#)),
140 Applicability::MachineApplicable,