clippy_lints 0.0.155

A bunch of helpful lints to avoid common pitfalls in Rust
Documentation
use rustc::hir::*;
use rustc::hir::map::Node::{NodeItem, NodeImplItem};
use rustc::lint::*;
use utils::paths;
use utils::{is_expn_of, match_def_path, resolve_node, span_lint, match_path};
use format::get_argument_fmtstr_parts;

/// **What it does:** This lint warns when you using `print!()` with a format
/// string that
/// ends in a newline.
///
/// **Why is this bad?** You should use `println!()` instead, which appends the
/// newline.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// print!("Hello {}!\n", name);
/// ```
declare_lint! {
    pub PRINT_WITH_NEWLINE,
    Warn,
    "using `print!()` with a format string that ends in a newline"
}

/// **What it does:** Checks for printing on *stdout*. The purpose of this lint
/// is to catch debugging remnants.
///
/// **Why is this bad?** People often print on *stdout* while debugging an
/// application and might forget to remove those prints afterward.
///
/// **Known problems:** Only catches `print!` and `println!` calls.
///
/// **Example:**
/// ```rust
/// println!("Hello world!");
/// ```
declare_lint! {
    pub PRINT_STDOUT,
    Allow,
    "printing on stdout"
}

/// **What it does:** Checks for use of `Debug` formatting. The purpose of this
/// lint is to catch debugging remnants.
///
/// **Why is this bad?** The purpose of the `Debug` trait is to facilitate
/// debugging Rust code. It should not be used in in user-facing output.
///
/// **Example:**
/// ```rust
/// println!("{:?}", foo);
/// ```
declare_lint! {
    pub USE_DEBUG,
    Allow,
    "use of `Debug`-based formatting"
}

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

impl LintPass for Pass {
    fn get_lints(&self) -> LintArray {
        lint_array!(PRINT_WITH_NEWLINE, PRINT_STDOUT, USE_DEBUG)
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
        if_let_chain! {[
            let ExprCall(ref fun, ref args) = expr.node,
            let ExprPath(ref qpath) = fun.node,
        ], {
            let fun = resolve_node(cx, qpath, fun.hir_id);
            let fun_id = fun.def_id();

            // Search for `std::io::_print(..)` which is unique in a
            // `print!` expansion.
            if match_def_path(cx.tcx, fun_id, &paths::IO_PRINT) {
                if let Some(span) = is_expn_of(expr.span, "print") {
                    // `println!` uses `print!`.
                    let (span, name) = match is_expn_of(span, "println") {
                        Some(span) => (span, "println"),
                        None => (span, "print"),
                    };

                    span_lint(cx, PRINT_STDOUT, span, &format!("use of `{}!`", name));

                    // Check print! with format string ending in "\n".
                    if_let_chain!{[
                        name == "print",

                        // ensure we're calling Arguments::new_v1
                        args.len() == 1,
                        let ExprCall(ref args_fun, ref args_args) = args[0].node,
                        let ExprPath(ref qpath) = args_fun.node,
                        match_def_path(cx.tcx,
                                       resolve_node(cx, qpath, args_fun.hir_id).def_id(),
                                       &paths::FMT_ARGUMENTS_NEWV1),
                        args_args.len() == 2,
                        let ExprAddrOf(_, ref match_expr) = args_args[1].node,
                        let ExprMatch(ref args, _, _) = match_expr.node,
                        let ExprTup(ref args) = args.node,

                        // collect the format string parts and check the last one
                        let Some(fmtstrs) = get_argument_fmtstr_parts(cx, &args_args[0]),
                        let Some(last_str) = fmtstrs.last(),
                        let Some('\n') = last_str.chars().last(),

                        // "foo{}bar" is made into two strings + one argument,
                        // if the format string starts with `{}` (eg. "{}foo"),
                        // the string array is prepended an empty string "".
                        // We only want to check the last string after any `{}`:
                        args.len() < fmtstrs.len(),
                    ], {
                        span_lint(cx, PRINT_WITH_NEWLINE, span,
                                  "using `print!()` with a format string that ends in a \
                                   newline, consider using `println!()` instead");
                    }}
                }
            }
            // Search for something like
            // `::std::fmt::ArgumentV1::new(__arg0, ::std::fmt::Debug::fmt)`
            else if args.len() == 2 && match_def_path(cx.tcx, fun_id, &paths::FMT_ARGUMENTV1_NEW) {
                if let ExprPath(ref qpath) = args[1].node {
                    let def_id = cx.tables.qpath_def(qpath, args[1].hir_id).def_id();
                    if match_def_path(cx.tcx, def_id, &paths::DEBUG_FMT_METHOD) && !is_in_debug_impl(cx, expr) &&
                       is_expn_of(expr.span, "panic").is_none() {
                        span_lint(cx, USE_DEBUG, args[0].span, "use of `Debug`-based formatting");
                    }
                }
            }
        }}
    }
}

fn is_in_debug_impl(cx: &LateContext, expr: &Expr) -> bool {
    let map = &cx.tcx.hir;

    // `fmt` method
    if let Some(NodeImplItem(item)) = map.find(map.get_parent(expr.id)) {
        // `Debug` impl
        if let Some(NodeItem(item)) = map.find(map.get_parent(item.id)) {
            if let ItemImpl(_, _, _, _, Some(ref tr), _, _) = item.node {
                return match_path(&tr.path, &["Debug"]);
            }
        }
    }

    false
}