diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index 4773aab27da..67c72bd9859 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -7,7 +7,7 @@ use syntax::ptr; use syntax::symbol::InternedString; use syntax_pos::Span; use utils::{is_expn_of, match_def_path, match_path, resolve_node, span_lint, span_lint_and_sugg}; -use utils::{opt_def_id, paths}; +use utils::{opt_def_id, paths, last_path_segment}; /// **What it does:** This lint warns when you use `println!("")` to /// print a newline. @@ -266,7 +266,6 @@ fn check_print_variants<'a, 'tcx>( }; span_lint(cx, PRINT_STDOUT, span, &format!("use of `{}!`", name)); - if_chain! { // ensure we're calling Arguments::new_v1 if args.len() == 1; @@ -339,7 +338,9 @@ where F: Fn(Span), { if_chain! { - if args.len() > 1; + if args.len() >= 2; + + // the match statement if let ExprAddrOf(_, ref match_expr) = args[1].node; if let ExprMatch(ref matchee, ref arms, _) = match_expr.node; if let ExprTup(ref tup) = matchee.node; @@ -355,15 +356,31 @@ where if let ExprLit(_) = tup_val.node; // next, check the corresponding match arm body to ensure - // this is "{}", or DISPLAY_FMT_METHOD + // this is DISPLAY_FMT_METHOD if let ExprCall(_, ref body_args) = arm_body_exprs[idx].node; if body_args.len() == 2; if let ExprPath(ref body_qpath) = body_args[1].node; if let Some(fun_def_id) = opt_def_id(resolve_node(cx, body_qpath, body_args[1].hir_id)); - if match_def_path(cx.tcx, fun_def_id, &paths::DISPLAY_FMT_METHOD) || - match_def_path(cx.tcx, fun_def_id, &paths::DEBUG_FMT_METHOD); + if match_def_path(cx.tcx, fun_def_id, &paths::DISPLAY_FMT_METHOD); then { - lint_fn(tup_val.span); + if args.len() == 2 { + lint_fn(tup_val.span); + } + + // ensure the format str has no options (e.g., width, precision, alignment, etc.) + // and is just "{}" + if_chain! { + if args.len() == 3; + if let ExprAddrOf(_, ref format_expr) = args[2].node; + if let ExprArray(ref format_exprs) = format_expr.node; + if format_exprs.len() >= 1; + if let ExprStruct(_, ref fields, _) = format_exprs[idx].node; + if let Some(format_field) = fields.iter().find(|f| f.name.node == "format"); + if check_unformatted(&format_field.expr); + then { + lint_fn(tup_val.span); + } + } } } } @@ -438,3 +455,33 @@ fn is_in_debug_impl(cx: &LateContext, expr: &Expr) -> bool { } false } + +/// Checks if the expression matches +/// ```rust,ignore +/// &[_ { +/// format: _ { +/// width: _::Implied, +/// ... +/// }, +/// ..., +/// }] +/// ``` +pub fn check_unformatted(format_field: &Expr) -> bool { + if_chain! { + if let ExprStruct(_, ref fields, _) = format_field.node; + if let Some(width_field) = fields.iter().find(|f| f.name.node == "width"); + if let ExprPath(ref qpath) = width_field.expr.node; + if last_path_segment(qpath).name == "Implied"; + if let Some(align_field) = fields.iter().find(|f| f.name.node == "align"); + if let ExprPath(ref qpath) = align_field.expr.node; + if last_path_segment(qpath).name == "Unknown"; + if let Some(precision_field) = fields.iter().find(|f| f.name.node == "precision"); + if let ExprPath(ref qpath_precision) = precision_field.expr.node; + if last_path_segment(qpath_precision).name == "Implied"; + then { + return true; + } + } + + false +} diff --git a/tests/ui/print_literal.rs b/tests/ui/print_literal.rs index c803294ab0a..272e1c168d3 100644 --- a/tests/ui/print_literal.rs +++ b/tests/ui/print_literal.rs @@ -9,16 +9,23 @@ fn main() { let world = "world"; println!("Hello {}", world); println!("3 in hex is {:X}", 3); + println!("2 + 1 = {:.4}", 3); + println!("2 + 1 = {:5.4}", 3); + println!("Debug test {:?}", "hello, world"); + println!("{0:8} {1:>8}", "hello", "world"); + println!("{1:8} {0:>8}", "hello", "world"); + println!("{foo:8} {bar:>8}", foo="hello", bar="world"); + println!("{bar:8} {foo:>8}", foo="hello", bar="world"); + println!("{number:>width$}", number=1, width=6); + println!("{number:>0width$}", number=1, width=6); // these should throw warnings + println!("{} of {:b} people know binary, the other half doesn't", 1, 2); print!("Hello {}", "world"); println!("Hello {} {}", world, "world"); println!("Hello {}", "world"); println!("10 / 4 is {}", 2.5); println!("2 + 1 = {}", 3); - println!("2 + 1 = {:.4}", 3); - println!("2 + 1 = {:5.4}", 3); - println!("Debug test {:?}", "hello, world"); // positional args don't change the fact // that we're using a literal -- this should diff --git a/tests/ui/print_literal.stderr b/tests/ui/print_literal.stderr index 982be7dc537..d1e4b49cbdd 100644 --- a/tests/ui/print_literal.stderr +++ b/tests/ui/print_literal.stderr @@ -1,100 +1,88 @@ error: printing a literal with an empty format string - --> $DIR/print_literal.rs:14:24 + --> $DIR/print_literal.rs:23:71 | -14 | print!("Hello {}", "world"); - | ^^^^^^^ +23 | println!("{} of {:b} people know binary, the other half doesn't", 1, 2); + | ^ | = note: `-D print-literal` implied by `-D warnings` error: printing a literal with an empty format string - --> $DIR/print_literal.rs:15:36 + --> $DIR/print_literal.rs:24:24 | -15 | println!("Hello {} {}", world, "world"); +24 | print!("Hello {}", "world"); + | ^^^^^^^ + +error: printing a literal with an empty format string + --> $DIR/print_literal.rs:25:36 + | +25 | println!("Hello {} {}", world, "world"); | ^^^^^^^ error: printing a literal with an empty format string - --> $DIR/print_literal.rs:16:26 + --> $DIR/print_literal.rs:26:26 | -16 | println!("Hello {}", "world"); +26 | println!("Hello {}", "world"); | ^^^^^^^ error: printing a literal with an empty format string - --> $DIR/print_literal.rs:17:30 + --> $DIR/print_literal.rs:27:30 | -17 | println!("10 / 4 is {}", 2.5); +27 | println!("10 / 4 is {}", 2.5); | ^^^ error: printing a literal with an empty format string - --> $DIR/print_literal.rs:18:28 + --> $DIR/print_literal.rs:28:28 | -18 | println!("2 + 1 = {}", 3); +28 | println!("2 + 1 = {}", 3); | ^ error: printing a literal with an empty format string - --> $DIR/print_literal.rs:19:31 + --> $DIR/print_literal.rs:33:25 | -19 | println!("2 + 1 = {:.4}", 3); - | ^ - -error: printing a literal with an empty format string - --> $DIR/print_literal.rs:20:32 - | -20 | println!("2 + 1 = {:5.4}", 3); - | ^ - -error: printing a literal with an empty format string - --> $DIR/print_literal.rs:21:33 - | -21 | println!("Debug test {:?}", "hello, world"); - | ^^^^^^^^^^^^^^ - -error: printing a literal with an empty format string - --> $DIR/print_literal.rs:26:25 - | -26 | println!("{0} {1}", "hello", "world"); +33 | println!("{0} {1}", "hello", "world"); | ^^^^^^^ error: printing a literal with an empty format string - --> $DIR/print_literal.rs:26:34 + --> $DIR/print_literal.rs:33:34 | -26 | println!("{0} {1}", "hello", "world"); +33 | println!("{0} {1}", "hello", "world"); | ^^^^^^^ error: printing a literal with an empty format string - --> $DIR/print_literal.rs:27:25 + --> $DIR/print_literal.rs:34:25 | -27 | println!("{1} {0}", "hello", "world"); +34 | println!("{1} {0}", "hello", "world"); | ^^^^^^^ error: printing a literal with an empty format string - --> $DIR/print_literal.rs:27:34 + --> $DIR/print_literal.rs:34:34 | -27 | println!("{1} {0}", "hello", "world"); +34 | println!("{1} {0}", "hello", "world"); | ^^^^^^^ error: printing a literal with an empty format string - --> $DIR/print_literal.rs:30:33 + --> $DIR/print_literal.rs:37:33 | -30 | println!("{foo} {bar}", foo="hello", bar="world"); +37 | println!("{foo} {bar}", foo="hello", bar="world"); | ^^^^^^^ error: printing a literal with an empty format string - --> $DIR/print_literal.rs:30:46 + --> $DIR/print_literal.rs:37:46 | -30 | println!("{foo} {bar}", foo="hello", bar="world"); +37 | println!("{foo} {bar}", foo="hello", bar="world"); | ^^^^^^^ error: printing a literal with an empty format string - --> $DIR/print_literal.rs:31:33 + --> $DIR/print_literal.rs:38:33 | -31 | println!("{bar} {foo}", foo="hello", bar="world"); +38 | println!("{bar} {foo}", foo="hello", bar="world"); | ^^^^^^^ error: printing a literal with an empty format string - --> $DIR/print_literal.rs:31:46 + --> $DIR/print_literal.rs:38:46 | -31 | println!("{bar} {foo}", foo="hello", bar="world"); +38 | println!("{bar} {foo}", foo="hello", bar="world"); | ^^^^^^^ -error: aborting due to 16 previous errors +error: aborting due to 14 previous errors diff --git a/tests/ui/write_literal.rs b/tests/ui/write_literal.rs index dd3a869eb4e..b09640a18eb 100644 --- a/tests/ui/write_literal.rs +++ b/tests/ui/write_literal.rs @@ -6,22 +6,29 @@ use std::io::Write; fn main() { let mut v = Vec::new(); - // These should be fine + // these should be fine write!(&mut v, "Hello"); writeln!(&mut v, "Hello"); let world = "world"; writeln!(&mut v, "Hello {}", world); writeln!(&mut v, "3 in hex is {:X}", 3); + writeln!(&mut v, "2 + 1 = {:.4}", 3); + writeln!(&mut v, "2 + 1 = {:5.4}", 3); + writeln!(&mut v, "Debug test {:?}", "hello, world"); + writeln!(&mut v, "{0:8} {1:>8}", "hello", "world"); + writeln!(&mut v, "{1:8} {0:>8}", "hello", "world"); + writeln!(&mut v, "{foo:8} {bar:>8}", foo="hello", bar="world"); + writeln!(&mut v, "{bar:8} {foo:>8}", foo="hello", bar="world"); + writeln!(&mut v, "{number:>width$}", number=1, width=6); + writeln!(&mut v, "{number:>0width$}", number=1, width=6); - // These should throw warnings + // these should throw warnings + writeln!(&mut v, "{} of {:b} people know binary, the other half doesn't", 1, 2); write!(&mut v, "Hello {}", "world"); writeln!(&mut v, "Hello {} {}", world, "world"); writeln!(&mut v, "Hello {}", "world"); writeln!(&mut v, "10 / 4 is {}", 2.5); writeln!(&mut v, "2 + 1 = {}", 3); - writeln!(&mut v, "2 + 1 = {:.4}", 3); - writeln!(&mut v, "2 + 1 = {:5.4}", 3); - writeln!(&mut v, "Debug test {:?}", "hello, world"); // positional args don't change the fact // that we're using a literal -- this should @@ -30,6 +37,6 @@ fn main() { writeln!(&mut v, "{1} {0}", "hello", "world"); // named args shouldn't change anything either - writeln!(&mut v, "{foo} {bar}", foo = "hello", bar = "world"); - writeln!(&mut v, "{bar} {foo}", foo = "hello", bar = "world"); + writeln!(&mut v, "{foo} {bar}", foo="hello", bar="world"); + writeln!(&mut v, "{bar} {foo}", foo="hello", bar="world"); } diff --git a/tests/ui/write_literal.stderr b/tests/ui/write_literal.stderr index 9c068f1332d..323a83e244a 100644 --- a/tests/ui/write_literal.stderr +++ b/tests/ui/write_literal.stderr @@ -1,100 +1,88 @@ error: writing a literal with an empty format string - --> $DIR/write_literal.rs:17:32 + --> $DIR/write_literal.rs:26:79 | -17 | write!(&mut v, "Hello {}", "world"); - | ^^^^^^^ +26 | writeln!(&mut v, "{} of {:b} people know binary, the other half doesn't", 1, 2); + | ^ | = note: `-D write-literal` implied by `-D warnings` error: writing a literal with an empty format string - --> $DIR/write_literal.rs:18:44 + --> $DIR/write_literal.rs:27:32 | -18 | writeln!(&mut v, "Hello {} {}", world, "world"); +27 | write!(&mut v, "Hello {}", "world"); + | ^^^^^^^ + +error: writing a literal with an empty format string + --> $DIR/write_literal.rs:28:44 + | +28 | writeln!(&mut v, "Hello {} {}", world, "world"); | ^^^^^^^ error: writing a literal with an empty format string - --> $DIR/write_literal.rs:19:34 + --> $DIR/write_literal.rs:29:34 | -19 | writeln!(&mut v, "Hello {}", "world"); +29 | writeln!(&mut v, "Hello {}", "world"); | ^^^^^^^ error: writing a literal with an empty format string - --> $DIR/write_literal.rs:20:38 + --> $DIR/write_literal.rs:30:38 | -20 | writeln!(&mut v, "10 / 4 is {}", 2.5); +30 | writeln!(&mut v, "10 / 4 is {}", 2.5); | ^^^ error: writing a literal with an empty format string - --> $DIR/write_literal.rs:21:36 + --> $DIR/write_literal.rs:31:36 | -21 | writeln!(&mut v, "2 + 1 = {}", 3); +31 | writeln!(&mut v, "2 + 1 = {}", 3); | ^ error: writing a literal with an empty format string - --> $DIR/write_literal.rs:22:39 + --> $DIR/write_literal.rs:36:33 | -22 | writeln!(&mut v, "2 + 1 = {:.4}", 3); - | ^ - -error: writing a literal with an empty format string - --> $DIR/write_literal.rs:23:40 - | -23 | writeln!(&mut v, "2 + 1 = {:5.4}", 3); - | ^ - -error: writing a literal with an empty format string - --> $DIR/write_literal.rs:24:41 - | -24 | writeln!(&mut v, "Debug test {:?}", "hello, world"); - | ^^^^^^^^^^^^^^ - -error: writing a literal with an empty format string - --> $DIR/write_literal.rs:29:33 - | -29 | writeln!(&mut v, "{0} {1}", "hello", "world"); +36 | writeln!(&mut v, "{0} {1}", "hello", "world"); | ^^^^^^^ error: writing a literal with an empty format string - --> $DIR/write_literal.rs:29:42 + --> $DIR/write_literal.rs:36:42 | -29 | writeln!(&mut v, "{0} {1}", "hello", "world"); +36 | writeln!(&mut v, "{0} {1}", "hello", "world"); | ^^^^^^^ error: writing a literal with an empty format string - --> $DIR/write_literal.rs:30:33 + --> $DIR/write_literal.rs:37:33 | -30 | writeln!(&mut v, "{1} {0}", "hello", "world"); +37 | writeln!(&mut v, "{1} {0}", "hello", "world"); | ^^^^^^^ error: writing a literal with an empty format string - --> $DIR/write_literal.rs:30:42 + --> $DIR/write_literal.rs:37:42 | -30 | writeln!(&mut v, "{1} {0}", "hello", "world"); +37 | writeln!(&mut v, "{1} {0}", "hello", "world"); | ^^^^^^^ error: writing a literal with an empty format string - --> $DIR/write_literal.rs:33:43 + --> $DIR/write_literal.rs:40:41 | -33 | writeln!(&mut v, "{foo} {bar}", foo = "hello", bar = "world"); - | ^^^^^^^ +40 | writeln!(&mut v, "{foo} {bar}", foo="hello", bar="world"); + | ^^^^^^^ error: writing a literal with an empty format string - --> $DIR/write_literal.rs:33:58 + --> $DIR/write_literal.rs:40:54 | -33 | writeln!(&mut v, "{foo} {bar}", foo = "hello", bar = "world"); - | ^^^^^^^ +40 | writeln!(&mut v, "{foo} {bar}", foo="hello", bar="world"); + | ^^^^^^^ error: writing a literal with an empty format string - --> $DIR/write_literal.rs:34:43 + --> $DIR/write_literal.rs:41:41 | -34 | writeln!(&mut v, "{bar} {foo}", foo = "hello", bar = "world"); - | ^^^^^^^ +41 | writeln!(&mut v, "{bar} {foo}", foo="hello", bar="world"); + | ^^^^^^^ error: writing a literal with an empty format string - --> $DIR/write_literal.rs:34:58 + --> $DIR/write_literal.rs:41:54 | -34 | writeln!(&mut v, "{bar} {foo}", foo = "hello", bar = "world"); - | ^^^^^^^ +41 | writeln!(&mut v, "{bar} {foo}", foo="hello", bar="world"); + | ^^^^^^^ -error: aborting due to 16 previous errors +error: aborting due to 14 previous errors