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`
```
This commit is contained in:
Eduard-Mihai Burtescu 2016-11-12 10:38:40 +02:00 committed by GitHub
commit b619dcdaeb
6 changed files with 1163 additions and 2 deletions

View file

@ -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");
}

View file

@ -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