From faf70d9d4765508e271adebbcd045d927b3e1c0e Mon Sep 17 00:00:00 2001 From: Macdonald Umoren Date: Sun, 6 Sep 2020 20:28:10 -0400 Subject: [PATCH] Add format specifier suggestion When format specifier is missing from the format string: - Found format specifiers are pointed at - Suggest format specifier if none is present Fix #68293 --- compiler/rustc_builtin_macros/src/format.rs | 53 ++++++++++++++++--- ...rmat-args-capture-missing-variables.stderr | 4 +- src/test/ui/if/ifmt-bad-arg.stderr | 32 ++++++----- src/test/ui/macros/format-foreign.stderr | 4 +- .../ui/macros/format-unused-lables.stderr | 6 +++ .../issue-68293-missing-format-specifier.rs | 38 +++++++++++++ ...ssue-68293-missing-format-specifier.stderr | 41 ++++++++++++++ 7 files changed, 154 insertions(+), 24 deletions(-) create mode 100644 src/test/ui/suggestions/issue-68293-missing-format-specifier.rs create mode 100644 src/test/ui/suggestions/issue-68293-missing-format-specifier.stderr diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index 5d6f791f13719..856ed2237e24d 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -1090,6 +1090,7 @@ pub fn expand_preparsed_format_args( if !errs.is_empty() { let args_used = cx.arg_types.len() - errs_len; let args_unused = errs_len; + let found_fmt_specs = &cx.arg_spans; let mut diag = { if let [(sp, msg)] = &errs[..] { @@ -1097,13 +1098,13 @@ pub fn expand_preparsed_format_args( diag.span_label(*sp, *msg); diag } else { - let mut diag = cx.ecx.struct_span_err( - errs.iter().map(|&(sp, _)| sp).collect::>(), - "multiple unused formatting arguments", - ); + let mut diag = cx + .ecx + .struct_span_err(errs.iter().map(|&(sp, _)| sp).collect::>(), + "multiple unused formatting arguments"); diag.span_label(cx.fmtsp, "multiple missing formatting specifiers"); - for (sp, msg) in errs { - diag.span_label(sp, msg); + for (sp, msg) in &errs { + diag.span_label(*sp, *msg); } diag } @@ -1178,6 +1179,46 @@ pub fn expand_preparsed_format_args( if !found_foreign && errs_len == 1 { diag.span_label(cx.fmtsp, "formatting specifier missing"); } + if !found_foreign && found_fmt_specs.is_empty() { + diag.note("format specifiers use curly braces: `{}`"); + } + + if !found_foreign && !found_fmt_specs.is_empty() { + let mut note_span: MultiSpan = cx.arg_spans.clone().into(); + let used_args = cx + .arg_types + .iter() + .enumerate() + .filter(|(_i, ty)| !ty.is_empty()) + .map(|(i, _)| { + (cx.args[i].span, i) + }) + .collect::>(); + + let names_reversed: FxHashMap = cx.names.iter().map(|(symbol, pos)| { + (*pos, *symbol) + }).collect(); + + for (span, index) in &used_args { + let mut fmt_arg_type = "positional"; + let mut fmt_arg_ident = (*index).to_string(); + + if let Some(&symbol) = names_reversed.get(index) { + fmt_arg_type = "named"; + fmt_arg_ident = symbol.to_ident_string(); + } + + note_span.push_span_label(*span, format!("used {} formatting argument `{}`", fmt_arg_type, fmt_arg_ident)); + + // for each reference to a formatting argument(identified by index), get the span of its formatting specifier + // for &ref in &cx.arg_index_map[*index] { + // let sp = &cx.arg_spans[ref]; + // note_span.push_span_label(*sp, format!("{} argument `{}` used here: ",fmt_arg_type, fmt_arg_ident)); + // } + } + + diag.span_note(note_span, "the following existing formatting specifiers are used"); + } diag.emit(); } diff --git a/src/test/ui/fmt/format-args-capture-missing-variables.stderr b/src/test/ui/fmt/format-args-capture-missing-variables.stderr index c3d740eef9d3c..1aff160a00231 100644 --- a/src/test/ui/fmt/format-args-capture-missing-variables.stderr +++ b/src/test/ui/fmt/format-args-capture-missing-variables.stderr @@ -1,8 +1,8 @@ error: named argument never used - --> $DIR/format-args-capture-missing-variables.rs:10:51 + --> $DIR/format-args-capture-missing-variables.rs:10:14 | LL | format!("{valuea} {valueb}", valuea=5, valuec=7); - | ------------------- ^ named argument never used + | -^^^^^^^^-^^^^^^^^- ^ named argument never used | | | formatting specifier missing diff --git a/src/test/ui/if/ifmt-bad-arg.stderr b/src/test/ui/if/ifmt-bad-arg.stderr index 0ff478826f728..afed21badcb81 100644 --- a/src/test/ui/if/ifmt-bad-arg.stderr +++ b/src/test/ui/if/ifmt-bad-arg.stderr @@ -13,10 +13,10 @@ LL | format!("{1}", 1); = note: positional arguments are zero-based error: argument never used - --> $DIR/ifmt-bad-arg.rs:9:20 + --> $DIR/ifmt-bad-arg.rs:9:14 | LL | format!("{1}", 1); - | ----- ^ argument never used + | -^^^- ^ argument never used | | | formatting specifier missing @@ -90,36 +90,38 @@ LL | format!("", 1, 2); | | | | | argument never used | multiple missing formatting specifiers + | + = note: format specifiers use curly braces: `{}` error: argument never used - --> $DIR/ifmt-bad-arg.rs:33:22 + --> $DIR/ifmt-bad-arg.rs:33:14 | LL | format!("{}", 1, 2); - | ---- ^ argument never used + | -^^- ^ argument never used | | | formatting specifier missing error: argument never used - --> $DIR/ifmt-bad-arg.rs:34:20 + --> $DIR/ifmt-bad-arg.rs:34:14 | LL | format!("{1}", 1, 2); - | ----- ^ argument never used + | -^^^- ^ argument never used | | | formatting specifier missing error: named argument never used - --> $DIR/ifmt-bad-arg.rs:35:26 + --> $DIR/ifmt-bad-arg.rs:35:14 | LL | format!("{}", 1, foo=2); - | ---- ^ named argument never used + | -^^- ^ named argument never used | | | formatting specifier missing error: argument never used - --> $DIR/ifmt-bad-arg.rs:36:22 + --> $DIR/ifmt-bad-arg.rs:36:14 | LL | format!("{foo}", 1, foo=2); - | ------- ^ argument never used + | -^^^^^- ^ argument never used | | | formatting specifier missing @@ -130,12 +132,14 @@ LL | format!("", foo=2); | -- ^ named argument never used | | | formatting specifier missing + | + = note: format specifiers use curly braces: `{}` error: multiple unused formatting arguments - --> $DIR/ifmt-bad-arg.rs:38:32 + --> $DIR/ifmt-bad-arg.rs:38:14 | LL | format!("{} {}", 1, 2, foo=1, bar=2); - | ------- ^ ^ named argument never used + | -^^-^^- ^ ^ named argument never used | | | | | named argument never used | multiple missing formatting specifiers @@ -165,10 +169,10 @@ LL | format!("{valuea} {valueb}", valuea=5, valuec=7); = help: if you intended to capture `valueb` from the surrounding scope, add `#![feature(format_args_capture)]` to the crate attributes error: named argument never used - --> $DIR/ifmt-bad-arg.rs:45:51 + --> $DIR/ifmt-bad-arg.rs:45:14 | LL | format!("{valuea} {valueb}", valuea=5, valuec=7); - | ------------------- ^ named argument never used + | -^^^^^^^^-^^^^^^^^- ^ named argument never used | | | formatting specifier missing diff --git a/src/test/ui/macros/format-foreign.stderr b/src/test/ui/macros/format-foreign.stderr index e2f2f14dce319..dc979de64369e 100644 --- a/src/test/ui/macros/format-foreign.stderr +++ b/src/test/ui/macros/format-foreign.stderr @@ -45,10 +45,10 @@ LL | {}!\n | error: argument never used - --> $DIR/format-foreign.rs:12:30 + --> $DIR/format-foreign.rs:12:15 | LL | println!("{} %f", "one", 2.0); - | ------- ^^^ argument never used + | -^^---- ^^^ argument never used | | | formatting specifier missing diff --git a/src/test/ui/macros/format-unused-lables.stderr b/src/test/ui/macros/format-unused-lables.stderr index 7423c7b7c8b47..7a4ea0a190a21 100644 --- a/src/test/ui/macros/format-unused-lables.stderr +++ b/src/test/ui/macros/format-unused-lables.stderr @@ -7,6 +7,8 @@ LL | println!("Test", 123, 456, 789); | | | argument never used | | argument never used | multiple missing formatting specifiers + | + = note: format specifiers use curly braces: `{}` error: multiple unused formatting arguments --> $DIR/format-unused-lables.rs:6:9 @@ -19,6 +21,8 @@ LL | 456, | ^^^ argument never used LL | 789 | ^^^ argument never used + | + = note: format specifiers use curly braces: `{}` error: named argument never used --> $DIR/format-unused-lables.rs:11:35 @@ -27,6 +31,8 @@ LL | println!("Some stuff", UNUSED="args"); | ------------ ^^^^^^ named argument never used | | | formatting specifier missing + | + = note: format specifiers use curly braces: `{}` error: multiple unused formatting arguments --> $DIR/format-unused-lables.rs:14:9 diff --git a/src/test/ui/suggestions/issue-68293-missing-format-specifier.rs b/src/test/ui/suggestions/issue-68293-missing-format-specifier.rs new file mode 100644 index 0000000000000..b73671edd5f2b --- /dev/null +++ b/src/test/ui/suggestions/issue-68293-missing-format-specifier.rs @@ -0,0 +1,38 @@ +// Issue 68293: This tests that the following changes work: +// the suggestion "format specifiers use curly braces: `{}`" is made +// found format specifiers are pointed at + +fn no_format_specifiers_one_unused_argument() { + println!("list: ", 1); + //~^ ERROR argument never used + //~| NOTE formatting specifier missing + //~| NOTE format specifiers use curly braces: `{}` + //~| NOTE argument never used +} + +fn no_format_specifiers_multiple_unused_arguments() { + println!("list: ", 3, 4, 5); + //~^ ERROR multiple unused formatting arguments + //~| NOTE multiple missing formatting specifiers + //~| NOTE format specifiers use curly braces: `{}` + //~| NOTE argument never used + //~| NOTE argument never used + //~| NOTE argument never used +} + +fn missing_format_specifiers_one_unused_argument() { + println!("list: a{}, b{}", 1, 2, 3); + //~^ ERROR argument never used + //~| NOTE formatting specifier missing + //~| NOTE argument never used +} + +fn missing_format_specifiers_multiple_unused_arguments() { + println!("list: a{}, b{} c{}", 1, 2, 3, 4, 5); + //~^ ERROR multiple unused formatting arguments + //~| NOTE multiple missing formatting specifiers + //~| NOTE argument never used + //~| NOTE argument never used +} + +fn main() {} diff --git a/src/test/ui/suggestions/issue-68293-missing-format-specifier.stderr b/src/test/ui/suggestions/issue-68293-missing-format-specifier.stderr new file mode 100644 index 0000000000000..5281b71f36d32 --- /dev/null +++ b/src/test/ui/suggestions/issue-68293-missing-format-specifier.stderr @@ -0,0 +1,41 @@ +error: argument never used + --> $DIR/issue-68293-missing-format-specifier.rs:6:22 + | +LL | println!("list: ", 1); + | -------- ^ argument never used + | | + | formatting specifier missing + | + = note: format specifiers use curly braces: `{}` + +error: multiple unused formatting arguments + --> $DIR/issue-68293-missing-format-specifier.rs:14:22 + | +LL | println!("list: ", 3, 4, 5); + | -------- ^ ^ ^ argument never used + | | | | + | | | argument never used + | | argument never used + | multiple missing formatting specifiers + | + = note: format specifiers use curly braces: `{}` + +error: argument never used + --> $DIR/issue-68293-missing-format-specifier.rs:24:20 + | +LL | println!("list: a{}, b{}", 1, 2, 3); + | --------^^---^^- ^ argument never used + | | + | formatting specifier missing + +error: multiple unused formatting arguments + --> $DIR/issue-68293-missing-format-specifier.rs:31:20 + | +LL | println!("list: a{}, b{} c{}", 1, 2, 3, 4, 5); + | --------^^---^^--^^- ^ ^ argument never used + | | | + | | argument never used + | multiple missing formatting specifiers + +error: aborting due to 4 previous errors +