about summary refs log tree commit diff
path: root/src/test
diff options
context:
space:
mode:
authorEduard-Mihai Burtescu <edy.burt@gmail.com>2016-11-12 10:38:40 +0200
committerGitHub <noreply@github.com>2016-11-12 10:38:40 +0200
commitb619dcdaeb223bd4df6896e808f53ffa295c3ed0 (patch)
tree86c9e9e4c0b2e7fe06c1a90b6b1d442b6409baba /src/test
parent25f1deee520a752dbc87fe04d639d50411b5c475 (diff)
parent455723c638b0e9db34d427a82bc13f95b75a304d (diff)
downloadrust-b619dcdaeb223bd4df6896e808f53ffa295c3ed0.tar.gz
rust-b619dcdaeb223bd4df6896e808f53ffa295c3ed0.zip
Rollup merge of #37613 - DanielKeep:eww-you-got-printf-in-your-format, r=alexcrichton
Add foreign formatting directive detection.

This teaches `format_args!` how to interpret format printf- and
shell-style format directives.  This is used in cases where there are
unused formatting arguments, and the reason for that *might* be because
the programmer is trying to use the wrong kind of formatting string.

This was prompted by an issue encountered by simulacrum on the #rust IRC
channel.  In short: although `println!` told them that they weren't using
all of the conversion arguments, the problem was in using printf-syle
directives rather than ones `println!` would undertand.

Where possible, `format_args!` will tell the programmer what they should
use instead.  For example, it will suggest replacing `%05d` with `{:0>5}`,
or `%2$.*3$s` with `{1:.3$}`.  Even if it cannot suggest a replacement,
it will explicitly note that Rust does not support that style of directive,
and direct the user to the `std::fmt` documentation.

-----

**Example**: given:

```rust
fn main() {
    println!("%.*3$s %s!\n", "Hello,", "World", 4);
    println!("%1$*2$.*3$f", 123.456);
}
```

The compiler outputs the following:

```text
error: multiple unused formatting arguments
 --> local/fmt.rs:2:5
  |
2 |     println!("%.*3$s %s!\n", "Hello,", "World", 4);
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |
note: argument never used
 --> local/fmt.rs:2:30
  |
2 |     println!("%.*3$s %s!\n", "Hello,", "World", 4);
  |                              ^^^^^^^^
note: argument never used
 --> local/fmt.rs:2:40
  |
2 |     println!("%.*3$s %s!\n", "Hello,", "World", 4);
  |                                        ^^^^^^^
note: argument never used
 --> local/fmt.rs:2:49
  |
2 |     println!("%.*3$s %s!\n", "Hello,", "World", 4);
  |                                                 ^
  = help: `%.*3$s` should be written as `{:.2$}`
  = help: `%s` should be written as `{}`
  = note: printf formatting not supported; see the documentation for `std::fmt`
  = note: this error originates in a macro outside of the current crate

error: argument never used
 --> local/fmt.rs:6:29
  |
6 |     println!("%1$*2$.*3$f", 123.456);
  |                             ^^^^^^^
  |
  = help: `%1$*2$.*3$f` should be written as `{0:1$.2$}`
  = note: printf formatting not supported; see the documentation for `std::fmt`
```
Diffstat (limited to 'src/test')
-rw-r--r--src/test/compile-fail/ifmt-bad-arg.rs3
-rw-r--r--src/test/ui/macros/format-foreign.rs20
-rw-r--r--src/test/ui/macros/format-foreign.stderr52
3 files changed, 75 insertions, 0 deletions
diff --git a/src/test/compile-fail/ifmt-bad-arg.rs b/src/test/compile-fail/ifmt-bad-arg.rs
index 59c61a42e07..a23b4b07741 100644
--- a/src/test/compile-fail/ifmt-bad-arg.rs
+++ b/src/test/compile-fail/ifmt-bad-arg.rs
@@ -17,6 +17,7 @@ fn main() {
                             //~^ ERROR: argument never used
     format!("{foo}");         //~ ERROR: no argument named `foo`
 
+    format!("", 1, 2);                 //~ ERROR: multiple unused formatting arguments
     format!("{}", 1, 2);               //~ ERROR: argument never used
     format!("{1}", 1, 2);              //~ ERROR: argument never used
     format!("{}", 1, foo=2);           //~ ERROR: named argument never used
@@ -53,4 +54,6 @@ fn main() {
 
     format!("foo } bar"); //~ ERROR: unmatched `}` found
     format!("foo }"); //~ ERROR: unmatched `}` found
+
+    format!("foo %s baz", "bar"); //~ ERROR: argument never used
 }
diff --git a/src/test/ui/macros/format-foreign.rs b/src/test/ui/macros/format-foreign.rs
new file mode 100644
index 00000000000..cca45ca9ecd
--- /dev/null
+++ b/src/test/ui/macros/format-foreign.rs
@@ -0,0 +1,20 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+fn main() {
+    println!("%.*3$s %s!\n", "Hello,", "World", 4);
+    println!("%1$*2$.*3$f", 123.456);
+
+    // This should *not* produce hints, on the basis that there's equally as
+    // many "correct" format specifiers.  It's *probably* just an actual typo.
+    println!("{} %f", "one", 2.0);
+
+    println!("Hi there, $NAME.", NAME="Tim");
+}
diff --git a/src/test/ui/macros/format-foreign.stderr b/src/test/ui/macros/format-foreign.stderr
new file mode 100644
index 00000000000..0283052a89f
--- /dev/null
+++ b/src/test/ui/macros/format-foreign.stderr
@@ -0,0 +1,52 @@
+error: multiple unused formatting arguments
+  --> $DIR/format-foreign.rs:12:5
+   |
+12 |     println!("%.*3$s %s!/n", "Hello,", "World", 4);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: argument never used
+  --> $DIR/format-foreign.rs:12:30
+   |
+12 |     println!("%.*3$s %s!/n", "Hello,", "World", 4);
+   |                              ^^^^^^^^
+note: argument never used
+  --> $DIR/format-foreign.rs:12:40
+   |
+12 |     println!("%.*3$s %s!/n", "Hello,", "World", 4);
+   |                                        ^^^^^^^
+note: argument never used
+  --> $DIR/format-foreign.rs:12:49
+   |
+12 |     println!("%.*3$s %s!/n", "Hello,", "World", 4);
+   |                                                 ^
+   = help: `%.*3$s` should be written as `{:.2$}`
+   = help: `%s` should be written as `{}`
+   = note: printf formatting not supported; see the documentation for `std::fmt`
+   = note: this error originates in a macro outside of the current crate
+
+error: argument never used
+  --> $DIR/format-foreign.rs:13:29
+   |
+13 |     println!("%1$*2$.*3$f", 123.456);
+   |                             ^^^^^^^
+   |
+   = help: `%1$*2$.*3$f` should be written as `{0:1$.2$}`
+   = note: printf formatting not supported; see the documentation for `std::fmt`
+
+error: argument never used
+  --> $DIR/format-foreign.rs:17:30
+   |
+17 |     println!("{} %f", "one", 2.0);
+   |                              ^^^
+
+error: named argument never used
+  --> $DIR/format-foreign.rs:19:39
+   |
+19 |     println!("Hi there, $NAME.", NAME="Tim");
+   |                                       ^^^^^
+   |
+   = help: `$NAME` should be written as `{NAME}`
+   = note: shell formatting not supported; see the documentation for `std::fmt`
+
+error: aborting due to 4 previous errors
+