Skip to content

Enhance error reporting for write!/writeln! macros #139371

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions compiler/rustc_hir_typeck/src/method/suggest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,30 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
return guar;
}

// Check for the write!/writeln! macro special case
let is_write_fmt = item_name.name == sym::write_fmt;
let is_write_macro =
expr_span.ctxt().outer_expn_data().macro_def_id.is_some_and(|def_id| {
self.tcx.is_diagnostic_item(sym::write_macro, def_id)
|| self.tcx.is_diagnostic_item(sym::writeln_macro, def_id)
});

if is_write_fmt && is_write_macro {
// This is a write!/writeln! macro call with write_fmt method error
let mut file = None;
let mut err = struct_span_code_err!(
self.dcx(),
span,
E0599,
"cannot write into `{}`",
self.tcx.short_string(rcvr_ty, &mut file)
);
*err.long_ty_path() = file;
err.note("type does not implement the `write_fmt` method");
err.help("try adding `use std::fmt::Write;` or `use std::io::Write;` to bring the appropriate trait into scope");
return err.emit();
}

match error {
MethodError::NoMatch(mut no_match_data) => self.report_no_match_method_error(
span,
Expand Down
23 changes: 23 additions & 0 deletions tests/ui/E0599-write-macro/io-write-fmt-method-error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Issue #139051 - Test for the case where io::Write would be more appropriate
//
// Test that when using write! on a type that doesn't implement std::io::Write trait,
// we get a clear error message suggesting to import the appropriate trait.
//
// edition:2021
// ignore-msvc
// ignore-emscripten
// run-fail
// check-pass

fn main() {
// Simple struct that doesn't implement std::io::Write
struct MyIoStruct {
value: i32,
}

let mut s = MyIoStruct { value: 42 };

// This should generate E0599 with the improved error message
// suggesting io::Write instead
write!(s, "Hello, world!"); //~ ERROR cannot write into `MyIoStruct`
}
13 changes: 13 additions & 0 deletions tests/ui/E0599-write-macro/io-write-fmt-method-error.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
error[E0599]: cannot write into `MyIoStruct`
--> $DIR/io-write-fmt-method-error.rs:22:5
|
LL | write!(s, "Hello, world!");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: type does not implement the `write_fmt` method
= help: try adding `use std::fmt::Write;` or `use std::io::Write;` to bring the appropriate trait into scope
= note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info)

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0599`.
24 changes: 24 additions & 0 deletions tests/ui/E0599-write-macro/write-fmt-method-error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Issue #139051 - Ensure we don't get confusing suggestions for E0599
// on write!/writeln! macros
//
// Test that when using write!/writeln! macros with a type that doesn't implement
// std::fmt::Write trait, we get a clear error message without irrelevant suggestions.
//
// edition:2021
// ignore-msvc
// ignore-emscripten
// run-fail
// check-pass

fn main() {
// Simple struct that doesn't implement std::fmt::Write
struct MyStruct {
value: i32,
}

let mut s = MyStruct { value: 42 };

// This should generate E0599 with the improved error message
// and not suggest irrelevant methods like write_str or push_str
write!(s, "Hello, world!"); //~ ERROR cannot write into `MyStruct`
}
13 changes: 13 additions & 0 deletions tests/ui/E0599-write-macro/write-fmt-method-error.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
error[E0599]: cannot write into `MyStruct`
--> $DIR/write-fmt-method-error.rs:23:5
|
LL | write!(s, "Hello, world!");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: type does not implement the `write_fmt` method
= help: try adding `use std::fmt::Write;` or `use std::io::Write;` to bring the appropriate trait into scope
= note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info)

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0599`.
24 changes: 24 additions & 0 deletions tests/ui/E0599-write-macro/writeln-fmt-method-error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Issue #139051 - Ensure we don't get confusing suggestions for E0599
// on write!/writeln! macros
//
// Test that when using write!/writeln! macros with a type that doesn't implement
// std::fmt::Write trait, we get a clear error message without irrelevant suggestions.
//
// edition:2021
// ignore-msvc
// ignore-emscripten
// run-fail
// check-pass

fn main() {
// Simple struct that doesn't implement std::fmt::Write
struct MyStruct {
value: i32,
}

let mut s = MyStruct { value: 42 };

// This should generate E0599 with the improved error message
// and not suggest irrelevant methods like write_str or push_str
writeln!(s, "Hello, world!"); //~ ERROR cannot write into `MyStruct`
}
13 changes: 13 additions & 0 deletions tests/ui/E0599-write-macro/writeln-fmt-method-error.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
error[E0599]: cannot write into `MyStruct`
--> $DIR/writeln-fmt-method-error.rs:23:5
|
LL | writeln!(s, "Hello, world!");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: type does not implement the `write_fmt` method
= help: try adding `use std::fmt::Write;` or `use std::io::Write;` to bring the appropriate trait into scope
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MyStruct doesn't implement either Write, so this doesn't help. The point of showing an error message that suggests importing traits is that we know the type implements the trait, but that the user cannot call its methods because the trait is not in scope. That's not the case here.

= note: this error originates in the macro `writeln` (in Nightly builds, run with -Z macro-backtrace for more info)

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0599`.
8 changes: 4 additions & 4 deletions tests/ui/macros/missing-writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ fn main() {
//~^ 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
//~| NOTE type does not implement the `write_fmt` method
//~| HELP try adding `use std::fmt::Write;` or `use std::io::Write;` to bring the appropriate trait into scope
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
//~| NOTE type does not implement the `write_fmt` method
//~| HELP try adding `use std::fmt::Write;` or `use std::io::Write;` to bring the appropriate trait into scope
}
34 changes: 10 additions & 24 deletions tests/ui/macros/missing-writer.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -21,38 +21,24 @@ LL | writeln!("{}_{}", "{} {}", x, y);
| ++++++++

error[E0599]: cannot write into `&'static str`
--> $DIR/missing-writer.rs:5:12
--> $DIR/missing-writer.rs:5:5
|
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);
| ^
= note: type does not implement the `write_fmt` method
= help: try adding `use std::fmt::Write;` or `use std::io::Write;` to bring the appropriate trait into scope
= note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info)
Comment on lines -29 to +31
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a regression, bringing the Write trait into scope doesn't fix the problem because the problem is that the user forgot to even use a writer.


error[E0599]: cannot write into `&'static str`
--> $DIR/missing-writer.rs:11:14
--> $DIR/missing-writer.rs:11:5
|
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);
| ^
= note: type does not implement the `write_fmt` method
= help: try adding `use std::fmt::Write;` or `use std::io::Write;` to bring the appropriate trait into scope
= note: this error originates in the macro `writeln` (in Nightly builds, run with -Z macro-backtrace for more info)

error: aborting due to 4 previous errors

Expand Down
2 changes: 1 addition & 1 deletion tests/ui/suggestions/mut-borrow-needed-by-trait.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,5 @@ fn main() {
//~^ ERROR the trait bound `&dyn std::io::Write: std::io::Write` is not satisfied
//~| ERROR the trait bound `&dyn std::io::Write: std::io::Write` is not satisfied

writeln!(fp, "hello world").unwrap(); //~ ERROR the method
writeln!(fp, "hello world").unwrap(); //~ ERROR cannot write into `BufWriter<&dyn std::io::Write>`
}
17 changes: 6 additions & 11 deletions tests/ui/suggestions/mut-borrow-needed-by-trait.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,15 @@ LL | let fp = BufWriter::new(fp);
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:14
error[E0599]: cannot write into `BufWriter<&dyn std::io::Write>`
--> $DIR/mut-borrow-needed-by-trait.rs:21:5
|
LL | writeln!(fp, "hello world").unwrap();
| ---------^^---------------- method cannot be called on `BufWriter<&dyn Write>` due to unsatisfied trait bounds
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
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: type does not implement the `write_fmt` method
= help: try adding `use std::fmt::Write;` or `use std::io::Write;` to bring the appropriate trait into scope
= 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

Expand Down
Loading