Skip to content

Commit 2db6965

Browse files
committed
Lint usage of Debug-based formatting
1 parent 1a8b8cd commit 2db6965

File tree

5 files changed

+81
-5
lines changed

5 files changed

+81
-5
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ A collection of lints to catch common mistakes and improve your Rust code.
66
[Jump to usage instructions](#usage)
77

88
##Lints
9-
There are 117 lints included in this crate:
9+
There are 118 lints included in this crate:
1010

1111
name | default | meaning
1212
---------------------------------------------------------------------------------------------------------------|---------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -118,6 +118,7 @@ name
118118
[unstable_as_slice](https://github.com/Manishearth/rust-clippy/wiki#unstable_as_slice) | warn | as_slice is not stable and can be replaced by & v[..]see https://github.com/rust-lang/rust/issues/27729
119119
[unused_collect](https://github.com/Manishearth/rust-clippy/wiki#unused_collect) | warn | `collect()`ing an iterator without using the result; this is usually better written as a for loop
120120
[unused_lifetimes](https://github.com/Manishearth/rust-clippy/wiki#unused_lifetimes) | warn | unused lifetimes in function definitions
121+
[use_debug](https://github.com/Manishearth/rust-clippy/wiki#use_debug) | allow | use `Debug`-based formatting
121122
[used_underscore_binding](https://github.com/Manishearth/rust-clippy/wiki#used_underscore_binding) | warn | using a binding which is prefixed with an underscore
122123
[useless_transmute](https://github.com/Manishearth/rust-clippy/wiki#useless_transmute) | warn | transmutes that have the same to and from types
123124
[useless_vec](https://github.com/Manishearth/rust-clippy/wiki#useless_vec) | warn | useless `vec!`

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ pub fn plugin_registrar(reg: &mut Registry) {
169169
mut_mut::MUT_MUT,
170170
mutex_atomic::MUTEX_INTEGER,
171171
print::PRINT_STDOUT,
172+
print::USE_DEBUG,
172173
shadow::SHADOW_REUSE,
173174
shadow::SHADOW_SAME,
174175
shadow::SHADOW_UNRELATED,

src/print.rs

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
use rustc::lint::*;
22
use rustc_front::hir::*;
3-
use utils::{IO_PRINT_PATH, is_expn_of, match_path, span_lint};
3+
use rustc::front::map::Node::{NodeItem, NodeImplItem};
4+
use utils::{FMT_ARGUMENTV1_NEW_PATH, DEBUG_FMT_METHOD_PATH, IO_PRINT_PATH};
5+
use utils::{is_expn_of, match_path, span_lint};
46

57
/// **What it does:** This lint warns whenever you print on *stdout*. The purpose of this lint is to catch debugging remnants.
68
///
@@ -16,21 +18,36 @@ declare_lint! {
1618
"printing on stdout"
1719
}
1820

21+
/// **What it does:** This lint warns whenever you use `Debug` formatting. The purpose of this lint is to catch debugging remnants.
22+
///
23+
/// **Why is this bad?** The purpose of the `Debug` trait is to facilitate debugging Rust code. It
24+
/// should not be used in in user-facing output.
25+
///
26+
/// **Example:** `println!("{:?}", foo);`
27+
declare_lint! {
28+
pub USE_DEBUG,
29+
Allow,
30+
"use `Debug`-based formatting"
31+
}
32+
1933
#[derive(Copy, Clone, Debug)]
2034
pub struct PrintLint;
2135

2236
impl LintPass for PrintLint {
2337
fn get_lints(&self) -> LintArray {
24-
lint_array!(PRINT_STDOUT)
38+
lint_array!(PRINT_STDOUT, USE_DEBUG)
2539
}
2640
}
2741

2842
impl LateLintPass for PrintLint {
2943
fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
30-
if let ExprCall(ref fun, _) = expr.node {
44+
if let ExprCall(ref fun, ref args) = expr.node {
3145
if let ExprPath(_, ref path) = fun.node {
46+
// Search for `std::io::_print(..)` which is unique in a
47+
// `print!` expansion.
3248
if match_path(path, &IO_PRINT_PATH) {
3349
if let Some(span) = is_expn_of(cx, expr.span, "print") {
50+
// `println!` uses `print!`.
3451
let (span, name) = match is_expn_of(cx, span, "println") {
3552
Some(span) => (span, "println"),
3653
None => (span, "print"),
@@ -39,7 +56,32 @@ impl LateLintPass for PrintLint {
3956
span_lint(cx, PRINT_STDOUT, span, &format!("use of `{}!`", name));
4057
}
4158
}
59+
// Search for something like
60+
// `::std::fmt::ArgumentV1::new(__arg0, ::std::fmt::Debug::fmt)`
61+
else if args.len() == 2 && match_path(path, &FMT_ARGUMENTV1_NEW_PATH) {
62+
if let ExprPath(None, ref path) = args[1].node {
63+
if match_path(path, &DEBUG_FMT_METHOD_PATH) &&
64+
!is_in_debug_impl(cx, expr) &&
65+
is_expn_of(cx, expr.span, "panic").is_none() {
66+
span_lint(cx, USE_DEBUG, args[0].span, "use of `Debug`-based formatting");
67+
}
68+
}
69+
}
4270
}
4371
}
4472
}
4573
}
74+
75+
fn is_in_debug_impl(cx: &LateContext, expr: &Expr) -> bool {
76+
let map = &cx.tcx.map;
77+
78+
if let Some(NodeImplItem(item)) = map.find(map.get_parent(expr.id)) { // `fmt` method
79+
if let Some(NodeItem(item)) = map.find(map.get_parent(item.id)) { // `Debug` impl
80+
if let ItemImpl(_, _, _, Some(ref tr), _, _) = item.node {
81+
return match_path(&tr.path, &["Debug"]);
82+
}
83+
}
84+
}
85+
86+
false
87+
}

src/utils.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,10 @@ pub const BTREEMAP_PATH: [&'static str; 4] = ["collections", "btree", "map", "BT
2626
pub const CLONE_PATH: [&'static str; 3] = ["clone", "Clone", "clone"];
2727
pub const CLONE_TRAIT_PATH: [&'static str; 2] = ["clone", "Clone"];
2828
pub const COW_PATH: [&'static str; 3] = ["collections", "borrow", "Cow"];
29+
pub const DEBUG_FMT_METHOD_PATH: [&'static str; 4] = ["std", "fmt", "Debug", "fmt"];
2930
pub const DEFAULT_TRAIT_PATH: [&'static str; 3] = ["core", "default", "Default"];
3031
pub const DROP_PATH: [&'static str; 3] = ["core", "mem", "drop"];
32+
pub const FMT_ARGUMENTV1_NEW_PATH: [&'static str; 4] = ["std", "fmt", "ArgumentV1", "new"];
3133
pub const HASHMAP_ENTRY_PATH: [&'static str; 5] = ["std", "collections", "hash", "map", "Entry"];
3234
pub const HASHMAP_PATH: [&'static str; 5] = ["std", "collections", "hash", "map", "HashMap"];
3335
pub const HASH_PATH: [&'static str; 2] = ["hash", "Hash"];

tests/compile-fail/print.rs

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,41 @@
11
#![feature(plugin)]
22
#![plugin(clippy)]
3+
#![deny(print_stdout, use_debug)]
34

4-
#[deny(print_stdout)]
5+
use std::fmt::{Debug, Display, Formatter, Result};
6+
7+
#[allow(dead_code)]
8+
struct Foo;
9+
10+
impl Display for Foo {
11+
fn fmt(&self, f: &mut Formatter) -> Result {
12+
write!(f, "{:?}", 43.1415)
13+
//~^ ERROR use of `Debug`-based formatting
14+
}
15+
}
16+
17+
impl Debug for Foo {
18+
fn fmt(&self, f: &mut Formatter) -> Result {
19+
// ok, we can use `Debug` formatting in `Debug` implementations
20+
write!(f, "{:?}", 42.718)
21+
}
22+
}
523

624
fn main() {
725
println!("Hello"); //~ERROR use of `println!`
826
print!("Hello"); //~ERROR use of `print!`
927

28+
print!("Hello {}", "World"); //~ERROR use of `print!`
29+
30+
print!("Hello {:?}", "World");
31+
//~^ ERROR use of `print!`
32+
//~| ERROR use of `Debug`-based formatting
33+
34+
print!("Hello {:#?}", "#orld");
35+
//~^ ERROR use of `print!`
36+
//~| ERROR use of `Debug`-based formatting
37+
38+
assert_eq!(42, 1337);
39+
1040
vec![1, 2];
1141
}

0 commit comments

Comments
 (0)