Skip to content

Commit

Permalink
Lint print!("...\n") (closes #455)
Browse files Browse the repository at this point in the history
  • Loading branch information
birkenfeld committed Aug 16, 2016
1 parent 909efec commit ffad9a8
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 14 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -252,6 +252,7 @@ All notable changes to this project will be documented in this file.
[`panic_params`]: https://github.com/Manishearth/rust-clippy/wiki#panic_params
[`precedence`]: https://github.com/Manishearth/rust-clippy/wiki#precedence
[`print_stdout`]: https://github.com/Manishearth/rust-clippy/wiki#print_stdout
[`print_with_newline`]: https://github.com/Manishearth/rust-clippy/wiki#print_with_newline
[`ptr_arg`]: https://github.com/Manishearth/rust-clippy/wiki#ptr_arg
[`range_step_by_zero`]: https://github.com/Manishearth/rust-clippy/wiki#range_step_by_zero
[`range_zip_with_len`]: https://github.com/Manishearth/rust-clippy/wiki#range_zip_with_len
Expand Down
3 changes: 2 additions & 1 deletion README.md
Expand Up @@ -17,7 +17,7 @@ Table of contents:

## Lints

There are 164 lints included in this crate:
There are 165 lints included in this crate:

name | default | triggers on
---------------------------------------------------------------------------------------------------------------------|---------|----------------------------------------------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -130,6 +130,7 @@ name
[panic_params](https://github.com/Manishearth/rust-clippy/wiki#panic_params) | warn | missing parameters in `panic!` calls
[precedence](https://github.com/Manishearth/rust-clippy/wiki#precedence) | warn | operations where precedence may be unclear
[print_stdout](https://github.com/Manishearth/rust-clippy/wiki#print_stdout) | allow | printing on stdout
[print_with_newline](https://github.com/Manishearth/rust-clippy/wiki#print_with_newline) | warn | using `print!()` with a format string that ends in a newline
[ptr_arg](https://github.com/Manishearth/rust-clippy/wiki#ptr_arg) | warn | arguments of the type `&Vec<...>` (instead of `&[...]`) or `&String` (instead of `&str`)
[range_step_by_zero](https://github.com/Manishearth/rust-clippy/wiki#range_step_by_zero) | warn | using `Range::step_by(0)`, which produces an infinite iterator
[range_zip_with_len](https://github.com/Manishearth/rust-clippy/wiki#range_zip_with_len) | warn | zipping iterator with a range when `enumerate()` would do
Expand Down
38 changes: 26 additions & 12 deletions clippy_lints/src/format.rs
Expand Up @@ -69,11 +69,10 @@ impl LateLintPass for Pass {
}
}

/// Checks if the expressions matches
/// ```rust
/// { static __STATIC_FMTSTR: &[""] = _; __STATIC_FMTSTR }
/// ```
fn check_static_str(cx: &LateContext, expr: &Expr) -> bool {
/// Returns the slice of format string parts in an `Arguments::new_v1` call.
/// Public because it's shared with a lint in print.rs.
pub fn get_argument_fmtstr_parts<'a, 'b>(cx: &LateContext<'a, 'b>, expr: &'a Expr)
-> Option<Vec<&'a str>> {
if_let_chain! {[
let ExprBlock(ref block) = expr.node,
block.stmts.len() == 1,
Expand All @@ -83,16 +82,31 @@ fn check_static_str(cx: &LateContext, expr: &Expr) -> bool {
decl.name.as_str() == "__STATIC_FMTSTR",
let ItemStatic(_, _, ref expr) = decl.node,
let ExprAddrOf(_, ref expr) = expr.node, // &[""]
let ExprVec(ref expr) = expr.node,
expr.len() == 1,
let ExprLit(ref lit) = expr[0].node,
let LitKind::Str(ref lit, _) = lit.node,
lit.is_empty()
let ExprVec(ref exprs) = expr.node,
], {
return true;
let mut result = Vec::new();
for expr in exprs {
if let ExprLit(ref lit) = expr.node {
if let LitKind::Str(ref lit, _) = lit.node {
result.push(&**lit);
}
}
}
return Some(result);
}}
None
}

false
/// Checks if the expressions matches
/// ```rust
/// { static __STATIC_FMTSTR: &[""] = _; __STATIC_FMTSTR }
/// ```
fn check_static_str(cx: &LateContext, expr: &Expr) -> bool {
if let Some(expr) = get_argument_fmtstr_parts(cx, expr) {
expr.len() == 1 && expr[0].is_empty()
} else {
false
}
}

/// Checks if the expressions matches
Expand Down
1 change: 1 addition & 0 deletions clippy_lints/src/lib.rs
Expand Up @@ -403,6 +403,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL,
panic::PANIC_PARAMS,
precedence::PRECEDENCE,
print::PRINT_WITH_NEWLINE,
ptr_arg::PTR_ARG,
ranges::RANGE_STEP_BY_ZERO,
ranges::RANGE_ZIP_WITH_LEN,
Expand Down
40 changes: 39 additions & 1 deletion clippy_lints/src/print.rs
Expand Up @@ -3,6 +3,24 @@ use rustc::hir::map::Node::{NodeItem, NodeImplItem};
use rustc::lint::*;
use utils::paths;
use utils::{is_expn_of, match_path, span_lint};
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.
Expand Down Expand Up @@ -43,7 +61,7 @@ pub struct Pass;

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

Expand All @@ -62,6 +80,26 @@ impl LateLintPass for Pass {
};

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 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");
}}
}
}
// Search for something like
Expand Down
15 changes: 15 additions & 0 deletions tests/compile-fail/print_with_newline.rs
@@ -0,0 +1,15 @@
#![feature(plugin)]
#![plugin(clippy)]
#![deny(print_with_newline)]

fn main() {
print!("Hello");
print!("Hello\n"); //~ERROR using `print!()` with a format string
print!("Hello {}\n", "world"); //~ERROR using `print!()` with a format string
print!("Hello {} {}\n\n", "world", "#2"); //~ERROR using `print!()` with a format string

// these are all fine
println!("Hello");
println!("Hello\n");
println!("Hello {}\n", "world");
}

0 comments on commit ffad9a8

Please sign in to comment.