]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/needless_question_mark.rs
Rollup merge of #85760 - ChrisDenton:path-doc-platform-specific, r=m-ou-se
[rust.git] / src / tools / clippy / clippy_lints / src / needless_question_mark.rs
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::{Body, Expr, ExprKind, 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};
11
12 declare_clippy_lint! {
13     /// **What it does:**
14     /// Suggests alternatives for useless applications of `?` in terminating expressions
15     ///
16     /// **Why is this bad?** There's no reason to use `?` to short-circuit when execution of the body will end there anyway.
17     ///
18     /// **Known problems:** None.
19     ///
20     /// **Example:**
21     ///
22     /// ```rust
23     /// struct TO {
24     ///     magic: Option<usize>,
25     /// }
26     ///
27     /// fn f(to: TO) -> Option<usize> {
28     ///     Some(to.magic?)
29     /// }
30     ///
31     /// struct TR {
32     ///     magic: Result<usize, bool>,
33     /// }
34     ///
35     /// fn g(tr: Result<TR, bool>) -> Result<usize, bool> {
36     ///     tr.and_then(|t| Ok(t.magic?))
37     /// }
38     ///
39     /// ```
40     /// Use instead:
41     /// ```rust
42     /// struct TO {
43     ///     magic: Option<usize>,
44     /// }
45     ///
46     /// fn f(to: TO) -> Option<usize> {
47     ///    to.magic
48     /// }
49     ///
50     /// struct TR {
51     ///     magic: Result<usize, bool>,
52     /// }
53     ///
54     /// fn g(tr: Result<TR, bool>) -> Result<usize, bool> {
55     ///     tr.and_then(|t| t.magic)
56     /// }
57     /// ```
58     pub NEEDLESS_QUESTION_MARK,
59     complexity,
60     "Suggest `value.inner_option` instead of `Some(value.inner_option?)`. The same goes for `Result<T, E>`."
61 }
62
63 declare_lint_pass!(NeedlessQuestionMark => [NEEDLESS_QUESTION_MARK]);
64
65 impl LateLintPass<'_> for NeedlessQuestionMark {
66     /*
67      * The question mark operator is compatible with both Result<T, E> and Option<T>,
68      * from Rust 1.13 and 1.22 respectively.
69      */
70
71     /*
72      * What do we match:
73      * Expressions that look like this:
74      * Some(option?), Ok(result?)
75      *
76      * Where do we match:
77      *      Last expression of a body
78      *      Return statement
79      *      A body's value (single line closure)
80      *
81      * What do we not match:
82      *      Implicit calls to `from(..)` on the error value
83      */
84
85     fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) {
86         if let ExprKind::Ret(Some(e)) = expr.kind {
87             check(cx, e);
88         }
89     }
90
91     fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) {
92         check(cx, body.value.peel_blocks());
93     }
94 }
95
96 fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
97     let inner_expr = if_chain! {
98         if let ExprKind::Call(path, [arg]) = &expr.kind;
99         if let ExprKind::Path(ref qpath) = &path.kind;
100         if is_lang_ctor(cx, qpath, OptionSome) || is_lang_ctor(cx, qpath, ResultOk);
101         if let ExprKind::Match(inner_expr_with_q, _, MatchSource::TryDesugar) = &arg.kind;
102         if let ExprKind::Call(called, [inner_expr]) = &inner_expr_with_q.kind;
103         if let ExprKind::Path(QPath::LangItem(LangItem::TryTraitBranch, _)) = &called.kind;
104         if expr.span.ctxt() == inner_expr.span.ctxt();
105         let expr_ty = cx.typeck_results().expr_ty(expr);
106         let inner_ty = cx.typeck_results().expr_ty(inner_expr);
107         if TyS::same_type(expr_ty, inner_ty);
108         then { inner_expr } else { return; }
109     };
110     span_lint_and_sugg(
111         cx,
112         NEEDLESS_QUESTION_MARK,
113         expr.span,
114         "question mark operator is useless here",
115         "try",
116         format!("{}", snippet(cx, inner_expr.span, r#""...""#)),
117         Applicability::MachineApplicable,
118     );
119 }