Skip to content

Commit 9ee4626

Browse files
committed
Merge pull request rust-lang#637 from mcarton/debug
Lint usage of `Debug`-based formatting
2 parents c1edd6f + 2db6965 commit 9ee4626

File tree

10 files changed

+147
-116
lines changed

10 files changed

+147
-116
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ name
119119
[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
120120
[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
121121
[unused_lifetimes](https://github.com/Manishearth/rust-clippy/wiki#unused_lifetimes) | warn | unused lifetimes in function definitions
122+
[use_debug](https://github.com/Manishearth/rust-clippy/wiki#use_debug) | allow | use `Debug`-based formatting
122123
[used_underscore_binding](https://github.com/Manishearth/rust-clippy/wiki#used_underscore_binding) | warn | using a binding which is prefixed with an underscore
123124
[useless_transmute](https://github.com/Manishearth/rust-clippy/wiki#useless_transmute) | warn | transmutes that have the same to and from types
124125
[useless_vec](https://github.com/Manishearth/rust-clippy/wiki#useless_vec) | warn | useless `vec!`

src/consts.rs

Lines changed: 0 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,10 @@ use rustc::middle::def::PathResolution;
66
use rustc::middle::def::Def;
77
use rustc_front::hir::*;
88
use syntax::ptr::P;
9-
use std::char;
109
use std::cmp::PartialOrd;
1110
use std::cmp::Ordering::{self, Greater, Less, Equal};
1211
use std::rc::Rc;
1312
use std::ops::Deref;
14-
use std::fmt;
1513

1614
use syntax::ast::Lit_;
1715
use syntax::ast::LitIntType;
@@ -173,90 +171,6 @@ impl PartialOrd for Constant {
173171
}
174172
}
175173

176-
fn format_byte(fmt: &mut fmt::Formatter, b: u8) -> fmt::Result {
177-
if b == b'\\' {
178-
write!(fmt, "\\\\")
179-
} else if 0x20 <= b && b <= 0x7e {
180-
write!(fmt, "{}", char::from_u32(b as u32).expect("all u8 are valid char"))
181-
} else if b == 0x0a {
182-
write!(fmt, "\\n")
183-
} else if b == 0x0d {
184-
write!(fmt, "\\r")
185-
} else {
186-
write!(fmt, "\\x{:02x}", b)
187-
}
188-
}
189-
190-
impl fmt::Display for Constant {
191-
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
192-
match *self {
193-
Constant::Str(ref s, _) => write!(fmt, "{:?}", s),
194-
Constant::Byte(ref b) => {
195-
write!(fmt, "b'")
196-
.and_then(|_| format_byte(fmt, *b))
197-
.and_then(|_| write!(fmt, "'"))
198-
}
199-
Constant::Binary(ref bs) => {
200-
try!(write!(fmt, "b\""));
201-
for b in bs.iter() {
202-
try!(format_byte(fmt, *b));
203-
}
204-
write!(fmt, "\"")
205-
}
206-
Constant::Char(ref c) => write!(fmt, "'{}'", c),
207-
Constant::Int(ref i, ref ity) => {
208-
let (sign, suffix) = match *ity {
209-
LitIntType::SignedIntLit(ref sity, ref sign) => {
210-
(if let Sign::Minus = *sign {
211-
"-"
212-
} else {
213-
""
214-
},
215-
sity.ty_to_string())
216-
}
217-
LitIntType::UnsignedIntLit(ref uity) => ("", uity.ty_to_string()),
218-
LitIntType::UnsuffixedIntLit(ref sign) => {
219-
(if let Sign::Minus = *sign {
220-
"-"
221-
} else {
222-
""
223-
},
224-
"".into())
225-
}
226-
};
227-
write!(fmt, "{}{}{}", sign, i, suffix)
228-
}
229-
Constant::Float(ref s, ref fw) => {
230-
let suffix = match *fw {
231-
FloatWidth::Fw32 => "f32",
232-
FloatWidth::Fw64 => "f64",
233-
FloatWidth::FwAny => "",
234-
};
235-
write!(fmt, "{}{}", s, suffix)
236-
}
237-
Constant::Bool(ref b) => write!(fmt, "{}", b),
238-
Constant::Repeat(ref c, ref n) => write!(fmt, "[{}; {}]", c, n),
239-
Constant::Vec(ref v) => {
240-
write!(fmt,
241-
"[{}]",
242-
v.iter()
243-
.map(|i| format!("{}", i))
244-
.collect::<Vec<_>>()
245-
.join(", "))
246-
}
247-
Constant::Tuple(ref t) => {
248-
write!(fmt,
249-
"({})",
250-
t.iter()
251-
.map(|i| format!("{}", i))
252-
.collect::<Vec<_>>()
253-
.join(", "))
254-
}
255-
}
256-
}
257-
}
258-
259-
260174
fn lit_to_constant(lit: &Lit_) -> Constant {
261175
match *lit {
262176
Lit_::LitStr(ref is, style) => Constant::Str(is.to_string(), style),

src/lib.rs

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

src/loops.rs

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1+
use reexport::*;
2+
use rustc::front::map::Node::NodeBlock;
13
use rustc::lint::*;
4+
use rustc::middle::const_eval::EvalHint::ExprTypeChecked;
5+
use rustc::middle::const_eval::{ConstVal, eval_const_expr_partial};
6+
use rustc::middle::def::Def;
7+
use rustc::middle::ty;
28
use rustc_front::hir::*;
3-
use reexport::*;
49
use rustc_front::intravisit::{Visitor, walk_expr, walk_block, walk_decl};
5-
use rustc::middle::ty;
6-
use rustc::middle::def::Def;
7-
use consts::{constant_simple, Constant};
8-
use rustc::front::map::Node::NodeBlock;
910
use std::borrow::Cow;
1011
use std::collections::{HashSet, HashMap};
1112

@@ -421,22 +422,36 @@ fn check_for_loop_reverse_range(cx: &LateContext, arg: &Expr, expr: &Expr) {
421422
// if this for loop is iterating over a two-sided range...
422423
if let ExprRange(Some(ref start_expr), Some(ref stop_expr)) = arg.node {
423424
// ...and both sides are compile-time constant integers...
424-
if let Some(start_idx @ Constant::Int(..)) = constant_simple(start_expr) {
425-
if let Some(stop_idx @ Constant::Int(..)) = constant_simple(stop_expr) {
425+
if let Ok(start_idx) = eval_const_expr_partial(&cx.tcx, start_expr, ExprTypeChecked, None) {
426+
if let Ok(stop_idx) = eval_const_expr_partial(&cx.tcx, stop_expr, ExprTypeChecked, None) {
426427
// ...and the start index is greater than the stop index,
427428
// this loop will never run. This is often confusing for developers
428429
// who think that this will iterate from the larger value to the
429430
// smaller value.
430-
if start_idx > stop_idx {
431-
span_help_and_lint(cx,
431+
let (sup, eq) = match (start_idx, stop_idx) {
432+
(ConstVal::Int(start_idx), ConstVal::Int(stop_idx)) => (start_idx > stop_idx, start_idx == stop_idx),
433+
(ConstVal::Uint(start_idx), ConstVal::Uint(stop_idx)) => (start_idx > stop_idx, start_idx == stop_idx),
434+
_ => (false, false),
435+
};
436+
437+
if sup {
438+
let start_snippet = snippet(cx, start_expr.span, "_");
439+
let stop_snippet = snippet(cx, stop_expr.span, "_");
440+
441+
span_lint_and_then(cx,
432442
REVERSE_RANGE_LOOP,
433443
expr.span,
434444
"this range is empty so this for loop will never run",
435-
&format!("Consider using `({}..{}).rev()` if you are attempting to iterate \
436-
over this range in reverse",
437-
stop_idx,
438-
start_idx));
439-
} else if start_idx == stop_idx {
445+
|db| {
446+
db.span_suggestion(expr.span,
447+
"consider using the following if \
448+
you are attempting to iterate \
449+
over this range in reverse",
450+
format!("({}..{}).rev()` ",
451+
stop_snippet,
452+
start_snippet));
453+
});
454+
} else if eq {
440455
// if they are equal, it's also problematic - this loop
441456
// will never run.
442457
span_lint(cx,

src/misc.rs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use rustc::middle::const_eval::eval_const_expr_partial;
1111
use rustc::middle::const_eval::EvalHint::ExprTypeChecked;
1212

1313
use utils::{get_item_name, match_path, snippet, get_parent_expr, span_lint};
14-
use utils::{span_help_and_lint, walk_ptrs_ty, is_integer_literal, implements_trait};
14+
use utils::{span_lint_and_then, walk_ptrs_ty, is_integer_literal, implements_trait};
1515

1616
/// **What it does:** This lint checks for function arguments and let bindings denoted as `ref`.
1717
///
@@ -62,16 +62,22 @@ impl LateLintPass for TopLevelRefPass {
6262
let Some(ref init) = l.init
6363
], {
6464
let tyopt = if let Some(ref ty) = l.ty {
65-
format!(": {:?} ", ty)
65+
format!(": {}", snippet(cx, ty.span, "_"))
6666
} else {
6767
"".to_owned()
6868
};
69-
span_help_and_lint(cx,
69+
span_lint_and_then(cx,
7070
TOPLEVEL_REF_ARG,
7171
l.pat.span,
7272
"`ref` on an entire `let` pattern is discouraged, take a reference with & instead",
73-
&format!("try `let {} {}= &{};`", snippet(cx, i.span, "_"),
74-
tyopt, snippet(cx, init.span, "_"))
73+
|db| {
74+
db.span_suggestion(s.span,
75+
"try",
76+
format!("let {}{} = &{};",
77+
snippet(cx, i.span, "_"),
78+
tyopt,
79+
snippet(cx, init.span, "_")));
80+
}
7581
);
7682
}
7783
};

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/for_loop.rs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ fn for_loop_over_option_and_result() {
6565
break;
6666
}
6767

68-
// while let false positive for Option
68+
// while let false positive for Result
6969
while let Ok(x) = result {
7070
println!("{}", x);
7171
break;
@@ -85,8 +85,10 @@ impl Unrelated {
8585

8686
#[deny(needless_range_loop, explicit_iter_loop, iter_next_loop, reverse_range_loop, explicit_counter_loop)]
8787
#[deny(unused_collect)]
88-
#[allow(linkedlist,shadow_unrelated,unnecessary_mut_passed, cyclomatic_complexity)]
88+
#[allow(linkedlist, shadow_unrelated, unnecessary_mut_passed, cyclomatic_complexity)]
8989
fn main() {
90+
const MAX_LEN: usize = 42;
91+
9092
let mut vec = vec![1, 2, 3, 4];
9193
let vec2 = vec![1, 2, 3, 4];
9294
for i in 0..vec.len() {
@@ -111,6 +113,11 @@ fn main() {
111113
println!("{}", vec[i]);
112114
}
113115

116+
for i in 0..MAX_LEN {
117+
//~^ ERROR `i` is only used to index `vec`. Consider using `for item in vec.iter().take(MAX_LEN)`
118+
println!("{}", vec[i]);
119+
}
120+
114121
for i in 5..10 {
115122
//~^ ERROR `i` is only used to index `vec`. Consider using `for item in vec.iter().take(10).skip(5)`
116123
println!("{}", vec[i]);
@@ -126,7 +133,16 @@ fn main() {
126133
println!("{} {}", vec[i], i);
127134
}
128135

129-
for i in 10..0 { //~ERROR this range is empty so this for loop will never run
136+
for i in 10..0 {
137+
//~^ERROR this range is empty so this for loop will never run
138+
//~|HELP consider
139+
//~|SUGGESTION (0..10).rev()
140+
println!("{}", i);
141+
}
142+
143+
for i in MAX_LEN..0 { //~ERROR this range is empty so this for loop will never run
144+
//~|HELP consider
145+
//~|SUGGESTION (0..MAX_LEN).rev()
130146
println!("{}", i);
131147
}
132148

0 commit comments

Comments
 (0)