<commit_before>use rustc_front::hir::*;
use rustc::lint::*;
use rustc::middle::ty;
use rustc::middle::subst::{Subst, TypeSpace};
use std::iter;
use std::borrow::Cow;

use utils::{snippet, span_lint, span_note_and_lint, match_path, match_type, walk_ptrs_ty_depth,
    walk_ptrs_ty};
use utils::{OPTION_PATH, RESULT_PATH, STRING_PATH};

use self::SelfKind::*;
use self::OutType::*;

#[derive(Clone)]
pub struct MethodsPass;

declare_lint!(pub OPTION_UNWRAP_USED, Allow,
              "using `Option.unwrap()`, which should at least get a better message using `expect()`");
declare_lint!(pub RESULT_UNWRAP_USED, Allow,
              "using `Result.unwrap()`, which might be better handled");
declare_lint!(pub STR_TO_STRING, Warn,
              "using `to_string()` on a str, which should be `to_owned()`");
declare_lint!(pub STRING_TO_STRING, Warn,
              "calling `String.to_string()` which is a no-op");
declare_lint!(pub SHOULD_IMPLEMENT_TRAIT, Warn,
              "defining a method that should be implementing a std trait");
declare_lint!(pub WRONG_SELF_CONVENTION, Warn,
              "defining a method named with an established prefix (like \"into_\") that takes \
               `self` with the wrong convention");
declare_lint!(pub WRONG_PUB_SELF_CONVENTION, Allow,
              "defining a public method named with an established prefix (like \"into_\") that takes \
               `self` with the wrong convention");
declare_lint!(pub OK_EXPECT, Warn,
              "using `ok().expect()`, which gives worse error messages than \
               calling `expect` directly on the Result");
declare_lint!(pub OPTION_MAP_UNWRAP_OR, Warn,
              "using `Option.map(f).unwrap_or(a)`, which is more succinctly expressed as \
               `map_or(a, f)`)");
declare_lint!(pub OPTION_MAP_UNWRAP_OR_ELSE, Warn,
              "using `Option.map(f).unwrap_or_else(g)`, which is more succinctly expressed as \
               `map_or_else(g, f)`)");

impl LintPass for MethodsPass {
    fn get_lints(&self) -> LintArray {
        lint_array!(OPTION_UNWRAP_USED, RESULT_UNWRAP_USED, STR_TO_STRING, STRING_TO_STRING,
                    SHOULD_IMPLEMENT_TRAIT, WRONG_SELF_CONVENTION, OK_EXPECT, OPTION_MAP_UNWRAP_OR,
                    OPTION_MAP_UNWRAP_OR_ELSE)
    }
}

impl LateLintPass for MethodsPass {
    fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {

        if let ExprMethodCall(ref name, _, ref args) = expr.node {
            let (obj_ty, ptr_depth) = walk_ptrs_ty_depth(cx.tcx.expr_ty(&args[0]));
            if name.node.as_str() == "unwrap" {
                if match_type(cx, obj_ty, &OPTION_PATH) {
                    span_lint(cx, OPTION_UNWRAP_USED, expr.span,
                              "used unwrap() on an Option value. If you don't want \
                               to handle the None case gracefully, consider using \
                               expect() to provide a better panic message");
                } else if match_type(cx, obj_ty, &RESULT_PATH) {
                    span_lint(cx, RESULT_UNWRAP_USED, expr.span,
                              "used unwrap() on a Result value. Graceful handling \
                               of Err values is preferred");
                }
            }
            else if name.node.as_str() == "to_string" {
                if obj_ty.sty == ty::TyStr {
                    let mut arg_str = snippet(cx, args[0].span, "_");
                    if ptr_depth > 1 {
                        arg_str = Cow::Owned(format!(
                            "({}{})",
                            iter::repeat('*').take(ptr_depth - 1).collect::<String>(),
                            arg_str));
                    }
                    span_lint(cx, STR_TO_STRING, expr.span, &format!(
                        "`{}.to_owned()` is faster", arg_str));
                } else if match_type(cx, obj_ty, &STRING_PATH) {
                    span_lint(cx, STRING_TO_STRING, expr.span, "`String.to_string()` is a no-op; use \
                                                                `clone()` to make a copy");
                }
            }
            else if name.node.as_str() == "expect" {
                if let ExprMethodCall(ref inner_name, _, ref inner_args) = args[0].node {
                    if inner_name.node.as_str() == "ok"
                            && match_type(cx, cx.tcx.expr_ty(&inner_args[0]), &RESULT_PATH) {
                        let result_type = cx.tcx.expr_ty(&inner_args[0]);
                        if let Some(error_type) = get_error_type(cx, result_type) {
                            if has_debug_impl(error_type, cx) {
                                span_lint(cx, OK_EXPECT, expr.span,
                                         "called `ok().expect()` on a Result \
                                          value. You can call `expect` directly \
                                          on the `Result`");
                            }
                        }
                    }
                }
            }
            // check Option.map(_).unwrap_or(_)
            else if name.node.as_str() == "unwrap_or" {
                if let ExprMethodCall(ref inner_name, _, ref inner_args) = args[0].node {
                    if inner_name.node.as_str() == "map"
                            && match_type(cx, cx.tcx.expr_ty(&inner_args[0]), &OPTION_PATH) {
                        // lint message
                        let msg =
                            "called `map(f).unwrap_or(a)` on an Option value. This can be done \
                             more directly by calling `map_or(a, f)` instead";
                        // get args to map() and unwrap_or()
                        let map_arg = snippet(cx, inner_args[1].span, "..");
                        let unwrap_arg = snippet(cx, args[1].span, "..");
                        // lint, with note if neither arg is > 1 line and both map() and
                        // unwrap_or() have the same span
                        let multiline = map_arg.lines().count() > 1
                                        || unwrap_arg.lines().count() > 1;
                        let same_span = inner_args[1].span.expn_id == args[1].span.expn_id;
                        if same_span && !multiline {
                            span_note_and_lint(
                                cx, OPTION_MAP_UNWRAP_OR, expr.span, msg, expr.span,
                                &format!("replace this with map_or({1}, {0})",
                                         map_arg, unwrap_arg)
                            );
                        }
                        else if same_span && multiline {
                            span_lint(cx, OPTION_MAP_UNWRAP_OR, expr.span, msg);
                        };
                    }
                }
            }
            // check Option.map(_).unwrap_or_else(_)
            else if name.node.as_str() == "unwrap_or_else" {
                if let ExprMethodCall(ref inner_name, _, ref inner_args) = args[0].node {
                    if inner_name.node.as_str() == "map"
                            && match_type(cx, cx.tcx.expr_ty(&inner_args[0]), &OPTION_PATH) {
                        // lint message
                        let msg =
                            "called `map(f).unwrap_or_else(g)` on an Option value. This can be \
                             done more directly by calling `map_or_else(g, f)` instead";
                        // get args to map() and unwrap_or_else()
                        let map_arg = snippet(cx, inner_args[1].span, "..");
                        let unwrap_arg = snippet(cx, args[1].span, "..");
                        // lint, with note if neither arg is > 1 line and both map() and
                        // unwrap_or_else() have the same span
                        let multiline = map_arg.lines().count() > 1
                                        || unwrap_arg.lines().count() > 1;
                        let same_span = inner_args[1].span.expn_id == args[1].span.expn_id;
                        if same_span && !multiline {
                            span_note_and_lint(
                                cx, OPTION_MAP_UNWRAP_OR_ELSE, expr.span, msg, expr.span,
                                &format!("replace this with map_or_else({1}, {0})",
                                         map_arg, unwrap_arg)
                            );
                        }
                        else if same_span && multiline {
                            span_lint(cx, OPTION_MAP_UNWRAP_OR_ELSE, expr.span, msg);
                        };
                    }
                }
            }
        }
    }

    fn check_item(&mut self, cx: &LateContext, item: &Item) {
        if let ItemImpl(_, _, _, None, ref ty, ref items) = item.node {
            for implitem in items {
                let name = implitem.name;
                if let ImplItemKind::Method(ref sig, _) = implitem.node {
                    // check missing trait implementations
                    for &(method_name, n_args, self_kind, out_type, trait_name) in &TRAIT_METHODS {
                        if_let_chain! {
                            [<commit_msg>improve cc of function<commit_after>use rustc_front::hir::*;
use rustc::lint::*;
use rustc::middle::ty;
use rustc::middle::subst::{Subst, TypeSpace};
use std::iter;
use std::borrow::Cow;

use utils::{snippet, span_lint, span_note_and_lint, match_path, match_type, walk_ptrs_ty_depth,
    walk_ptrs_ty};
use utils::{OPTION_PATH, RESULT_PATH, STRING_PATH};

use self::SelfKind::*;
use self::OutType::*;

#[derive(Clone)]
pub struct MethodsPass;

declare_lint!(pub OPTION_UNWRAP_USED, Allow,
              "using `Option.unwrap()`, which should at least get a better message using `expect()`");
declare_lint!(pub RESULT_UNWRAP_USED, Allow,
              "using `Result.unwrap()`, which might be better handled");
declare_lint!(pub STR_TO_STRING, Warn,
              "using `to_string()` on a str, which should be `to_owned()`");
declare_lint!(pub STRING_TO_STRING, Warn,
              "calling `String.to_string()` which is a no-op");
declare_lint!(pub SHOULD_IMPLEMENT_TRAIT, Warn,
              "defining a method that should be implementing a std trait");
declare_lint!(pub WRONG_SELF_CONVENTION, Warn,
              "defining a method named with an established prefix (like \"into_\") that takes \
               `self` with the wrong convention");
declare_lint!(pub WRONG_PUB_SELF_CONVENTION, Allow,
              "defining a public method named with an established prefix (like \"into_\") that takes \
               `self` with the wrong convention");
declare_lint!(pub OK_EXPECT, Warn,
              "using `ok().expect()`, which gives worse error messages than \
               calling `expect` directly on the Result");
declare_lint!(pub OPTION_MAP_UNWRAP_OR, Warn,
              "using `Option.map(f).unwrap_or(a)`, which is more succinctly expressed as \
               `map_or(a, f)`)");
declare_lint!(pub OPTION_MAP_UNWRAP_OR_ELSE, Warn,
              "using `Option.map(f).unwrap_or_else(g)`, which is more succinctly expressed as \
               `map_or_else(g, f)`)");

impl LintPass for MethodsPass {
    fn get_lints(&self) -> LintArray {
        lint_array!(OPTION_UNWRAP_USED, RESULT_UNWRAP_USED, STR_TO_STRING, STRING_TO_STRING,
                    SHOULD_IMPLEMENT_TRAIT, WRONG_SELF_CONVENTION, OK_EXPECT, OPTION_MAP_UNWRAP_OR,
                    OPTION_MAP_UNWRAP_OR_ELSE)
    }
}

impl LateLintPass for MethodsPass {
    fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {

        if let ExprMethodCall(ref name, _, ref args) = expr.node {
            let (obj_ty, ptr_depth) = walk_ptrs_ty_depth(cx.tcx.expr_ty(&args[0]));
            match &*name.node.as_str() {
                "unwrap" if match_type(cx, obj_ty, &OPTION_PATH) => {
                    span_lint(cx, OPTION_UNWRAP_USED, expr.span,
                              "used unwrap() on an Option value. If you don't want \
                               to handle the None case gracefully, consider using \
                               expect() to provide a better panic message");
                },
                "unwrap" if match_type(cx, obj_ty, &RESULT_PATH) => {
                    span_lint(cx, RESULT_UNWRAP_USED, expr.span,
                              "used unwrap() on a Result value. Graceful handling \
                               of Err values is preferred");
                },
                "to_string" if obj_ty.sty == ty::TyStr => {
                    let mut arg_str = snippet(cx, args[0].span, "_");
                    if ptr_depth > 1 {
                        arg_str = Cow::Owned(format!(
                            "({}{})",
                            iter::repeat('*').take(ptr_depth - 1).collect::<String>(),
                            arg_str));
                    }
                    span_lint(cx, STR_TO_STRING, expr.span, &format!(
                        "`{}.to_owned()` is faster", arg_str));
                },
                "to_string" if match_type(cx, obj_ty, &STRING_PATH) => {
                    span_lint(cx, STRING_TO_STRING, expr.span, "`String.to_string()` is a no-op; use \
                                                                `clone()` to make a copy");
                },
                "expect" => if let ExprMethodCall(ref inner_name, _, ref inner_args) = args[0].node {
                    if inner_name.node.as_str() == "ok"
                            && match_type(cx, cx.tcx.expr_ty(&inner_args[0]), &RESULT_PATH) {
                        let result_type = cx.tcx.expr_ty(&inner_args[0]);
                        if let Some(error_type) = get_error_type(cx, result_type) {
                            if has_debug_impl(error_type, cx) {
                                span_lint(cx, OK_EXPECT, expr.span,
                                         "called `ok().expect()` on a Result \
                                          value. You can call `expect` directly \
                                          on the `Result`");
                            }
                        }
                    }
                },
                // check Option.map(_).unwrap_or(_)
                "unwrap_or" => if let ExprMethodCall(ref inner_name, _, ref inner_args) = args[0].node {
                    if inner_name.node.as_str() == "map"
                            && match_type(cx, cx.tcx.expr_ty(&inner_args[0]), &OPTION_PATH) {
                        // lint message
                        let msg =
                            "called `map(f).unwrap_or(a)` on an Option value. This can be done \
                             more directly by calling `map_or(a, f)` instead";
                        // get args to map() and unwrap_or()
                        let map_arg = snippet(cx, inner_args[1].span, "..");
                        let unwrap_arg = snippet(cx, args[1].span, "..");
                        // lint, with note if neither arg is > 1 line and both map() and
                        // unwrap_or() have the same span
                        let multiline = map_arg.lines().count() > 1
                                        || unwrap_arg.lines().count() > 1;
                        let same_span = inner_args[1].span.expn_id == args[1].span.expn_id;
                        if same_span && !multiline {
                            span_note_and_lint(
                                cx, OPTION_MAP_UNWRAP_OR, expr.span, msg, expr.span,
                                &format!("replace this with map_or({1}, {0})",
                                         map_arg, unwrap_arg)
                            );
                        }
                        else if same_span && multiline {
                            span_lint(cx, OPTION_MAP_UNWRAP_OR, expr.span, msg);
                        };
                    }
                },
                // check Option.map(_).unwrap_or_else(_)
                "unwrap_or_else" => if let ExprMethodCall(ref inner_name, _, ref inner_args) = args[0].node {
                    if inner_name.node.as_str() == "map"
                            && match_type(cx, cx.tcx.expr_ty(&inner_args[0]), &OPTION_PATH) {
                        // lint message
                        let msg =
                            "called `map(f).unwrap_or_else(g)` on an Option value. This can be \
                             done more directly by calling `map_or_else(g, f)` instead";
                        // get args to map() and unwrap_or_else()
                        let map_arg = snippet(cx, inner_args[1].span, "..");
                        let unwrap_arg = snippet(cx, args[1].span, "..");
                        // lint, with note if neither arg is > 1 line and both map() and
                        // unwrap_or_else() have the same span
                        let multiline = map_arg.lines().count() > 1
                                        || unwrap_arg.lines().count() > 1;
                        let same_span = inner_args[1].span.expn_id == args[1].span.expn_id;
                        if same_span && !multiline {
                            span_note_and_lint(
                                cx, OPTION_MAP_UNWRAP_OR_ELSE, expr.span, msg, expr.span,
                                &format!("replace this with map_or_else({1}, {0})",
                                         map_arg, unwrap_arg)
                            );
                        }
                        else if same_span && multiline {
                            span_lint(cx, OPTION_MAP_UNWRAP_OR_ELSE, expr.span, msg);
                        };
                    }
                },
                _ => {},
            }
        }
    }

    fn check_item(&mut self, cx: &LateContext, item: &Item) {
        if let ItemImpl(_, _, _, None, ref ty, ref items) = item.node {
            for implitem in items {
                let name = implitem.name;
                if let ImplItemKind::Method(ref sig, _) = implitem.node {
                    // check missing trait implementations
                    for &(method_name, n_args, self_kind, out_type, trait_name) in &TRAIT_METHODS {
                        if_let_chain! {
                            [
{'fraction_non_alphanumeric': 0.10673942701227832, 'fraction_numerical': 0.003165075034106412, 'mean_word_length': 1.379073088407114, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 29, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 40, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3572014', 'n_tokens_mistral': 5186, 'n_tokens_neox': 5001, 'n_words': 1219}