3 use rustc
::hir
::def
::Def
;
7 use utils
::{match_def_path, match_type, span_lint_and_then}
;
10 /// **What it does:** Checks for expressions that could be replaced by the question mark operator
12 /// **Why is this bad?** Question mark usage is more idiomatic
14 /// **Known problems:** None
18 /// if option.is_none() {
31 "checks for expressions that could be replaced by the question mark operator"
34 #[derive(Copy, Clone)]
35 pub struct QuestionMarkPass
;
37 impl LintPass
for QuestionMarkPass
{
38 fn get_lints(&self) -> LintArray
{
39 lint_array
!(QUESTION_MARK
)
43 impl QuestionMarkPass
{
44 /// Check if the given expression on the given context matches the following structure:
47 /// if option.is_none() {
52 /// If it matches, it will suggest to use the question mark operator instead
53 fn check_is_none_and_early_return_none(cx
: &LateContext
, expr
: &Expr
) {
55 if let ExprIf(ref if_expr
, ref body
, _
) = expr
.node
;
56 if let ExprMethodCall(ref segment
, _
, ref args
) = if_expr
.node
;
57 if segment
.name
== "is_none";
58 if Self::expression_returns_none(cx
, body
);
59 if let Some(subject
) = args
.get(0);
60 if Self::is_option(cx
, subject
);
67 "this block may be rewritten with the `?` operator",
69 let receiver_str
= &Sugg
::hir(cx
, subject
, "..");
74 format
!("{}?;", receiver_str
),
82 fn is_option(cx
: &LateContext
, expression
: &Expr
) -> bool
{
83 let expr_ty
= cx
.tables
.expr_ty(expression
);
85 match_type(cx
, expr_ty
, &OPTION
)
88 fn expression_returns_none(cx
: &LateContext
, expression
: &Expr
) -> bool
{
89 match expression
.node
{
90 ExprBlock(ref block
, _
) => {
91 if let Some(return_expression
) = Self::return_expression(block
) {
92 return Self::expression_returns_none(cx
, &return_expression
);
97 ExprRet(Some(ref expr
)) => {
98 Self::expression_returns_none(cx
, expr
)
100 ExprPath(ref qp
) => {
101 if let Def
::VariantCtor(def_id
, _
) = cx
.tables
.qpath_def(qp
, expression
.hir_id
) {
102 return match_def_path(cx
.tcx
, def_id
, &OPTION_NONE
);
111 fn return_expression(block
: &Block
) -> Option
<P
<Expr
>> {
112 // Check if last expression is a return statement. Then, return the expression
114 if block
.stmts
.len() == 1;
115 if let Some(expr
) = block
.stmts
.iter().last();
116 if let StmtSemi(ref expr
, _
) = expr
.node
;
117 if let ExprRet(ref ret_expr
) = expr
.node
;
118 if let &Some(ref ret_expr
) = ret_expr
;
121 return Some(ret_expr
.clone());
125 // Check if the block has an implicit return expression
126 if let Some(ref ret_expr
) = block
.expr
{
127 return Some(ret_expr
.clone());
134 impl<'a
, 'tcx
> LateLintPass
<'a
, 'tcx
> for QuestionMarkPass
{
135 fn check_expr(&mut self, cx
: &LateContext
<'a
, 'tcx
>, expr
: &'tcx Expr
) {
136 Self::check_is_none_and_early_return_none(cx
, expr
);