From c7c75470ec80c253a197b365f4571569ab53a8d9 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 30 Jan 2024 22:18:29 -0800 Subject: [PATCH 1/9] Update ui test suite to nightly-2024-01-31 --- tests/ui/source-enum-not-error.stderr | 5 +---- tests/ui/source-enum-unnamed-field-not-error.stderr | 5 +---- tests/ui/source-struct-not-error.stderr | 6 +----- tests/ui/source-struct-unnamed-field-not-error.stderr | 6 +----- tests/ui/transparent-enum-not-error.stderr | 5 +---- tests/ui/transparent-enum-unnamed-field-not-error.stderr | 5 +---- tests/ui/transparent-struct-not-error.stderr | 5 +---- tests/ui/transparent-struct-unnamed-field-not-error.stderr | 5 +---- 8 files changed, 8 insertions(+), 34 deletions(-) diff --git a/tests/ui/source-enum-not-error.stderr b/tests/ui/source-enum-not-error.stderr index 4c44742..2f676f3 100644 --- a/tests/ui/source-enum-not-error.stderr +++ b/tests/ui/source-enum-not-error.stderr @@ -2,10 +2,7 @@ error[E0599]: the method `as_dyn_error` exists for reference `&NotError`, but it --> tests/ui/source-enum-not-error.rs:9:14 | 4 | pub struct NotError; - | ------------------- - | | - | doesn't satisfy `NotError: AsDynError<'_>` - | doesn't satisfy `NotError: std::error::Error` + | ------------------- doesn't satisfy `NotError: AsDynError<'_>` or `NotError: std::error::Error` ... 9 | Broken { source: NotError }, | ^^^^^^ method cannot be called on `&NotError` due to unsatisfied trait bounds diff --git a/tests/ui/source-enum-unnamed-field-not-error.stderr b/tests/ui/source-enum-unnamed-field-not-error.stderr index da6d225..4d36567 100644 --- a/tests/ui/source-enum-unnamed-field-not-error.stderr +++ b/tests/ui/source-enum-unnamed-field-not-error.stderr @@ -2,10 +2,7 @@ error[E0599]: the method `as_dyn_error` exists for reference `&NotError`, but it --> tests/ui/source-enum-unnamed-field-not-error.rs:9:14 | 4 | pub struct NotError; - | ------------------- - | | - | doesn't satisfy `NotError: AsDynError<'_>` - | doesn't satisfy `NotError: std::error::Error` + | ------------------- doesn't satisfy `NotError: AsDynError<'_>` or `NotError: std::error::Error` ... 9 | Broken(#[source] NotError), | ^^^^^^ method cannot be called on `&NotError` due to unsatisfied trait bounds diff --git a/tests/ui/source-struct-not-error.stderr b/tests/ui/source-struct-not-error.stderr index b98460f..76941d0 100644 --- a/tests/ui/source-struct-not-error.stderr +++ b/tests/ui/source-struct-not-error.stderr @@ -2,11 +2,7 @@ error[E0599]: the method `as_dyn_error` exists for struct `NotError`, but its tr --> tests/ui/source-struct-not-error.rs:9:5 | 4 | struct NotError; - | --------------- - | | - | method `as_dyn_error` not found for this struct - | doesn't satisfy `NotError: AsDynError<'_>` - | doesn't satisfy `NotError: std::error::Error` + | --------------- method `as_dyn_error` not found for this struct because it doesn't satisfy `NotError: AsDynError<'_>` or `NotError: std::error::Error` ... 9 | source: NotError, | ^^^^^^ method cannot be called on `NotError` due to unsatisfied trait bounds diff --git a/tests/ui/source-struct-unnamed-field-not-error.stderr b/tests/ui/source-struct-unnamed-field-not-error.stderr index a23f268..788f1e5 100644 --- a/tests/ui/source-struct-unnamed-field-not-error.stderr +++ b/tests/ui/source-struct-unnamed-field-not-error.stderr @@ -2,11 +2,7 @@ error[E0599]: the method `as_dyn_error` exists for struct `NotError`, but its tr --> tests/ui/source-struct-unnamed-field-not-error.rs:8:26 | 4 | struct NotError; - | --------------- - | | - | method `as_dyn_error` not found for this struct - | doesn't satisfy `NotError: AsDynError<'_>` - | doesn't satisfy `NotError: std::error::Error` + | --------------- method `as_dyn_error` not found for this struct because it doesn't satisfy `NotError: AsDynError<'_>` or `NotError: std::error::Error` ... 8 | pub struct ErrorStruct(#[source] NotError); | ^^^^^^ method cannot be called on `NotError` due to unsatisfied trait bounds diff --git a/tests/ui/transparent-enum-not-error.stderr b/tests/ui/transparent-enum-not-error.stderr index 9be5143..bb836d4 100644 --- a/tests/ui/transparent-enum-not-error.stderr +++ b/tests/ui/transparent-enum-not-error.stderr @@ -7,10 +7,7 @@ error[E0599]: the method `as_dyn_error` exists for reference `&String`, but its ::: $RUST/alloc/src/string.rs | | pub struct String { - | ----------------- - | | - | doesn't satisfy `String: AsDynError<'_>` - | doesn't satisfy `String: std::error::Error` + | ----------------- doesn't satisfy `String: AsDynError<'_>` or `String: std::error::Error` | = note: the following trait bounds were not satisfied: `String: std::error::Error` diff --git a/tests/ui/transparent-enum-unnamed-field-not-error.stderr b/tests/ui/transparent-enum-unnamed-field-not-error.stderr index 3d23c3a..f337c59 100644 --- a/tests/ui/transparent-enum-unnamed-field-not-error.stderr +++ b/tests/ui/transparent-enum-unnamed-field-not-error.stderr @@ -7,10 +7,7 @@ error[E0599]: the method `as_dyn_error` exists for reference `&String`, but its ::: $RUST/alloc/src/string.rs | | pub struct String { - | ----------------- - | | - | doesn't satisfy `String: AsDynError<'_>` - | doesn't satisfy `String: std::error::Error` + | ----------------- doesn't satisfy `String: AsDynError<'_>` or `String: std::error::Error` | = note: the following trait bounds were not satisfied: `String: std::error::Error` diff --git a/tests/ui/transparent-struct-not-error.stderr b/tests/ui/transparent-struct-not-error.stderr index d67a694..ee50d03 100644 --- a/tests/ui/transparent-struct-not-error.stderr +++ b/tests/ui/transparent-struct-not-error.stderr @@ -7,10 +7,7 @@ error[E0599]: the method `as_dyn_error` exists for struct `String`, but its trai ::: $RUST/alloc/src/string.rs | | pub struct String { - | ----------------- - | | - | doesn't satisfy `String: AsDynError<'_>` - | doesn't satisfy `String: std::error::Error` + | ----------------- doesn't satisfy `String: AsDynError<'_>` or `String: std::error::Error` | = note: the following trait bounds were not satisfied: `String: std::error::Error` diff --git a/tests/ui/transparent-struct-unnamed-field-not-error.stderr b/tests/ui/transparent-struct-unnamed-field-not-error.stderr index f715a15..c3d6c00 100644 --- a/tests/ui/transparent-struct-unnamed-field-not-error.stderr +++ b/tests/ui/transparent-struct-unnamed-field-not-error.stderr @@ -7,10 +7,7 @@ error[E0599]: the method `as_dyn_error` exists for struct `String`, but its trai ::: $RUST/alloc/src/string.rs | | pub struct String { - | ----------------- - | | - | doesn't satisfy `String: AsDynError<'_>` - | doesn't satisfy `String: std::error::Error` + | ----------------- doesn't satisfy `String: AsDynError<'_>` or `String: std::error::Error` | = note: the following trait bounds were not satisfied: `String: std::error::Error` From 0717de3f507293f6faa7a32d987febb5c39a7048 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 7 Feb 2024 21:13:02 -0800 Subject: [PATCH 2/9] Update ui test suite to nightly-2024-02-08 --- tests/ui/no-display.stderr | 3 +++ tests/ui/source-enum-not-error.stderr | 3 +++ tests/ui/source-enum-unnamed-field-not-error.stderr | 3 +++ tests/ui/source-struct-not-error.stderr | 3 +++ tests/ui/source-struct-unnamed-field-not-error.stderr | 3 +++ 5 files changed, 15 insertions(+) diff --git a/tests/ui/no-display.stderr b/tests/ui/no-display.stderr index 0f47c24..88d0092 100644 --- a/tests/ui/no-display.stderr +++ b/tests/ui/no-display.stderr @@ -15,3 +15,6 @@ note: the trait `std::fmt::Display` must be implemented | | pub trait Display { | ^^^^^^^^^^^^^^^^^ + = help: items from traits can only be used if the trait is implemented and in scope + = note: the following trait defines an item `as_display`, perhaps you need to implement it: + candidate #1: `AsDisplay` diff --git a/tests/ui/source-enum-not-error.stderr b/tests/ui/source-enum-not-error.stderr index 2f676f3..649d77d 100644 --- a/tests/ui/source-enum-not-error.stderr +++ b/tests/ui/source-enum-not-error.stderr @@ -17,3 +17,6 @@ note: the trait `std::error::Error` must be implemented | | pub trait Error: Debug + Display { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: items from traits can only be used if the trait is implemented and in scope + = note: the following trait defines an item `as_dyn_error`, perhaps you need to implement it: + candidate #1: `AsDynError` diff --git a/tests/ui/source-enum-unnamed-field-not-error.stderr b/tests/ui/source-enum-unnamed-field-not-error.stderr index 4d36567..a1fe2b5 100644 --- a/tests/ui/source-enum-unnamed-field-not-error.stderr +++ b/tests/ui/source-enum-unnamed-field-not-error.stderr @@ -17,3 +17,6 @@ note: the trait `std::error::Error` must be implemented | | pub trait Error: Debug + Display { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: items from traits can only be used if the trait is implemented and in scope + = note: the following trait defines an item `as_dyn_error`, perhaps you need to implement it: + candidate #1: `AsDynError` diff --git a/tests/ui/source-struct-not-error.stderr b/tests/ui/source-struct-not-error.stderr index 76941d0..07cd67a 100644 --- a/tests/ui/source-struct-not-error.stderr +++ b/tests/ui/source-struct-not-error.stderr @@ -15,3 +15,6 @@ note: the trait `std::error::Error` must be implemented | | pub trait Error: Debug + Display { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: items from traits can only be used if the trait is implemented and in scope + = note: the following trait defines an item `as_dyn_error`, perhaps you need to implement it: + candidate #1: `AsDynError` diff --git a/tests/ui/source-struct-unnamed-field-not-error.stderr b/tests/ui/source-struct-unnamed-field-not-error.stderr index 788f1e5..2022ea6 100644 --- a/tests/ui/source-struct-unnamed-field-not-error.stderr +++ b/tests/ui/source-struct-unnamed-field-not-error.stderr @@ -15,3 +15,6 @@ note: the trait `std::error::Error` must be implemented | | pub trait Error: Debug + Display { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: items from traits can only be used if the trait is implemented and in scope + = note: the following trait defines an item `as_dyn_error`, perhaps you need to implement it: + candidate #1: `AsDynError` From d7e738e1d8e339d35a1ea0c4c252b055c66c3526 Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Thu, 8 Feb 2024 23:12:52 -0500 Subject: [PATCH 3/9] Optimize simple literals for Display::fmt Compiler is unable to generate as efficient code for `write!(f, "text")` as it does for `f.write_str("text")`. This PR checks if the `#[error("text")]` uses a simple string literal without the `{` and `}` characters, and without arguments, and uses `write_str` if so. --- impl/src/attr.rs | 19 +++++++++++++-- tests/test_display.rs | 56 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 2 deletions(-) diff --git a/impl/src/attr.rs b/impl/src/attr.rs index 4beb8c9..743c146 100644 --- a/impl/src/attr.rs +++ b/impl/src/attr.rs @@ -196,8 +196,23 @@ impl ToTokens for Display<'_> { fn to_tokens(&self, tokens: &mut TokenStream) { let fmt = &self.fmt; let args = &self.args; - tokens.extend(quote! { - ::core::write!(__formatter, #fmt #args) + + // Currently compiler is unable to generate as efficient code for + // write!(f, "text") as it does for f.write_str("text"), + // so handle it here when the literal string has no braces/no args. + let use_write_str = self.args.is_empty() && { + let value = fmt.value(); + !value.contains('{') && !value.contains('}') + }; + + tokens.extend(if use_write_str { + quote! { + __formatter.write_str(#fmt) + } + } else { + quote! { + ::core::write!(__formatter, #fmt #args) + } }); } } diff --git a/tests/test_display.rs b/tests/test_display.rs index 6f60388..faa8968 100644 --- a/tests/test_display.rs +++ b/tests/test_display.rs @@ -301,3 +301,59 @@ fn test_keyword() { assert("error: 1", Error); } + +#[test] +fn test_str_special_chars() { + #[derive(Error, Debug)] + pub enum Error { + #[error("text")] + Text, + #[error("braces {{}}")] + Braces, + #[error("braces2 \x7B\x7B\x7D\x7D")] + Braces2, + #[error("braces3 \u{7B}\u{7B}\u{7D}\u{7D}")] + Braces3, + #[error( + "new_\ +line" + )] + NewLine, + #[error("escape24 \u{78}")] + Escape24, + } + + assert("text", Error::Text); + assert("braces {}", Error::Braces); + assert("braces2 {}", Error::Braces2); + assert("braces3 {}", Error::Braces3); + assert("new_line", Error::NewLine); + assert("escape24 x", Error::Escape24); +} + +#[test] +fn test_raw_str() { + #[derive(Error, Debug)] + pub enum Error { + #[error(r#"raw_text"#)] + Text, + #[error(r#"raw_braces {{}}"#)] + Braces, + #[error(r#"raw_braces2 \x7B\x7D"#)] + Braces2, + #[error( + r#"raw_new_\ +line"# + )] + NewLine, + } + + assert(r#"raw_text"#, Error::Text); + assert(r#"raw_braces {}"#, Error::Braces); + assert(r#"raw_braces2 \x7B\x7D"#, Error::Braces2); + assert( + r#"raw_new_\ +line"#, + Error::NewLine, + ); +} From cd79876fe8f2eead51a1d9efa0b0f42467b9bef8 Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Thu, 8 Feb 2024 23:56:26 -0500 Subject: [PATCH 4/9] optimize by avoiding second fmt.value() call --- impl/src/attr.rs | 16 ++++++------ impl/src/fmt.rs | 5 ++++ tests/test_display.rs | 61 +++++++++++++++++++++---------------------- 3 files changed, 43 insertions(+), 39 deletions(-) diff --git a/impl/src/attr.rs b/impl/src/attr.rs index 743c146..7bba4ad 100644 --- a/impl/src/attr.rs +++ b/impl/src/attr.rs @@ -18,6 +18,7 @@ pub struct Attrs<'a> { #[derive(Clone)] pub struct Display<'a> { pub original: &'a Attribute, + pub use_write_str: bool, pub fmt: LitStr, pub args: TokenStream, pub has_bonus_display: bool, @@ -103,10 +104,14 @@ fn parse_error_attribute<'a>(attrs: &mut Attrs<'a>, attr: &'a Attribute) -> Resu return Ok(()); } + let fmt = input.parse()?; + let args = parse_token_expr(input, false)?; let display = Display { original: attr, - fmt: input.parse()?, - args: parse_token_expr(input, false)?, + // This will be updated later if format_args are still required (i.e. has braces) + use_write_str: args.is_empty(), + fmt, + args, has_bonus_display: false, implied_bounds: Set::new(), }; @@ -200,12 +205,7 @@ impl ToTokens for Display<'_> { // Currently compiler is unable to generate as efficient code for // write!(f, "text") as it does for f.write_str("text"), // so handle it here when the literal string has no braces/no args. - let use_write_str = self.args.is_empty() && { - let value = fmt.value(); - !value.contains('{') && !value.contains('}') - }; - - tokens.extend(if use_write_str { + tokens.extend(if self.use_write_str { quote! { __formatter.write_str(#fmt) } diff --git a/impl/src/fmt.rs b/impl/src/fmt.rs index 807dfb9..a5b44ce 100644 --- a/impl/src/fmt.rs +++ b/impl/src/fmt.rs @@ -32,7 +32,12 @@ impl Display<'_> { } } + if self.use_write_str && fmt.contains('}') { + self.use_write_str = false; + } + while let Some(brace) = read.find('{') { + self.use_write_str = false; out += &read[..brace + 1]; read = &read[brace + 1..]; if read.starts_with('{') { diff --git a/tests/test_display.rs b/tests/test_display.rs index faa8968..95c1a86 100644 --- a/tests/test_display.rs +++ b/tests/test_display.rs @@ -306,14 +306,18 @@ fn test_keyword() { fn test_str_special_chars() { #[derive(Error, Debug)] pub enum Error { - #[error("text")] - Text, - #[error("braces {{}}")] - Braces, - #[error("braces2 \x7B\x7B\x7D\x7D")] - Braces2, - #[error("braces3 \u{7B}\u{7B}\u{7D}\u{7D}")] - Braces3, + #[error("brace left {{")] + BraceLeft, + #[error("brace left 2 \x7B\x7B")] + BraceLeft2, + #[error("brace left 3 \u{7B}\u{7B}")] + BraceLeft3, + #[error("brace right }}")] + BraceRight, + #[error("brace right 2 \x7D\x7D")] + BraceRight2, + #[error("brace right 3 \u{7D}\u{7D}")] + BraceRight3, #[error( "new_\ line" @@ -323,10 +327,12 @@ line" Escape24, } - assert("text", Error::Text); - assert("braces {}", Error::Braces); - assert("braces2 {}", Error::Braces2); - assert("braces3 {}", Error::Braces3); + assert("brace left {", Error::BraceLeft); + assert("brace left 2 {", Error::BraceLeft2); + assert("brace left 3 {", Error::BraceLeft3); + assert("brace right }", Error::BraceRight); + assert("brace right 2 }", Error::BraceRight2); + assert("brace right 3 }", Error::BraceRight3); assert("new_line", Error::NewLine); assert("escape24 x", Error::Escape24); } @@ -335,25 +341,18 @@ line" fn test_raw_str() { #[derive(Error, Debug)] pub enum Error { - #[error(r#"raw_text"#)] - Text, - #[error(r#"raw_braces {{}}"#)] - Braces, - #[error(r#"raw_braces2 \x7B\x7D"#)] - Braces2, - #[error( - r#"raw_new_\ -line"# - )] - NewLine, + #[error(r#"raw brace left {{"#)] + BraceLeft, + #[error(r#"raw brace left 2 \x7B"#)] + BraceLeft2, + #[error(r#"raw brace right }}"#)] + BraceRight, + #[error(r#"raw brace right 2 \x7D"#)] + BraceRight2, } - assert(r#"raw_text"#, Error::Text); - assert(r#"raw_braces {}"#, Error::Braces); - assert(r#"raw_braces2 \x7B\x7D"#, Error::Braces2); - assert( - r#"raw_new_\ -line"#, - Error::NewLine, - ); + assert(r#"raw brace left {"#, Error::BraceLeft); + assert(r#"raw brace left 2 \x7B"#, Error::BraceLeft2); + assert(r#"raw brace right }"#, Error::BraceRight); + assert(r#"raw brace right 2 \x7D"#, Error::BraceRight2); } From d09c4182955b459a4699adaf9b045077308a1d1a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 11 Feb 2024 10:06:36 -0800 Subject: [PATCH 5/9] Touch up PR 286 --- impl/src/attr.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/impl/src/attr.rs b/impl/src/attr.rs index 7bba4ad..a54a3b1 100644 --- a/impl/src/attr.rs +++ b/impl/src/attr.rs @@ -104,7 +104,7 @@ fn parse_error_attribute<'a>(attrs: &mut Attrs<'a>, attr: &'a Attribute) -> Resu return Ok(()); } - let fmt = input.parse()?; + let fmt: LitStr = input.parse()?; let args = parse_token_expr(input, false)?; let display = Display { original: attr, @@ -202,9 +202,9 @@ impl ToTokens for Display<'_> { let fmt = &self.fmt; let args = &self.args; - // Currently compiler is unable to generate as efficient code for - // write!(f, "text") as it does for f.write_str("text"), - // so handle it here when the literal string has no braces/no args. + // Currently `write!(f, "text")` produces less efficient code than + // `f.write_str("text")`. We recognize the case when the format string + // has no braces and no interpolated values, and generate simpler code. tokens.extend(if self.use_write_str { quote! { __formatter.write_str(#fmt) From d43b759e3aa02d54dcad59c5eadfc78a8e96536f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 11 Feb 2024 10:27:37 -0800 Subject: [PATCH 6/9] Ignore needless_raw_string_hashes pedantic clippy lint in test warning: unnecessary hashes around raw string literal --> tests/test_display.rs:354:12 | 354 | assert(r#"raw brace left {"#, Error::BraceLeft); | ^^^^^^^^^^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_raw_string_hashes = note: `-W clippy::needless-raw-string-hashes` implied by `-W clippy::pedantic` = help: to override `-W clippy::pedantic` add `#[allow(clippy::needless_raw_string_hashes)]` help: remove all the hashes around the string literal | 354 - assert(r#"raw brace left {"#, Error::BraceLeft); 354 + assert(r"raw brace left {", Error::BraceLeft); | warning: unnecessary hashes around raw string literal --> tests/test_display.rs:355:12 | 355 | assert(r#"raw brace left 2 \x7B"#, Error::BraceLeft2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_raw_string_hashes help: remove all the hashes around the string literal | 355 - assert(r#"raw brace left 2 \x7B"#, Error::BraceLeft2); 355 + assert(r"raw brace left 2 \x7B", Error::BraceLeft2); | warning: unnecessary hashes around raw string literal --> tests/test_display.rs:356:12 | 356 | assert(r#"raw brace right }"#, Error::BraceRight); | ^^^^^^^^^^^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_raw_string_hashes help: remove all the hashes around the string literal | 356 - assert(r#"raw brace right }"#, Error::BraceRight); 356 + assert(r"raw brace right }", Error::BraceRight); | warning: unnecessary hashes around raw string literal --> tests/test_display.rs:357:12 | 357 | assert(r#"raw brace right 2 \x7D"#, Error::BraceRight2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_raw_string_hashes help: remove all the hashes around the string literal | 357 - assert(r#"raw brace right 2 \x7D"#, Error::BraceRight2); 357 + assert(r"raw brace right 2 \x7D", Error::BraceRight2); | --- tests/test_display.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_display.rs b/tests/test_display.rs index 95c1a86..95a210f 100644 --- a/tests/test_display.rs +++ b/tests/test_display.rs @@ -1,4 +1,4 @@ -#![allow(clippy::uninlined_format_args)] +#![allow(clippy::needless_raw_string_hashes, clippy::uninlined_format_args)] use std::fmt::{self, Display}; use thiserror::Error; From f790bee2a401d71ac6e5492c7d1f8bb3a18a0e1c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 11 Feb 2024 10:27:02 -0800 Subject: [PATCH 7/9] Phrase flag in terms of whether core::fmt machinery is required --- impl/src/attr.rs | 12 ++++++------ impl/src/fmt.rs | 6 ++---- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/impl/src/attr.rs b/impl/src/attr.rs index a54a3b1..0e30d29 100644 --- a/impl/src/attr.rs +++ b/impl/src/attr.rs @@ -18,9 +18,9 @@ pub struct Attrs<'a> { #[derive(Clone)] pub struct Display<'a> { pub original: &'a Attribute, - pub use_write_str: bool, pub fmt: LitStr, pub args: TokenStream, + pub requires_fmt_machinery: bool, pub has_bonus_display: bool, pub implied_bounds: Set<(usize, Trait)>, } @@ -106,12 +106,12 @@ fn parse_error_attribute<'a>(attrs: &mut Attrs<'a>, attr: &'a Attribute) -> Resu let fmt: LitStr = input.parse()?; let args = parse_token_expr(input, false)?; + let requires_fmt_machinery = !args.is_empty(); let display = Display { original: attr, - // This will be updated later if format_args are still required (i.e. has braces) - use_write_str: args.is_empty(), fmt, args, + requires_fmt_machinery, has_bonus_display: false, implied_bounds: Set::new(), }; @@ -205,13 +205,13 @@ impl ToTokens for Display<'_> { // Currently `write!(f, "text")` produces less efficient code than // `f.write_str("text")`. We recognize the case when the format string // has no braces and no interpolated values, and generate simpler code. - tokens.extend(if self.use_write_str { + tokens.extend(if self.requires_fmt_machinery { quote! { - __formatter.write_str(#fmt) + ::core::write!(__formatter, #fmt #args) } } else { quote! { - ::core::write!(__formatter, #fmt #args) + __formatter.write_str(#fmt) } }); } diff --git a/impl/src/fmt.rs b/impl/src/fmt.rs index a5b44ce..b38b7bf 100644 --- a/impl/src/fmt.rs +++ b/impl/src/fmt.rs @@ -32,12 +32,10 @@ impl Display<'_> { } } - if self.use_write_str && fmt.contains('}') { - self.use_write_str = false; - } + self.requires_fmt_machinery = self.requires_fmt_machinery || fmt.contains('}'); while let Some(brace) = read.find('{') { - self.use_write_str = false; + self.requires_fmt_machinery = true; out += &read[..brace + 1]; read = &read[brace + 1..]; if read.starts_with('{') { From 8a5c4d1b76eaa667a71dfaeb1373bca36fda4e78 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 11 Feb 2024 10:40:31 -0800 Subject: [PATCH 8/9] Use write_str when args only consists of trailing comma --- impl/src/attr.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/impl/src/attr.rs b/impl/src/attr.rs index 0e30d29..269c69e 100644 --- a/impl/src/attr.rs +++ b/impl/src/attr.rs @@ -1,6 +1,7 @@ use proc_macro2::{Delimiter, Group, Span, TokenStream, TokenTree}; use quote::{format_ident, quote, ToTokens}; use std::collections::BTreeSet as Set; +use syn::parse::discouraged::Speculative; use syn::parse::ParseStream; use syn::{ braced, bracketed, parenthesized, token, Attribute, Error, Ident, Index, LitInt, LitStr, Meta, @@ -105,8 +106,18 @@ fn parse_error_attribute<'a>(attrs: &mut Attrs<'a>, attr: &'a Attribute) -> Resu } let fmt: LitStr = input.parse()?; - let args = parse_token_expr(input, false)?; + + let ahead = input.fork(); + ahead.parse::>()?; + let args = if ahead.is_empty() { + input.advance_to(&ahead); + TokenStream::new() + } else { + parse_token_expr(input, false)? + }; + let requires_fmt_machinery = !args.is_empty(); + let display = Display { original: attr, fmt, From 1d106b169c1ba328bcd64d70d06687413906d751 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 11 Feb 2024 10:43:07 -0800 Subject: [PATCH 9/9] Release 1.0.57 --- Cargo.toml | 4 ++-- impl/Cargo.toml | 2 +- src/lib.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index fd6cf03..d369754 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "thiserror" -version = "1.0.56" +version = "1.0.57" authors = ["David Tolnay "] categories = ["rust-patterns"] description = "derive(Error)" @@ -12,7 +12,7 @@ repository = "https://github.com/dtolnay/thiserror" rust-version = "1.56" [dependencies] -thiserror-impl = { version = "=1.0.56", path = "impl" } +thiserror-impl = { version = "=1.0.57", path = "impl" } [dev-dependencies] anyhow = "1.0.73" diff --git a/impl/Cargo.toml b/impl/Cargo.toml index 1fd1975..026611c 100644 --- a/impl/Cargo.toml +++ b/impl/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "thiserror-impl" -version = "1.0.56" +version = "1.0.57" authors = ["David Tolnay "] description = "Implementation detail of the `thiserror` crate" edition = "2021" diff --git a/src/lib.rs b/src/lib.rs index 73e6e21..717cdc6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -228,7 +228,7 @@ //! //! [`anyhow`]: https://github.com/dtolnay/anyhow -#![doc(html_root_url = "https://docs.rs/thiserror/1.0.56")] +#![doc(html_root_url = "https://docs.rs/thiserror/1.0.57")] #![allow( clippy::module_name_repetitions, clippy::needless_lifetimes,