rust-clippy/clippy_lints/src/print.rs

135 lines
4.8 KiB
Rust
Raw Normal View History

2016-04-14 16:13:15 +00:00
use rustc::hir::*;
use rustc::hir::map::Node::{NodeItem, NodeImplItem};
use rustc::lint::*;
2016-04-14 16:13:15 +00:00
use utils::paths;
2016-02-07 17:30:57 +00:00
use utils::{is_expn_of, match_path, span_lint};
2016-08-05 15:52:58 +00:00
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.
///
2016-07-15 22:25:44 +00:00
/// **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.
2016-02-07 17:30:57 +00:00
///
/// **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.
2016-02-07 17:30:57 +00:00
///
2016-07-15 22:25:44 +00:00
/// **Example:**
/// ```rust
/// println!("{:?}", foo);
/// ```
2016-02-07 17:30:57 +00:00
declare_lint! {
pub USE_DEBUG,
Allow,
"use of `Debug`-based formatting"
2016-02-07 17:30:57 +00:00
}
#[derive(Copy, Clone, Debug)]
2016-06-10 14:17:20 +00:00
pub struct Pass;
2016-06-10 14:17:20 +00:00
impl LintPass for Pass {
fn get_lints(&self) -> LintArray {
2016-08-05 15:52:58 +00:00
lint_array!(PRINT_WITH_NEWLINE, PRINT_STDOUT, USE_DEBUG)
}
}
2016-06-10 14:17:20 +00:00
impl LateLintPass for Pass {
fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
2016-02-07 17:30:57 +00:00
if let ExprCall(ref fun, ref args) = expr.node {
if let ExprPath(_, ref path) = fun.node {
2016-02-07 17:30:57 +00:00
// Search for `std::io::_print(..)` which is unique in a
// `print!` expansion.
2016-04-14 16:13:15 +00:00
if match_path(path, &paths::IO_PRINT) {
if let Some(span) = is_expn_of(cx, expr.span, "print") {
2016-02-07 17:30:57 +00:00
// `println!` uses `print!`.
let (span, name) = match is_expn_of(cx, span, "println") {
Some(span) => (span, "println"),
None => (span, "print"),
};
2016-01-30 12:48:39 +00:00
span_lint(cx, PRINT_STDOUT, span, &format!("use of `{}!`", name));
2016-08-05 15:52:58 +00:00
// 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 args_path) = args_fun.node,
match_path(args_path, &paths::FMT_ARGUMENTS_NEWV1),
args_args.len() == 2,
// 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(last_chr) = last_str.chars().last(),
last_chr == '\n'
], {
span_lint(cx, PRINT_WITH_NEWLINE, span,
"using `print!()` with a format string that ends in a \
newline, consider using `println!()` instead");
}}
}
}
2016-02-07 17:30:57 +00:00
// Search for something like
// `::std::fmt::ArgumentV1::new(__arg0, ::std::fmt::Debug::fmt)`
2016-04-14 16:13:15 +00:00
else if args.len() == 2 && match_path(path, &paths::FMT_ARGUMENTV1_NEW) {
2016-02-07 17:30:57 +00:00
if let ExprPath(None, ref path) = args[1].node {
2016-04-14 16:13:15 +00:00
if match_path(path, &paths::DEBUG_FMT_METHOD) && !is_in_debug_impl(cx, expr) &&
2016-02-29 11:19:32 +00:00
is_expn_of(cx, expr.span, "panic").is_none() {
2016-02-07 17:30:57 +00:00
span_lint(cx, USE_DEBUG, args[0].span, "use of `Debug`-based formatting");
}
}
}
}
}
}
}
2016-02-07 17:30:57 +00:00
fn is_in_debug_impl(cx: &LateContext, expr: &Expr) -> bool {
let map = &cx.tcx.map;
2016-02-29 11:19:32 +00:00
// `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)) {
2016-02-07 17:30:57 +00:00
if let ItemImpl(_, _, _, Some(ref tr), _, _) = item.node {
return match_path(&tr.path, &["Debug"]);
}
}
}
false
}