<commit_before>// Copyright 2014-2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use crate::rustc::hir::def::Def;
use crate::rustc::hir::*;
use crate::rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
use crate::rustc::{declare_tool_lint, lint_array};
use crate::syntax::ptr::P;
use crate::utils::sugg::Sugg;
use if_chain::if_chain;

use crate::rustc_errors::Applicability;
use crate::utils::paths::*;
use crate::utils::{match_def_path, match_type, span_lint_and_then, SpanlessEq};

/// **What it does:** Checks for expressions that could be replaced by the question mark operator
///
/// **Why is this bad?** Question mark usage is more idiomatic
///
/// **Known problems:** None
///
/// **Example:**
/// ```rust
/// if option.is_none() {
///     return None;
/// }
/// ```
///
/// Could be written:
///
/// ```rust
/// option?;
/// ```
declare_clippy_lint! {
    pub QUESTION_MARK,
    style,
    "checks for expressions that could be replaced by the question mark operator"
}

#[derive(Copy, Clone)]
pub struct Pass;

impl LintPass for Pass {
    fn get_lints(&self) -> LintArray {
        lint_array!(QUESTION_MARK)
    }
}

impl Pass {
    /// Check if the given expression on the given context matches the following structure:
    ///
    /// ```ignore
    /// if option.is_none() {
    ///    return None;
    /// }
    /// ```
    ///
    /// If it matches, it will suggest to use the question mark operator instead
    fn check_is_none_and_early_return_none(cx: &LateContext<'_, '_>, expr: &Expr) {
        if_chain! {
            if let ExprKind::If(if_expr, body, else_) = &expr.node;
            if let ExprKind::MethodCall(segment, _, args) = &if_expr.node;
            if segment.ident.name == "is_none";
            if Self::expression_returns_none(cx, body);
            if let Some(subject) = args.get(0);
            if Self::is_option(cx, subject);

            then {
                if let Some(else_) = else_ {
                    if_chain! {
                        if let ExprKind::Block(block, None) = &else_.node;
                        if block.stmts.len() == 0;
                        if let Some(block_expr) = &block.expr;
                        if SpanlessEq::new(cx).ignore_fn().eq_expr(subject, block_expr);
                        then {
                            span_lint_and_then(
                                cx,
                                QUESTION_MARK,
                                expr.span,
                                "this block may be rewritten with the `?` operator",
                                |db| {
                                    db.span_suggestion_with_applicability(
                                        expr.span,
                                        "replace_it_with",
                                        format!("Some({}?)", Sugg::hir(cx, subject, "..")),
                                        Applicability::MaybeIncorrect, // snippet
                                    );
                                }
                            )
                        }
                    }
                    return;
                }

                span_lint_and_then(
                    cx,
                    QUESTION_MARK,
                    expr.span,
                    "this block may be rewritten with the `?` operator",
                    |db| {
                        let receiver_str = &Sugg::hir(cx, subject, "..");

                        db.span_suggestion_with_applicability(
                            expr.span,
                            "replace_it_with",
                            format!("{}?;", receiver_str),
                            Applicability::MaybeIncorrect, // snippet
                        );
                    }
                )
            }
        }
    }

    fn is_option(cx: &LateContext<'_, '_>, expression: &Expr) -> bool {
        let expr_ty = cx.tables.expr_ty(expression);

        match_type(cx, expr_ty, &OPTION)
    }

    fn expression_returns_none(cx: &LateContext<'_, '_>, expression: &Expr) -> bool {
        match expression.node {
            ExprKind::Block(ref block, _) => {
                if let Some(return_expression) = Self::return_expression(block) {
                    return Self::expression_returns_none(cx, &return_expression);
                }

                false
            },
            ExprKind::Ret(Some(ref expr)) => Self::expression_returns_none(cx, expr),
            ExprKind::Path(ref qp) => {
                if let Def::VariantCtor(def_id, _) = cx.tables.qpath_def(qp, expression.hir_id) {
                    return match_def_path(cx.tcx, def_id, &OPTION_NONE);
                }

                false
            },
            _ => false,
        }
    }

    fn return_expression(block: &Block) -> Option<P<Expr>> {
        // Check if last expression is a return statement. Then, return the expression
        if_chain! {
            if block.stmts.len() == 1;
            if let Some(expr) = block.stmts.iter().last();
            if let StmtKind::Semi(ref expr, _) = expr.node;
            if let ExprKind::Ret(ref ret_expr) = expr.node;
            if let &Some(ref ret_expr) = ret_expr;

            then {
                return Some(ret_expr.clone());
            }
        }

        // Check for `return` without a semicolon.
        if_chain! {
            if block.stmts.len() == 0;
            if let Some(ExprKind::Ret(Some(ret_expr))) = block.expr.as_ref().map(|e| &e.node);
            then {
                return Some(ret_expr.clone());
            }
        }
<commit_msg>Deduplicate some code?<commit_after>// Copyright 2014-2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use crate::rustc::hir::def::Def;
use crate::rustc::hir::*;
use crate::rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
use crate::rustc::{declare_tool_lint, lint_array};
use crate::syntax::ptr::P;
use crate::utils::sugg::Sugg;
use if_chain::if_chain;

use crate::rustc_errors::Applicability;
use crate::utils::paths::*;
use crate::utils::{match_def_path, match_type, span_lint_and_then, SpanlessEq};

/// **What it does:** Checks for expressions that could be replaced by the question mark operator
///
/// **Why is this bad?** Question mark usage is more idiomatic
///
/// **Known problems:** None
///
/// **Example:**
/// ```rust
/// if option.is_none() {
///     return None;
/// }
/// ```
///
/// Could be written:
///
/// ```rust
/// option?;
/// ```
declare_clippy_lint! {
    pub QUESTION_MARK,
    style,
    "checks for expressions that could be replaced by the question mark operator"
}

#[derive(Copy, Clone)]
pub struct Pass;

impl LintPass for Pass {
    fn get_lints(&self) -> LintArray {
        lint_array!(QUESTION_MARK)
    }
}

impl Pass {
    /// Check if the given expression on the given context matches the following structure:
    ///
    /// ```ignore
    /// if option.is_none() {
    ///    return None;
    /// }
    /// ```
    ///
    /// If it matches, it will suggest to use the question mark operator instead
    fn check_is_none_and_early_return_none(cx: &LateContext<'_, '_>, expr: &Expr) {
        if_chain! {
            if let ExprKind::If(if_expr, body, else_) = &expr.node;
            if let ExprKind::MethodCall(segment, _, args) = &if_expr.node;
            if segment.ident.name == "is_none";
            if Self::expression_returns_none(cx, body);
            if let Some(subject) = args.get(0);
            if Self::is_option(cx, subject);

            then {
                let receiver_str = &Sugg::hir(cx, subject, "..");
                let mut replacement_str = String::new();
                if let Some(else_) = else_ {
                    if_chain! {
                        if let ExprKind::Block(block, None) = &else_.node;
                        if block.stmts.len() == 0;
                        if let Some(block_expr) = &block.expr;
                        if SpanlessEq::new(cx).ignore_fn().eq_expr(subject, block_expr);
                        then {
                            replacement_str = format!("Some({}?)", receiver_str);
                        }
                    }
                } else {
                        replacement_str = format!("{}?;", receiver_str);
                }
                span_lint_and_then(
                    cx,
                    QUESTION_MARK,
                    expr.span,
                    "this block may be rewritten with the `?` operator",
                    |db| {
                        db.span_suggestion_with_applicability(
                            expr.span,
                            "replace_it_with",
                            replacement_str,
                            Applicability::MaybeIncorrect, // snippet
                        );
                    }
                )
            }
        }
    }

    fn is_option(cx: &LateContext<'_, '_>, expression: &Expr) -> bool {
        let expr_ty = cx.tables.expr_ty(expression);

        match_type(cx, expr_ty, &OPTION)
    }

    fn expression_returns_none(cx: &LateContext<'_, '_>, expression: &Expr) -> bool {
        match expression.node {
            ExprKind::Block(ref block, _) => {
                if let Some(return_expression) = Self::return_expression(block) {
                    return Self::expression_returns_none(cx, &return_expression);
                }

                false
            }
            ExprKind::Ret(Some(ref expr)) => Self::expression_returns_none(cx, expr),
            ExprKind::Path(ref qp) => {
                if let Def::VariantCtor(def_id, _) = cx.tables.qpath_def(qp, expression.hir_id) {
                    return match_def_path(cx.tcx, def_id, &OPTION_NONE);
                }

                false
            }
            _ => false,
        }
    }

    fn return_expression(block: &Block) -> Option<P<Expr>> {
        // Check if last expression is a return statement. Then, return the expression
        if_chain! {
            if block.stmts.len() == 1;
            if let Some(expr) = block.stmts.iter().last();
            if let StmtKind::Semi(ref expr, _) = expr.node;
            if let ExprKind::Ret(ref ret_expr) = expr.node;
            if let &Some(ref ret_expr) = ret_expr;

            then {
                return Some(ret_expr.clone());
            }
        }

        // Check for `return` without a semicolon.
        if_chain! {
            if block.stmts.len() == 0;
            if let Some(ExprKind::Ret(Some(ret_expr))) = block.expr.as_ref().map(|e| &e.node);
            then {
                return Some(ret_expr.clone());
            }
        }

{'fraction_non_alphanumeric': 0.12629636139919143, 'fraction_numerical': 0.0028124450694322375, 'mean_word_length': 1.577933846850929, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 33, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6866802', 'n_tokens_mistral': 3260, 'n_tokens_neox': 3145, 'n_words': 760}