about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_hir_typeck/src/method/suggest.rs52
-rw-r--r--tests/ui/macros/missing-writer.rs17
-rw-r--r--tests/ui/macros/missing-writer.stderr59
-rw-r--r--tests/ui/suggestions/mut-borrow-needed-by-trait.stderr10
4 files changed, 125 insertions, 13 deletions
diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs
index 55f684599e7..95d1a7df698 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 00000000000..7df965c3684
--- /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 00000000000..86dfe7d65ea
--- /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 6910b77d9bc..94710f4503f 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