1 use clippy_utils::diagnostics::span_lint_and_sugg;
2 use clippy_utils::source::snippet;
3 use clippy_utils::ty::is_type_diagnostic_item;
4 use clippy_utils::{differing_macro_contexts, is_lang_ctor};
5 use if_chain::if_chain;
6 use rustc_errors::Applicability;
7 use rustc_hir::LangItem::{OptionSome, ResultOk};
8 use rustc_hir::{Body, Expr, ExprKind, LangItem, MatchSource, QPath};
9 use rustc_lint::{LateContext, LateLintPass};
10 use rustc_session::{declare_lint_pass, declare_tool_lint};
13 declare_clippy_lint! {
15 /// Suggests alternatives for useless applications of `?` in terminating expressions
17 /// **Why is this bad?** There's no reason to use `?` to short-circuit when execution of the body will end there anyway.
19 /// **Known problems:** None.
25 /// magic: Option<usize>,
28 /// fn f(to: TO) -> Option<usize> {
33 /// magic: Result<usize, bool>,
36 /// fn g(tr: Result<TR, bool>) -> Result<usize, bool> {
37 /// tr.and_then(|t| Ok(t.magic?))
44 /// magic: Option<usize>,
47 /// fn f(to: TO) -> Option<usize> {
52 /// magic: Result<usize, bool>,
55 /// fn g(tr: Result<TR, bool>) -> Result<usize, bool> {
56 /// tr.and_then(|t| t.magic)
59 pub NEEDLESS_QUESTION_MARK,
61 "Suggest `value.inner_option` instead of `Some(value.inner_option?)`. The same goes for `Result<T, E>`."
64 declare_lint_pass!(NeedlessQuestionMark => [NEEDLESS_QUESTION_MARK]);
68 SomeCall(&'a Expr<'a>, &'a Expr<'a>),
69 OkCall(&'a Expr<'a>, &'a Expr<'a>),
72 impl LateLintPass<'_> for NeedlessQuestionMark {
74 * The question mark operator is compatible with both Result<T, E> and Option<T>,
75 * from Rust 1.13 and 1.22 respectively.
80 * Expressions that look like this:
81 * Some(option?), Ok(result?)
84 * Last expression of a body
86 * A body's value (single line closure)
88 * What do we not match:
89 * Implicit calls to `from(..)` on the error value
92 fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) {
93 let e = match &expr.kind {
94 ExprKind::Ret(Some(e)) => e,
98 if let Some(ok_some_call) = is_some_or_ok_call(cx, e) {
99 emit_lint(cx, &ok_some_call);
103 fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) {
104 // Function / Closure block
105 let expr_opt = if let ExprKind::Block(block, _) = &body.value.kind {
108 // Single line closure
113 if let Some(expr) = expr_opt;
114 if let Some(ok_some_call) = is_some_or_ok_call(cx, expr);
116 emit_lint(cx, &ok_some_call);
122 fn emit_lint(cx: &LateContext<'_>, expr: &SomeOkCall<'_>) {
123 let (entire_expr, inner_expr) = match expr {
124 SomeOkCall::OkCall(outer, inner) | SomeOkCall::SomeCall(outer, inner) => (outer, inner),
129 NEEDLESS_QUESTION_MARK,
131 "question mark operator is useless here",
133 format!("{}", snippet(cx, inner_expr.span, r#""...""#)),
134 Applicability::MachineApplicable,
138 fn is_some_or_ok_call<'a>(cx: &'a LateContext<'_>, expr: &'a Expr<'_>) -> Option<SomeOkCall<'a>> {
140 // Check outer expression matches CALL_IDENT(ARGUMENT) format
141 if let ExprKind::Call(path, args) = &expr.kind;
142 if let ExprKind::Path(ref qpath) = &path.kind;
143 if is_lang_ctor(cx, qpath, OptionSome) || is_lang_ctor(cx, qpath, ResultOk);
145 // Extract inner expression from ARGUMENT
146 if let ExprKind::Match(inner_expr_with_q, _, MatchSource::TryDesugar) = &args[0].kind;
147 if let ExprKind::Call(called, args) = &inner_expr_with_q.kind;
150 if let ExprKind::Path(QPath::LangItem(LangItem::TryIntoResult, _)) = &called.kind;
152 // Extract inner expr type from match argument generated by
153 // question mark operator
154 let inner_expr = &args[0];
156 // if the inner expr is inside macro but the outer one is not, do not lint (#6921)
157 if differing_macro_contexts(expr.span, inner_expr.span) {
161 let inner_ty = cx.typeck_results().expr_ty(inner_expr);
162 let outer_ty = cx.typeck_results().expr_ty(expr);
164 // Check if outer and inner type are Option
165 let outer_is_some = is_type_diagnostic_item(cx, outer_ty, sym::option_type);
166 let inner_is_some = is_type_diagnostic_item(cx, inner_ty, sym::option_type);
168 // Check for Option MSRV
169 if outer_is_some && inner_is_some {
170 return Some(SomeOkCall::SomeCall(expr, inner_expr));
173 // Check if outer and inner type are Result
174 let outer_is_result = is_type_diagnostic_item(cx, outer_ty, sym::result_type);
175 let inner_is_result = is_type_diagnostic_item(cx, inner_ty, sym::result_type);
177 // Additional check: if the error type of the Result can be converted
178 // via the From trait, then don't match
179 let does_not_call_from = !has_implicit_error_from(cx, expr, inner_expr);
181 // Must meet Result MSRV
182 if outer_is_result && inner_is_result && does_not_call_from {
183 return Some(SomeOkCall::OkCall(expr, inner_expr));
191 fn has_implicit_error_from(cx: &LateContext<'_>, entire_expr: &Expr<'_>, inner_result_expr: &Expr<'_>) -> bool {
192 return cx.typeck_results().expr_ty(entire_expr) != cx.typeck_results().expr_ty(inner_result_expr);