diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 55f684599e7cc..95d1a7df69867 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -245,6 +245,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { None } + fn suggest_missing_writer( + &self, + rcvr_ty: Ty<'tcx>, + args: (&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>]), + ) -> DiagnosticBuilder<'_, ErrorGuaranteed> { + let (ty_str, _ty_file) = self.tcx.short_ty_string(rcvr_ty); + let mut err = + struct_span_err!(self.tcx.sess, args.0.span, E0599, "cannot write into `{}`", ty_str); + err.span_note( + args.0.span, + "must implement `io::Write`, `fmt::Write`, or have a `write_fmt` method", + ); + if let ExprKind::Lit(_) = args.0.kind { + err.span_help( + args.0.span.shrink_to_lo(), + "a writer is needed before this format string", + ); + }; + + err + } + pub fn report_no_match_method_error( &self, mut span: Span, @@ -323,16 +345,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - let mut err = struct_span_err!( - tcx.sess, - span, - E0599, - "no {} named `{}` found for {} `{}` in the current scope", - item_kind, - item_name, - rcvr_ty.prefix_string(self.tcx), - ty_str_reported, - ); + let is_write = sugg_span.ctxt().outer_expn_data().macro_def_id.map_or(false, |def_id| { + tcx.is_diagnostic_item(sym::write_macro, def_id) + || tcx.is_diagnostic_item(sym::writeln_macro, def_id) + }) && item_name.name == Symbol::intern("write_fmt"); + let mut err = if is_write + && let Some(args) = args + { + self.suggest_missing_writer(rcvr_ty, args) + } else { + struct_span_err!( + tcx.sess, + span, + E0599, + "no {} named `{}` found for {} `{}` in the current scope", + item_kind, + item_name, + rcvr_ty.prefix_string(self.tcx), + ty_str_reported, + ) + }; if tcx.sess.source_map().is_multiline(sugg_span) { err.span_label(sugg_span.with_hi(span.lo()), ""); } diff --git a/tests/ui/macros/missing-writer.rs b/tests/ui/macros/missing-writer.rs new file mode 100644 index 0000000000000..7df965c3684e2 --- /dev/null +++ b/tests/ui/macros/missing-writer.rs @@ -0,0 +1,17 @@ +// Check error for missing writer in writeln! and write! macro +fn main() { + let x = 1; + let y = 2; + write!("{}_{}", x, y); + //~^ ERROR format argument must be a string literal + //~| HELP you might be missing a string literal to format with + //~| ERROR cannot write into `&'static str` + //~| NOTE must implement `io::Write`, `fmt::Write`, or have a `write_fmt` method + //~| HELP a writer is needed before this format string + writeln!("{}_{}", x, y); + //~^ ERROR format argument must be a string literal + //~| HELP you might be missing a string literal to format with + //~| ERROR cannot write into `&'static str` + //~| NOTE must implement `io::Write`, `fmt::Write`, or have a `write_fmt` method + //~| HELP a writer is needed before this format string +} diff --git a/tests/ui/macros/missing-writer.stderr b/tests/ui/macros/missing-writer.stderr new file mode 100644 index 0000000000000..86dfe7d65ea62 --- /dev/null +++ b/tests/ui/macros/missing-writer.stderr @@ -0,0 +1,59 @@ +error: format argument must be a string literal + --> $DIR/missing-writer.rs:5:21 + | +LL | write!("{}_{}", x, y); + | ^ + | +help: you might be missing a string literal to format with + | +LL | write!("{}_{}", "{} {}", x, y); + | ++++++++ + +error: format argument must be a string literal + --> $DIR/missing-writer.rs:11:23 + | +LL | writeln!("{}_{}", x, y); + | ^ + | +help: you might be missing a string literal to format with + | +LL | writeln!("{}_{}", "{} {}", x, y); + | ++++++++ + +error[E0599]: cannot write into `&'static str` + --> $DIR/missing-writer.rs:5:12 + | +LL | write!("{}_{}", x, y); + | -------^^^^^^^------- method not found in `&str` + | +note: must implement `io::Write`, `fmt::Write`, or have a `write_fmt` method + --> $DIR/missing-writer.rs:5:12 + | +LL | write!("{}_{}", x, y); + | ^^^^^^^ +help: a writer is needed before this format string + --> $DIR/missing-writer.rs:5:12 + | +LL | write!("{}_{}", x, y); + | ^ + +error[E0599]: cannot write into `&'static str` + --> $DIR/missing-writer.rs:11:14 + | +LL | writeln!("{}_{}", x, y); + | ---------^^^^^^^------- method not found in `&str` + | +note: must implement `io::Write`, `fmt::Write`, or have a `write_fmt` method + --> $DIR/missing-writer.rs:11:14 + | +LL | writeln!("{}_{}", x, y); + | ^^^^^^^ +help: a writer is needed before this format string + --> $DIR/missing-writer.rs:11:14 + | +LL | writeln!("{}_{}", x, y); + | ^ + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0599`. diff --git a/tests/ui/suggestions/mut-borrow-needed-by-trait.stderr b/tests/ui/suggestions/mut-borrow-needed-by-trait.stderr index 6910b77d9bc3f..94710f4503f56 100644 --- a/tests/ui/suggestions/mut-borrow-needed-by-trait.stderr +++ b/tests/ui/suggestions/mut-borrow-needed-by-trait.stderr @@ -21,18 +21,22 @@ note: required by a bound in `BufWriter` --> $SRC_DIR/std/src/io/buffered/bufwriter.rs:LL:COL error[E0599]: the method `write_fmt` exists for struct `BufWriter<&dyn Write>`, but its trait bounds were not satisfied - --> $DIR/mut-borrow-needed-by-trait.rs:21:5 + --> $DIR/mut-borrow-needed-by-trait.rs:21:14 | LL | writeln!(fp, "hello world").unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ method cannot be called on `BufWriter<&dyn Write>` due to unsatisfied trait bounds + | ---------^^---------------- method cannot be called on `BufWriter<&dyn Write>` due to unsatisfied trait bounds --> $SRC_DIR/std/src/io/buffered/bufwriter.rs:LL:COL | = note: doesn't satisfy `BufWriter<&dyn std::io::Write>: std::io::Write` | +note: must implement `io::Write`, `fmt::Write`, or have a `write_fmt` method + --> $DIR/mut-borrow-needed-by-trait.rs:21:14 + | +LL | writeln!(fp, "hello world").unwrap(); + | ^^ = note: the following trait bounds were not satisfied: `&dyn std::io::Write: std::io::Write` which is required by `BufWriter<&dyn std::io::Write>: std::io::Write` - = note: this error originates in the macro `writeln` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 3 previous errors