Merge pull request #586 from mcarton/print

Add a lint to warn about use of `print{,ln}!`
This commit is contained in:
llogiq 2016-01-28 21:25:59 +01:00
commit 783b342bc1
5 changed files with 93 additions and 1 deletions

View file

@ -6,7 +6,7 @@ A collection of lints to catch common mistakes and improve your Rust code.
[Jump to usage instructions](#usage)
##Lints
There are 100 lints included in this crate:
There are 101 lints included in this crate:
name | default | meaning
---------------------------------------------------------------------------------------------------------------|---------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@ -73,6 +73,7 @@ name
[out_of_bounds_indexing](https://github.com/Manishearth/rust-clippy/wiki#out_of_bounds_indexing) | deny | out of bound constant indexing
[panic_params](https://github.com/Manishearth/rust-clippy/wiki#panic_params) | warn | missing parameters in `panic!`
[precedence](https://github.com/Manishearth/rust-clippy/wiki#precedence) | warn | catches operations where precedence may be unclear. See the wiki for a list of cases caught
[print_stdout](https://github.com/Manishearth/rust-clippy/wiki#print_stdout) | allow | printing on stdout
[ptr_arg](https://github.com/Manishearth/rust-clippy/wiki#ptr_arg) | warn | fn arguments of the type `&Vec<...>` or `&String`, suggesting to use `&[...]` or `&str` instead, respectively
[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

View file

@ -6,6 +6,7 @@
// this only exists to allow the "dogfood" integration test to work
#[allow(dead_code)]
#[allow(print_stdout)]
fn main() {
println!("What are you doing? Don't run clippy as an executable");
}
@ -77,6 +78,7 @@ pub mod misc_early;
pub mod array_indexing;
pub mod panic;
pub mod derive;
pub mod print;
mod reexport {
pub use syntax::ast::{Name, NodeId};
@ -141,6 +143,7 @@ pub fn plugin_registrar(reg: &mut Registry) {
reg.register_late_lint_pass(box strings::StringLitAsBytes);
reg.register_late_lint_pass(box derive::Derive);
reg.register_late_lint_pass(box types::CharLitAsU8);
reg.register_late_lint_pass(box print::PrintLint);
reg.register_lint_group("clippy_pedantic", vec![
@ -149,6 +152,7 @@ pub fn plugin_registrar(reg: &mut Registry) {
methods::WRONG_PUB_SELF_CONVENTION,
mut_mut::MUT_MUT,
mutex_atomic::MUTEX_INTEGER,
print::PRINT_STDOUT,
shadow::SHADOW_REUSE,
shadow::SHADOW_SAME,
shadow::SHADOW_UNRELATED,

49
src/print.rs Normal file
View file

@ -0,0 +1,49 @@
use rustc::lint::*;
use rustc_front::hir::*;
use utils::{IO_PRINT_PATH, is_expn_of, match_path, span_lint};
/// **What it does:** This lint warns whenever you print on *stdout*. This lint is `Allow` by
/// default, the purpose 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:** `println!("Hello world!");`
declare_lint! {
pub PRINT_STDOUT,
Allow,
"printing on stdout"
}
#[derive(Copy, Clone, Debug)]
pub struct PrintLint;
impl LintPass for PrintLint {
fn get_lints(&self) -> LintArray {
lint_array!(PRINT_STDOUT)
}
}
impl LateLintPass for PrintLint {
fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
if let ExprCall(ref fun, _) = expr.node {
if let ExprPath(_, ref path) = fun.node {
if match_path(path, &IO_PRINT_PATH) {
if let Some(span) = is_expn_of(cx, expr.span, "print") {
let (span, name) = match is_expn_of(cx, span, "println") {
Some(span) => (span, "println"),
None => (span, "print"),
};
span_lint(cx,
PRINT_STDOUT,
span,
&format!("use of `{}!`", name));
}
}
}
}
}
}

View file

@ -29,6 +29,7 @@ pub const DEFAULT_TRAIT_PATH: [&'static str; 3] = ["core", "default", "Default"]
pub const HASHMAP_ENTRY_PATH: [&'static str; 5] = ["std", "collections", "hash", "map", "Entry"];
pub const HASHMAP_PATH: [&'static str; 5] = ["std", "collections", "hash", "map", "HashMap"];
pub const HASH_PATH: [&'static str; 2] = ["hash", "Hash"];
pub const IO_PRINT_PATH: [&'static str; 3] = ["std", "io", "_print"];
pub const LL_PATH: [&'static str; 3] = ["collections", "linked_list", "LinkedList"];
pub const MUTEX_PATH: [&'static str; 4] = ["std", "sync", "mutex", "Mutex"];
pub const OPEN_OPTIONS_PATH: [&'static str; 3] = ["std", "fs", "OpenOptions"];
@ -645,3 +646,29 @@ fn is_cast_ty_equal(left: &Ty, right: &Ty) -> bool {
_ => false,
}
}
/// Return the pre-expansion span is this comes from a expansion of the macro `name`.
pub fn is_expn_of(cx: &LateContext, mut span: Span, name: &str) -> Option<Span> {
loop {
let span_name_span = cx.tcx.sess.codemap().with_expn_info(span.expn_id, |expn| {
expn.map(|ei| {
(ei.callee.name(), ei.call_site)
})
});
return match span_name_span {
Some((mac_name, new_span)) => {
if mac_name.as_str() == name {
Some(new_span)
}
else {
span = new_span;
continue;
}
}
None => {
None
}
};
}
}

11
tests/compile-fail/print.rs Executable file
View file

@ -0,0 +1,11 @@
#![feature(plugin)]
#![plugin(clippy)]
#[deny(print_stdout)]
fn main() {
println!("Hello"); //~ERROR use of `println!`
print!("Hello"); //~ERROR use of `print!`
vec![1, 2];
}