From 7436bb23db8b5e5aea51df8bcdca6ab828b32935 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 31 Oct 2024 15:49:48 -0700 Subject: [PATCH 001/121] Allow `parse_quote!` to produce Vec --- src/parse_quote.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/parse_quote.rs b/src/parse_quote.rs index 22cd98effb..fa7ea79f47 100644 --- a/src/parse_quote.rs +++ b/src/parse_quote.rs @@ -153,6 +153,17 @@ impl ParseQuote for Attribute { } } +#[cfg(any(feature = "full", feature = "derive"))] +impl ParseQuote for Vec { + fn parse(input: ParseStream) -> Result { + let mut attrs = Vec::new(); + while !input.is_empty() { + attrs.push(ParseQuote::parse(input)?); + } + Ok(attrs) + } +} + #[cfg(any(feature = "full", feature = "derive"))] impl ParseQuote for Field { fn parse(input: ParseStream) -> Result { From ab43277a5334460ecca13fed7f03c15dcd24f434 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 18 May 2024 11:58:45 -0700 Subject: [PATCH 002/121] Support peeking End --- src/lookahead.rs | 173 ++++++++++++++++++++++++++++++++++++++++++++++- src/parse.rs | 7 +- 2 files changed, 176 insertions(+), 4 deletions(-) diff --git a/src/lookahead.rs b/src/lookahead.rs index 75e3a658a3..da13ffc47a 100644 --- a/src/lookahead.rs +++ b/src/lookahead.rs @@ -2,8 +2,8 @@ use crate::buffer::Cursor; use crate::error::{self, Error}; use crate::sealed::lookahead::Sealed; use crate::span::IntoSpans; -use crate::token::Token; -use proc_macro2::Span; +use crate::token::{CustomToken, Token}; +use proc_macro2::{Delimiter, Span}; use std::cell::RefCell; /// Support for checking the next token in a stream to decide how to parse. @@ -110,7 +110,18 @@ impl<'a> Lookahead1<'a> { /// The error message will identify all of the expected token types that /// have been peeked against this lookahead instance. pub fn error(self) -> Error { - let comparisons = self.comparisons.into_inner(); + let mut comparisons = self.comparisons.into_inner(); + comparisons.retain_mut(|display| { + if *display == "`)`" { + *display = match self.cursor.scope_delimiter() { + Delimiter::Parenthesis => "`)`", + Delimiter::Brace => "`}`", + Delimiter::Bracket => "`]`", + Delimiter::None => return false, + } + } + true + }); match comparisons.len() { 0 => { if self.cursor.eof() { @@ -150,6 +161,160 @@ pub trait Peek: Sealed { type Token: Token; } +/// Pseudo-token used for peeking the end of a parse stream. +/// +/// This type is only useful as an argument to one of the following functions: +/// +/// - [`ParseStream::peek`][crate::parse::ParseBuffer::peek] +/// - [`ParseStream::peek2`][crate::parse::ParseBuffer::peek2] +/// - [`ParseStream::peek3`][crate::parse::ParseBuffer::peek3] +/// - [`Lookahead1::peek`] +/// +/// The peek will return `true` if there are no remaining tokens after that +/// point in the parse stream. +/// +/// # Example +/// +/// Suppose we are parsing attributes containing core::fmt inspired formatting +/// arguments: +/// +/// - `#[fmt("simple example")]` +/// - `#[fmt("interpolation e{}ample", self.x)]` +/// - `#[fmt("interpolation e{x}ample")]` +/// +/// and we want to recognize the cases where no interpolation occurs so that +/// more efficient code can be generated. +/// +/// The following implementation uses `input.peek(Token![,]) && +/// input.peek2(End)` to recognize the case of a trailing comma without +/// consuming the comma from the parse stream, because if it isn't a trailing +/// comma, that same comma needs to be parsed as part of `args`. +/// +/// ``` +/// use proc_macro2::TokenStream; +/// use quote::quote; +/// use syn::parse::{End, Parse, ParseStream, Result}; +/// use syn::{parse_quote, Attribute, LitStr, Token}; +/// +/// struct FormatArgs { +/// template: LitStr, // "...{}..." +/// args: TokenStream, // , self.x +/// } +/// +/// impl Parse for FormatArgs { +/// fn parse(input: ParseStream) -> Result { +/// let template: LitStr = input.parse()?; +/// +/// let args = if input.is_empty() +/// || input.peek(Token![,]) && input.peek2(End) +/// { +/// input.parse::>()?; +/// TokenStream::new() +/// } else { +/// input.parse()? +/// }; +/// +/// Ok(FormatArgs { +/// template, +/// args, +/// }) +/// } +/// } +/// +/// fn main() -> Result<()> { +/// let attrs: Vec = parse_quote! { +/// #[fmt("simple example")] +/// #[fmt("interpolation e{}ample", self.x)] +/// #[fmt("interpolation e{x}ample")] +/// }; +/// +/// for attr in &attrs { +/// let FormatArgs { template, args } = attr.parse_args()?; +/// let requires_fmt_machinery = +/// !args.is_empty() || template.value().contains(['{', '}']); +/// let out = if requires_fmt_machinery { +/// quote! { +/// ::core::write!(__formatter, #template #args) +/// } +/// } else { +/// quote! { +/// __formatter.write_str(#template) +/// } +/// }; +/// println!("{}", out); +/// } +/// Ok(()) +/// } +/// ``` +/// +/// Implementing this parsing logic without `peek2(End)` is more clumsy because +/// we'd need a parse stream actually advanced past the comma before being able +/// to find out whether there is anything after it. It would look something +/// like: +/// +/// ``` +/// # use proc_macro2::TokenStream; +/// # use syn::parse::{ParseStream, Result}; +/// # use syn::Token; +/// # +/// # fn parse(input: ParseStream) -> Result<()> { +/// use syn::parse::discouraged::Speculative as _; +/// +/// let ahead = input.fork(); +/// ahead.parse::>()?; +/// let args = if ahead.is_empty() { +/// input.advance_to(&ahead); +/// TokenStream::new() +/// } else { +/// input.parse()? +/// }; +/// # Ok(()) +/// # } +/// ``` +/// +/// or: +/// +/// ``` +/// # use proc_macro2::TokenStream; +/// # use syn::parse::{ParseStream, Result}; +/// # use syn::Token; +/// # +/// # fn parse(input: ParseStream) -> Result<()> { +/// use quote::ToTokens as _; +/// +/// let comma: Option = input.parse()?; +/// let mut args = TokenStream::new(); +/// if !input.is_empty() { +/// comma.to_tokens(&mut args); +/// input.parse::()?.to_tokens(&mut args); +/// } +/// # Ok(()) +/// # } +/// ``` +pub struct End; + +impl Copy for End {} + +impl Clone for End { + fn clone(&self) -> Self { + *self + } +} + +impl Peek for End { + type Token = Self; +} + +impl CustomToken for End { + fn peek(cursor: Cursor) -> bool { + cursor.eof() + } + + fn display() -> &'static str { + "`)`" // Lookahead1 error message will fill in the expected close delimiter + } +} + impl T, T: Token> Peek for F { type Token = T; } @@ -163,3 +328,5 @@ impl IntoSpans for TokenMarker { } impl T, T: Token> Sealed for F {} + +impl Sealed for End {} diff --git a/src/parse.rs b/src/parse.rs index f000711635..b7f8831d67 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -202,7 +202,7 @@ use std::rc::Rc; use std::str::FromStr; pub use crate::error::{Error, Result}; -pub use crate::lookahead::{Lookahead1, Peek}; +pub use crate::lookahead::{End, Lookahead1, Peek}; /// Parsing interface implemented by all types that can be parsed in a default /// way from a token stream. @@ -751,6 +751,11 @@ impl<'a> ParseBuffer<'a> { /// set of delimiters, as well as at the end of the tokens provided to the /// outermost parsing entry point. /// + /// This is equivalent to + /// .peek(syn::parse::End). + /// Use `.peek2(End)` or `.peek3(End)` to look for the end of a parse stream + /// further ahead than the current position. + /// /// # Example /// /// ``` From 12b0d1d8fd2ffe4202e457daf560617addbfbf45 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 31 Oct 2024 16:34:55 -0700 Subject: [PATCH 003/121] Release 2.0.86 --- Cargo.toml | 2 +- src/lib.rs | 2 +- syn.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 68ffa971b0..a40b541591 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "syn" -version = "2.0.85" +version = "2.0.86" authors = ["David Tolnay "] categories = ["development-tools::procedural-macro-helpers", "parser-implementations"] description = "Parser for Rust source code" diff --git a/src/lib.rs b/src/lib.rs index 8d11455b9d..2eb370ad92 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -249,7 +249,7 @@ //! dynamic library libproc_macro from rustc toolchain. // Syn types in rustdoc of other crates get linked to here. -#![doc(html_root_url = "https://docs.rs/syn/2.0.85")] +#![doc(html_root_url = "https://docs.rs/syn/2.0.86")] #![cfg_attr(docsrs, feature(doc_cfg))] #![deny(unsafe_op_in_unsafe_fn)] #![allow(non_camel_case_types)] diff --git a/syn.json b/syn.json index ed106885e0..55a24b3cc7 100644 --- a/syn.json +++ b/syn.json @@ -1,5 +1,5 @@ { - "version": "2.0.85", + "version": "2.0.86", "types": [ { "ident": "Abi", From ce9f4b5783e74592daaf8191fa03cece28f32a9d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 31 Oct 2024 22:49:21 -0700 Subject: [PATCH 004/121] Parse raw addr syntax in discriminants --- src/data.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/data.rs b/src/data.rs index 9e73f02d3c..6c76887516 100644 --- a/src/data.rs +++ b/src/data.rs @@ -322,7 +322,7 @@ pub(crate) mod parsing { loop { if initial { if consume![&] { - input.parse::>()?; + initial = consume![mut] || !consume![raw] || consume![const] || consume![mut]; } else if consume![if] || consume![match] || consume![while] { depth += 1; } else if input.parse::>()?.is_some() From c949ba1a427198dccec3aa620c8b965dd3f95a19 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 1 Nov 2024 20:27:53 -0700 Subject: [PATCH 005/121] Reorder Cursor methods to put simpler signatures first --- src/buffer.rs | 92 +++++++++++++++++++++++++-------------------------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/src/buffer.rs b/src/buffer.rs index c28440a29b..3dd47198a8 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -183,52 +183,6 @@ impl<'a> Cursor<'a> { self.ptr == self.scope } - /// If the cursor is pointing at a `Group` with the given delimiter, returns - /// a cursor into that group and one pointing to the next `TokenTree`. - pub fn group(mut self, delim: Delimiter) -> Option<(Cursor<'a>, DelimSpan, Cursor<'a>)> { - // If we're not trying to enter a none-delimited group, we want to - // ignore them. We have to make sure to _not_ ignore them when we want - // to enter them, of course. For obvious reasons. - if delim != Delimiter::None { - self.ignore_none(); - } - - if let Entry::Group(group, end_offset) = self.entry() { - if group.delimiter() == delim { - let span = group.delim_span(); - let end_of_group = unsafe { self.ptr.add(*end_offset) }; - let inside_of_group = unsafe { Cursor::create(self.ptr.add(1), end_of_group) }; - let after_group = unsafe { Cursor::create(end_of_group, self.scope) }; - return Some((inside_of_group, span, after_group)); - } - } - - None - } - - pub(crate) fn any_group(self) -> Option<(Cursor<'a>, Delimiter, DelimSpan, Cursor<'a>)> { - if let Entry::Group(group, end_offset) = self.entry() { - let delimiter = group.delimiter(); - let span = group.delim_span(); - let end_of_group = unsafe { self.ptr.add(*end_offset) }; - let inside_of_group = unsafe { Cursor::create(self.ptr.add(1), end_of_group) }; - let after_group = unsafe { Cursor::create(end_of_group, self.scope) }; - return Some((inside_of_group, delimiter, span, after_group)); - } - - None - } - - pub(crate) fn any_group_token(self) -> Option<(Group, Cursor<'a>)> { - if let Entry::Group(group, end_offset) = self.entry() { - let end_of_group = unsafe { self.ptr.add(*end_offset) }; - let after_group = unsafe { Cursor::create(end_of_group, self.scope) }; - return Some((group.clone(), after_group)); - } - - None - } - /// If the cursor is pointing at a `Ident`, returns it along with a cursor /// pointing at the next `TokenTree`. pub fn ident(mut self) -> Option<(Ident, Cursor<'a>)> { @@ -279,6 +233,52 @@ impl<'a> Cursor<'a> { } } + /// If the cursor is pointing at a `Group` with the given delimiter, returns + /// a cursor into that group and one pointing to the next `TokenTree`. + pub fn group(mut self, delim: Delimiter) -> Option<(Cursor<'a>, DelimSpan, Cursor<'a>)> { + // If we're not trying to enter a none-delimited group, we want to + // ignore them. We have to make sure to _not_ ignore them when we want + // to enter them, of course. For obvious reasons. + if delim != Delimiter::None { + self.ignore_none(); + } + + if let Entry::Group(group, end_offset) = self.entry() { + if group.delimiter() == delim { + let span = group.delim_span(); + let end_of_group = unsafe { self.ptr.add(*end_offset) }; + let inside_of_group = unsafe { Cursor::create(self.ptr.add(1), end_of_group) }; + let after_group = unsafe { Cursor::create(end_of_group, self.scope) }; + return Some((inside_of_group, span, after_group)); + } + } + + None + } + + pub(crate) fn any_group(self) -> Option<(Cursor<'a>, Delimiter, DelimSpan, Cursor<'a>)> { + if let Entry::Group(group, end_offset) = self.entry() { + let delimiter = group.delimiter(); + let span = group.delim_span(); + let end_of_group = unsafe { self.ptr.add(*end_offset) }; + let inside_of_group = unsafe { Cursor::create(self.ptr.add(1), end_of_group) }; + let after_group = unsafe { Cursor::create(end_of_group, self.scope) }; + return Some((inside_of_group, delimiter, span, after_group)); + } + + None + } + + pub(crate) fn any_group_token(self) -> Option<(Group, Cursor<'a>)> { + if let Entry::Group(group, end_offset) = self.entry() { + let end_of_group = unsafe { self.ptr.add(*end_offset) }; + let after_group = unsafe { Cursor::create(end_of_group, self.scope) }; + return Some((group.clone(), after_group)); + } + + None + } + /// Copies all remaining tokens visible from this cursor into a /// `TokenStream`. pub fn token_stream(self) -> TokenStream { From 648fca940ad5d77f2991bacd891580f822531326 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 1 Nov 2024 20:34:50 -0700 Subject: [PATCH 006/121] Update test suite to nightly-2024-11-02 --- tests/common/eq.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/common/eq.rs b/tests/common/eq.rs index bf09494b4d..119df2b73e 100644 --- a/tests/common/eq.rs +++ b/tests/common/eq.rs @@ -498,7 +498,7 @@ spanless_eq_struct!(Fn; defaultness generics sig body); spanless_eq_struct!(FnDecl; inputs output); spanless_eq_struct!(FnHeader; constness coroutine_kind safety ext); spanless_eq_struct!(FnSig; header decl span); -spanless_eq_struct!(ForeignMod; safety abi items); +spanless_eq_struct!(ForeignMod; extern_span safety abi items); spanless_eq_struct!(FormatArgPosition; index kind span); spanless_eq_struct!(FormatArgs; span template arguments); spanless_eq_struct!(FormatArgument; kind expr); From 31d93e1bd0f11abf5d418964833a71e12c71f717 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 1 Nov 2024 20:36:04 -0700 Subject: [PATCH 007/121] Make Cursor::any_group public --- src/buffer.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/buffer.rs b/src/buffer.rs index 3dd47198a8..b4d1980725 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -256,7 +256,9 @@ impl<'a> Cursor<'a> { None } - pub(crate) fn any_group(self) -> Option<(Cursor<'a>, Delimiter, DelimSpan, Cursor<'a>)> { + /// If the cursor is pointing at a `Group`, returns a cursor into the group + /// and one pointing to the next `TokenTree`. + pub fn any_group(self) -> Option<(Cursor<'a>, Delimiter, DelimSpan, Cursor<'a>)> { if let Entry::Group(group, end_offset) = self.entry() { let delimiter = group.delimiter(); let span = group.delim_span(); From a890e9da22b463b05c06696d4cc767c6cb9d3114 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 1 Nov 2024 20:56:47 -0700 Subject: [PATCH 008/121] Expose can_begin_expr as Expr::peek --- src/expr.rs | 63 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 37 insertions(+), 26 deletions(-) diff --git a/src/expr.rs b/src/expr.rs index dc18cd1aa3..f68f6322fe 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -1,15 +1,17 @@ use crate::attr::Attribute; #[cfg(all(feature = "parsing", feature = "full"))] use crate::error::Result; +#[cfg(feature = "parsing")] +use crate::ext::IdentExt as _; #[cfg(feature = "full")] use crate::generics::BoundLifetimes; use crate::ident::Ident; -#[cfg(feature = "full")] +#[cfg(any(feature = "parsing", feature = "full"))] use crate::lifetime::Lifetime; use crate::lit::Lit; use crate::mac::Macro; use crate::op::{BinOp, UnOp}; -#[cfg(all(feature = "parsing", feature = "full"))] +#[cfg(feature = "parsing")] use crate::parse::ParseStream; #[cfg(feature = "full")] use crate::pat::Pat; @@ -889,6 +891,36 @@ impl Expr { parsing::parse_with_earlier_boundary_rule(input) } + /// Returns whether the next token in the parse stream is one that might + /// possibly form the beginning of an expr. + /// + /// This classification is a load-bearing part of the grammar of some Rust + /// expressions, notably `return` and `break`. For example `return < …` will + /// never parse `<` as a binary operator regardless of what comes after, + /// because `<` is a legal starting token for an expression and so it's + /// required to be continued as a return value, such as `return ::CONST`. Meanwhile `return > …` treats the `>` as a binary + /// operator because it cannot be a starting token for any Rust expression. + #[cfg(feature = "parsing")] + #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] + pub fn peek(input: ParseStream) -> bool { + input.peek(Ident::peek_any) // value name or keyword + || input.peek(token::Paren) // tuple + || input.peek(token::Bracket) // array + || input.peek(token::Brace) // block + || input.peek(Lit) // literal + || input.peek(Token![!]) && !input.peek(Token![!=]) // operator not + || input.peek(Token![-]) && !input.peek(Token![-=]) && !input.peek(Token![->]) // unary minus + || input.peek(Token![*]) && !input.peek(Token![*=]) // dereference + || input.peek(Token![|]) && !input.peek(Token![|=]) // closure + || input.peek(Token![&]) && !input.peek(Token![&=]) // reference + || input.peek(Token![..]) // range + || input.peek(Token![<]) && !input.peek(Token![<=]) && !input.peek(Token![<<=]) // associated path + || input.peek(Token![::]) // absolute path + || input.peek(Lifetime) // labeled loop + || input.peek(Token![#]) // expression attributes + } + #[cfg(all(feature = "parsing", feature = "full"))] pub(crate) fn replace_attrs(&mut self, new: Vec) -> Vec { match self { @@ -1147,8 +1179,6 @@ pub(crate) mod parsing { FieldValue, Index, Member, }; #[cfg(feature = "full")] - use crate::ext::IdentExt as _; - #[cfg(feature = "full")] use crate::generics::BoundLifetimes; use crate::ident::Ident; #[cfg(feature = "full")] @@ -1266,25 +1296,6 @@ pub(crate) mod parsing { } } - #[cfg(feature = "full")] - fn can_begin_expr(input: ParseStream) -> bool { - input.peek(Ident::peek_any) // value name or keyword - || input.peek(token::Paren) // tuple - || input.peek(token::Bracket) // array - || input.peek(token::Brace) // block - || input.peek(Lit) // literal - || input.peek(Token![!]) && !input.peek(Token![!=]) // operator not - || input.peek(Token![-]) && !input.peek(Token![-=]) && !input.peek(Token![->]) // unary minus - || input.peek(Token![*]) && !input.peek(Token![*=]) // dereference - || input.peek(Token![|]) && !input.peek(Token![|=]) // closure - || input.peek(Token![&]) && !input.peek(Token![&=]) // reference - || input.peek(Token![..]) // range notation - || input.peek(Token![<]) && !input.peek(Token![<=]) && !input.peek(Token![<<=]) // associated path - || input.peek(Token![::]) // global path - || input.peek(Lifetime) // labeled loop - || input.peek(Token![#]) // expression attributes - } - #[cfg(feature = "full")] fn parse_expr( input: ParseStream, @@ -2439,7 +2450,7 @@ pub(crate) mod parsing { attrs: Vec::new(), return_token: input.parse()?, expr: { - if can_begin_expr(input) { + if Expr::peek(input) { Some(input.parse()?) } else { None @@ -2477,7 +2488,7 @@ pub(crate) mod parsing { attrs: Vec::new(), yield_token: input.parse()?, expr: { - if can_begin_expr(input) { + if Expr::peek(input) { Some(input.parse()?) } else { None @@ -2690,7 +2701,7 @@ pub(crate) mod parsing { } input.advance_to(&ahead); - let expr = if can_begin_expr(input) && (allow_struct.0 || !input.peek(token::Brace)) { + let expr = if Expr::peek(input) && (allow_struct.0 || !input.peek(token::Brace)) { Some(input.parse()?) } else { None From 7c102c3c8b3dc076c03cbe842266a2b140be6323 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 1 Nov 2024 14:31:18 -0700 Subject: [PATCH 009/121] Extract non-full expr scanner to module --- src/data.rs | 83 ++---------------------------------------------- src/lib.rs | 3 ++ src/scan_expr.rs | 80 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 86 insertions(+), 80 deletions(-) create mode 100644 src/scan_expr.rs diff --git a/src/data.rs b/src/data.rs index 6c76887516..96db2a0b7c 100644 --- a/src/data.rs +++ b/src/data.rs @@ -248,6 +248,8 @@ pub(crate) mod parsing { use crate::parse::discouraged::Speculative as _; use crate::parse::{Parse, ParseStream}; use crate::restriction::{FieldMutability, Visibility}; + #[cfg(not(feature = "full"))] + use crate::scan_expr::scan_expr; use crate::token; use crate::ty::Type; use crate::verbatim; @@ -276,7 +278,7 @@ pub(crate) mod parsing { let mut discriminant: Result = ahead.parse(); if discriminant.is_ok() { input.advance_to(&ahead); - } else if scan_lenient_discriminant(input).is_ok() { + } else if scan_expr(input).is_ok() { discriminant = Ok(Expr::Verbatim(verbatim::between(&begin, input))); } discriminant? @@ -294,85 +296,6 @@ pub(crate) mod parsing { } } - #[cfg(not(feature = "full"))] - pub(crate) fn scan_lenient_discriminant(input: ParseStream) -> Result<()> { - use crate::expr::Member; - use crate::lifetime::Lifetime; - use crate::lit::Lit; - use crate::lit::LitFloat; - use crate::op::{BinOp, UnOp}; - use crate::path::{self, AngleBracketedGenericArguments}; - use proc_macro2::Delimiter::{self, Brace, Bracket, Parenthesis}; - - let consume = |delimiter: Delimiter| { - Result::unwrap(input.step(|cursor| match cursor.group(delimiter) { - Some((_inside, _span, rest)) => Ok((true, rest)), - None => Ok((false, *cursor)), - })) - }; - - macro_rules! consume { - [$token:tt] => { - input.parse::>().unwrap().is_some() - }; - } - - let mut initial = true; - let mut depth = 0usize; - loop { - if initial { - if consume![&] { - initial = consume![mut] || !consume![raw] || consume![const] || consume![mut]; - } else if consume![if] || consume![match] || consume![while] { - depth += 1; - } else if input.parse::>()?.is_some() - || (consume(Brace) || consume(Bracket) || consume(Parenthesis)) - || (consume![async] || consume![const] || consume![loop] || consume![unsafe]) - && (consume(Brace) || break) - { - initial = false; - } else if consume![let] { - while !consume![=] { - if !((consume![|] || consume![ref] || consume![mut] || consume![@]) - || (consume![!] || input.parse::>()?.is_some()) - || (consume![..=] || consume![..] || consume![&] || consume![_]) - || (consume(Brace) || consume(Bracket) || consume(Parenthesis))) - { - path::parsing::qpath(input, true)?; - } - } - } else if input.parse::>()?.is_some() && !consume![:] { - break; - } else if input.parse::().is_err() { - path::parsing::qpath(input, true)?; - initial = consume![!] || depth == 0 && input.peek(token::Brace); - } - } else if input.is_empty() || input.peek(Token![,]) { - return Ok(()); - } else if depth > 0 && consume(Brace) { - if consume![else] && !consume(Brace) { - initial = consume![if] || break; - } else { - depth -= 1; - } - } else if input.parse::().is_ok() || (consume![..] | consume![=]) { - initial = true; - } else if consume![.] { - if input.parse::>()?.is_none() - && (input.parse::()?.is_named() && consume![::]) - { - AngleBracketedGenericArguments::do_parse(None, input)?; - } - } else if consume![as] { - input.parse::()?; - } else if !(consume(Brace) || consume(Bracket) || consume(Parenthesis)) { - break; - } - } - - Err(input.error("unsupported expression")) - } - #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] impl Parse for FieldsNamed { fn parse(input: ParseStream) -> Result { diff --git a/src/lib.rs b/src/lib.rs index 2eb370ad92..ee6e716d7b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -509,6 +509,9 @@ pub use crate::restriction::{FieldMutability, VisRestricted, Visibility}; mod sealed; +#[cfg(all(feature = "parsing", feature = "derive", not(feature = "full")))] +mod scan_expr; + mod span; #[cfg(all(feature = "parsing", feature = "printing"))] diff --git a/src/scan_expr.rs b/src/scan_expr.rs new file mode 100644 index 0000000000..04db8dc302 --- /dev/null +++ b/src/scan_expr.rs @@ -0,0 +1,80 @@ +use crate::expr::Member; +use crate::lifetime::Lifetime; +use crate::lit::Lit; +use crate::lit::LitFloat; +use crate::op::{BinOp, UnOp}; +use crate::parse::{ParseStream, Result}; +use crate::path::{self, AngleBracketedGenericArguments}; +use crate::token; +use crate::ty::Type; +use proc_macro2::Delimiter::{self, Brace, Bracket, Parenthesis}; + +pub(crate) fn scan_expr(input: ParseStream) -> Result<()> { + let consume = |delimiter: Delimiter| { + Result::unwrap(input.step(|cursor| match cursor.group(delimiter) { + Some((_inside, _span, rest)) => Ok((true, rest)), + None => Ok((false, *cursor)), + })) + }; + + macro_rules! consume { + [$token:tt] => { + input.parse::>().unwrap().is_some() + }; + } + + let mut initial = true; + let mut depth = 0usize; + loop { + if initial { + if consume![&] { + initial = consume![mut] || !consume![raw] || consume![const] || consume![mut]; + } else if consume![if] || consume![match] || consume![while] { + depth += 1; + } else if input.parse::>()?.is_some() + || (consume(Brace) || consume(Bracket) || consume(Parenthesis)) + || (consume![async] || consume![const] || consume![loop] || consume![unsafe]) + && (consume(Brace) || break) + { + initial = false; + } else if consume![let] { + while !consume![=] { + if !((consume![|] || consume![ref] || consume![mut] || consume![@]) + || (consume![!] || input.parse::>()?.is_some()) + || (consume![..=] || consume![..] || consume![&] || consume![_]) + || (consume(Brace) || consume(Bracket) || consume(Parenthesis))) + { + path::parsing::qpath(input, true)?; + } + } + } else if input.parse::>()?.is_some() && !consume![:] { + break; + } else if input.parse::().is_err() { + path::parsing::qpath(input, true)?; + initial = consume![!] || depth == 0 && input.peek(token::Brace); + } + } else if input.is_empty() || input.peek(Token![,]) { + return Ok(()); + } else if depth > 0 && consume(Brace) { + if consume![else] && !consume(Brace) { + initial = consume![if] || break; + } else { + depth -= 1; + } + } else if input.parse::().is_ok() || (consume![..] | consume![=]) { + initial = true; + } else if consume![.] { + if input.parse::>()?.is_none() + && (input.parse::()?.is_named() && consume![::]) + { + AngleBracketedGenericArguments::do_parse(None, input)?; + } + } else if consume![as] { + input.parse::()?; + } else if !(consume(Brace) || consume(Bracket) || consume(Parenthesis)) { + break; + } + } + + Err(input.error("unsupported expression")) +} From 0132c447fe045431906945178bea219816d5e55f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 1 Nov 2024 14:37:42 -0700 Subject: [PATCH 010/121] Make scan_expr compilable from integration test --- src/lib.rs | 2 ++ src/scan_expr.rs | 22 +++++++++------------- tests/test_precedence.rs | 4 ++++ 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index ee6e716d7b..4c5c70f069 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -307,6 +307,8 @@ clippy::wildcard_imports, )] +extern crate self as syn; + #[cfg(feature = "proc-macro")] extern crate proc_macro; diff --git a/src/scan_expr.rs b/src/scan_expr.rs index 04db8dc302..a3ec347082 100644 --- a/src/scan_expr.rs +++ b/src/scan_expr.rs @@ -1,13 +1,9 @@ -use crate::expr::Member; -use crate::lifetime::Lifetime; -use crate::lit::Lit; -use crate::lit::LitFloat; -use crate::op::{BinOp, UnOp}; -use crate::parse::{ParseStream, Result}; -use crate::path::{self, AngleBracketedGenericArguments}; -use crate::token; -use crate::ty::Type; use proc_macro2::Delimiter::{self, Brace, Bracket, Parenthesis}; +use syn::parse::{ParseStream, Result}; +use syn::{ + token, AngleBracketedGenericArguments, BinOp, ExprPath, Lifetime, Lit, LitFloat, Member, Token, + Type, UnOp, +}; pub(crate) fn scan_expr(input: ParseStream) -> Result<()> { let consume = |delimiter: Delimiter| { @@ -44,13 +40,13 @@ pub(crate) fn scan_expr(input: ParseStream) -> Result<()> { || (consume![..=] || consume![..] || consume![&] || consume![_]) || (consume(Brace) || consume(Bracket) || consume(Parenthesis))) { - path::parsing::qpath(input, true)?; + input.parse::()?; } } } else if input.parse::>()?.is_some() && !consume![:] { break; } else if input.parse::().is_err() { - path::parsing::qpath(input, true)?; + input.parse::()?; initial = consume![!] || depth == 0 && input.peek(token::Brace); } } else if input.is_empty() || input.peek(Token![,]) { @@ -65,9 +61,9 @@ pub(crate) fn scan_expr(input: ParseStream) -> Result<()> { initial = true; } else if consume![.] { if input.parse::>()?.is_none() - && (input.parse::()?.is_named() && consume![::]) + && (matches!(input.parse()?, Member::Named(_)) && input.peek(Token![::])) { - AngleBracketedGenericArguments::do_parse(None, input)?; + input.parse::()?; } } else if consume![as] { input.parse::()?; diff --git a/tests/test_precedence.rs b/tests/test_precedence.rs index 7231bd6409..077e8d4867 100644 --- a/tests/test_precedence.rs +++ b/tests/test_precedence.rs @@ -56,6 +56,10 @@ mod macros; mod common; mod repo; +#[path = "../src/scan_expr.rs"] +#[allow(dead_code)] +mod scan_expr; + #[test] fn test_rustc_precedence() { repo::rayon_init(); From 8039cb37a02cbf080f48416651141d4c77c05075 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 1 Nov 2024 14:47:22 -0700 Subject: [PATCH 011/121] Test that every expr can be scanned --- tests/test_precedence.rs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/tests/test_precedence.rs b/tests/test_precedence.rs index 077e8d4867..1fa64b3205 100644 --- a/tests/test_precedence.rs +++ b/tests/test_precedence.rs @@ -49,6 +49,7 @@ use std::fs; use std::path::Path; use std::process; use std::sync::atomic::{AtomicUsize, Ordering}; +use syn::parse::Parser as _; #[macro_use] mod macros; @@ -57,7 +58,6 @@ mod common; mod repo; #[path = "../src/scan_expr.rs"] -#[allow(dead_code)] mod scan_expr; #[test] @@ -119,7 +119,8 @@ fn test_expressions(path: &Path, edition: Edition, exprs: Vec) -> (us rustc_span::create_session_if_not_set_then(edition, |_| { for expr in exprs { - let source_code = expr.to_token_stream().to_string(); + let expr_tokens = expr.to_token_stream(); + let source_code = expr_tokens.to_string(); let librustc_ast = if let Some(e) = librustc_parse_and_rewrite(&source_code) { e } else { @@ -177,6 +178,16 @@ fn test_expressions(path: &Path, edition: Edition, exprs: Vec) -> (us continue; } + if scan_expr::scan_expr.parse2(expr_tokens).is_err() { + failed += 1; + errorf!( + "\nFAIL {} - failed to scan expr\n{}\n", + path.display(), + source_code, + ); + continue; + } + passed += 1; } }); From ca97c7d82d9837c1b49c085a546a481cf879e619 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 1 Nov 2024 14:55:30 -0700 Subject: [PATCH 012/121] Translate expr scanner to table driven --- src/scan_expr.rs | 320 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 254 insertions(+), 66 deletions(-) diff --git a/src/scan_expr.rs b/src/scan_expr.rs index a3ec347082..155b5b63bf 100644 --- a/src/scan_expr.rs +++ b/src/scan_expr.rs @@ -1,76 +1,264 @@ -use proc_macro2::Delimiter::{self, Brace, Bracket, Parenthesis}; +use self::{Action::*, Input::*}; +use proc_macro2::{Delimiter, Ident, Spacing, TokenTree}; use syn::parse::{ParseStream, Result}; -use syn::{ - token, AngleBracketedGenericArguments, BinOp, ExprPath, Lifetime, Lit, LitFloat, Member, Token, - Type, UnOp, -}; +use syn::{AngleBracketedGenericArguments, BinOp, Expr, ExprPath, Lifetime, Lit, Token, Type}; -pub(crate) fn scan_expr(input: ParseStream) -> Result<()> { - let consume = |delimiter: Delimiter| { - Result::unwrap(input.step(|cursor| match cursor.group(delimiter) { - Some((_inside, _span, rest)) => Ok((true, rest)), - None => Ok((false, *cursor)), - })) - }; - - macro_rules! consume { - [$token:tt] => { - input.parse::>().unwrap().is_some() - }; - } +enum Input { + Keyword(&'static str), + Punct(&'static str), + ConsumeAny, + ConsumeBinOp, + ConsumeBrace, + ConsumeDelimiter, + ConsumeIdent, + ConsumeLifetime, + ConsumeLiteral, + ConsumeNestedBrace, + ExpectPath, + ExpectTurbofish, + ExpectType, + CanBeginExpr, + Otherwise, + Empty, +} + +enum Action { + SetState(&'static [(Input, Action)]), + IncDepth, + DecDepth, + Finish, +} + +static INIT: [(Input, Action); 28] = [ + (ConsumeDelimiter, SetState(&POSTFIX)), + (Keyword("async"), SetState(&ASYNC)), + (Keyword("break"), SetState(&BREAK_LABEL)), + (Keyword("const"), SetState(&CONST)), + (Keyword("continue"), SetState(&CONTINUE)), + (Keyword("for"), SetState(&FOR)), + (Keyword("if"), IncDepth), + (Keyword("let"), SetState(&PATTERN)), + (Keyword("loop"), SetState(&BLOCK)), + (Keyword("match"), IncDepth), + (Keyword("move"), SetState(&CLOSURE)), + (Keyword("return"), SetState(&RETURN)), + (Keyword("static"), SetState(&CLOSURE)), + (Keyword("unsafe"), SetState(&BLOCK)), + (Keyword("while"), IncDepth), + (Keyword("yield"), SetState(&RETURN)), + (Keyword("_"), SetState(&POSTFIX)), + (Punct("!"), SetState(&INIT)), + (Punct("#"), SetState(&[(ConsumeDelimiter, SetState(&INIT))])), + (Punct("&"), SetState(&REFERENCE)), + (Punct("*"), SetState(&INIT)), + (Punct("-"), SetState(&INIT)), + (Punct("..="), SetState(&INIT)), + (Punct(".."), SetState(&RANGE)), + (Punct("|"), SetState(&CLOSURE_ARGS)), + (ConsumeLifetime, SetState(&[(Punct(":"), SetState(&INIT))])), + (ConsumeLiteral, SetState(&POSTFIX)), + (ExpectPath, SetState(&PATH)), +]; + +static POSTFIX: [(Input, Action); 10] = [ + (Keyword("as"), SetState(&[(ExpectType, SetState(&POSTFIX))])), + (Punct("..="), SetState(&INIT)), + (Punct(".."), SetState(&RANGE)), + (Punct("."), SetState(&DOT)), + (Punct("?"), SetState(&POSTFIX)), + (ConsumeBinOp, SetState(&INIT)), + (Punct("="), SetState(&INIT)), + (ConsumeNestedBrace, SetState(&IF_THEN)), + (ConsumeDelimiter, SetState(&POSTFIX)), + (Empty, Finish), +]; + +static ASYNC: [(Input, Action); 3] = [ + (Keyword("move"), SetState(&ASYNC)), + (Punct("|"), SetState(&CLOSURE_ARGS)), + (ConsumeBrace, SetState(&POSTFIX)), +]; + +static BLOCK: [(Input, Action); 1] = [(ConsumeBrace, SetState(&POSTFIX))]; + +static BREAK_LABEL: [(Input, Action); 2] = [ + (ConsumeLifetime, SetState(&BREAK_VALUE)), + (Otherwise, SetState(&BREAK_VALUE)), +]; + +static BREAK_VALUE: [(Input, Action); 3] = [ + (ConsumeNestedBrace, SetState(&IF_THEN)), + (CanBeginExpr, SetState(&INIT)), + (Otherwise, SetState(&POSTFIX)), +]; + +static CLOSURE: [(Input, Action); 6] = [ + (Keyword("async"), SetState(&CLOSURE)), + (Keyword("move"), SetState(&CLOSURE)), + (Punct(","), SetState(&CLOSURE)), + (Punct(">"), SetState(&CLOSURE)), + (Punct("|"), SetState(&CLOSURE_ARGS)), + (ConsumeLifetime, SetState(&CLOSURE)), +]; + +static CLOSURE_ARGS: [(Input, Action); 2] = [ + (Punct("|"), SetState(&CLOSURE_RET)), + (ConsumeAny, SetState(&CLOSURE_ARGS)), +]; + +static CLOSURE_RET: [(Input, Action); 2] = [ + (Punct("->"), SetState(&[(ExpectType, SetState(&BLOCK))])), + (Otherwise, SetState(&INIT)), +]; + +static CONST: [(Input, Action); 2] = [ + (Punct("|"), SetState(&CLOSURE_ARGS)), + (ConsumeBrace, SetState(&POSTFIX)), +]; + +static CONTINUE: [(Input, Action); 2] = [ + (ConsumeLifetime, SetState(&POSTFIX)), + (Otherwise, SetState(&POSTFIX)), +]; + +static DOT: [(Input, Action); 3] = [ + (Keyword("await"), SetState(&POSTFIX)), + (ConsumeIdent, SetState(&METHOD)), + (ConsumeLiteral, SetState(&POSTFIX)), +]; - let mut initial = true; +static FOR: [(Input, Action); 2] = [ + (Punct("<"), SetState(&CLOSURE)), + (Otherwise, SetState(&PATTERN)), +]; + +static IF_ELSE: [(Input, Action); 2] = [(Keyword("if"), SetState(&INIT)), (ConsumeBrace, DecDepth)]; +static IF_THEN: [(Input, Action); 2] = + [(Keyword("else"), SetState(&IF_ELSE)), (Otherwise, DecDepth)]; + +static METHOD: [(Input, Action); 1] = [(ExpectTurbofish, SetState(&POSTFIX))]; + +static PATH: [(Input, Action); 4] = [ + (Punct("!="), SetState(&INIT)), + (Punct("!"), SetState(&INIT)), + (ConsumeNestedBrace, SetState(&IF_THEN)), + (Otherwise, SetState(&POSTFIX)), +]; + +static PATTERN: [(Input, Action); 15] = [ + (ConsumeDelimiter, SetState(&PATTERN)), + (Keyword("box"), SetState(&PATTERN)), + (Keyword("in"), IncDepth), + (Keyword("mut"), SetState(&PATTERN)), + (Keyword("ref"), SetState(&PATTERN)), + (Keyword("_"), SetState(&PATTERN)), + (Punct("!"), SetState(&PATTERN)), + (Punct("&"), SetState(&PATTERN)), + (Punct("..="), SetState(&PATTERN)), + (Punct(".."), SetState(&PATTERN)), + (Punct("="), SetState(&INIT)), + (Punct("@"), SetState(&PATTERN)), + (Punct("|"), SetState(&PATTERN)), + (ConsumeLiteral, SetState(&PATTERN)), + (ExpectPath, SetState(&PATTERN)), +]; + +static RANGE: [(Input, Action); 6] = [ + (Punct("..="), SetState(&INIT)), + (Punct(".."), SetState(&RANGE)), + (Punct("."), SetState(&DOT)), + (ConsumeNestedBrace, SetState(&IF_THEN)), + (Empty, Finish), + (Otherwise, SetState(&INIT)), +]; + +static RAW: [(Input, Action); 3] = [ + (Keyword("const"), SetState(&INIT)), + (Keyword("mut"), SetState(&INIT)), + (Otherwise, SetState(&POSTFIX)), +]; + +static REFERENCE: [(Input, Action); 3] = [ + (Keyword("mut"), SetState(&INIT)), + (Keyword("raw"), SetState(&RAW)), + (Otherwise, SetState(&INIT)), +]; + +static RETURN: [(Input, Action); 2] = [ + (CanBeginExpr, SetState(&INIT)), + (Otherwise, SetState(&POSTFIX)), +]; + +pub(crate) fn scan_expr(input: ParseStream) -> Result<()> { + let mut state = INIT.as_slice(); let mut depth = 0usize; - loop { - if initial { - if consume![&] { - initial = consume![mut] || !consume![raw] || consume![const] || consume![mut]; - } else if consume![if] || consume![match] || consume![while] { - depth += 1; - } else if input.parse::>()?.is_some() - || (consume(Brace) || consume(Bracket) || consume(Parenthesis)) - || (consume![async] || consume![const] || consume![loop] || consume![unsafe]) - && (consume(Brace) || break) - { - initial = false; - } else if consume![let] { - while !consume![=] { - if !((consume![|] || consume![ref] || consume![mut] || consume![@]) - || (consume![!] || input.parse::>()?.is_some()) - || (consume![..=] || consume![..] || consume![&] || consume![_]) - || (consume(Brace) || consume(Bracket) || consume(Parenthesis))) - { - input.parse::()?; + 'table: loop { + for rule in state { + if match rule.0 { + Input::Keyword(expected) => input.step(|cursor| match cursor.ident() { + Some((ident, rest)) if ident == expected => Ok((true, rest)), + _ => Ok((false, *cursor)), + })?, + Input::Punct(expected) => input.step(|cursor| { + let begin = *cursor; + let mut cursor = begin; + for (i, ch) in expected.chars().enumerate() { + match cursor.punct() { + Some((punct, _)) if punct.as_char() != ch => break, + Some((_, rest)) if i == expected.len() - 1 => { + return Ok((true, rest)); + } + Some((punct, rest)) if punct.spacing() == Spacing::Joint => { + cursor = rest; + } + _ => break, + } } + Ok((false, begin)) + })?, + Input::ConsumeAny => input.parse::>()?.is_some(), + Input::ConsumeBinOp => input.parse::().is_ok(), + Input::ConsumeBrace | Input::ConsumeNestedBrace => { + (matches!(rule.0, Input::ConsumeBrace) || depth > 0) + && input.step(|cursor| match cursor.group(Delimiter::Brace) { + Some((_inside, _span, rest)) => Ok((true, rest)), + None => Ok((false, *cursor)), + })? } - } else if input.parse::>()?.is_some() && !consume![:] { - break; - } else if input.parse::().is_err() { - input.parse::()?; - initial = consume![!] || depth == 0 && input.peek(token::Brace); - } - } else if input.is_empty() || input.peek(Token![,]) { - return Ok(()); - } else if depth > 0 && consume(Brace) { - if consume![else] && !consume(Brace) { - initial = consume![if] || break; - } else { - depth -= 1; - } - } else if input.parse::().is_ok() || (consume![..] | consume![=]) { - initial = true; - } else if consume![.] { - if input.parse::>()?.is_none() - && (matches!(input.parse()?, Member::Named(_)) && input.peek(Token![::])) - { - input.parse::()?; + Input::ConsumeDelimiter => input.step(|cursor| match cursor.any_group() { + Some((_inside, _delimiter, _span, rest)) => Ok((true, rest)), + None => Ok((false, *cursor)), + })?, + Input::ConsumeIdent => input.parse::>()?.is_some(), + Input::ConsumeLifetime => input.parse::>()?.is_some(), + Input::ConsumeLiteral => input.parse::>()?.is_some(), + Input::ExpectPath => { + input.parse::()?; + true + } + Input::ExpectTurbofish => { + if input.peek(Token![::]) { + input.parse::()?; + } + true + } + Input::ExpectType => { + Type::without_plus(input)?; + true + } + Input::CanBeginExpr => Expr::peek(input), + Input::Otherwise => true, + Input::Empty => input.is_empty() || input.peek(Token![,]), + } { + state = match rule.1 { + Action::SetState(next) => next, + Action::IncDepth => (depth += 1, &INIT).1, + Action::DecDepth => (depth -= 1, &POSTFIX).1, + Action::Finish => return if depth == 0 { Ok(()) } else { break }, + }; + continue 'table; } - } else if consume![as] { - input.parse::()?; - } else if !(consume(Brace) || consume(Bracket) || consume(Parenthesis)) { - break; } + return Err(input.error("unsupported expression")); } - - Err(input.error("unsupported expression")) } From 0986a66e1764ed37a4931dde8c509412474636fe Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 1 Nov 2024 21:36:43 -0700 Subject: [PATCH 013/121] Ignore enum_glob_use pedantic clippy lint warning: usage of wildcard import for enum variants --> src/scan_expr.rs:1:12 | 1 | use self::{Action::*, Input::*}; | ^^^^^^^^^ help: try: `Action::{DecDepth, Finish, IncDepth, SetState}` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#enum_glob_use = note: `-W clippy::enum-glob-use` implied by `-W clippy::pedantic` = help: to override `-W clippy::pedantic` add `#[allow(clippy::enum_glob_use)]` warning: usage of wildcard import for enum variants --> src/scan_expr.rs:1:23 | 1 | use self::{Action::*, Input::*}; | ^^^^^^^^ help: try: `Input::{CanBeginExpr, ConsumeAny, ConsumeBinOp, ConsumeBrace, ConsumeDelimiter, ConsumeIdent, ConsumeLifetime, ConsumeLiteral, ConsumeNestedBrace, Empty, ExpectPath, ExpectTurbofish, ExpectType, Keyword, Otherwise, Punct}` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#enum_glob_use --- src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib.rs b/src/lib.rs index 4c5c70f069..d97f3b0477 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -264,6 +264,7 @@ clippy::derivable_impls, clippy::diverging_sub_expression, clippy::doc_markdown, + clippy::enum_glob_use, clippy::expl_impl_clone_on_copy, clippy::explicit_auto_deref, clippy::if_not_else, From a777cff00528f270b43f40b0a58c5c26fc85a2bd Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 2 Nov 2024 09:26:44 -0700 Subject: [PATCH 014/121] Release 2.0.87 --- Cargo.toml | 2 +- src/lib.rs | 2 +- syn.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a40b541591..e3af17d491 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "syn" -version = "2.0.86" +version = "2.0.87" authors = ["David Tolnay "] categories = ["development-tools::procedural-macro-helpers", "parser-implementations"] description = "Parser for Rust source code" diff --git a/src/lib.rs b/src/lib.rs index d97f3b0477..19736acf92 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -249,7 +249,7 @@ //! dynamic library libproc_macro from rustc toolchain. // Syn types in rustdoc of other crates get linked to here. -#![doc(html_root_url = "https://docs.rs/syn/2.0.86")] +#![doc(html_root_url = "https://docs.rs/syn/2.0.87")] #![cfg_attr(docsrs, feature(doc_cfg))] #![deny(unsafe_op_in_unsafe_fn)] #![allow(non_camel_case_types)] diff --git a/syn.json b/syn.json index 55a24b3cc7..c8ca6b678c 100644 --- a/syn.json +++ b/syn.json @@ -1,5 +1,5 @@ { - "version": "2.0.86", + "version": "2.0.87", "types": [ { "ident": "Abi", From 2b45e9331e5eece0f1cea01a9378f4296c1f1d60 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 4 Nov 2024 18:23:59 -0800 Subject: [PATCH 015/121] Raise minimum version for syn-codegen crate to Rust 1.65 Required by hashbrown 0.15.1. error: package `hashbrown v0.15.1` cannot be built because it requires rustc 1.65.0 or newer, while the currently active rustc version is 1.63.0 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 95c6d6fdad..009613cbd0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -42,7 +42,7 @@ jobs: strategy: fail-fast: false matrix: - rust: [stable, beta, 1.63.0, 1.61.0] + rust: [stable, beta, 1.65.0, 1.61.0] include: - rust: nightly components: rustc-dev From 5bbe46a8c873e52ba289071ac30cd058c29a6e77 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 8 Nov 2024 21:45:50 -0500 Subject: [PATCH 016/121] Prevent upload-artifact step from causing CI failure This step has been failing way more than reasonable across my various repos. With the provided path, there will be 1 file uploaded Artifact name is valid! Root directory input is valid! Attempt 1 of 5 failed with error: Request timeout: /twirp/github.actions.results.api.v1.ArtifactService/CreateArtifact. Retrying request in 3000 ms... Attempt 2 of 5 failed with error: Request timeout: /twirp/github.actions.results.api.v1.ArtifactService/CreateArtifact. Retrying request in 6029 ms... Attempt 3 of 5 failed with error: Request timeout: /twirp/github.actions.results.api.v1.ArtifactService/CreateArtifact. Retrying request in 8270 ms... Attempt 4 of 5 failed with error: Request timeout: /twirp/github.actions.results.api.v1.ArtifactService/CreateArtifact. Retrying request in 12577 ms... Error: Failed to CreateArtifact: Failed to make request after 5 attempts: Request timeout: /twirp/github.actions.results.api.v1.ArtifactService/CreateArtifact --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 009613cbd0..24cd6acf8c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,6 +33,7 @@ jobs: with: name: Cargo.lock path: Cargo.lock + continue-on-error: true build: name: ${{matrix.name || format('Rust {0}', matrix.rust)}} From 5c1c057bfbcc55d05938a5b866428d86e6fe9f0a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 12 Nov 2024 15:21:42 -0800 Subject: [PATCH 017/121] Disable toml "display" feature --- codegen/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codegen/Cargo.toml b/codegen/Cargo.toml index 1e25da5129..76fe30674a 100644 --- a/codegen/Cargo.toml +++ b/codegen/Cargo.toml @@ -24,7 +24,7 @@ serde = "1.0.88" serde_json = "1.0.38" syn = { version = "2", features = ["derive", "full", "parsing", "printing"], default-features = false } syn-codegen = { path = "../json", default-features = false } -toml = "0.8" +toml = { version = "0.8", default-features = false, features = ["parse"] } [workspace] [patch.crates-io] From 0ccac34ff92fac4ddb488e8f1c7f9527e12b231b Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 15 Nov 2024 19:24:46 -0800 Subject: [PATCH 018/121] Resolve question_mark clippy lint warning: this `match` expression can be replaced with `?` --> src/generics.rs:191:20 | 191 | let next = match self.0.next() { | ____________________^ 192 | | Some(item) => item, 193 | | None => return None, 194 | | }; | |_________^ help: try instead: `self.0.next()?` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#question_mark = note: `-W clippy::question-mark` implied by `-W clippy::all` = help: to override `-W clippy::all` add `#[allow(clippy::question_mark)]` warning: this `match` expression can be replaced with `?` --> src/generics.rs:209:20 | 209 | let next = match self.0.next() { | ____________________^ 210 | | Some(item) => item, 211 | | None => return None, 212 | | }; | |_________^ help: try instead: `self.0.next()?` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#question_mark warning: this `match` expression can be replaced with `?` --> src/generics.rs:227:20 | 227 | let next = match self.0.next() { | ____________________^ 228 | | Some(item) => item, 229 | | None => return None, 230 | | }; | |_________^ help: try instead: `self.0.next()?` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#question_mark warning: this `match` expression can be replaced with `?` --> src/generics.rs:245:20 | 245 | let next = match self.0.next() { | ____________________^ 246 | | Some(item) => item, 247 | | None => return None, 248 | | }; | |_________^ help: try instead: `self.0.next()?` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#question_mark warning: this `match` expression can be replaced with `?` --> src/generics.rs:263:20 | 263 | let next = match self.0.next() { | ____________________^ 264 | | Some(item) => item, 265 | | None => return None, 266 | | }; | |_________^ help: try instead: `self.0.next()?` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#question_mark warning: this `match` expression can be replaced with `?` --> src/generics.rs:281:20 | 281 | let next = match self.0.next() { | ____________________^ 282 | | Some(item) => item, 283 | | None => return None, 284 | | }; | |_________^ help: try instead: `self.0.next()?` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#question_mark --- src/generics.rs | 36 ++++++------------------------------ 1 file changed, 6 insertions(+), 30 deletions(-) diff --git a/src/generics.rs b/src/generics.rs index 1e8fccf039..b0723691e4 100644 --- a/src/generics.rs +++ b/src/generics.rs @@ -188,11 +188,7 @@ impl<'a> Iterator for Lifetimes<'a> { type Item = &'a LifetimeParam; fn next(&mut self) -> Option { - let next = match self.0.next() { - Some(item) => item, - None => return None, - }; - if let GenericParam::Lifetime(lifetime) = next { + if let GenericParam::Lifetime(lifetime) = self.0.next()? { Some(lifetime) } else { self.next() @@ -206,11 +202,7 @@ impl<'a> Iterator for LifetimesMut<'a> { type Item = &'a mut LifetimeParam; fn next(&mut self) -> Option { - let next = match self.0.next() { - Some(item) => item, - None => return None, - }; - if let GenericParam::Lifetime(lifetime) = next { + if let GenericParam::Lifetime(lifetime) = self.0.next()? { Some(lifetime) } else { self.next() @@ -224,11 +216,7 @@ impl<'a> Iterator for TypeParams<'a> { type Item = &'a TypeParam; fn next(&mut self) -> Option { - let next = match self.0.next() { - Some(item) => item, - None => return None, - }; - if let GenericParam::Type(type_param) = next { + if let GenericParam::Type(type_param) = self.0.next()? { Some(type_param) } else { self.next() @@ -242,11 +230,7 @@ impl<'a> Iterator for TypeParamsMut<'a> { type Item = &'a mut TypeParam; fn next(&mut self) -> Option { - let next = match self.0.next() { - Some(item) => item, - None => return None, - }; - if let GenericParam::Type(type_param) = next { + if let GenericParam::Type(type_param) = self.0.next()? { Some(type_param) } else { self.next() @@ -260,11 +244,7 @@ impl<'a> Iterator for ConstParams<'a> { type Item = &'a ConstParam; fn next(&mut self) -> Option { - let next = match self.0.next() { - Some(item) => item, - None => return None, - }; - if let GenericParam::Const(const_param) = next { + if let GenericParam::Const(const_param) = self.0.next()? { Some(const_param) } else { self.next() @@ -278,11 +258,7 @@ impl<'a> Iterator for ConstParamsMut<'a> { type Item = &'a mut ConstParam; fn next(&mut self) -> Option { - let next = match self.0.next() { - Some(item) => item, - None => return None, - }; - if let GenericParam::Const(const_param) = next { + if let GenericParam::Const(const_param) = self.0.next()? { Some(const_param) } else { self.next() From fc133ebb037aa74c9a6ca10f9d394a7a9ee509fd Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 15 Nov 2024 19:26:27 -0800 Subject: [PATCH 019/121] Resolve unnecessary_map_or clippy lint warning: this `map_or` is redundant --> src/snapshot.rs:278:40 | 278 | ... if variants.get("None").map_or(false, Vec::is_empty) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_some_and instead: `variants.get("None").is_some_and(Vec::is_empty)` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_map_or = note: `-W clippy::unnecessary-map-or` implied by `-W clippy::all` = help: to override `-W clippy::all` add `#[allow(clippy::unnecessary_map_or)]` --- codegen/src/snapshot.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codegen/src/snapshot.rs b/codegen/src/snapshot.rs index 17588dbcc0..c138830dd7 100644 --- a/codegen/src/snapshot.rs +++ b/codegen/src/snapshot.rs @@ -275,7 +275,7 @@ fn expand_impl_body(defs: &Definitions, node: &Node, name: &str, val: &Operand) for node in &defs.types { if node.ident == *inner { if let Data::Enum(variants) = &node.data { - if variants.get("None").map_or(false, Vec::is_empty) { + if variants.get("None").is_some_and(Vec::is_empty) { let ty = rust_type(ty); call = quote! { match #val.#ident { From f46a6f32246696a1585c2c9e68c78b44079d68a5 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 17 Nov 2024 19:55:53 -0800 Subject: [PATCH 020/121] Update test suite to nightly-2024-11-18 --- tests/test_precedence.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_precedence.rs b/tests/test_precedence.rs index 1fa64b3205..e64cbf1dd3 100644 --- a/tests/test_precedence.rs +++ b/tests/test_precedence.rs @@ -205,7 +205,7 @@ fn librustc_parenthesize(mut librustc_expr: P) -> P { ExprKind, GenericArg, GenericBound, Local, LocalKind, Pat, PolyTraitRef, Stmt, StmtKind, StructExpr, StructRest, TraitBoundModifiers, Ty, }; - use rustc_ast::mut_visit::{walk_flat_map_item, MutVisitor}; + use rustc_ast::mut_visit::{walk_flat_map_assoc_item, MutVisitor}; use rustc_ast::visit::{AssocCtxt, BoundKind}; use rustc_data_structures::flat_map_in_place::FlatMapInPlace; use rustc_span::DUMMY_SP; @@ -349,7 +349,7 @@ fn librustc_parenthesize(mut librustc_expr: P) -> P { fn flat_map_assoc_item( &mut self, item: P, - _ctxt: AssocCtxt, + ctxt: AssocCtxt, ) -> SmallVec<[P; 1]> { match &item.kind { AssocItemKind::Const(const_item) @@ -358,7 +358,7 @@ fn librustc_parenthesize(mut librustc_expr: P) -> P { { SmallVec::from([item]) } - _ => walk_flat_map_item(self, item), + _ => walk_flat_map_assoc_item(self, item, ctxt), } } From 2375d9ac019e86f65eb07bed45c3ffd8ead92690 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 20 Nov 2024 19:32:14 -0800 Subject: [PATCH 021/121] Pull in proc-macro2 FromStr's more robust error recovery --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index e3af17d491..9a6911f057 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,7 +35,7 @@ proc-macro = ["proc-macro2/proc-macro", "quote?/proc-macro"] test = ["syn-test-suite/all-features"] [dependencies] -proc-macro2 = { version = "1.0.83", default-features = false } +proc-macro2 = { version = "1.0.90", default-features = false } quote = { version = "1.0.35", optional = true, default-features = false } unicode-ident = "1" From 424e484905c4fa83e2f5a396c6996f7d066c5572 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 20 Nov 2024 19:35:50 -0800 Subject: [PATCH 022/121] Release 2.0.88 --- Cargo.toml | 2 +- src/lib.rs | 2 +- syn.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9a6911f057..4a6f118f04 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "syn" -version = "2.0.87" +version = "2.0.88" authors = ["David Tolnay "] categories = ["development-tools::procedural-macro-helpers", "parser-implementations"] description = "Parser for Rust source code" diff --git a/src/lib.rs b/src/lib.rs index 19736acf92..c3e219a471 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -249,7 +249,7 @@ //! dynamic library libproc_macro from rustc toolchain. // Syn types in rustdoc of other crates get linked to here. -#![doc(html_root_url = "https://docs.rs/syn/2.0.87")] +#![doc(html_root_url = "https://docs.rs/syn/2.0.88")] #![cfg_attr(docsrs, feature(doc_cfg))] #![deny(unsafe_op_in_unsafe_fn)] #![allow(non_camel_case_types)] diff --git a/syn.json b/syn.json index c8ca6b678c..6996f3f503 100644 --- a/syn.json +++ b/syn.json @@ -1,5 +1,5 @@ { - "version": "2.0.87", + "version": "2.0.88", "types": [ { "ident": "Abi", From 42b47472503523b56979e6e15a905b02be31cf0d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 20 Nov 2024 21:51:51 -0800 Subject: [PATCH 023/121] Fix "compiler/fallback mismatch 949" panic --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 4a6f118f04..c13bbce1e5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,7 +35,7 @@ proc-macro = ["proc-macro2/proc-macro", "quote?/proc-macro"] test = ["syn-test-suite/all-features"] [dependencies] -proc-macro2 = { version = "1.0.90", default-features = false } +proc-macro2 = { version = "1.0.91", default-features = false } quote = { version = "1.0.35", optional = true, default-features = false } unicode-ident = "1" From 06af36b972a9d50e961ebbe1bc6e8b56b1905cf4 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 20 Nov 2024 21:53:18 -0800 Subject: [PATCH 024/121] Release 2.0.89 --- Cargo.toml | 2 +- src/lib.rs | 2 +- syn.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c13bbce1e5..5997802aec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "syn" -version = "2.0.88" +version = "2.0.89" authors = ["David Tolnay "] categories = ["development-tools::procedural-macro-helpers", "parser-implementations"] description = "Parser for Rust source code" diff --git a/src/lib.rs b/src/lib.rs index c3e219a471..d5be866db2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -249,7 +249,7 @@ //! dynamic library libproc_macro from rustc toolchain. // Syn types in rustdoc of other crates get linked to here. -#![doc(html_root_url = "https://docs.rs/syn/2.0.88")] +#![doc(html_root_url = "https://docs.rs/syn/2.0.89")] #![cfg_attr(docsrs, feature(doc_cfg))] #![deny(unsafe_op_in_unsafe_fn)] #![allow(non_camel_case_types)] diff --git a/syn.json b/syn.json index 6996f3f503..72fa9e76e7 100644 --- a/syn.json +++ b/syn.json @@ -1,5 +1,5 @@ { - "version": "2.0.88", + "version": "2.0.89", "types": [ { "ident": "Abi", From 935eefe6740af842d37ace71ee26418c4fbc994b Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 25 Nov 2024 18:34:03 -0800 Subject: [PATCH 025/121] Update test suite to nightly-2024-11-26 --- tests/common/eq.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/common/eq.rs b/tests/common/eq.rs index 119df2b73e..337e14211c 100644 --- a/tests/common/eq.rs +++ b/tests/common/eq.rs @@ -493,7 +493,7 @@ spanless_eq_struct!(DelimSpacing; open close); spanless_eq_struct!(EnumDef; variants); spanless_eq_struct!(Expr; id kind span attrs !tokens); spanless_eq_struct!(ExprField; attrs id span ident expr is_shorthand is_placeholder); -spanless_eq_struct!(FieldDef; attrs id span vis ident ty is_placeholder); +spanless_eq_struct!(FieldDef; attrs id span vis safety ident ty is_placeholder); spanless_eq_struct!(Fn; defaultness generics sig body); spanless_eq_struct!(FnDecl; inputs output); spanless_eq_struct!(FnHeader; constness coroutine_kind safety ext); From 64fce43f35281d83498567617246ca59813bee79 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 26 Nov 2024 19:04:00 -0800 Subject: [PATCH 026/121] Update test suite to nightly-2024-11-27 --- tests/common/eq.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/common/eq.rs b/tests/common/eq.rs index 337e14211c..af1f616187 100644 --- a/tests/common/eq.rs +++ b/tests/common/eq.rs @@ -161,6 +161,7 @@ use rustc_ast::ast::WhereBoundPredicate; use rustc_ast::ast::WhereClause; use rustc_ast::ast::WhereEqPredicate; use rustc_ast::ast::WherePredicate; +use rustc_ast::ast::WherePredicateKind; use rustc_ast::ast::WhereRegionPredicate; use rustc_ast::ptr::P; use rustc_ast::token::{ @@ -545,10 +546,11 @@ spanless_eq_struct!(TyAliasWhereClauses; before after !split); spanless_eq_struct!(UseTree; prefix kind span); spanless_eq_struct!(Variant; attrs id span !vis ident data disr_expr is_placeholder); spanless_eq_struct!(Visibility; kind span tokens); -spanless_eq_struct!(WhereBoundPredicate; span bound_generic_params bounded_ty bounds); +spanless_eq_struct!(WhereBoundPredicate; bound_generic_params bounded_ty bounds); spanless_eq_struct!(WhereClause; has_where_token predicates span); -spanless_eq_struct!(WhereEqPredicate; span lhs_ty rhs_ty); -spanless_eq_struct!(WhereRegionPredicate; span lifetime bounds); +spanless_eq_struct!(WhereEqPredicate; lhs_ty rhs_ty); +spanless_eq_struct!(WherePredicate; kind id span); +spanless_eq_struct!(WhereRegionPredicate; lifetime bounds); spanless_eq_enum!(AngleBracketedArg; Arg(0) Constraint(0)); spanless_eq_enum!(AsmMacro; Asm GlobalAsm NakedAsm); spanless_eq_enum!(AssocItemConstraintKind; Equality(term) Bound(bounds)); @@ -620,7 +622,7 @@ spanless_eq_enum!(UnsafeSource; CompilerGenerated UserProvided); spanless_eq_enum!(UseTreeKind; Simple(0) Nested(items span) Glob); spanless_eq_enum!(VariantData; Struct(fields recovered) Tuple(0 1) Unit(0)); spanless_eq_enum!(VisibilityKind; Public Restricted(path id shorthand) Inherited); -spanless_eq_enum!(WherePredicate; BoundPredicate(0) RegionPredicate(0) EqPredicate(0)); +spanless_eq_enum!(WherePredicateKind; BoundPredicate(0) RegionPredicate(0) EqPredicate(0)); spanless_eq_enum!(CoroutineKind; Async(span closure_id return_impl_trait_id) Gen(span closure_id return_impl_trait_id) AsyncGen(span closure_id return_impl_trait_id)); From b245e2ad8b1d5b50551ddadc1dd29c3f3b6cca04 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 28 Nov 2024 18:34:47 -0800 Subject: [PATCH 027/121] Use new name of WASI preview1 target --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 24cd6acf8c..e7c41bd5b1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -52,7 +52,7 @@ jobs: target: wasm32-unknown-unknown - rust: nightly name: WASI - target: wasm32-wasi + target: wasm32-wasip1 - rust: nightly name: Windows os: windows From ee6cd5da1909b27ed93e54ea14af572887828fc9 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 28 Nov 2024 18:35:38 -0800 Subject: [PATCH 028/121] Add WASI preview2 job in CI --- .github/workflows/ci.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e7c41bd5b1..3b418fe6e8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -51,8 +51,11 @@ jobs: name: WebAssembly target: wasm32-unknown-unknown - rust: nightly - name: WASI + name: WASI preview1 target: wasm32-wasip1 + - rust: nightly + name: WASI preview2 + target: wasm32-wasip2 - rust: nightly name: Windows os: windows From 401399fbb6225caa75b2008daaa9dc284a268076 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 29 Nov 2024 13:13:04 -0800 Subject: [PATCH 029/121] Add test of attr precedence inside subexpression ---- test_fixup stdout ---- thread 'test_fixup' panicked at tests/test_expr.rs:716:9: original: (# [attr] thing) . field reconstructed: # [attr] thing . field --- tests/test_expr.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_expr.rs b/tests/test_expr.rs index f01fcb8ce2..6dd876221d 100644 --- a/tests/test_expr.rs +++ b/tests/test_expr.rs @@ -702,6 +702,7 @@ fn test_fixup() { quote! { (..) += () }, quote! { (1 < 2) == (3 < 4) }, quote! { { (let _ = ()) } }, + quote! { (#[attr] thing).field }, ] { let original: Expr = syn::parse2(tokens).unwrap(); From 204c1556e49c4c01f11481977d6deb9f1de36a15 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 29 Nov 2024 12:47:33 -0800 Subject: [PATCH 030/121] Lower precedence of expressions containing outer attrs --- src/precedence.rs | 54 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 51 insertions(+), 3 deletions(-) diff --git a/src/precedence.rs b/src/precedence.rs index 936a0c51d2..1891bfc202 100644 --- a/src/precedence.rs +++ b/src/precedence.rs @@ -1,7 +1,14 @@ +#[cfg(all(feature = "printing", feature = "full"))] +use crate::attr::{AttrStyle, Attribute}; #[cfg(feature = "printing")] use crate::expr::Expr; #[cfg(all(feature = "printing", feature = "full"))] -use crate::expr::{ExprBreak, ExprReturn, ExprYield}; +use crate::expr::{ + ExprArray, ExprAsync, ExprAwait, ExprBlock, ExprBreak, ExprCall, ExprConst, ExprContinue, + ExprField, ExprForLoop, ExprGroup, ExprIf, ExprIndex, ExprInfer, ExprLit, ExprLoop, ExprMacro, + ExprMatch, ExprMethodCall, ExprParen, ExprPath, ExprRepeat, ExprReturn, ExprStruct, ExprTry, + ExprTryBlock, ExprTuple, ExprUnsafe, ExprWhile, ExprYield, +}; use crate::op::BinOp; #[cfg(all(feature = "printing", feature = "full"))] use crate::ty::ReturnType; @@ -82,11 +89,21 @@ impl Precedence { #[cfg(feature = "printing")] pub(crate) fn of(e: &Expr) -> Self { + #[cfg(feature = "full")] + fn prefix_attrs(attrs: &[Attribute]) -> Precedence { + for attr in attrs { + if let AttrStyle::Outer = attr.style { + return Precedence::Prefix; + } + } + Precedence::Unambiguous + } + match e { #[cfg(feature = "full")] Expr::Closure(e) => match e.output { ReturnType::Default => Precedence::Jump, - ReturnType::Type(..) => Precedence::Unambiguous, + ReturnType::Type(..) => prefix_attrs(&e.attrs), }, #[cfg(feature = "full")] @@ -104,6 +121,36 @@ impl Precedence { Expr::Cast(_) => Precedence::Cast, Expr::RawAddr(_) | Expr::Reference(_) | Expr::Unary(_) => Precedence::Prefix, + #[cfg(feature = "full")] + Expr::Array(ExprArray { attrs, .. }) + | Expr::Async(ExprAsync { attrs, .. }) + | Expr::Await(ExprAwait { attrs, .. }) + | Expr::Block(ExprBlock { attrs, .. }) + | Expr::Call(ExprCall { attrs, .. }) + | Expr::Const(ExprConst { attrs, .. }) + | Expr::Continue(ExprContinue { attrs, .. }) + | Expr::Field(ExprField { attrs, .. }) + | Expr::ForLoop(ExprForLoop { attrs, .. }) + | Expr::Group(ExprGroup { attrs, .. }) + | Expr::If(ExprIf { attrs, .. }) + | Expr::Index(ExprIndex { attrs, .. }) + | Expr::Infer(ExprInfer { attrs, .. }) + | Expr::Lit(ExprLit { attrs, .. }) + | Expr::Loop(ExprLoop { attrs, .. }) + | Expr::Macro(ExprMacro { attrs, .. }) + | Expr::Match(ExprMatch { attrs, .. }) + | Expr::MethodCall(ExprMethodCall { attrs, .. }) + | Expr::Paren(ExprParen { attrs, .. }) + | Expr::Path(ExprPath { attrs, .. }) + | Expr::Repeat(ExprRepeat { attrs, .. }) + | Expr::Struct(ExprStruct { attrs, .. }) + | Expr::Try(ExprTry { attrs, .. }) + | Expr::TryBlock(ExprTryBlock { attrs, .. }) + | Expr::Tuple(ExprTuple { attrs, .. }) + | Expr::Unsafe(ExprUnsafe { attrs, .. }) + | Expr::While(ExprWhile { attrs, .. }) => prefix_attrs(attrs), + + #[cfg(not(feature = "full"))] Expr::Array(_) | Expr::Async(_) | Expr::Await(_) @@ -130,9 +177,10 @@ impl Precedence { | Expr::TryBlock(_) | Expr::Tuple(_) | Expr::Unsafe(_) - | Expr::Verbatim(_) | Expr::While(_) => Precedence::Unambiguous, + Expr::Verbatim(_) => Precedence::Unambiguous, + #[cfg(not(feature = "full"))] Expr::Break(_) | Expr::Closure(_) | Expr::Return(_) | Expr::Yield(_) => unreachable!(), } From 0e5c5686503d7ef41b344c2464e35e6329ed75f1 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 29 Nov 2024 12:56:45 -0800 Subject: [PATCH 031/121] Add test of parentheses needed in call of field ---- test_fixup stdout ---- thread 'test_fixup' panicked at tests/test_expr.rs:717:9: original: (self . f) () reconstructed: self . f () --- tests/test_expr.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_expr.rs b/tests/test_expr.rs index 6dd876221d..03ebd1db65 100644 --- a/tests/test_expr.rs +++ b/tests/test_expr.rs @@ -703,6 +703,7 @@ fn test_fixup() { quote! { (1 < 2) == (3 < 4) }, quote! { { (let _ = ()) } }, quote! { (#[attr] thing).field }, + quote! { (self.f)() }, ] { let original: Expr = syn::parse2(tokens).unwrap(); From 48d0101557047df08ca7859ab8c5eae9cb6c0805 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 29 Nov 2024 13:00:49 -0800 Subject: [PATCH 032/121] Fix parenthesization of field expressions in function calls --- src/expr.rs | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/expr.rs b/src/expr.rs index f68f6322fe..84e1e98956 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -3405,22 +3405,17 @@ pub(crate) mod printing { fn print_expr_call(e: &ExprCall, tokens: &mut TokenStream, fixup: FixupContext) { outer_attrs_to_tokens(&e.attrs, tokens); - let call_precedence = if let Expr::Field(_) = &*e.func { - Precedence::MIN - } else { - Precedence::Unambiguous - }; let func_fixup = fixup.leftmost_subexpression_with_begin_operator( #[cfg(feature = "full")] true, false, ); - print_subexpression( - &e.func, - func_fixup.leading_precedence(&e.func) < call_precedence, - tokens, - func_fixup, - ); + let needs_group = if let Expr::Field(_) = &*e.func { + true + } else { + func_fixup.leading_precedence(&e.func) < Precedence::Unambiguous + }; + print_subexpression(&e.func, needs_group, tokens, func_fixup); e.paren_token.surround(tokens, |tokens| { e.args.to_tokens(tokens); From 1445ccf5866dadeabcbd05f4d1bd3543298c1e2d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 29 Nov 2024 13:33:04 -0800 Subject: [PATCH 033/121] Do not parenthesize unnamed tuple struct fields in call --- src/expr.rs | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/expr.rs b/src/expr.rs index 84e1e98956..545b43dfd7 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -1036,6 +1036,16 @@ impl IdentFragment for Member { } } +#[cfg(any(feature = "parsing", feature = "printing"))] +impl Member { + pub(crate) fn is_named(&self) -> bool { + match self { + Member::Named(_) => true, + Member::Unnamed(_) => false, + } + } +} + ast_struct! { /// The index of an unnamed tuple struct field. #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))] @@ -3021,15 +3031,6 @@ pub(crate) mod parsing { Ok(!trailing_dot) } - impl Member { - pub(crate) fn is_named(&self) -> bool { - match self { - Member::Named(_) => true, - Member::Unnamed(_) => false, - } - } - } - #[cfg(feature = "full")] #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] impl Parse for PointerMutability { @@ -3410,8 +3411,8 @@ pub(crate) mod printing { true, false, ); - let needs_group = if let Expr::Field(_) = &*e.func { - true + let needs_group = if let Expr::Field(func) = &*e.func { + func.member.is_named() } else { func_fixup.leading_precedence(&e.func) < Precedence::Unambiguous }; From 1a549c077232951c8f7f87d168f67a5c36b2c83f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 29 Nov 2024 12:23:36 -0800 Subject: [PATCH 034/121] Improve test_unparenthesize to catch more false negatives --- tests/test_unparenthesize.rs | 50 +++++++++++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 4 deletions(-) diff --git a/tests/test_unparenthesize.rs b/tests/test_unparenthesize.rs index 5db2e57a52..5e155bff6b 100644 --- a/tests/test_unparenthesize.rs +++ b/tests/test_unparenthesize.rs @@ -12,7 +12,7 @@ use std::panic; use std::path::Path; use std::sync::atomic::{AtomicUsize, Ordering}; use syn::visit_mut::{self, VisitMut}; -use syn::Expr; +use syn::{Expr, Generics, LifetimeParam, TypeParam}; #[macro_use] mod macros; @@ -45,13 +45,55 @@ impl VisitMut for FlattenParens { } } +struct AsIfPrinted; + +impl VisitMut for AsIfPrinted { + fn visit_generics_mut(&mut self, generics: &mut Generics) { + if generics.params.is_empty() { + generics.lt_token = None; + generics.gt_token = None; + } + if let Some(where_clause) = &generics.where_clause { + if where_clause.predicates.is_empty() { + generics.where_clause = None; + } + } + visit_mut::visit_generics_mut(self, generics); + } + + fn visit_lifetime_param_mut(&mut self, param: &mut LifetimeParam) { + if param.bounds.is_empty() { + param.colon_token = None; + } + visit_mut::visit_lifetime_param_mut(self, param); + } + + fn visit_type_param_mut(&mut self, param: &mut TypeParam) { + if param.bounds.is_empty() { + param.colon_token = None; + } + visit_mut::visit_type_param_mut(self, param); + } +} + fn test(path: &Path, failed: &AtomicUsize) { let content = fs::read_to_string(path).unwrap(); match panic::catch_unwind(|| -> syn::Result<()> { - let mut syntax_tree = syn::parse_file(&content)?; - FlattenParens.visit_file_mut(&mut syntax_tree); - syn::parse2::(syntax_tree.to_token_stream())?; + let mut before = syn::parse_file(&content)?; + before.shebang = None; + FlattenParens.visit_file_mut(&mut before); + let printed = before.to_token_stream(); + let mut after = syn::parse2::(printed.clone())?; + FlattenParens.visit_file_mut(&mut after); + // Normalize features that we expect Syn not to print. + AsIfPrinted.visit_file_mut(&mut before); + if before != after { + errorf!("=== {}\n", path.display()); + if failed.fetch_add(1, Ordering::Relaxed) == 0 { + errorf!("BEFORE:\n{:#?}\nAFTER:\n{:#?}\n", before, after); + } + } Ok(()) }) { Err(_) => { From ac5b41c852137c3d147202e3e6b2666c924cb87c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 29 Nov 2024 13:45:58 -0800 Subject: [PATCH 035/121] Release 2.0.90 --- Cargo.toml | 2 +- src/lib.rs | 2 +- syn.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5997802aec..71332d4b53 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "syn" -version = "2.0.89" +version = "2.0.90" authors = ["David Tolnay "] categories = ["development-tools::procedural-macro-helpers", "parser-implementations"] description = "Parser for Rust source code" diff --git a/src/lib.rs b/src/lib.rs index d5be866db2..1c16c11962 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -249,7 +249,7 @@ //! dynamic library libproc_macro from rustc toolchain. // Syn types in rustdoc of other crates get linked to here. -#![doc(html_root_url = "https://docs.rs/syn/2.0.89")] +#![doc(html_root_url = "https://docs.rs/syn/2.0.90")] #![cfg_attr(docsrs, feature(doc_cfg))] #![deny(unsafe_op_in_unsafe_fn)] #![allow(non_camel_case_types)] diff --git a/syn.json b/syn.json index 72fa9e76e7..3a73407a55 100644 --- a/syn.json +++ b/syn.json @@ -1,5 +1,5 @@ { - "version": "2.0.89", + "version": "2.0.90", "types": [ { "ident": "Abi", From 7f12e3231285d8e8f66be18c7472fdbfd7f10a23 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 1 Dec 2024 15:12:25 -0800 Subject: [PATCH 036/121] Use mut_visit's visit_clobber helper in FullyParenthesize --- tests/test_precedence.rs | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/tests/test_precedence.rs b/tests/test_precedence.rs index e64cbf1dd3..c7b4aceca7 100644 --- a/tests/test_precedence.rs +++ b/tests/test_precedence.rs @@ -205,12 +205,11 @@ fn librustc_parenthesize(mut librustc_expr: P) -> P { ExprKind, GenericArg, GenericBound, Local, LocalKind, Pat, PolyTraitRef, Stmt, StmtKind, StructExpr, StructRest, TraitBoundModifiers, Ty, }; - use rustc_ast::mut_visit::{walk_flat_map_assoc_item, MutVisitor}; + use rustc_ast::mut_visit::{visit_clobber, walk_flat_map_assoc_item, MutVisitor}; use rustc_ast::visit::{AssocCtxt, BoundKind}; use rustc_data_structures::flat_map_in_place::FlatMapInPlace; use rustc_span::DUMMY_SP; use smallvec::SmallVec; - use std::mem; use std::ops::DerefMut; use thin_vec::ThinVec; @@ -280,19 +279,13 @@ fn librustc_parenthesize(mut librustc_expr: P) -> P { match e.kind { ExprKind::Block(..) | ExprKind::If(..) | ExprKind::Let(..) => {} ExprKind::Binary(..) if contains_let_chain(e) => {} - _ => { - let inner = mem::replace( - e, - P(Expr { - id: ast::DUMMY_NODE_ID, - kind: ExprKind::Dummy, - span: DUMMY_SP, - attrs: ThinVec::new(), - tokens: None, - }), - ); - e.kind = ExprKind::Paren(inner); - } + _ => visit_clobber(&mut **e, |inner| Expr { + id: ast::DUMMY_NODE_ID, + kind: ExprKind::Paren(P(inner)), + span: DUMMY_SP, + attrs: ThinVec::new(), + tokens: None, + }), } } From 91ffc5c4c53e1b3ab961dd3bd7e3fcf79d15cb7c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 3 Dec 2024 19:12:06 -0800 Subject: [PATCH 037/121] Update test suite to nightly-2024-12-04 --- tests/common/eq.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/common/eq.rs b/tests/common/eq.rs index af1f616187..7b0cb0f3da 100644 --- a/tests/common/eq.rs +++ b/tests/common/eq.rs @@ -555,7 +555,7 @@ spanless_eq_enum!(AngleBracketedArg; Arg(0) Constraint(0)); spanless_eq_enum!(AsmMacro; Asm GlobalAsm NakedAsm); spanless_eq_enum!(AssocItemConstraintKind; Equality(term) Bound(bounds)); spanless_eq_enum!(AssocItemKind; Const(0) Fn(0) Type(0) MacCall(0) Delegation(0) DelegationMac(0)); -spanless_eq_enum!(AttrArgs; Empty Delimited(0) Eq(0 1)); +spanless_eq_enum!(AttrArgs; Empty Delimited(0) Eq(eq_span value)); spanless_eq_enum!(AttrArgsEq; Ast(0) Hir(0)); spanless_eq_enum!(AttrStyle; Outer Inner); spanless_eq_enum!(AttrTokenTree; Token(0 1) Delimited(0 1 2 3) AttrsTarget(0)); @@ -898,7 +898,7 @@ impl SpanlessEq for AttrKind { SpanlessEq::eq(&path, &normal2.item.path) && match &normal2.item.args { AttrArgs::Empty | AttrArgs::Delimited(_) => false, - AttrArgs::Eq(_span, value) => { + AttrArgs::Eq { eq_span: _, value } => { is_escaped_literal_attr_args(value, *unescaped) } } From 0b9c7e91fa8f8da096e989779fd9528a71254c00 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 9 Dec 2024 21:06:27 -0800 Subject: [PATCH 038/121] Update test suite to nightly-2024-12-10 --- tests/common/eq.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/common/eq.rs b/tests/common/eq.rs index 7b0cb0f3da..dca7463e88 100644 --- a/tests/common/eq.rs +++ b/tests/common/eq.rs @@ -645,7 +645,7 @@ spanless_eq_enum!(LitKind; Str(0 1) ByteStr(0 1) CStr(0 1) Byte(0) Char(0) Int(0 1) Float(0 1) Bool(0) Err(0)); spanless_eq_enum!(PatKind; Wild Ident(0 1 2) Struct(0 1 2 3) TupleStruct(0 1 2) Or(0) Path(0 1) Tuple(0) Box(0) Deref(0) Ref(0 1) Lit(0) Range(0 1 2) - Slice(0) Rest Never Paren(0) MacCall(0) Err(0)); + Slice(0) Rest Never Guard(0 1) Paren(0) MacCall(0) Err(0)); spanless_eq_enum!(TyKind; Slice(0) Array(0 1) Ptr(0) Ref(0 1) PinnedRef(0 1) BareFn(0) Never Tup(0) Path(0 1) TraitObject(0 1) ImplTrait(0 1) Paren(0) Typeof(0) Infer ImplicitSelf MacCall(0) CVarArgs Pat(0 1) Dummy Err(0)); From 3fa17c9d5fd5e0a4f205b2da8e221f3a385194f0 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 9 Dec 2024 21:13:16 -0800 Subject: [PATCH 039/121] Fix signature in rustc_errors Emitter impl --- benches/rust.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/benches/rust.rs b/benches/rust.rs index 75a9f91010..96770ddfd3 100644 --- a/benches/rust.rs +++ b/benches/rust.rs @@ -60,7 +60,10 @@ mod librustc_parse { use crate::repo; use rustc_data_structures::sync::Lrc; use rustc_error_messages::FluentBundle; - use rustc_errors::{emitter::Emitter, translation::Translate, DiagCtxt, DiagInner}; + use rustc_errors::emitter::Emitter; + use rustc_errors::registry::Registry; + use rustc_errors::translation::Translate; + use rustc_errors::{DiagCtxt, DiagInner}; use rustc_session::parse::ParseSess; use rustc_span::source_map::{FilePathMapping, SourceMap}; use rustc_span::FileName; @@ -70,7 +73,7 @@ mod librustc_parse { struct SilentEmitter; impl Emitter for SilentEmitter { - fn emit_diagnostic(&mut self, _diag: DiagInner) {} + fn emit_diagnostic(&mut self, _diag: DiagInner, _registry: &Registry) {} fn source_map(&self) -> Option<&SourceMap> { None } From 08ffaf6465e65471efc02b7114dac58d9f2ffb0b Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 10 Dec 2024 18:30:29 -0800 Subject: [PATCH 040/121] Update test suite to nightly-2024-12-11 --- tests/common/eq.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/common/eq.rs b/tests/common/eq.rs index dca7463e88..4104f1c295 100644 --- a/tests/common/eq.rs +++ b/tests/common/eq.rs @@ -494,7 +494,7 @@ spanless_eq_struct!(DelimSpacing; open close); spanless_eq_struct!(EnumDef; variants); spanless_eq_struct!(Expr; id kind span attrs !tokens); spanless_eq_struct!(ExprField; attrs id span ident expr is_shorthand is_placeholder); -spanless_eq_struct!(FieldDef; attrs id span vis safety ident ty is_placeholder); +spanless_eq_struct!(FieldDef; attrs id span vis safety ident ty default is_placeholder); spanless_eq_struct!(Fn; defaultness generics sig body); spanless_eq_struct!(FnDecl; inputs output); spanless_eq_struct!(FnHeader; constness coroutine_kind safety ext); From 46fb73c9f14c4d02a53a5d916e1efba7b2c8faf6 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 14 Dec 2024 18:42:54 -0800 Subject: [PATCH 041/121] Update test suite to nightly-2024-12-15 --- tests/common/eq.rs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/tests/common/eq.rs b/tests/common/eq.rs index 4104f1c295..a324e7d3b8 100644 --- a/tests/common/eq.rs +++ b/tests/common/eq.rs @@ -150,6 +150,8 @@ use rustc_ast::ast::TyAliasWhereClauses; use rustc_ast::ast::TyKind; use rustc_ast::ast::UintTy; use rustc_ast::ast::UnOp; +use rustc_ast::ast::UnsafeBinderCastKind; +use rustc_ast::ast::UnsafeBinderTy; use rustc_ast::ast::UnsafeSource; use rustc_ast::ast::UseTree; use rustc_ast::ast::UseTreeKind; @@ -309,6 +311,7 @@ macro_rules! spanless_eq_partial_eq { }; } +spanless_eq_partial_eq!(()); spanless_eq_partial_eq!(bool); spanless_eq_partial_eq!(u8); spanless_eq_partial_eq!(u16); @@ -543,6 +546,7 @@ spanless_eq_struct!(Ty; id kind span tokens); spanless_eq_struct!(TyAlias; defaultness generics where_clauses bounds ty); spanless_eq_struct!(TyAliasWhereClause; !has_where_token span); spanless_eq_struct!(TyAliasWhereClauses; before after !split); +spanless_eq_struct!(UnsafeBinderTy; generic_params inner_ty); spanless_eq_struct!(UseTree; prefix kind span); spanless_eq_struct!(Variant; attrs id span !vis ident data disr_expr is_placeholder); spanless_eq_struct!(Visibility; kind span tokens); @@ -601,7 +605,7 @@ spanless_eq_enum!(MacStmtStyle; Semicolon Braces NoBraces); spanless_eq_enum!(MatchKind; Prefix Postfix); spanless_eq_enum!(MetaItemKind; Word List(0) NameValue(0)); spanless_eq_enum!(MetaItemInner; MetaItem(0) Lit(0)); -spanless_eq_enum!(ModKind; Loaded(0 1 2) Unloaded); +spanless_eq_enum!(ModKind; Loaded(0 1 2 3) Unloaded); spanless_eq_enum!(Movability; Static Movable); spanless_eq_enum!(Mutability; Mut Not); spanless_eq_enum!(PatFieldsRest; Rest None); @@ -618,6 +622,7 @@ spanless_eq_enum!(TokenTree; Token(0 1) Delimited(0 1 2 3)); spanless_eq_enum!(TraitObjectSyntax; Dyn DynStar None); spanless_eq_enum!(UintTy; Usize U8 U16 U32 U64 U128); spanless_eq_enum!(UnOp; Deref Not Neg); +spanless_eq_enum!(UnsafeBinderCastKind; Wrap Unwrap); spanless_eq_enum!(UnsafeSource; CompilerGenerated UserProvided); spanless_eq_enum!(UseTreeKind; Simple(0) Nested(items span) Glob); spanless_eq_enum!(VariantData; Struct(fields recovered) Tuple(0 1) Unit(0)); @@ -633,7 +638,8 @@ spanless_eq_enum!(ExprKind; Array(0) ConstBlock(0) Call(0 1) MethodCall(0) Assign(0 1 2) AssignOp(0 1 2) Field(0 1) Index(0 1 2) Underscore Range(0 1 2) Path(0 1) AddrOf(0 1 2) Break(0 1) Continue(0) Ret(0) InlineAsm(0) OffsetOf(0 1) MacCall(0) Struct(0) Repeat(0 1) Paren(0) Try(0) - Yield(0) Yeet(0) Become(0) IncludedBytes(0) FormatArgs(0) Err(0) Dummy); + Yield(0) Yeet(0) Become(0) IncludedBytes(0) FormatArgs(0) + UnsafeBinderCast(0 1 2) Err(0) Dummy); spanless_eq_enum!(InlineAsmOperand; In(reg expr) Out(reg late expr) InOut(reg late expr) SplitInOut(reg late in_expr out_expr) Const(anon_const) Sym(sym) Label(block)); @@ -647,8 +653,9 @@ spanless_eq_enum!(PatKind; Wild Ident(0 1 2) Struct(0 1 2 3) TupleStruct(0 1 2) Or(0) Path(0 1) Tuple(0) Box(0) Deref(0) Ref(0 1) Lit(0) Range(0 1 2) Slice(0) Rest Never Guard(0 1) Paren(0) MacCall(0) Err(0)); spanless_eq_enum!(TyKind; Slice(0) Array(0 1) Ptr(0) Ref(0 1) PinnedRef(0 1) - BareFn(0) Never Tup(0) Path(0 1) TraitObject(0 1) ImplTrait(0 1) Paren(0) - Typeof(0) Infer ImplicitSelf MacCall(0) CVarArgs Pat(0 1) Dummy Err(0)); + BareFn(0) UnsafeBinder(0) Never Tup(0) Path(0 1) TraitObject(0 1) + ImplTrait(0 1) Paren(0) Typeof(0) Infer ImplicitSelf MacCall(0) CVarArgs + Pat(0 1) Dummy Err(0)); impl SpanlessEq for Ident { fn eq(&self, other: &Self) -> bool { From 2e69ff066b5cf83133f2aae8722ec14072c04608 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 15 Dec 2024 10:17:33 -0800 Subject: [PATCH 042/121] Update test suite rustc commit to nightly-2024-12-15 --- tests/repo/mod.rs | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/tests/repo/mod.rs b/tests/repo/mod.rs index 96f039cb35..a3d233ced6 100644 --- a/tests/repo/mod.rs +++ b/tests/repo/mod.rs @@ -15,10 +15,36 @@ use std::path::{Path, PathBuf}; use tar::Archive; use walkdir::{DirEntry, WalkDir}; -const REVISION: &str = "86d69c705a552236a622eee3fdea94bf13c5f102"; +const REVISION: &str = "0aeaa5eb22180fdf12a8489e63c4daa18da6f236"; #[rustfmt::skip] static EXCLUDE_FILES: &[&str] = &[ + // TODO + "src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/assoc_type_bound.rs", + "src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/field_expr.rs", + "src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/generic_arg_bounds.rs", + "src/tools/rustfmt/tests/source/unsafe-binders.rs", + "src/tools/rustfmt/tests/source/unsafe-field.rs", + "src/tools/rustfmt/tests/target/guard_patterns.rs", + "src/tools/rustfmt/tests/target/unsafe-binders.rs", + "src/tools/rustfmt/tests/target/unsafe-field.rs", + "tests/ui/associated-type-bounds/all-generics-lookup.rs", + "tests/ui/associated-type-bounds/implied-from-self-where-clause.rs", + "tests/ui/async-await/pin-ergonomics/sugar.rs", + "tests/ui/lint/keyword-idents/auxiliary/multi_file_submod.rs", + "tests/ui/structs/auxiliary/struct_field_default.rs", + "tests/ui/structs/default-field-values-support.rs", + "tests/ui/traits/const-traits/const-bound-in-host.rs", + "tests/ui/traits/const-traits/const-drop.rs", + "tests/ui/traits/const-traits/const-impl-trait.rs", + "tests/ui/traits/const-traits/const-in-closure.rs", + "tests/ui/traits/const-traits/dont-ice-on-const-pred-for-bounds.rs", + "tests/ui/traits/const-traits/effects/auxiliary/minicore.rs", + "tests/ui/traits/const-traits/effects/dont-prefer-param-env-for-infer-self-ty.rs", + "tests/ui/traits/const-traits/effects/minicore-const-fn-early-bound.rs", + "tests/ui/traits/const-traits/predicate-entailment-passes.rs", + "tests/ui/unsafe-fields/auxiliary/unsafe-fields-crate-dep.rs", + // TODO: non-lifetime binders: `where for<'a, T> &'a Struct: Trait` // https://github.com/dtolnay/syn/issues/1435 "src/tools/rustfmt/tests/source/issue_5721.rs", @@ -31,7 +57,6 @@ static EXCLUDE_FILES: &[&str] = &[ // TODO: return type notation: `where T: Trait` and `where T::method(..): Send` // https://github.com/dtolnay/syn/issues/1434 - "src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/return_type_syntax_assoc_type_bound.rs", "src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/return_type_syntax_in_path.rs", "src/tools/rustfmt/tests/target/return-type-notation.rs", "tests/ui/associated-type-bounds/return-type-notation/basic.rs", @@ -180,7 +205,6 @@ static EXCLUDE_FILES: &[&str] = &[ // https://github.com/dtolnay/syn/issues/1770 "src/tools/rustfmt/tests/source/pin_sugar.rs", "src/tools/rustfmt/tests/target/pin_sugar.rs", - "tests/ui/async-await/pin-sugar.rs", // TODO: `|| .. .method()` "src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/closure_range_method_call.rs", @@ -231,10 +255,8 @@ static EXCLUDE_FILES: &[&str] = &[ "tests/rustdoc/generic-associated-types/gats.rs", // Deprecated trait object syntax with parenthesized generic arguments and no dyn keyword - "src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/bare_dyn_types_with_paren_as_generic_args.rs", "src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/path_fn_trait_args.rs", "src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/typepathfn_with_coloncolon.rs", - "src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/value_parameters_no_patterns.rs", "src/tools/rustfmt/tests/source/attrib.rs", "src/tools/rustfmt/tests/source/closure.rs", "src/tools/rustfmt/tests/source/existential_type.rs", From 4398b608ac7d5a23c2be103b4f4b184c4c67c795 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 15 Dec 2024 10:21:49 -0800 Subject: [PATCH 043/121] Categorize newly failing rust repo source files --- tests/repo/mod.rs | 58 ++++++++++++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/tests/repo/mod.rs b/tests/repo/mod.rs index a3d233ced6..feedb785b0 100644 --- a/tests/repo/mod.rs +++ b/tests/repo/mod.rs @@ -19,32 +19,6 @@ const REVISION: &str = "0aeaa5eb22180fdf12a8489e63c4daa18da6f236"; #[rustfmt::skip] static EXCLUDE_FILES: &[&str] = &[ - // TODO - "src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/assoc_type_bound.rs", - "src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/field_expr.rs", - "src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/generic_arg_bounds.rs", - "src/tools/rustfmt/tests/source/unsafe-binders.rs", - "src/tools/rustfmt/tests/source/unsafe-field.rs", - "src/tools/rustfmt/tests/target/guard_patterns.rs", - "src/tools/rustfmt/tests/target/unsafe-binders.rs", - "src/tools/rustfmt/tests/target/unsafe-field.rs", - "tests/ui/associated-type-bounds/all-generics-lookup.rs", - "tests/ui/associated-type-bounds/implied-from-self-where-clause.rs", - "tests/ui/async-await/pin-ergonomics/sugar.rs", - "tests/ui/lint/keyword-idents/auxiliary/multi_file_submod.rs", - "tests/ui/structs/auxiliary/struct_field_default.rs", - "tests/ui/structs/default-field-values-support.rs", - "tests/ui/traits/const-traits/const-bound-in-host.rs", - "tests/ui/traits/const-traits/const-drop.rs", - "tests/ui/traits/const-traits/const-impl-trait.rs", - "tests/ui/traits/const-traits/const-in-closure.rs", - "tests/ui/traits/const-traits/dont-ice-on-const-pred-for-bounds.rs", - "tests/ui/traits/const-traits/effects/auxiliary/minicore.rs", - "tests/ui/traits/const-traits/effects/dont-prefer-param-env-for-infer-self-ty.rs", - "tests/ui/traits/const-traits/effects/minicore-const-fn-early-bound.rs", - "tests/ui/traits/const-traits/predicate-entailment-passes.rs", - "tests/ui/unsafe-fields/auxiliary/unsafe-fields-crate-dep.rs", - // TODO: non-lifetime binders: `where for<'a, T> &'a Struct: Trait` // https://github.com/dtolnay/syn/issues/1435 "src/tools/rustfmt/tests/source/issue_5721.rs", @@ -55,10 +29,28 @@ static EXCLUDE_FILES: &[&str] = &[ "tests/rustdoc/inline_cross/auxiliary/non_lifetime_binders.rs", "tests/rustdoc/non_lifetime_binders.rs", + // TODO: unsafe binders: `unsafe<'a> &'a T` + "src/tools/rustfmt/tests/source/unsafe-binders.rs", + "src/tools/rustfmt/tests/target/unsafe-binders.rs", + + // TODO: unsafe fields: `struct S { unsafe field: T }` + "src/tools/rustfmt/tests/source/unsafe-field.rs", + "src/tools/rustfmt/tests/target/unsafe-field.rs", + "tests/ui/unsafe-fields/auxiliary/unsafe-fields-crate-dep.rs", + + // TODO: guard patterns: `match expr { (A if f()) | (B if g()) => {} }` + "src/tools/rustfmt/tests/target/guard_patterns.rs", + + // TODO: struct field default: `struct S { field: i32 = 1 }` + "tests/ui/structs/auxiliary/struct_field_default.rs", + "tests/ui/structs/default-field-values-support.rs", + // TODO: return type notation: `where T: Trait` and `where T::method(..): Send` // https://github.com/dtolnay/syn/issues/1434 "src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/return_type_syntax_in_path.rs", "src/tools/rustfmt/tests/target/return-type-notation.rs", + "tests/ui/associated-type-bounds/all-generics-lookup.rs", + "tests/ui/associated-type-bounds/implied-from-self-where-clause.rs", "tests/ui/associated-type-bounds/return-type-notation/basic.rs", "tests/ui/associated-type-bounds/return-type-notation/higher-ranked-bound-works.rs", "tests/ui/associated-type-bounds/return-type-notation/namespace-conflict.rs", @@ -194,6 +186,15 @@ static EXCLUDE_FILES: &[&str] = &[ // https://github.com/dtolnay/syn/issues/1632 "src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/const_trait_bound.rs", "tests/ui/generic-const-items/const-trait-impl.rs", + "tests/ui/traits/const-traits/const-bound-in-host.rs", + "tests/ui/traits/const-traits/const-drop.rs", + "tests/ui/traits/const-traits/const-impl-trait.rs", + "tests/ui/traits/const-traits/const-in-closure.rs", + "tests/ui/traits/const-traits/dont-ice-on-const-pred-for-bounds.rs", + "tests/ui/traits/const-traits/effects/auxiliary/minicore.rs", + "tests/ui/traits/const-traits/effects/dont-prefer-param-env-for-infer-self-ty.rs", + "tests/ui/traits/const-traits/effects/minicore-const-fn-early-bound.rs", + "tests/ui/traits/const-traits/predicate-entailment-passes.rs", "tests/ui/traits/const-traits/tilde-const-syntax.rs", // TODO: unparenthesized half-open range pattern inside slice pattern: `[1..]` @@ -205,6 +206,7 @@ static EXCLUDE_FILES: &[&str] = &[ // https://github.com/dtolnay/syn/issues/1770 "src/tools/rustfmt/tests/source/pin_sugar.rs", "src/tools/rustfmt/tests/target/pin_sugar.rs", + "tests/ui/async-await/pin-ergonomics/sugar.rs", // TODO: `|| .. .method()` "src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/closure_range_method_call.rs", @@ -290,7 +292,10 @@ static EXCLUDE_FILES: &[&str] = &[ "tests/ui/parser/bounds-obj-parens.rs", // Various extensions to Rust syntax made up by rust-analyzer + "src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/assoc_type_bound.rs", "src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/const_param_default_path.rs", + "src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/field_expr.rs", + "src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/generic_arg_bounds.rs", "src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/use_tree_abs_star.rs", "src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0015_use_tree.rs", "src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0029_range_forms.rs", @@ -327,6 +332,7 @@ static EXCLUDE_FILES: &[&str] = &[ "tests/ui/dyn-keyword/dyn-2015-no-warnings-without-lints.rs", "tests/ui/editions/edition-keywords-2015-2015.rs", "tests/ui/editions/edition-keywords-2015-2018.rs", + "tests/ui/lint/keyword-idents/auxiliary/multi_file_submod.rs", "tests/ui/lint/lint_pre_expansion_extern_module_aux.rs", "tests/ui/macros/macro-comma-support-rpass.rs", "tests/ui/macros/try-macro.rs", From 5d33783123103aa0edb72792b94f4582fb3345da Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 15 Dec 2024 10:40:44 -0800 Subject: [PATCH 044/121] Update open syntax issues --- tests/repo/mod.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/repo/mod.rs b/tests/repo/mod.rs index feedb785b0..81b29e2a0c 100644 --- a/tests/repo/mod.rs +++ b/tests/repo/mod.rs @@ -30,18 +30,22 @@ static EXCLUDE_FILES: &[&str] = &[ "tests/rustdoc/non_lifetime_binders.rs", // TODO: unsafe binders: `unsafe<'a> &'a T` + // https://github.com/dtolnay/syn/issues/1791 "src/tools/rustfmt/tests/source/unsafe-binders.rs", "src/tools/rustfmt/tests/target/unsafe-binders.rs", // TODO: unsafe fields: `struct S { unsafe field: T }` + // https://github.com/dtolnay/syn/issues/1792 "src/tools/rustfmt/tests/source/unsafe-field.rs", "src/tools/rustfmt/tests/target/unsafe-field.rs", "tests/ui/unsafe-fields/auxiliary/unsafe-fields-crate-dep.rs", // TODO: guard patterns: `match expr { (A if f()) | (B if g()) => {} }` + // https://github.com/dtolnay/syn/issues/1793 "src/tools/rustfmt/tests/target/guard_patterns.rs", // TODO: struct field default: `struct S { field: i32 = 1 }` + // https://github.com/dtolnay/syn/issues/1774 "tests/ui/structs/auxiliary/struct_field_default.rs", "tests/ui/structs/default-field-values-support.rs", From 6adf9e50d1940b9b1e89fe7a1b8bcd7ad6a08ab7 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 16 Dec 2024 19:58:06 -0800 Subject: [PATCH 045/121] Update test suite to nightly-2024-12-17 --- tests/common/eq.rs | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/tests/common/eq.rs b/tests/common/eq.rs index a324e7d3b8..384b9eaff6 100644 --- a/tests/common/eq.rs +++ b/tests/common/eq.rs @@ -15,7 +15,6 @@ use rustc_ast::ast::AssocItemConstraint; use rustc_ast::ast::AssocItemConstraintKind; use rustc_ast::ast::AssocItemKind; use rustc_ast::ast::AttrArgs; -use rustc_ast::ast::AttrArgsEq; use rustc_ast::ast::AttrId; use rustc_ast::ast::AttrItem; use rustc_ast::ast::AttrKind; @@ -559,8 +558,7 @@ spanless_eq_enum!(AngleBracketedArg; Arg(0) Constraint(0)); spanless_eq_enum!(AsmMacro; Asm GlobalAsm NakedAsm); spanless_eq_enum!(AssocItemConstraintKind; Equality(term) Bound(bounds)); spanless_eq_enum!(AssocItemKind; Const(0) Fn(0) Type(0) MacCall(0) Delegation(0) DelegationMac(0)); -spanless_eq_enum!(AttrArgs; Empty Delimited(0) Eq(eq_span value)); -spanless_eq_enum!(AttrArgsEq; Ast(0) Hir(0)); +spanless_eq_enum!(AttrArgs; Empty Delimited(0) Eq(eq_span expr)); spanless_eq_enum!(AttrStyle; Outer Inner); spanless_eq_enum!(AttrTokenTree; Token(0 1) Delimited(0 1 2 3) AttrsTarget(0)); spanless_eq_enum!(BinOpKind; Add Sub Mul Div Rem And Or BitXor BitAnd BitOr Shl Shr Eq Lt Le Ne Ge Gt); @@ -608,7 +606,7 @@ spanless_eq_enum!(MetaItemInner; MetaItem(0) Lit(0)); spanless_eq_enum!(ModKind; Loaded(0 1 2 3) Unloaded); spanless_eq_enum!(Movability; Static Movable); spanless_eq_enum!(Mutability; Mut Not); -spanless_eq_enum!(PatFieldsRest; Rest None); +spanless_eq_enum!(PatFieldsRest; Rest Recovered(0) None); spanless_eq_enum!(PreciseCapturingArg; Lifetime(0) Arg(0 1)); spanless_eq_enum!(RangeEnd; Included(0) Excluded); spanless_eq_enum!(RangeLimits; HalfOpen Closed); @@ -835,16 +833,6 @@ fn is_escaped_literal_token(token: &Token, unescaped: Symbol) -> bool { } } -fn is_escaped_literal_attr_args(value: &AttrArgsEq, unescaped: Symbol) -> bool { - match value { - AttrArgsEq::Ast(expr) => match &expr.kind { - ExprKind::Lit(lit) => is_escaped_lit(lit, unescaped), - _ => false, - }, - AttrArgsEq::Hir(lit) => is_escaped_literal_meta_item_lit(lit, unescaped), - } -} - fn is_escaped_literal_meta_item_lit(lit: &MetaItemLit, unescaped: Symbol) -> bool { match lit { MetaItemLit { @@ -905,9 +893,10 @@ impl SpanlessEq for AttrKind { SpanlessEq::eq(&path, &normal2.item.path) && match &normal2.item.args { AttrArgs::Empty | AttrArgs::Delimited(_) => false, - AttrArgs::Eq { eq_span: _, value } => { - is_escaped_literal_attr_args(value, *unescaped) - } + AttrArgs::Eq { eq_span: _, expr } => match &expr.kind { + ExprKind::Lit(lit) => is_escaped_lit(lit, *unescaped), + _ => false, + }, } } (AttrKind::Normal(_), AttrKind::DocComment(..)) => SpanlessEq::eq(other, self), From 384516b057cc8291001d62b128492314d376c554 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 18 Dec 2024 22:27:07 -0800 Subject: [PATCH 046/121] Update test suite to nightly-2024-12-19 --- tests/common/eq.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/common/eq.rs b/tests/common/eq.rs index 384b9eaff6..f857f64853 100644 --- a/tests/common/eq.rs +++ b/tests/common/eq.rs @@ -722,8 +722,8 @@ impl SpanlessEq for TokenKind { impl SpanlessEq for TokenStream { fn eq(&self, other: &Self) -> bool { - let mut this_trees = self.trees(); - let mut other_trees = other.trees(); + let mut this_trees = self.iter(); + let mut other_trees = other.iter(); loop { let this = match this_trees.next() { None => return other_trees.next().is_none(), @@ -781,7 +781,7 @@ fn doc_comment<'a>( Some(TokenTree::Delimited(_span, _spacing, Delimiter::Bracket, stream)) => stream, _ => return false, }; - let mut trees = stream.trees(); + let mut trees = stream.iter(); match trees.next() { Some(TokenTree::Token( Token { From d2bc3a3ec3c4a2193cd2a0995bd821b2e8729291 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 21 Dec 2024 17:35:01 -0800 Subject: [PATCH 047/121] Allow Vec in parse_quote --- src/expr.rs | 16 ++++++++++++---- src/parse_quote.rs | 9 ++++++++- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/expr.rs b/src/expr.rs index 545b43dfd7..4939078a49 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -2328,10 +2328,7 @@ pub(crate) mod parsing { let brace_token = braced!(content in input); attr::parsing::parse_inner(&content, &mut attrs)?; - let mut arms = Vec::new(); - while !content.is_empty() { - arms.push(content.call(Arm::parse)?); - } + let arms = Arm::parse_multiple(&content)?; Ok(ExprMatch { attrs, @@ -2945,6 +2942,17 @@ pub(crate) mod parsing { } } + #[cfg(feature = "full")] + impl Arm { + pub(crate) fn parse_multiple(input: ParseStream) -> Result> { + let mut arms = Vec::new(); + while !input.is_empty() { + arms.push(input.call(Arm::parse)?); + } + Ok(arms) + } + } + #[cfg(feature = "full")] #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] impl Parse for Arm { diff --git a/src/parse_quote.rs b/src/parse_quote.rs index fa7ea79f47..6d635b445a 100644 --- a/src/parse_quote.rs +++ b/src/parse_quote.rs @@ -140,7 +140,7 @@ use crate::punctuated::Punctuated; #[cfg(any(feature = "full", feature = "derive"))] use crate::{attr, Attribute, Field, FieldMutability, Ident, Type, Visibility}; #[cfg(feature = "full")] -use crate::{Block, Pat, Stmt}; +use crate::{Arm, Block, Pat, Stmt}; #[cfg(any(feature = "full", feature = "derive"))] impl ParseQuote for Attribute { @@ -220,3 +220,10 @@ impl ParseQuote for Vec { Block::parse_within(input) } } + +#[cfg(feature = "full")] +impl ParseQuote for Vec { + fn parse(input: ParseStream) -> Result { + Arm::parse_multiple(input) + } +} From 762ba9784804851a63ef9c51381668a5ebd89500 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 21 Dec 2024 17:46:04 -0800 Subject: [PATCH 048/121] Document more parse_quote special cases --- src/parse_quote.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/parse_quote.rs b/src/parse_quote.rs index 6d635b445a..2db20597c4 100644 --- a/src/parse_quote.rs +++ b/src/parse_quote.rs @@ -53,11 +53,22 @@ /// /// - [`Attribute`] — parses one attribute, allowing either outer like `#[...]` /// or inner like `#![...]` +/// - [`Vec`] — parses multiple attributes, including mixed kinds in +/// any order /// - [`Punctuated`] — parses zero or more `T` separated by punctuation /// `P` with optional trailing punctuation +/// - [`Vec`] — parses arms separated by optional commas according to the +/// same grammar as the inside of a `match` expression /// - [`Vec`] — parses the same as `Block::parse_within` +/// - [`Pat`], [`Box`] — parses the same as +/// `Pat::parse_multi_with_leading_vert` +/// - [`Field`] — parses a named or unnamed struct field /// +/// [`Vec`]: Attribute +/// [`Vec`]: Arm /// [`Vec`]: Block::parse_within +/// [`Pat`]: Pat::parse_multi_with_leading_vert +/// [`Box`]: Pat::parse_multi_with_leading_vert /// /// # Panics /// From 53de985062cbdc99ba002ac85a7926d49d92e611 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 21 Dec 2024 17:53:57 -0800 Subject: [PATCH 049/121] Release 2.0.91 --- Cargo.toml | 2 +- src/lib.rs | 2 +- syn.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 71332d4b53..98b490f339 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "syn" -version = "2.0.90" +version = "2.0.91" authors = ["David Tolnay "] categories = ["development-tools::procedural-macro-helpers", "parser-implementations"] description = "Parser for Rust source code" diff --git a/src/lib.rs b/src/lib.rs index 1c16c11962..ebaa4cb1ff 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -249,7 +249,7 @@ //! dynamic library libproc_macro from rustc toolchain. // Syn types in rustdoc of other crates get linked to here. -#![doc(html_root_url = "https://docs.rs/syn/2.0.90")] +#![doc(html_root_url = "https://docs.rs/syn/2.0.91")] #![cfg_attr(docsrs, feature(doc_cfg))] #![deny(unsafe_op_in_unsafe_fn)] #![allow(non_camel_case_types)] diff --git a/syn.json b/syn.json index 3a73407a55..5fe4c9bcc7 100644 --- a/syn.json +++ b/syn.json @@ -1,5 +1,5 @@ { - "version": "2.0.90", + "version": "2.0.91", "types": [ { "ident": "Abi", From 284ea653187f9b97fbb3275482a07663b0b2ea95 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 22 Dec 2024 21:50:47 -0800 Subject: [PATCH 050/121] Add regression test for jump in lhs of range This syntax tree incorrectly gets turned into `return ..=return` when printed, which is a return whose value is a RangeToInclusive. The original is a Range whose endpoints are returns. --- tests/test_expr.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test_expr.rs b/tests/test_expr.rs index 03ebd1db65..6d5d4f8729 100644 --- a/tests/test_expr.rs +++ b/tests/test_expr.rs @@ -704,6 +704,8 @@ fn test_fixup() { quote! { { (let _ = ()) } }, quote! { (#[attr] thing).field }, quote! { (self.f)() }, + quote! { (return)..=return }, + quote! { 1 + (return)..=1 + return }, ] { let original: Expr = syn::parse2(tokens).unwrap(); From 55462c8df86ceca6612409ba44e6cbff728a698e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 22 Dec 2024 21:59:05 -0800 Subject: [PATCH 051/121] Fix parenthesization of jumps in ranges --- src/expr.rs | 5 +++-- src/fixup.rs | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/expr.rs b/src/expr.rs index 4939078a49..961f308d9c 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -3752,11 +3752,12 @@ pub(crate) mod printing { fn print_expr_range(e: &ExprRange, tokens: &mut TokenStream, fixup: FixupContext) { outer_attrs_to_tokens(&e.attrs, tokens); if let Some(start) = &e.start { + let start_fixup = fixup.leftmost_subexpression_with_begin_operator(true, false); print_subexpression( start, - Precedence::of(start) <= Precedence::Range, + start_fixup.leading_precedence(start) <= Precedence::Range, tokens, - fixup.leftmost_subexpression(), + start_fixup, ); } e.limits.to_tokens(tokens); diff --git a/src/fixup.rs b/src/fixup.rs index 58ed9e73d4..fb1c42182e 100644 --- a/src/fixup.rs +++ b/src/fixup.rs @@ -323,7 +323,7 @@ impl FixupContext { _ => {} } } - self.precedence(expr) + self.leading_precedence(expr) } fn precedence(self, expr: &Expr) -> Precedence { From 331b4d0feef75e6bc546624f58fd9b9f3dd644eb Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 25 Dec 2024 21:29:11 -0800 Subject: [PATCH 052/121] Simplify distinction between leading and trailing precedence --- src/expr.rs | 20 ++++++++++---------- src/fixup.rs | 47 +++++++++++++++++++++-------------------------- 2 files changed, 31 insertions(+), 36 deletions(-) diff --git a/src/expr.rs b/src/expr.rs index 961f308d9c..e90816d485 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -3280,7 +3280,7 @@ pub(crate) mod printing { e.eq_token.to_tokens(tokens); print_subexpression( &e.right, - fixup.trailing_precedence(&e.right) < Precedence::Assign, + fixup.precedence(&e.right) < Precedence::Assign, tokens, fixup.subsequent_subexpression(), ); @@ -3348,8 +3348,8 @@ pub(crate) mod printing { ); let binop_prec = Precedence::of_binop(&e.op); - let left_prec = left_fixup.leading_precedence(&e.left); - let right_prec = fixup.trailing_precedence(&e.right); + let left_prec = left_fixup.precedence(&e.left); + let right_prec = fixup.precedence(&e.right); let (left_needs_group, right_needs_group) = match binop_prec { Precedence::Assign => (left_prec <= Precedence::Range, right_prec < binop_prec), Precedence::Compare => (left_prec <= binop_prec, right_prec <= binop_prec), @@ -3422,7 +3422,7 @@ pub(crate) mod printing { let needs_group = if let Expr::Field(func) = &*e.func { func.member.is_named() } else { - func_fixup.leading_precedence(&e.func) < Precedence::Unambiguous + func_fixup.precedence(&e.func) < Precedence::Unambiguous }; print_subexpression(&e.func, needs_group, tokens, func_fixup); @@ -3598,7 +3598,7 @@ pub(crate) mod printing { ); print_subexpression( &e.expr, - obj_fixup.leading_precedence(&e.expr) < Precedence::Unambiguous, + obj_fixup.precedence(&e.expr) < Precedence::Unambiguous, tokens, obj_fixup, ); @@ -3755,7 +3755,7 @@ pub(crate) mod printing { let start_fixup = fixup.leftmost_subexpression_with_begin_operator(true, false); print_subexpression( start, - start_fixup.leading_precedence(start) <= Precedence::Range, + start_fixup.precedence(start) <= Precedence::Range, tokens, start_fixup, ); @@ -3764,7 +3764,7 @@ pub(crate) mod printing { if let Some(end) = &e.end { print_subexpression( end, - fixup.trailing_precedence(end) <= Precedence::Range, + fixup.precedence(end) <= Precedence::Range, tokens, fixup.subsequent_subexpression(), ); @@ -3787,7 +3787,7 @@ pub(crate) mod printing { e.mutability.to_tokens(tokens); print_subexpression( &e.expr, - fixup.trailing_precedence(&e.expr) < Precedence::Prefix, + fixup.precedence(&e.expr) < Precedence::Prefix, tokens, fixup.subsequent_subexpression(), ); @@ -3806,7 +3806,7 @@ pub(crate) mod printing { e.mutability.to_tokens(tokens); print_subexpression( &e.expr, - fixup.trailing_precedence(&e.expr) < Precedence::Prefix, + fixup.precedence(&e.expr) < Precedence::Prefix, tokens, fixup.subsequent_subexpression(), ); @@ -3916,7 +3916,7 @@ pub(crate) mod printing { e.op.to_tokens(tokens); print_subexpression( &e.expr, - fixup.trailing_precedence(&e.expr) < Precedence::Prefix, + fixup.precedence(&e.expr) < Precedence::Prefix, tokens, fixup.subsequent_subexpression(), ); diff --git a/src/fixup.rs b/src/fixup.rs index fb1c42182e..fa19a7e10f 100644 --- a/src/fixup.rs +++ b/src/fixup.rs @@ -93,21 +93,21 @@ pub(crate) struct FixupContext { // This is the difference between: // - // let _ = 1 + return 1; // no parens if rightmost subexpression + // let _ = (return) - 1; // without paren, this would return -1 // - // let _ = 1 + (return 1) + 1; // needs parens + // let _ = return + 1; // no paren because '+' cannot begin expr // #[cfg(feature = "full")] - parenthesize_exterior_jump: bool, + next_operator_can_begin_expr: bool, // This is the difference between: // - // let _ = (return) - 1; // without paren, this would return -1 + // let _ = 1 + return 1; // no parens if rightmost subexpression // - // let _ = return + 1; // no paren because '+' cannot begin expr + // let _ = 1 + (return 1) + 1; // needs parens // #[cfg(feature = "full")] - next_operator_can_begin_expr: bool, + next_operator_can_continue_expr: bool, // This is the difference between: // @@ -134,9 +134,9 @@ impl FixupContext { #[cfg(feature = "full")] parenthesize_exterior_struct_lit: false, #[cfg(feature = "full")] - parenthesize_exterior_jump: false, - #[cfg(feature = "full")] next_operator_can_begin_expr: false, + #[cfg(feature = "full")] + next_operator_can_continue_expr: false, next_operator_can_begin_generics: false, }; @@ -195,7 +195,10 @@ impl FixupContext { leftmost_subexpression_in_match_arm: self.match_arm || self.leftmost_subexpression_in_match_arm, #[cfg(feature = "full")] - parenthesize_exterior_jump: true, + next_operator_can_begin_expr: false, + #[cfg(feature = "full")] + next_operator_can_continue_expr: true, + next_operator_can_begin_generics: false, ..self } } @@ -215,7 +218,10 @@ impl FixupContext { #[cfg(feature = "full")] leftmost_subexpression_in_match_arm: false, #[cfg(feature = "full")] - parenthesize_exterior_jump: true, + next_operator_can_begin_expr: false, + #[cfg(feature = "full")] + next_operator_can_continue_expr: true, + next_operator_can_begin_generics: false, ..self } } @@ -285,12 +291,12 @@ impl FixupContext { #[cfg(feature = "full")] pub fn needs_group_as_let_scrutinee(self, expr: &Expr) -> bool { self.parenthesize_exterior_struct_lit && classify::confusable_with_adjacent_block(expr) - || self.trailing_precedence(expr) < Precedence::Let + || self.precedence(expr) < Precedence::Let } - /// Determines the effective precedence of a left subexpression. Some - /// expressions have lower precedence when adjacent to particular operators. - pub fn leading_precedence(self, expr: &Expr) -> Precedence { + /// Determines the effective precedence of a subexpression. Some expressions + /// have higher or lower precedence when adjacent to particular operators. + pub fn precedence(self, expr: &Expr) -> Precedence { #[cfg(feature = "full")] if self.next_operator_can_begin_expr { // Decrease precedence of value-less jumps when followed by an @@ -300,15 +306,8 @@ impl FixupContext { return Precedence::Jump; } } - self.precedence(expr) - } - - /// Determines the effective precedence of a right subexpression. Some - /// expressions have higher precedence on the right side of a binary - /// operator than on the left. - pub fn trailing_precedence(self, expr: &Expr) -> Precedence { #[cfg(feature = "full")] - if !self.parenthesize_exterior_jump { + if !self.next_operator_can_continue_expr { match expr { // Increase precedence of expressions that extend to the end of // current statement or group. @@ -323,10 +322,6 @@ impl FixupContext { _ => {} } } - self.leading_precedence(expr) - } - - fn precedence(self, expr: &Expr) -> Precedence { if self.next_operator_can_begin_generics { if let Expr::Cast(cast) = expr { if classify::trailing_unparameterized_path(&cast.ty) { From eb0eb0c97b94dcddc8ca3a5006c7a32371e852a2 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 26 Dec 2024 13:28:15 -0800 Subject: [PATCH 053/121] Rename subsequent_subexpression -> rightmost_subexpression --- src/expr.rs | 18 +++++++++--------- src/fixup.rs | 19 +++++++++++-------- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/src/expr.rs b/src/expr.rs index e90816d485..d974a55187 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -3282,7 +3282,7 @@ pub(crate) mod printing { &e.right, fixup.precedence(&e.right) < Precedence::Assign, tokens, - fixup.subsequent_subexpression(), + fixup.rightmost_subexpression(), ); } @@ -3362,7 +3362,7 @@ pub(crate) mod printing { &e.right, right_needs_group, tokens, - fixup.subsequent_subexpression(), + fixup.rightmost_subexpression(), ); } @@ -3399,7 +3399,7 @@ pub(crate) mod printing { // ^---------------------------------^ e.label.is_none() && classify::expr_leading_label(value), tokens, - fixup.subsequent_subexpression(), + fixup.rightmost_subexpression(), ); } } @@ -3766,7 +3766,7 @@ pub(crate) mod printing { end, fixup.precedence(end) <= Precedence::Range, tokens, - fixup.subsequent_subexpression(), + fixup.rightmost_subexpression(), ); } } @@ -3789,7 +3789,7 @@ pub(crate) mod printing { &e.expr, fixup.precedence(&e.expr) < Precedence::Prefix, tokens, - fixup.subsequent_subexpression(), + fixup.rightmost_subexpression(), ); } @@ -3808,7 +3808,7 @@ pub(crate) mod printing { &e.expr, fixup.precedence(&e.expr) < Precedence::Prefix, tokens, - fixup.subsequent_subexpression(), + fixup.rightmost_subexpression(), ); } @@ -3838,7 +3838,7 @@ pub(crate) mod printing { outer_attrs_to_tokens(&e.attrs, tokens); e.return_token.to_tokens(tokens); if let Some(expr) = &e.expr { - print_expr(expr, tokens, fixup.subsequent_subexpression()); + print_expr(expr, tokens, fixup.rightmost_subexpression()); } } @@ -3918,7 +3918,7 @@ pub(crate) mod printing { &e.expr, fixup.precedence(&e.expr) < Precedence::Prefix, tokens, - fixup.subsequent_subexpression(), + fixup.rightmost_subexpression(), ); } @@ -3963,7 +3963,7 @@ pub(crate) mod printing { outer_attrs_to_tokens(&e.attrs, tokens); e.yield_token.to_tokens(tokens); if let Some(expr) = &e.expr { - print_expr(expr, tokens, fixup.subsequent_subexpression()); + print_expr(expr, tokens, fixup.rightmost_subexpression()); } } diff --git a/src/fixup.rs b/src/fixup.rs index fa19a7e10f..376f6d7ebd 100644 --- a/src/fixup.rs +++ b/src/fixup.rs @@ -242,15 +242,18 @@ impl FixupContext { } } - /// Transform this fixup into the one that should apply when printing any - /// subexpression that is neither a leftmost subexpression nor surrounded in - /// delimiters. + /// Transform this fixup into the one that should apply when printing the + /// rightmost subexpression of the current expression. + /// + /// The rightmost subexpression is any subexpression that has a different + /// first token than the current expression, but has the same last token. + /// + /// For example in `$a + $b` and `-$b`, the subexpression `$b` is a + /// rightmost subexpression. /// - /// This is for any subexpression that has a different first token than the - /// current expression, and is not surrounded by a paren/bracket/brace. For - /// example the `$b` in `$a + $b` and `-$b`, but not the one in `[$b]` or - /// `$a.f($b)`. - pub fn subsequent_subexpression(self) -> Self { + /// Not every expression has a rightmost subexpression. For example neither + /// `[$b]` nor `$a.f($b)` have one. + pub fn rightmost_subexpression(self) -> Self { FixupContext { #[cfg(feature = "full")] stmt: false, From 90c730d82db99caa58f81ac9a4ba01474ee331a9 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 26 Dec 2024 13:38:03 -0800 Subject: [PATCH 054/121] Add exhaustive test of FixupContext leading/trailing invariant --- src/fixup.rs | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/fixup.rs b/src/fixup.rs index 376f6d7ebd..d0f6982b3f 100644 --- a/src/fixup.rs +++ b/src/fixup.rs @@ -2,6 +2,7 @@ use crate::classify; use crate::expr::Expr; use crate::precedence::Precedence; +#[cfg_attr(test, derive(PartialEq, Debug))] pub(crate) struct FixupContext { // Print expression such that it can be parsed back as a statement // consisting of the original expression. @@ -343,3 +344,33 @@ impl Clone for FixupContext { *self } } + +#[cfg(feature = "full")] +#[test] +fn test_leftmost_rightmost_invariant() { + const BITS: usize = 8; + + for bits in 0u16..1 << BITS { + let mut i = 0; + let mut bit = || { + let mask = 1 << i; + i += 1; + (bits & mask) != 0 + }; + let fixup = FixupContext { + stmt: bit(), + leftmost_subexpression_in_stmt: bit(), + match_arm: bit(), + leftmost_subexpression_in_match_arm: bit(), + parenthesize_exterior_struct_lit: bit(), + next_operator_can_begin_expr: bit(), + next_operator_can_continue_expr: bit(), + next_operator_can_begin_generics: bit(), + }; + assert_eq!(i, BITS); + assert_eq!( + fixup.leftmost_subexpression().rightmost_subexpression(), + fixup.rightmost_subexpression().leftmost_subexpression(), + ); + } +} From 21ec874bd5ddeaf64aeb50df44ede6542eefbafa Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 26 Dec 2024 13:52:12 -0800 Subject: [PATCH 055/121] Rename leftmost_subexpression_with_begin_operator --- src/expr.rs | 8 ++++---- src/fixup.rs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/expr.rs b/src/expr.rs index d974a55187..a2e47d31b4 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -3328,7 +3328,7 @@ pub(crate) mod printing { fn print_expr_binary(e: &ExprBinary, tokens: &mut TokenStream, fixup: FixupContext) { outer_attrs_to_tokens(&e.attrs, tokens); - let left_fixup = fixup.leftmost_subexpression_with_begin_operator( + let left_fixup = fixup.leftmost_subexpression_with_operator( #[cfg(feature = "full")] match &e.op { BinOp::Sub(_) @@ -3414,7 +3414,7 @@ pub(crate) mod printing { fn print_expr_call(e: &ExprCall, tokens: &mut TokenStream, fixup: FixupContext) { outer_attrs_to_tokens(&e.attrs, tokens); - let func_fixup = fixup.leftmost_subexpression_with_begin_operator( + let func_fixup = fixup.leftmost_subexpression_with_operator( #[cfg(feature = "full")] true, false, @@ -3591,7 +3591,7 @@ pub(crate) mod printing { fn print_expr_index(e: &ExprIndex, tokens: &mut TokenStream, fixup: FixupContext) { outer_attrs_to_tokens(&e.attrs, tokens); - let obj_fixup = fixup.leftmost_subexpression_with_begin_operator( + let obj_fixup = fixup.leftmost_subexpression_with_operator( #[cfg(feature = "full")] true, false, @@ -3752,7 +3752,7 @@ pub(crate) mod printing { fn print_expr_range(e: &ExprRange, tokens: &mut TokenStream, fixup: FixupContext) { outer_attrs_to_tokens(&e.attrs, tokens); if let Some(start) = &e.start { - let start_fixup = fixup.leftmost_subexpression_with_begin_operator(true, false); + let start_fixup = fixup.leftmost_subexpression_with_operator(true, false); print_subexpression( start, start_fixup.precedence(start) <= Precedence::Range, diff --git a/src/fixup.rs b/src/fixup.rs index d0f6982b3f..03e13b09bc 100644 --- a/src/fixup.rs +++ b/src/fixup.rs @@ -230,7 +230,7 @@ impl FixupContext { /// Transform this fixup into the one that should apply when printing a /// leftmost subexpression followed by punctuation that is legal as the /// first token of an expression. - pub fn leftmost_subexpression_with_begin_operator( + pub fn leftmost_subexpression_with_operator( self, #[cfg(feature = "full")] next_operator_can_begin_expr: bool, next_operator_can_begin_generics: bool, From c494b9a2cdcf5a5605a3fdcbe1902bd55cc5c9bd Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 26 Dec 2024 14:59:23 -0800 Subject: [PATCH 056/121] Release 2.0.92 --- Cargo.toml | 2 +- src/lib.rs | 2 +- syn.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 98b490f339..e89dc93cc6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "syn" -version = "2.0.91" +version = "2.0.92" authors = ["David Tolnay "] categories = ["development-tools::procedural-macro-helpers", "parser-implementations"] description = "Parser for Rust source code" diff --git a/src/lib.rs b/src/lib.rs index ebaa4cb1ff..a423ba65f0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -249,7 +249,7 @@ //! dynamic library libproc_macro from rustc toolchain. // Syn types in rustdoc of other crates get linked to here. -#![doc(html_root_url = "https://docs.rs/syn/2.0.91")] +#![doc(html_root_url = "https://docs.rs/syn/2.0.92")] #![cfg_attr(docsrs, feature(doc_cfg))] #![deny(unsafe_op_in_unsafe_fn)] #![allow(non_camel_case_types)] diff --git a/syn.json b/syn.json index 5fe4c9bcc7..7405d0dcf5 100644 --- a/syn.json +++ b/syn.json @@ -1,5 +1,5 @@ { - "version": "2.0.91", + "version": "2.0.92", "types": [ { "ident": "Abi", From 8152751f24e634979a7ee3a691e3571406a4ea08 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 28 Dec 2024 10:20:35 -0800 Subject: [PATCH 057/121] Fix `x as T <<= y` --- src/path.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/path.rs b/src/path.rs index ce38eff313..7744d28338 100644 --- a/src/path.rs +++ b/src/path.rs @@ -528,7 +528,10 @@ pub(crate) mod parsing { input.parse()? }; - if !expr_style && input.peek(Token![<]) && !input.peek(Token![<=]) + if !expr_style + && input.peek(Token![<]) + && !input.peek(Token![<=]) + && !input.peek(Token![<<=]) || input.peek(Token![::]) && input.peek3(Token![<]) { Ok(PathSegment { From 96f4f91184095dddadce3803f3bd4eb5d9e433bc Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 28 Dec 2024 10:22:36 -0800 Subject: [PATCH 058/121] Fix `break as T` --- src/expr.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/expr.rs b/src/expr.rs index a2e47d31b4..67ccc7773e 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -904,7 +904,7 @@ impl Expr { #[cfg(feature = "parsing")] #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] pub fn peek(input: ParseStream) -> bool { - input.peek(Ident::peek_any) // value name or keyword + input.peek(Ident::peek_any) && !input.peek(Token![as]) // value name or keyword || input.peek(token::Paren) // tuple || input.peek(token::Bracket) // array || input.peek(token::Brace) // block From 6f57d631484be436eb85a9721929b66c9616078d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 28 Dec 2024 10:24:36 -0800 Subject: [PATCH 059/121] Fix `|| -> T 'a {}` --- src/expr.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/expr.rs b/src/expr.rs index 67ccc7773e..c76db2b151 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -3464,7 +3464,9 @@ pub(crate) mod printing { self.inputs.to_tokens(tokens); self.or2_token.to_tokens(tokens); self.output.to_tokens(tokens); - if matches!(self.output, ReturnType::Default) || matches!(*self.body, Expr::Block(_)) { + if matches!(self.output, ReturnType::Default) + || matches!(&*self.body, Expr::Block(body) if body.attrs.is_empty() && body.label.is_none()) + { self.body.to_tokens(tokens); } else { token::Brace::default().surround(tokens, |tokens| { From 23f8f3b07f76aaca4d90a87ac91c0a63c4aaa989 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 28 Dec 2024 11:05:28 -0800 Subject: [PATCH 060/121] Fix `match x { _ if .. => {} }` --- src/expr.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/expr.rs b/src/expr.rs index c76db2b151..b5661fed16 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -2867,6 +2867,7 @@ pub(crate) mod parsing { || input.peek(Token![,]) || input.peek(Token![;]) || input.peek(Token![.]) && !input.peek(Token![..]) + || input.peek(Token![=>]) || !allow_struct.0 && input.peek(token::Brace)) { Ok(None) From 7fbb4aeb291844c9849ef12da78fc871c0851586 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 28 Dec 2024 10:44:12 -0800 Subject: [PATCH 061/121] Fix `if break x {}` --- src/classify.rs | 96 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 70 insertions(+), 26 deletions(-) diff --git a/src/classify.rs b/src/classify.rs index b3a6ae960e..ab9dc230de 100644 --- a/src/classify.rs +++ b/src/classify.rs @@ -70,58 +70,103 @@ pub(crate) fn requires_comma_to_be_match_arm(expr: &Expr) -> bool { #[cfg(all(feature = "printing", feature = "full"))] pub(crate) fn confusable_with_adjacent_block(mut expr: &Expr) -> bool { let mut stack = Vec::new(); + let mut jump = false; + let mut tokens_right_of_empty_stack = false; while let Some(next) = match expr { Expr::Assign(e) => { - stack.push(&e.right); - Some(&e.left) + stack.push((jump, &e.right)); + Some((jump, &e.left)) + } + Expr::Await(e) => { + tokens_right_of_empty_stack |= stack.is_empty(); + Some((jump, &e.base)) } - Expr::Await(e) => Some(&e.base), Expr::Binary(e) => { - stack.push(&e.right); - Some(&e.left) + stack.push((jump, &e.right)); + Some((jump, &e.left)) } Expr::Break(e) => { - if let Some(Expr::Block(_)) = e.expr.as_deref() { + if let Some(value) = &e.expr { + if let Expr::Block(_) = **value { + return true; + } + Some((true, value)) + } else { + stack.pop() + } + } + Expr::Call(e) => { + tokens_right_of_empty_stack |= stack.is_empty(); + Some((jump, &e.func)) + } + Expr::Cast(e) => { + tokens_right_of_empty_stack |= stack.is_empty(); + Some((jump, &e.expr)) + } + Expr::Closure(e) => Some((true, &e.body)), + Expr::Field(e) => { + tokens_right_of_empty_stack |= stack.is_empty(); + Some((jump, &e.base)) + } + Expr::Index(e) => { + tokens_right_of_empty_stack |= stack.is_empty(); + Some((jump, &e.expr)) + } + Expr::MethodCall(e) => { + tokens_right_of_empty_stack |= stack.is_empty(); + Some((jump, &e.receiver)) + } + Expr::Path(_) => { + if jump && stack.is_empty() && !tokens_right_of_empty_stack { return true; } stack.pop() } - Expr::Call(e) => Some(&e.func), - Expr::Cast(e) => Some(&e.expr), - Expr::Closure(e) => Some(&e.body), - Expr::Field(e) => Some(&e.base), - Expr::Index(e) => Some(&e.expr), - Expr::MethodCall(e) => Some(&e.receiver), Expr::Range(e) => { if let Some(Expr::Block(_)) = e.end.as_deref() { return true; } match (&e.start, &e.end) { - (Some(start), end) => { - stack.extend(end); - Some(start) + (Some(start), Some(end)) => { + stack.push((jump, end)); + Some((jump, start)) + } + (Some(start), None) => { + tokens_right_of_empty_stack |= stack.is_empty(); + Some((jump, start)) } - (None, Some(end)) => Some(end), + (None, Some(end)) => Some((jump, end)), (None, None) => stack.pop(), } } - Expr::RawAddr(e) => Some(&e.expr), - Expr::Reference(e) => Some(&e.expr), + Expr::RawAddr(e) => Some((jump, &e.expr)), + Expr::Reference(e) => Some((jump, &e.expr)), Expr::Return(e) => { - if e.expr.is_none() && stack.is_empty() { + if e.expr.is_none() && stack.is_empty() && !tokens_right_of_empty_stack { return true; } - stack.pop() + if let Some(value) = &e.expr { + Some((true, value)) + } else { + stack.pop() + } } Expr::Struct(_) => return true, - Expr::Try(e) => Some(&e.expr), - Expr::Unary(e) => Some(&e.expr), + Expr::Try(e) => { + tokens_right_of_empty_stack |= stack.is_empty(); + Some((jump, &e.expr)) + } + Expr::Unary(e) => Some((jump, &e.expr)), Expr::Yield(e) => { - if e.expr.is_none() && stack.is_empty() { + if e.expr.is_none() && stack.is_empty() && !tokens_right_of_empty_stack { return true; } - stack.pop() + if let Some(value) = &e.expr { + Some((true, value)) + } else { + stack.pop() + } } Expr::Array(_) @@ -139,7 +184,6 @@ pub(crate) fn confusable_with_adjacent_block(mut expr: &Expr) -> bool { | Expr::Macro(_) | Expr::Match(_) | Expr::Paren(_) - | Expr::Path(_) | Expr::Repeat(_) | Expr::TryBlock(_) | Expr::Tuple(_) @@ -147,7 +191,7 @@ pub(crate) fn confusable_with_adjacent_block(mut expr: &Expr) -> bool { | Expr::Verbatim(_) | Expr::While(_) => stack.pop(), } { - expr = next; + (jump, expr) = next; } false From 672aca98aee9dbedc532e365f5d5ef5acc04e628 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 28 Dec 2024 11:49:39 -0800 Subject: [PATCH 062/121] Exclude slow expressions test from miri --- tests/test_expr.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_expr.rs b/tests/test_expr.rs index 6d5d4f8729..d63d3a3bcc 100644 --- a/tests/test_expr.rs +++ b/tests/test_expr.rs @@ -1,3 +1,4 @@ +#![cfg(not(miri))] #![allow( clippy::needless_lifetimes, clippy::single_element_loop, From c770215c8a2351c3f1c92bf6ea0c188d6bd62285 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 28 Dec 2024 09:17:46 -0800 Subject: [PATCH 063/121] Add exhaustive expressions parsing test --- tests/test_expr.rs | 477 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 475 insertions(+), 2 deletions(-) diff --git a/tests/test_expr.rs b/tests/test_expr.rs index d63d3a3bcc..46fc94cbf3 100644 --- a/tests/test_expr.rs +++ b/tests/test_expr.rs @@ -2,18 +2,26 @@ #![allow( clippy::needless_lifetimes, clippy::single_element_loop, + clippy::too_many_lines, clippy::uninlined_format_args )] #[macro_use] mod macros; -use proc_macro2::{Delimiter, Group}; +use proc_macro2::{Delimiter, Group, Span}; use quote::{quote, ToTokens as _}; use std::mem; +use std::process::ExitCode; use syn::punctuated::Punctuated; use syn::visit_mut::{self, VisitMut}; -use syn::{parse_quote, token, Expr, ExprRange, ExprTuple, Stmt, Token}; +use syn::{ + parse_quote, token, Arm, BinOp, Block, Expr, ExprAssign, ExprAwait, ExprBinary, ExprBlock, + ExprBreak, ExprCall, ExprCast, ExprClosure, ExprField, ExprForLoop, ExprIf, ExprIndex, + ExprMatch, ExprMethodCall, ExprRange, ExprRawAddr, ExprReference, ExprReturn, ExprTry, + ExprTuple, ExprUnary, ExprWhile, ExprYield, PointerMutability, RangeLimits, ReturnType, Stmt, + Token, UnOp, +}; #[test] fn test_expr_parse() { @@ -725,3 +733,468 @@ fn test_fixup() { ); } } + +#[test] +fn test_permutations() -> ExitCode { + fn iter(depth: usize, f: &mut dyn FnMut(Expr)) { + // Expr::Path + f(parse_quote!(x)); + f(parse_quote!(x::)); + f(parse_quote!(::CONST)); + + let Some(depth) = depth.checked_sub(1) else { + return; + }; + + // Expr::Array + f(parse_quote!([])); + + // Expr::Assign + iter(depth, &mut |expr| { + iter(0, &mut |simple| { + f(Expr::Assign(ExprAssign { + attrs: Vec::new(), + left: Box::new(simple.clone()), + eq_token: Token![=](Span::call_site()), + right: Box::new(expr.clone()), + })); + f(Expr::Assign(ExprAssign { + attrs: Vec::new(), + left: Box::new(expr.clone()), + eq_token: Token![=](Span::call_site()), + right: Box::new(simple), + })); + }); + }); + + // Expr::Async + f(parse_quote!(async {})); + + // Expr::Await + iter(depth, &mut |base| { + f(Expr::Await(ExprAwait { + attrs: Vec::new(), + base: Box::new(base), + dot_token: Token![.](Span::call_site()), + await_token: Token![await](Span::call_site()), + })); + }); + + // Expr::Binary + iter(depth, &mut |expr| { + iter(0, &mut |simple| { + for op in [ + BinOp::Add(Token![+](Span::call_site())), + BinOp::Sub(Token![-](Span::call_site())), + BinOp::Mul(Token![*](Span::call_site())), + BinOp::Div(Token![/](Span::call_site())), + BinOp::Rem(Token![%](Span::call_site())), + BinOp::And(Token![&&](Span::call_site())), + BinOp::Or(Token![||](Span::call_site())), + BinOp::BitXor(Token![^](Span::call_site())), + BinOp::BitAnd(Token![&](Span::call_site())), + BinOp::BitOr(Token![|](Span::call_site())), + BinOp::Shl(Token![<<](Span::call_site())), + BinOp::Shr(Token![>>](Span::call_site())), + BinOp::Eq(Token![==](Span::call_site())), + BinOp::Lt(Token![<](Span::call_site())), + BinOp::Le(Token![<=](Span::call_site())), + BinOp::Ne(Token![!=](Span::call_site())), + BinOp::Ge(Token![>=](Span::call_site())), + BinOp::Gt(Token![>](Span::call_site())), + BinOp::AddAssign(Token![+=](Span::call_site())), + BinOp::SubAssign(Token![-=](Span::call_site())), + BinOp::MulAssign(Token![*=](Span::call_site())), + BinOp::DivAssign(Token![/=](Span::call_site())), + BinOp::RemAssign(Token![%=](Span::call_site())), + BinOp::BitXorAssign(Token![^=](Span::call_site())), + BinOp::BitAndAssign(Token![&=](Span::call_site())), + BinOp::BitOrAssign(Token![|=](Span::call_site())), + BinOp::ShlAssign(Token![<<=](Span::call_site())), + BinOp::ShrAssign(Token![>>=](Span::call_site())), + ] { + f(Expr::Binary(ExprBinary { + attrs: Vec::new(), + left: Box::new(simple.clone()), + op, + right: Box::new(expr.clone()), + })); + f(Expr::Binary(ExprBinary { + attrs: Vec::new(), + left: Box::new(expr.clone()), + op, + right: Box::new(simple.clone()), + })); + } + }); + }); + + // Expr::Block + f(parse_quote!('a: {})); + iter(depth, &mut |expr| { + f(Expr::Block(ExprBlock { + attrs: Vec::new(), + label: None, + block: Block { + brace_token: token::Brace(Span::call_site()), + stmts: Vec::from([Stmt::Expr(expr.clone(), None)]), + }, + })); + f(Expr::Block(ExprBlock { + attrs: Vec::new(), + label: None, + block: Block { + brace_token: token::Brace(Span::call_site()), + stmts: Vec::from([Stmt::Expr( + expr.clone(), + Some(Token![;](Span::call_site())), + )]), + }, + })); + }); + + // Expr::Break + f(parse_quote!(break)); + f(parse_quote!(break 'a)); + iter(depth, &mut |expr| { + f(Expr::Break(ExprBreak { + attrs: Vec::new(), + break_token: Token![break](Span::call_site()), + label: None, + expr: Some(Box::new(expr.clone())), + })); + f(Expr::Break(ExprBreak { + attrs: Vec::new(), + break_token: Token![break](Span::call_site()), + label: Some(parse_quote!('a)), + expr: Some(Box::new(expr)), + })); + }); + + // Expr::Call + iter(depth, &mut |expr| { + f(Expr::Call(ExprCall { + attrs: Vec::new(), + func: Box::new(expr), + paren_token: token::Paren(Span::call_site()), + args: Punctuated::new(), + })); + }); + + // Expr::Cast + iter(depth, &mut |expr| { + f(Expr::Cast(ExprCast { + attrs: Vec::new(), + expr: Box::new(expr.clone()), + as_token: Token![as](Span::call_site()), + ty: parse_quote!(T), + })); + f(Expr::Cast(ExprCast { + attrs: Vec::new(), + expr: Box::new(expr), + as_token: Token![as](Span::call_site()), + ty: parse_quote!(Thing), + })); + }); + + // Expr::Closure + iter(depth, &mut |expr| { + f(Expr::Closure(ExprClosure { + attrs: Vec::new(), + lifetimes: None, + constness: None, + movability: None, + asyncness: None, + capture: None, + or1_token: Token![|](Span::call_site()), + inputs: Punctuated::new(), + or2_token: Token![|](Span::call_site()), + output: ReturnType::Default, + body: Box::new(expr.clone()), + })); + f(Expr::Closure(ExprClosure { + attrs: Vec::new(), + lifetimes: None, + constness: None, + movability: None, + asyncness: None, + capture: None, + or1_token: Token![|](Span::call_site()), + inputs: Punctuated::new(), + or2_token: Token![|](Span::call_site()), + output: ReturnType::Type(Token![->](Span::call_site()), parse_quote!(T)), + body: Box::new(expr), + })); + }); + + // Expr::Const + f(parse_quote!(const {})); + + // Expr::Continue + f(parse_quote!(continue)); + f(parse_quote!(continue 'a)); + + // Expr::Field + iter(depth, &mut |expr| { + f(Expr::Field(ExprField { + attrs: Vec::new(), + base: Box::new(expr.clone()), + dot_token: Token![.](Span::call_site()), + member: parse_quote!(field), + })); + f(Expr::Field(ExprField { + attrs: Vec::new(), + base: Box::new(expr), + dot_token: Token![.](Span::call_site()), + member: parse_quote!(0), + })); + }); + + // Expr::ForLoop + iter(depth, &mut |expr| { + f(Expr::ForLoop(ExprForLoop { + attrs: Vec::new(), + label: None, + for_token: Token![for](Span::call_site()), + pat: parse_quote!(_), + in_token: Token![in](Span::call_site()), + expr: Box::new(expr.clone()), + body: parse_quote!({}), + })); + f(Expr::ForLoop(ExprForLoop { + attrs: Vec::new(), + label: Some(parse_quote!('a:)), + for_token: Token![for](Span::call_site()), + pat: parse_quote!(_), + in_token: Token![in](Span::call_site()), + expr: Box::new(expr), + body: parse_quote!({}), + })); + }); + + // Expr::If + iter(depth, &mut |expr| { + f(Expr::If(ExprIf { + attrs: Vec::new(), + if_token: Token![if](Span::call_site()), + cond: Box::new(expr), + then_branch: parse_quote!({}), + else_branch: None, + })); + }); + + // Expr::Index + iter(depth, &mut |expr| { + f(Expr::Index(ExprIndex { + attrs: Vec::new(), + expr: Box::new(expr), + bracket_token: token::Bracket(Span::call_site()), + index: parse_quote!(0), + })); + }); + + // Expr::Loop + f(parse_quote!(loop {})); + f(parse_quote!('a: loop {})); + + // Expr::Macro + f(parse_quote!(m!())); + f(parse_quote!(m! {})); + + // Expr::Match + iter(depth, &mut |expr| { + f(Expr::Match(ExprMatch { + attrs: Vec::new(), + match_token: Token![match](Span::call_site()), + expr: Box::new(expr.clone()), + brace_token: token::Brace(Span::call_site()), + arms: Vec::new(), + })); + f(Expr::Match(ExprMatch { + attrs: Vec::new(), + match_token: Token![match](Span::call_site()), + expr: parse_quote!(x), + brace_token: token::Brace(Span::call_site()), + arms: Vec::from([Arm { + attrs: Vec::new(), + pat: parse_quote!(_), + guard: None, + fat_arrow_token: Token![=>](Span::call_site()), + body: Box::new(expr.clone()), + comma: None, + }]), + })); + f(Expr::Match(ExprMatch { + attrs: Vec::new(), + match_token: Token![match](Span::call_site()), + expr: parse_quote!(x), + brace_token: token::Brace(Span::call_site()), + arms: Vec::from([Arm { + attrs: Vec::new(), + pat: parse_quote!(_), + guard: Some((Token![if](Span::call_site()), Box::new(expr))), + fat_arrow_token: Token![=>](Span::call_site()), + body: parse_quote!({}), + comma: None, + }]), + })); + }); + + // Expr::MethodCall + iter(depth, &mut |expr| { + f(Expr::MethodCall(ExprMethodCall { + attrs: Vec::new(), + receiver: Box::new(expr.clone()), + dot_token: Token![.](Span::call_site()), + method: parse_quote!(method), + turbofish: None, + paren_token: token::Paren(Span::call_site()), + args: Punctuated::new(), + })); + f(Expr::MethodCall(ExprMethodCall { + attrs: Vec::new(), + receiver: Box::new(expr), + dot_token: Token![.](Span::call_site()), + method: parse_quote!(method), + turbofish: Some(parse_quote!(::)), + paren_token: token::Paren(Span::call_site()), + args: Punctuated::new(), + })); + }); + + // Expr::Range + f(parse_quote!(..)); + f(parse_quote!(0..)); + f(parse_quote!(..0)); + iter(depth, &mut |expr| { + f(Expr::Range(ExprRange { + attrs: Vec::new(), + start: None, + limits: RangeLimits::HalfOpen(Token![..](Span::call_site())), + end: Some(Box::new(expr.clone())), + })); + f(Expr::Range(ExprRange { + attrs: Vec::new(), + start: Some(Box::new(expr.clone())), + limits: RangeLimits::HalfOpen(Token![..](Span::call_site())), + end: None, + })); + }); + + // Expr::RawAddr + iter(depth, &mut |expr| { + f(Expr::RawAddr(ExprRawAddr { + attrs: Vec::new(), + and_token: Token![&](Span::call_site()), + raw: Token![raw](Span::call_site()), + mutability: PointerMutability::Const(Token![const](Span::call_site())), + expr: Box::new(expr), + })); + }); + + // Expr::Reference + iter(depth, &mut |expr| { + f(Expr::Reference(ExprReference { + attrs: Vec::new(), + and_token: Token![&](Span::call_site()), + mutability: None, + expr: Box::new(expr.clone()), + })); + f(Expr::Reference(ExprReference { + attrs: Vec::new(), + and_token: Token![&](Span::call_site()), + mutability: Some(Token![mut](Span::call_site())), + expr: Box::new(expr), + })); + }); + + // Expr::Return + f(parse_quote!(return)); + iter(depth, &mut |expr| { + f(Expr::Return(ExprReturn { + attrs: Vec::new(), + return_token: Token![return](Span::call_site()), + expr: Some(Box::new(expr)), + })); + }); + + // Expr::Struct + f(parse_quote!(Struct {})); + + // Expr::Try + iter(depth, &mut |expr| { + f(Expr::Try(ExprTry { + attrs: Vec::new(), + expr: Box::new(expr), + question_token: Token![?](Span::call_site()), + })); + }); + + // Expr::TryBlock + f(parse_quote!(try {})); + + // Expr::Unary + iter(depth, &mut |expr| { + for op in [ + UnOp::Deref(Token![*](Span::call_site())), + UnOp::Not(Token![!](Span::call_site())), + UnOp::Neg(Token![-](Span::call_site())), + ] { + f(Expr::Unary(ExprUnary { + attrs: Vec::new(), + op, + expr: Box::new(expr.clone()), + })); + } + }); + + // Expr::Unsafe + f(parse_quote!(unsafe {})); + + // Expr::While + iter(depth, &mut |expr| { + f(Expr::While(ExprWhile { + attrs: Vec::new(), + label: None, + while_token: Token![while](Span::call_site()), + cond: Box::new(expr.clone()), + body: parse_quote!({}), + })); + f(Expr::While(ExprWhile { + attrs: Vec::new(), + label: Some(parse_quote!('a:)), + while_token: Token![while](Span::call_site()), + cond: Box::new(expr), + body: parse_quote!({}), + })); + }); + + // Expr::Yield + f(parse_quote!(yield)); + iter(depth, &mut |expr| { + f(Expr::Yield(ExprYield { + attrs: Vec::new(), + yield_token: Token![yield](Span::call_site()), + expr: Some(Box::new(expr)), + })); + }); + } + + let mut status = ExitCode::SUCCESS; + macro_rules! fail { + ($($message:tt)*) => {{ + eprintln!($($message)*); + status = ExitCode::FAILURE; + return; + }}; + } + let mut assert = |expr: Expr| { + let tokens = expr.to_token_stream(); + if syn::parse2::(tokens.clone()).is_err() { + fail!("failed to parse: {}", tokens); + } + }; + + iter(2, &mut assert); + status +} From 6240d9cbede77a5e5375e8f99f5aeb571a93836c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 28 Dec 2024 11:24:19 -0800 Subject: [PATCH 064/121] Recursive implementation of confusable_with_adjacent_block --- src/classify.rs | 191 ++++++++++++++++++------------------------------ 1 file changed, 72 insertions(+), 119 deletions(-) diff --git a/src/classify.rs b/src/classify.rs index ab9dc230de..e1cd25dce5 100644 --- a/src/classify.rs +++ b/src/classify.rs @@ -68,133 +68,86 @@ pub(crate) fn requires_comma_to_be_match_arm(expr: &Expr) -> bool { } #[cfg(all(feature = "printing", feature = "full"))] -pub(crate) fn confusable_with_adjacent_block(mut expr: &Expr) -> bool { - let mut stack = Vec::new(); - let mut jump = false; - let mut tokens_right_of_empty_stack = false; +pub(crate) fn confusable_with_adjacent_block(expr: &Expr) -> bool { + let jump = false; + let rightmost_subexpression = true; + return confusable(expr, jump, rightmost_subexpression); - while let Some(next) = match expr { - Expr::Assign(e) => { - stack.push((jump, &e.right)); - Some((jump, &e.left)) - } - Expr::Await(e) => { - tokens_right_of_empty_stack |= stack.is_empty(); - Some((jump, &e.base)) - } - Expr::Binary(e) => { - stack.push((jump, &e.right)); - Some((jump, &e.left)) - } - Expr::Break(e) => { - if let Some(value) = &e.expr { - if let Expr::Block(_) = **value { - return true; - } - Some((true, value)) - } else { - stack.pop() - } - } - Expr::Call(e) => { - tokens_right_of_empty_stack |= stack.is_empty(); - Some((jump, &e.func)) - } - Expr::Cast(e) => { - tokens_right_of_empty_stack |= stack.is_empty(); - Some((jump, &e.expr)) - } - Expr::Closure(e) => Some((true, &e.body)), - Expr::Field(e) => { - tokens_right_of_empty_stack |= stack.is_empty(); - Some((jump, &e.base)) - } - Expr::Index(e) => { - tokens_right_of_empty_stack |= stack.is_empty(); - Some((jump, &e.expr)) - } - Expr::MethodCall(e) => { - tokens_right_of_empty_stack |= stack.is_empty(); - Some((jump, &e.receiver)) - } - Expr::Path(_) => { - if jump && stack.is_empty() && !tokens_right_of_empty_stack { - return true; + fn confusable(expr: &Expr, jump: bool, rightmost_subexpression: bool) -> bool { + match expr { + Expr::Assign(e) => { + confusable(&e.left, jump, false) + || confusable(&e.right, jump, rightmost_subexpression) } - stack.pop() - } - Expr::Range(e) => { - if let Some(Expr::Block(_)) = e.end.as_deref() { - return true; + Expr::Await(e) => confusable(&e.base, jump, false), + Expr::Binary(e) => { + confusable(&e.left, jump, false) + || confusable(&e.right, jump, rightmost_subexpression) } - match (&e.start, &e.end) { - (Some(start), Some(end)) => { - stack.push((jump, end)); - Some((jump, start)) - } - (Some(start), None) => { - tokens_right_of_empty_stack |= stack.is_empty(); - Some((jump, start)) + Expr::Break(e) => { + if let Some(value) = &e.expr { + matches!(**value, Expr::Block(_)) + || confusable(value, true, rightmost_subexpression) + } else { + false } - (None, Some(end)) => Some((jump, end)), - (None, None) => stack.pop(), - } - } - Expr::RawAddr(e) => Some((jump, &e.expr)), - Expr::Reference(e) => Some((jump, &e.expr)), - Expr::Return(e) => { - if e.expr.is_none() && stack.is_empty() && !tokens_right_of_empty_stack { - return true; } - if let Some(value) = &e.expr { - Some((true, value)) - } else { - stack.pop() + Expr::Call(e) => confusable(&e.func, jump, false), + Expr::Cast(e) => confusable(&e.expr, jump, false), + Expr::Closure(e) => confusable(&e.body, true, rightmost_subexpression), + Expr::Field(e) => confusable(&e.base, jump, false), + Expr::Index(e) => confusable(&e.expr, jump, false), + Expr::MethodCall(e) => confusable(&e.receiver, jump, false), + Expr::Path(_) => jump && rightmost_subexpression, + Expr::Range(e) => { + (match &e.start { + Some(start) => confusable(start, jump, false), + None => false, + } || match &e.end { + Some(end) => { + matches!(**end, Expr::Block(_)) + || confusable(end, jump, rightmost_subexpression) + } + None => false, + }) } - } - Expr::Struct(_) => return true, - Expr::Try(e) => { - tokens_right_of_empty_stack |= stack.is_empty(); - Some((jump, &e.expr)) - } - Expr::Unary(e) => Some((jump, &e.expr)), - Expr::Yield(e) => { - if e.expr.is_none() && stack.is_empty() && !tokens_right_of_empty_stack { - return true; - } - if let Some(value) = &e.expr { - Some((true, value)) - } else { - stack.pop() - } - } + Expr::RawAddr(e) => confusable(&e.expr, jump, rightmost_subexpression), + Expr::Reference(e) => confusable(&e.expr, jump, rightmost_subexpression), + Expr::Return(e) => match &e.expr { + Some(expr) => confusable(expr, true, rightmost_subexpression), + None => rightmost_subexpression, + }, + Expr::Struct(_) => !jump, + Expr::Try(e) => confusable(&e.expr, jump, false), + Expr::Unary(e) => confusable(&e.expr, jump, rightmost_subexpression), + Expr::Yield(e) => match &e.expr { + Some(expr) => confusable(expr, true, rightmost_subexpression), + None => rightmost_subexpression, + }, - Expr::Array(_) - | Expr::Async(_) - | Expr::Block(_) - | Expr::Const(_) - | Expr::Continue(_) - | Expr::ForLoop(_) - | Expr::Group(_) - | Expr::If(_) - | Expr::Infer(_) - | Expr::Let(_) - | Expr::Lit(_) - | Expr::Loop(_) - | Expr::Macro(_) - | Expr::Match(_) - | Expr::Paren(_) - | Expr::Repeat(_) - | Expr::TryBlock(_) - | Expr::Tuple(_) - | Expr::Unsafe(_) - | Expr::Verbatim(_) - | Expr::While(_) => stack.pop(), - } { - (jump, expr) = next; + Expr::Array(_) + | Expr::Async(_) + | Expr::Block(_) + | Expr::Const(_) + | Expr::Continue(_) + | Expr::ForLoop(_) + | Expr::Group(_) + | Expr::If(_) + | Expr::Infer(_) + | Expr::Let(_) + | Expr::Lit(_) + | Expr::Loop(_) + | Expr::Macro(_) + | Expr::Match(_) + | Expr::Paren(_) + | Expr::Repeat(_) + | Expr::TryBlock(_) + | Expr::Tuple(_) + | Expr::Unsafe(_) + | Expr::Verbatim(_) + | Expr::While(_) => false, + } } - - false } #[cfg(feature = "printing")] From 3f2acdc5ed26505d73a9f80e8d0f2c89e41bd1b6 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 28 Dec 2024 12:09:26 -0800 Subject: [PATCH 065/121] Release 2.0.93 --- Cargo.toml | 2 +- src/lib.rs | 2 +- syn.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e89dc93cc6..e0b6e08bfe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "syn" -version = "2.0.92" +version = "2.0.93" authors = ["David Tolnay "] categories = ["development-tools::procedural-macro-helpers", "parser-implementations"] description = "Parser for Rust source code" diff --git a/src/lib.rs b/src/lib.rs index a423ba65f0..38974a9f50 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -249,7 +249,7 @@ //! dynamic library libproc_macro from rustc toolchain. // Syn types in rustdoc of other crates get linked to here. -#![doc(html_root_url = "https://docs.rs/syn/2.0.92")] +#![doc(html_root_url = "https://docs.rs/syn/2.0.93")] #![cfg_attr(docsrs, feature(doc_cfg))] #![deny(unsafe_op_in_unsafe_fn)] #![allow(non_camel_case_types)] diff --git a/syn.json b/syn.json index 7405d0dcf5..a45c6e3550 100644 --- a/syn.json +++ b/syn.json @@ -1,5 +1,5 @@ { - "version": "2.0.92", + "version": "2.0.93", "types": [ { "ident": "Abi", From e749a2031eec6a85d99321160f7919949faebfbe Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 28 Dec 2024 18:33:10 -0800 Subject: [PATCH 066/121] Count total number of generated expressions in permutations test --- tests/test_expr.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/test_expr.rs b/tests/test_expr.rs index 46fc94cbf3..053c93881e 100644 --- a/tests/test_expr.rs +++ b/tests/test_expr.rs @@ -1180,6 +1180,7 @@ fn test_permutations() -> ExitCode { }); } + let mut count = 0; let mut status = ExitCode::SUCCESS; macro_rules! fail { ($($message:tt)*) => {{ @@ -1189,6 +1190,7 @@ fn test_permutations() -> ExitCode { }}; } let mut assert = |expr: Expr| { + count += 1; let tokens = expr.to_token_stream(); if syn::parse2::(tokens.clone()).is_err() { fail!("failed to parse: {}", tokens); @@ -1196,5 +1198,6 @@ fn test_permutations() -> ExitCode { }; iter(2, &mut assert); + assert_eq!(count, 134599); status } From f10c2eab33bc8cd476febdccee1b7750cf907d30 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 28 Dec 2024 18:34:25 -0800 Subject: [PATCH 067/121] Reduce number of effectively equivalent generated closure expressions --- tests/test_expr.rs | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/tests/test_expr.rs b/tests/test_expr.rs index 053c93881e..a1adfff60a 100644 --- a/tests/test_expr.rs +++ b/tests/test_expr.rs @@ -898,6 +898,7 @@ fn test_permutations() -> ExitCode { }); // Expr::Closure + f(parse_quote!(|| -> T {})); iter(depth, &mut |expr| { f(Expr::Closure(ExprClosure { attrs: Vec::new(), @@ -912,19 +913,6 @@ fn test_permutations() -> ExitCode { output: ReturnType::Default, body: Box::new(expr.clone()), })); - f(Expr::Closure(ExprClosure { - attrs: Vec::new(), - lifetimes: None, - constness: None, - movability: None, - asyncness: None, - capture: None, - or1_token: Token![|](Span::call_site()), - inputs: Punctuated::new(), - or2_token: Token![|](Span::call_site()), - output: ReturnType::Type(Token![->](Span::call_site()), parse_quote!(T)), - body: Box::new(expr), - })); }); // Expr::Const @@ -1198,6 +1186,6 @@ fn test_permutations() -> ExitCode { }; iter(2, &mut assert); - assert_eq!(count, 134599); + assert_eq!(count, 133539); status } From 8d1ca56f9417398d4a8b2025a512386cc8a6dcb7 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 28 Dec 2024 18:42:06 -0800 Subject: [PATCH 068/121] Ignore unreadable_literal pedantic clippy lint in test warning: long literal lacking separators --> tests/test_expr.rs:1189:23 | 1189 | assert_eq!(count, 133539); | ^^^^^^ help: consider: `133_539` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unreadable_literal = note: `-W clippy::unreadable-literal` implied by `-W clippy::pedantic` = help: to override `-W clippy::pedantic` add `#[allow(clippy::unreadable_literal)]` --- tests/test_expr.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_expr.rs b/tests/test_expr.rs index a1adfff60a..7aa610b008 100644 --- a/tests/test_expr.rs +++ b/tests/test_expr.rs @@ -3,7 +3,8 @@ clippy::needless_lifetimes, clippy::single_element_loop, clippy::too_many_lines, - clippy::uninlined_format_args + clippy::uninlined_format_args, + clippy::unreadable_literal )] #[macro_use] From 3cfb0e789baa08b4395b0848c1f31238a587192d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 28 Dec 2024 18:36:52 -0800 Subject: [PATCH 069/121] Extract common visitors used by tests --- tests/common/mod.rs | 1 + tests/common/visit.rs | 45 +++++++++++++++++++++++++++++++ tests/test_expr.rs | 20 +++++--------- tests/test_unparenthesize.rs | 51 +++++------------------------------- 4 files changed, 59 insertions(+), 58 deletions(-) create mode 100644 tests/common/visit.rs diff --git a/tests/common/mod.rs b/tests/common/mod.rs index c85ac0b4c9..ead830f811 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -3,3 +3,4 @@ pub mod eq; pub mod parse; +pub mod visit; diff --git a/tests/common/visit.rs b/tests/common/visit.rs new file mode 100644 index 0000000000..746dbd099d --- /dev/null +++ b/tests/common/visit.rs @@ -0,0 +1,45 @@ +use std::mem; +use syn::visit_mut::{self, VisitMut}; +use syn::{Expr, Generics, LifetimeParam, TypeParam}; + +pub struct FlattenParens; + +impl VisitMut for FlattenParens { + fn visit_expr_mut(&mut self, e: &mut Expr) { + while let Expr::Paren(paren) = e { + *e = mem::replace(&mut *paren.expr, Expr::PLACEHOLDER); + } + visit_mut::visit_expr_mut(self, e); + } +} + +pub struct AsIfPrinted; + +impl VisitMut for AsIfPrinted { + fn visit_generics_mut(&mut self, generics: &mut Generics) { + if generics.params.is_empty() { + generics.lt_token = None; + generics.gt_token = None; + } + if let Some(where_clause) = &generics.where_clause { + if where_clause.predicates.is_empty() { + generics.where_clause = None; + } + } + visit_mut::visit_generics_mut(self, generics); + } + + fn visit_lifetime_param_mut(&mut self, param: &mut LifetimeParam) { + if param.bounds.is_empty() { + param.colon_token = None; + } + visit_mut::visit_lifetime_param_mut(self, param); + } + + fn visit_type_param_mut(&mut self, param: &mut TypeParam) { + if param.bounds.is_empty() { + param.colon_token = None; + } + visit_mut::visit_type_param_mut(self, param); + } +} diff --git a/tests/test_expr.rs b/tests/test_expr.rs index 7aa610b008..ade9b8cc0b 100644 --- a/tests/test_expr.rs +++ b/tests/test_expr.rs @@ -1,5 +1,8 @@ #![cfg(not(miri))] +#![recursion_limit = "1024"] +#![feature(rustc_private)] #![allow( + clippy::match_like_matches_macro, clippy::needless_lifetimes, clippy::single_element_loop, clippy::too_many_lines, @@ -10,12 +13,14 @@ #[macro_use] mod macros; +mod common; + +use crate::common::visit::FlattenParens; use proc_macro2::{Delimiter, Group, Span}; use quote::{quote, ToTokens as _}; -use std::mem; use std::process::ExitCode; use syn::punctuated::Punctuated; -use syn::visit_mut::{self, VisitMut}; +use syn::visit_mut::VisitMut as _; use syn::{ parse_quote, token, Arm, BinOp, Block, Expr, ExprAssign, ExprAwait, ExprBinary, ExprBlock, ExprBreak, ExprCall, ExprCast, ExprClosure, ExprField, ExprForLoop, ExprIf, ExprIndex, @@ -675,17 +680,6 @@ fn test_chained_comparison() { #[test] fn test_fixup() { - struct FlattenParens; - - impl VisitMut for FlattenParens { - fn visit_expr_mut(&mut self, e: &mut Expr) { - while let Expr::Paren(paren) = e { - *e = mem::replace(&mut *paren.expr, Expr::PLACEHOLDER); - } - visit_mut::visit_expr_mut(self, e); - } - } - for tokens in [ quote! { 2 * (1 + 1) }, quote! { 0 + (0 + 0) }, diff --git a/tests/test_unparenthesize.rs b/tests/test_unparenthesize.rs index 5e155bff6b..e2a1f371a5 100644 --- a/tests/test_unparenthesize.rs +++ b/tests/test_unparenthesize.rs @@ -1,22 +1,25 @@ #![cfg(not(miri))] +#![recursion_limit = "1024"] +#![feature(rustc_private)] #![allow( clippy::manual_assert, + clippy::match_like_matches_macro, clippy::needless_lifetimes, clippy::uninlined_format_args )] +use crate::common::visit::{AsIfPrinted, FlattenParens}; use quote::ToTokens as _; use std::fs; -use std::mem; use std::panic; use std::path::Path; use std::sync::atomic::{AtomicUsize, Ordering}; -use syn::visit_mut::{self, VisitMut}; -use syn::{Expr, Generics, LifetimeParam, TypeParam}; +use syn::visit_mut::VisitMut as _; #[macro_use] mod macros; +mod common; mod repo; #[test] @@ -34,48 +37,6 @@ fn test_unparenthesize() { } } -struct FlattenParens; - -impl VisitMut for FlattenParens { - fn visit_expr_mut(&mut self, e: &mut Expr) { - while let Expr::Paren(paren) = e { - *e = mem::replace(&mut *paren.expr, Expr::PLACEHOLDER); - } - visit_mut::visit_expr_mut(self, e); - } -} - -struct AsIfPrinted; - -impl VisitMut for AsIfPrinted { - fn visit_generics_mut(&mut self, generics: &mut Generics) { - if generics.params.is_empty() { - generics.lt_token = None; - generics.gt_token = None; - } - if let Some(where_clause) = &generics.where_clause { - if where_clause.predicates.is_empty() { - generics.where_clause = None; - } - } - visit_mut::visit_generics_mut(self, generics); - } - - fn visit_lifetime_param_mut(&mut self, param: &mut LifetimeParam) { - if param.bounds.is_empty() { - param.colon_token = None; - } - visit_mut::visit_lifetime_param_mut(self, param); - } - - fn visit_type_param_mut(&mut self, param: &mut TypeParam) { - if param.bounds.is_empty() { - param.colon_token = None; - } - visit_mut::visit_type_param_mut(self, param); - } -} - fn test(path: &Path, failed: &AtomicUsize) { let content = fs::read_to_string(path).unwrap(); From cf9d79657292898aad553520d4ad242341963b00 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 28 Dec 2024 18:45:31 -0800 Subject: [PATCH 070/121] Move shebang normalization into AsIfPrinted visitor --- tests/common/visit.rs | 7 ++++++- tests/test_unparenthesize.rs | 1 - 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/common/visit.rs b/tests/common/visit.rs index 746dbd099d..df2e150187 100644 --- a/tests/common/visit.rs +++ b/tests/common/visit.rs @@ -1,6 +1,6 @@ use std::mem; use syn::visit_mut::{self, VisitMut}; -use syn::{Expr, Generics, LifetimeParam, TypeParam}; +use syn::{Expr, File, Generics, LifetimeParam, TypeParam}; pub struct FlattenParens; @@ -16,6 +16,11 @@ impl VisitMut for FlattenParens { pub struct AsIfPrinted; impl VisitMut for AsIfPrinted { + fn visit_file_mut(&mut self, file: &mut File) { + file.shebang = None; + visit_mut::visit_file_mut(self, file); + } + fn visit_generics_mut(&mut self, generics: &mut Generics) { if generics.params.is_empty() { generics.lt_token = None; diff --git a/tests/test_unparenthesize.rs b/tests/test_unparenthesize.rs index e2a1f371a5..1330aa2bad 100644 --- a/tests/test_unparenthesize.rs +++ b/tests/test_unparenthesize.rs @@ -42,7 +42,6 @@ fn test(path: &Path, failed: &AtomicUsize) { match panic::catch_unwind(|| -> syn::Result<()> { let mut before = syn::parse_file(&content)?; - before.shebang = None; FlattenParens.visit_file_mut(&mut before); let printed = before.to_token_stream(); let mut after = syn::parse2::(printed.clone())?; From a83b1586cb91c763182c86a0095a50aba841cc15 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 28 Dec 2024 20:20:04 -0800 Subject: [PATCH 071/121] Normalize Stmt::Expr(Expr::Macro) to Stmt::Macro --- tests/common/visit.rs | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/tests/common/visit.rs b/tests/common/visit.rs index df2e150187..7330a5727e 100644 --- a/tests/common/visit.rs +++ b/tests/common/visit.rs @@ -1,6 +1,6 @@ use std::mem; use syn::visit_mut::{self, VisitMut}; -use syn::{Expr, File, Generics, LifetimeParam, TypeParam}; +use syn::{Expr, File, Generics, LifetimeParam, MacroDelimiter, Stmt, StmtMacro, TypeParam}; pub struct FlattenParens; @@ -41,6 +41,27 @@ impl VisitMut for AsIfPrinted { visit_mut::visit_lifetime_param_mut(self, param); } + fn visit_stmt_mut(&mut self, stmt: &mut Stmt) { + if let Stmt::Expr(expr, semi) = stmt { + if let Expr::Macro(e) = expr { + if match e.mac.delimiter { + MacroDelimiter::Brace(_) => true, + MacroDelimiter::Paren(_) | MacroDelimiter::Bracket(_) => semi.is_some(), + } { + let Expr::Macro(expr) = mem::replace(expr, Expr::PLACEHOLDER) else { + unreachable!(); + }; + *stmt = Stmt::Macro(StmtMacro { + attrs: expr.attrs, + mac: expr.mac, + semi_token: *semi, + }); + } + } + } + visit_mut::visit_stmt_mut(self, stmt); + } + fn visit_type_param_mut(&mut self, param: &mut TypeParam) { if param.bounds.is_empty() { param.colon_token = None; From ee76c9ee71b6f33a3f6e220c99e058c71aa9b4da Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 28 Dec 2024 20:17:54 -0800 Subject: [PATCH 072/121] Add assertion in test_permutations that parsed expr is correct --- tests/test_expr.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/tests/test_expr.rs b/tests/test_expr.rs index ade9b8cc0b..69ce1b141e 100644 --- a/tests/test_expr.rs +++ b/tests/test_expr.rs @@ -15,7 +15,7 @@ mod macros; mod common; -use crate::common::visit::FlattenParens; +use crate::common::visit::{AsIfPrinted, FlattenParens}; use proc_macro2::{Delimiter, Group, Span}; use quote::{quote, ToTokens as _}; use std::process::ExitCode; @@ -1172,11 +1172,16 @@ fn test_permutations() -> ExitCode { return; }}; } - let mut assert = |expr: Expr| { + let mut assert = |mut original: Expr| { count += 1; - let tokens = expr.to_token_stream(); - if syn::parse2::(tokens.clone()).is_err() { + let tokens = original.to_token_stream(); + let Ok(mut parsed) = syn::parse2::(tokens.clone()) else { fail!("failed to parse: {}", tokens); + }; + AsIfPrinted.visit_expr_mut(&mut original); + FlattenParens.visit_expr_mut(&mut parsed); + if original != parsed { + fail!("before: {}\nafter: {}", tokens, parsed.to_token_stream()); } }; From c811b33d4fe49e3cf823947a38948f48f38342b7 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 29 Dec 2024 07:52:56 -0800 Subject: [PATCH 073/121] Tweak block confusable logic inside of jumps --- src/classify.rs | 6 +++--- tests/test_expr.rs | 3 +++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/classify.rs b/src/classify.rs index e1cd25dce5..607c1bbc61 100644 --- a/src/classify.rs +++ b/src/classify.rs @@ -89,7 +89,7 @@ pub(crate) fn confusable_with_adjacent_block(expr: &Expr) -> bool { matches!(**value, Expr::Block(_)) || confusable(value, true, rightmost_subexpression) } else { - false + jump && rightmost_subexpression } } Expr::Call(e) => confusable(&e.func, jump, false), @@ -108,7 +108,7 @@ pub(crate) fn confusable_with_adjacent_block(expr: &Expr) -> bool { matches!(**end, Expr::Block(_)) || confusable(end, jump, rightmost_subexpression) } - None => false, + None => jump && rightmost_subexpression, }) } Expr::RawAddr(e) => confusable(&e.expr, jump, rightmost_subexpression), @@ -117,7 +117,7 @@ pub(crate) fn confusable_with_adjacent_block(expr: &Expr) -> bool { Some(expr) => confusable(expr, true, rightmost_subexpression), None => rightmost_subexpression, }, - Expr::Struct(_) => !jump, + Expr::Struct(_) => !jump || rightmost_subexpression, Expr::Try(e) => confusable(&e.expr, jump, false), Expr::Unary(e) => confusable(&e.expr, jump, rightmost_subexpression), Expr::Yield(e) => match &e.expr { diff --git a/tests/test_expr.rs b/tests/test_expr.rs index 69ce1b141e..1ab15d5689 100644 --- a/tests/test_expr.rs +++ b/tests/test_expr.rs @@ -701,6 +701,9 @@ fn test_fixup() { quote! { a + (|| b) + c }, quote! { if let _ = ((break) - 1 || true) {} }, quote! { if let _ = (break + 1 || true) {} }, + quote! { if (break break) {} }, + quote! { if (return ..) {} }, + quote! { if (|| Struct {}) {} }, quote! { (break)() }, quote! { (..) = () }, quote! { (..) += () }, From d954d2d2ec0966a3e2a6b0be135d5d99f6dbd21f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 29 Dec 2024 08:07:27 -0800 Subject: [PATCH 074/121] Tweak block confusable logic for labeled blocks --- src/classify.rs | 4 ++-- tests/test_expr.rs | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/classify.rs b/src/classify.rs index 607c1bbc61..ad821eff1e 100644 --- a/src/classify.rs +++ b/src/classify.rs @@ -86,7 +86,7 @@ pub(crate) fn confusable_with_adjacent_block(expr: &Expr) -> bool { } Expr::Break(e) => { if let Some(value) = &e.expr { - matches!(**value, Expr::Block(_)) + matches!(&**value, Expr::Block(block) if block.attrs.is_empty() && block.label.is_none()) || confusable(value, true, rightmost_subexpression) } else { jump && rightmost_subexpression @@ -105,7 +105,7 @@ pub(crate) fn confusable_with_adjacent_block(expr: &Expr) -> bool { None => false, } || match &e.end { Some(end) => { - matches!(**end, Expr::Block(_)) + matches!(&**end, Expr::Block(block) if block.attrs.is_empty() && block.label.is_none()) || confusable(end, jump, rightmost_subexpression) } None => jump && rightmost_subexpression, diff --git a/tests/test_expr.rs b/tests/test_expr.rs index 1ab15d5689..a26a8ca4f0 100644 --- a/tests/test_expr.rs +++ b/tests/test_expr.rs @@ -704,6 +704,8 @@ fn test_fixup() { quote! { if (break break) {} }, quote! { if (return ..) {} }, quote! { if (|| Struct {}) {} }, + quote! { if break 'outer 'block: {} {} }, + quote! { if ..'block: {} {} }, quote! { (break)() }, quote! { (..) = () }, quote! { (..) += () }, From b11d4b9a6af942dbab6610a141475f8be3a7ce5a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 29 Dec 2024 08:35:39 -0800 Subject: [PATCH 075/121] Tweak block confusable logic for break value starting with block --- src/classify.rs | 65 +++++++++++++++++++++++++++------------------- tests/test_expr.rs | 1 + 2 files changed, 39 insertions(+), 27 deletions(-) diff --git a/src/classify.rs b/src/classify.rs index ad821eff1e..20b8d97037 100644 --- a/src/classify.rs +++ b/src/classify.rs @@ -70,64 +70,75 @@ pub(crate) fn requires_comma_to_be_match_arm(expr: &Expr) -> bool { #[cfg(all(feature = "printing", feature = "full"))] pub(crate) fn confusable_with_adjacent_block(expr: &Expr) -> bool { let jump = false; + let optional_leftmost_subexpression = false; let rightmost_subexpression = true; - return confusable(expr, jump, rightmost_subexpression); + return confusable( + expr, + jump, + optional_leftmost_subexpression, + rightmost_subexpression, + ); - fn confusable(expr: &Expr, jump: bool, rightmost_subexpression: bool) -> bool { + fn confusable( + expr: &Expr, + jump: bool, + optional_leftmost_subexpression: bool, + rightmost_subexpression: bool, + ) -> bool { match expr { Expr::Assign(e) => { - confusable(&e.left, jump, false) - || confusable(&e.right, jump, rightmost_subexpression) + confusable(&e.left, jump, optional_leftmost_subexpression, false) + || confusable(&e.right, jump, false, rightmost_subexpression) } - Expr::Await(e) => confusable(&e.base, jump, false), + Expr::Await(e) => confusable(&e.base, jump, optional_leftmost_subexpression, false), Expr::Binary(e) => { - confusable(&e.left, jump, false) - || confusable(&e.right, jump, rightmost_subexpression) + confusable(&e.left, jump, optional_leftmost_subexpression, false) + || confusable(&e.right, jump, false, rightmost_subexpression) + } + Expr::Block(e) => { + optional_leftmost_subexpression && e.attrs.is_empty() && e.label.is_none() } Expr::Break(e) => { if let Some(value) = &e.expr { - matches!(&**value, Expr::Block(block) if block.attrs.is_empty() && block.label.is_none()) - || confusable(value, true, rightmost_subexpression) + confusable(value, true, true, rightmost_subexpression) } else { jump && rightmost_subexpression } } - Expr::Call(e) => confusable(&e.func, jump, false), - Expr::Cast(e) => confusable(&e.expr, jump, false), - Expr::Closure(e) => confusable(&e.body, true, rightmost_subexpression), - Expr::Field(e) => confusable(&e.base, jump, false), - Expr::Index(e) => confusable(&e.expr, jump, false), - Expr::MethodCall(e) => confusable(&e.receiver, jump, false), + Expr::Call(e) => confusable(&e.func, jump, optional_leftmost_subexpression, false), + Expr::Cast(e) => confusable(&e.expr, jump, optional_leftmost_subexpression, false), + Expr::Closure(e) => confusable(&e.body, true, false, rightmost_subexpression), + Expr::Field(e) => confusable(&e.base, jump, optional_leftmost_subexpression, false), + Expr::Index(e) => confusable(&e.expr, jump, optional_leftmost_subexpression, false), + Expr::MethodCall(e) => { + confusable(&e.receiver, jump, optional_leftmost_subexpression, false) + } Expr::Path(_) => jump && rightmost_subexpression, Expr::Range(e) => { (match &e.start { - Some(start) => confusable(start, jump, false), + Some(start) => confusable(start, jump, optional_leftmost_subexpression, false), None => false, } || match &e.end { - Some(end) => { - matches!(&**end, Expr::Block(block) if block.attrs.is_empty() && block.label.is_none()) - || confusable(end, jump, rightmost_subexpression) - } + Some(end) => confusable(end, jump, true, rightmost_subexpression), None => jump && rightmost_subexpression, }) } - Expr::RawAddr(e) => confusable(&e.expr, jump, rightmost_subexpression), - Expr::Reference(e) => confusable(&e.expr, jump, rightmost_subexpression), + Expr::RawAddr(e) => confusable(&e.expr, jump, false, rightmost_subexpression), + Expr::Reference(e) => confusable(&e.expr, jump, false, rightmost_subexpression), Expr::Return(e) => match &e.expr { - Some(expr) => confusable(expr, true, rightmost_subexpression), + Some(expr) => confusable(expr, true, false, rightmost_subexpression), None => rightmost_subexpression, }, Expr::Struct(_) => !jump || rightmost_subexpression, - Expr::Try(e) => confusable(&e.expr, jump, false), - Expr::Unary(e) => confusable(&e.expr, jump, rightmost_subexpression), + Expr::Try(e) => confusable(&e.expr, jump, optional_leftmost_subexpression, false), + Expr::Unary(e) => confusable(&e.expr, jump, false, rightmost_subexpression), Expr::Yield(e) => match &e.expr { - Some(expr) => confusable(expr, true, rightmost_subexpression), + Some(expr) => confusable(expr, true, false, rightmost_subexpression), None => rightmost_subexpression, }, Expr::Array(_) | Expr::Async(_) - | Expr::Block(_) | Expr::Const(_) | Expr::Continue(_) | Expr::ForLoop(_) diff --git a/tests/test_expr.rs b/tests/test_expr.rs index a26a8ca4f0..b5d7704d96 100644 --- a/tests/test_expr.rs +++ b/tests/test_expr.rs @@ -706,6 +706,7 @@ fn test_fixup() { quote! { if (|| Struct {}) {} }, quote! { if break 'outer 'block: {} {} }, quote! { if ..'block: {} {} }, + quote! { if (break {}.await) {} }, quote! { (break)() }, quote! { (..) = () }, quote! { (..) += () }, From 9aa54114585e2cac0930ad26759f4998538be11d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 29 Dec 2024 08:46:29 -0800 Subject: [PATCH 076/121] Tweak block confusable logic for structs inside closures --- src/classify.rs | 107 ++++++++++++++++++++++++++++++++++----------- src/lib.rs | 1 + tests/test_expr.rs | 2 + 3 files changed, 85 insertions(+), 25 deletions(-) diff --git a/src/classify.rs b/src/classify.rs index 20b8d97037..1b57f20cf5 100644 --- a/src/classify.rs +++ b/src/classify.rs @@ -70,11 +70,13 @@ pub(crate) fn requires_comma_to_be_match_arm(expr: &Expr) -> bool { #[cfg(all(feature = "printing", feature = "full"))] pub(crate) fn confusable_with_adjacent_block(expr: &Expr) -> bool { let jump = false; + let closure = false; let optional_leftmost_subexpression = false; let rightmost_subexpression = true; return confusable( expr, jump, + closure, optional_leftmost_subexpression, rightmost_subexpression, ); @@ -82,58 +84,113 @@ pub(crate) fn confusable_with_adjacent_block(expr: &Expr) -> bool { fn confusable( expr: &Expr, jump: bool, + closure: bool, optional_leftmost_subexpression: bool, rightmost_subexpression: bool, ) -> bool { match expr { Expr::Assign(e) => { - confusable(&e.left, jump, optional_leftmost_subexpression, false) - || confusable(&e.right, jump, false, rightmost_subexpression) + confusable( + &e.left, + jump, + closure, + optional_leftmost_subexpression, + false, + ) || confusable(&e.right, jump, closure, false, rightmost_subexpression) } - Expr::Await(e) => confusable(&e.base, jump, optional_leftmost_subexpression, false), + Expr::Await(e) => confusable( + &e.base, + jump, + closure, + optional_leftmost_subexpression, + false, + ), Expr::Binary(e) => { - confusable(&e.left, jump, optional_leftmost_subexpression, false) - || confusable(&e.right, jump, false, rightmost_subexpression) + confusable( + &e.left, + jump, + closure, + optional_leftmost_subexpression, + false, + ) || confusable(&e.right, jump, closure, false, rightmost_subexpression) } Expr::Block(e) => { optional_leftmost_subexpression && e.attrs.is_empty() && e.label.is_none() } Expr::Break(e) => { if let Some(value) = &e.expr { - confusable(value, true, true, rightmost_subexpression) + confusable(value, true, false, true, rightmost_subexpression) } else { - jump && rightmost_subexpression + (jump || closure) && rightmost_subexpression } } - Expr::Call(e) => confusable(&e.func, jump, optional_leftmost_subexpression, false), - Expr::Cast(e) => confusable(&e.expr, jump, optional_leftmost_subexpression, false), - Expr::Closure(e) => confusable(&e.body, true, false, rightmost_subexpression), - Expr::Field(e) => confusable(&e.base, jump, optional_leftmost_subexpression, false), - Expr::Index(e) => confusable(&e.expr, jump, optional_leftmost_subexpression, false), - Expr::MethodCall(e) => { - confusable(&e.receiver, jump, optional_leftmost_subexpression, false) - } - Expr::Path(_) => jump && rightmost_subexpression, + Expr::Call(e) => confusable( + &e.func, + jump, + closure, + optional_leftmost_subexpression, + false, + ), + Expr::Cast(e) => confusable( + &e.expr, + jump, + closure, + optional_leftmost_subexpression, + false, + ), + Expr::Closure(e) => confusable(&e.body, jump, true, false, rightmost_subexpression), + Expr::Field(e) => confusable( + &e.base, + jump, + closure, + optional_leftmost_subexpression, + false, + ), + Expr::Index(e) => confusable( + &e.expr, + jump, + closure, + optional_leftmost_subexpression, + false, + ), + Expr::MethodCall(e) => confusable( + &e.receiver, + jump, + closure, + optional_leftmost_subexpression, + false, + ), + Expr::Path(_) => (jump || closure) && rightmost_subexpression, Expr::Range(e) => { (match &e.start { - Some(start) => confusable(start, jump, optional_leftmost_subexpression, false), + Some(start) => { + confusable(start, jump, closure, optional_leftmost_subexpression, false) + } None => false, } || match &e.end { - Some(end) => confusable(end, jump, true, rightmost_subexpression), - None => jump && rightmost_subexpression, + Some(end) => confusable(end, jump, closure, true, rightmost_subexpression), + None => (jump || closure) && rightmost_subexpression, }) } - Expr::RawAddr(e) => confusable(&e.expr, jump, false, rightmost_subexpression), - Expr::Reference(e) => confusable(&e.expr, jump, false, rightmost_subexpression), + Expr::RawAddr(e) => confusable(&e.expr, jump, closure, false, rightmost_subexpression), + Expr::Reference(e) => { + confusable(&e.expr, jump, closure, false, rightmost_subexpression) + } Expr::Return(e) => match &e.expr { - Some(expr) => confusable(expr, true, false, rightmost_subexpression), + Some(expr) => confusable(expr, true, false, false, rightmost_subexpression), None => rightmost_subexpression, }, Expr::Struct(_) => !jump || rightmost_subexpression, - Expr::Try(e) => confusable(&e.expr, jump, optional_leftmost_subexpression, false), - Expr::Unary(e) => confusable(&e.expr, jump, false, rightmost_subexpression), + Expr::Try(e) => confusable( + &e.expr, + jump, + closure, + optional_leftmost_subexpression, + false, + ), + Expr::Unary(e) => confusable(&e.expr, jump, closure, false, rightmost_subexpression), Expr::Yield(e) => match &e.expr { - Some(expr) => confusable(expr, true, false, rightmost_subexpression), + Some(expr) => confusable(expr, true, false, false, rightmost_subexpression), None => rightmost_subexpression, }, diff --git a/src/lib.rs b/src/lib.rs index 38974a9f50..18d3a5ef1d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -267,6 +267,7 @@ clippy::enum_glob_use, clippy::expl_impl_clone_on_copy, clippy::explicit_auto_deref, + clippy::fn_params_excessive_bools, clippy::if_not_else, clippy::inherent_to_string, clippy::into_iter_without_iter, diff --git a/tests/test_expr.rs b/tests/test_expr.rs index b5d7704d96..36b0929af9 100644 --- a/tests/test_expr.rs +++ b/tests/test_expr.rs @@ -704,6 +704,8 @@ fn test_fixup() { quote! { if (break break) {} }, quote! { if (return ..) {} }, quote! { if (|| Struct {}) {} }, + quote! { if (|| Struct {}.await) {} }, + quote! { if break || Struct {}.await {} }, quote! { if break 'outer 'block: {} {} }, quote! { if ..'block: {} {} }, quote! { if (break {}.await) {} }, From f5db50ab24e63b7b53f8976c6d347714421ca561 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 29 Dec 2024 10:26:27 -0800 Subject: [PATCH 077/121] Fix incorrect parse of statement macro followed by range --- src/stmt.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/stmt.rs b/src/stmt.rs index ac8238a98a..849afd4897 100644 --- a/src/stmt.rs +++ b/src/stmt.rs @@ -208,7 +208,8 @@ pub(crate) mod parsing { if ahead.peek2(Ident) || ahead.peek2(Token![try]) { is_item_macro = true; } else if ahead.peek2(token::Brace) - && !(ahead.peek3(Token![.]) || ahead.peek3(Token![?])) + && !(ahead.peek3(Token![.]) && !ahead.peek3(Token![..]) + || ahead.peek3(Token![?])) { input.advance_to(&ahead); return stmt_mac(input, attrs, path).map(Stmt::Macro); From 6526b644814c0159d087f43149b386e142543f35 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 29 Dec 2024 21:39:48 -0800 Subject: [PATCH 078/121] Simplify confusable_with_adjacent_block --- src/classify.rs | 68 ++++++++++++++++++++----------------------------- 1 file changed, 27 insertions(+), 41 deletions(-) diff --git a/src/classify.rs b/src/classify.rs index 1b57f20cf5..53bd6722e6 100644 --- a/src/classify.rs +++ b/src/classify.rs @@ -69,22 +69,19 @@ pub(crate) fn requires_comma_to_be_match_arm(expr: &Expr) -> bool { #[cfg(all(feature = "printing", feature = "full"))] pub(crate) fn confusable_with_adjacent_block(expr: &Expr) -> bool { - let jump = false; - let closure = false; + let allow_struct = false; let optional_leftmost_subexpression = false; let rightmost_subexpression = true; return confusable( expr, - jump, - closure, + allow_struct, optional_leftmost_subexpression, rightmost_subexpression, ); fn confusable( expr: &Expr, - jump: bool, - closure: bool, + allow_struct: bool, optional_leftmost_subexpression: bool, rightmost_subexpression: bool, ) -> bool { @@ -92,105 +89,94 @@ pub(crate) fn confusable_with_adjacent_block(expr: &Expr) -> bool { Expr::Assign(e) => { confusable( &e.left, - jump, - closure, + allow_struct, optional_leftmost_subexpression, false, - ) || confusable(&e.right, jump, closure, false, rightmost_subexpression) + ) || confusable(&e.right, allow_struct, false, rightmost_subexpression) } Expr::Await(e) => confusable( &e.base, - jump, - closure, + allow_struct, optional_leftmost_subexpression, false, ), Expr::Binary(e) => { confusable( &e.left, - jump, - closure, + allow_struct, optional_leftmost_subexpression, false, - ) || confusable(&e.right, jump, closure, false, rightmost_subexpression) + ) || confusable(&e.right, allow_struct, false, rightmost_subexpression) } Expr::Block(e) => { optional_leftmost_subexpression && e.attrs.is_empty() && e.label.is_none() } Expr::Break(e) => { if let Some(value) = &e.expr { - confusable(value, true, false, true, rightmost_subexpression) + confusable(value, true, true, rightmost_subexpression) } else { - (jump || closure) && rightmost_subexpression + allow_struct && rightmost_subexpression } } Expr::Call(e) => confusable( &e.func, - jump, - closure, + allow_struct, optional_leftmost_subexpression, false, ), Expr::Cast(e) => confusable( &e.expr, - jump, - closure, + allow_struct, optional_leftmost_subexpression, false, ), - Expr::Closure(e) => confusable(&e.body, jump, true, false, rightmost_subexpression), + Expr::Closure(e) => confusable(&e.body, allow_struct, false, rightmost_subexpression), Expr::Field(e) => confusable( &e.base, - jump, - closure, + allow_struct, optional_leftmost_subexpression, false, ), Expr::Index(e) => confusable( &e.expr, - jump, - closure, + allow_struct, optional_leftmost_subexpression, false, ), Expr::MethodCall(e) => confusable( &e.receiver, - jump, - closure, + allow_struct, optional_leftmost_subexpression, false, ), - Expr::Path(_) => (jump || closure) && rightmost_subexpression, + Expr::Path(_) => allow_struct && rightmost_subexpression, Expr::Range(e) => { (match &e.start { Some(start) => { - confusable(start, jump, closure, optional_leftmost_subexpression, false) + confusable(start, allow_struct, optional_leftmost_subexpression, false) } None => false, } || match &e.end { - Some(end) => confusable(end, jump, closure, true, rightmost_subexpression), - None => (jump || closure) && rightmost_subexpression, + Some(end) => confusable(end, allow_struct, true, rightmost_subexpression), + None => allow_struct && rightmost_subexpression, }) } - Expr::RawAddr(e) => confusable(&e.expr, jump, closure, false, rightmost_subexpression), - Expr::Reference(e) => { - confusable(&e.expr, jump, closure, false, rightmost_subexpression) - } + Expr::RawAddr(e) => confusable(&e.expr, allow_struct, false, rightmost_subexpression), + Expr::Reference(e) => confusable(&e.expr, allow_struct, false, rightmost_subexpression), Expr::Return(e) => match &e.expr { - Some(expr) => confusable(expr, true, false, false, rightmost_subexpression), + Some(expr) => confusable(expr, true, false, rightmost_subexpression), None => rightmost_subexpression, }, - Expr::Struct(_) => !jump || rightmost_subexpression, + Expr::Struct(_) => !allow_struct, Expr::Try(e) => confusable( &e.expr, - jump, - closure, + allow_struct, optional_leftmost_subexpression, false, ), - Expr::Unary(e) => confusable(&e.expr, jump, closure, false, rightmost_subexpression), + Expr::Unary(e) => confusable(&e.expr, allow_struct, false, rightmost_subexpression), Expr::Yield(e) => match &e.expr { - Some(expr) => confusable(expr, true, false, false, rightmost_subexpression), + Some(expr) => confusable(expr, true, false, rightmost_subexpression), None => rightmost_subexpression, }, From 2533d4573b2c061ac386a1d235b3182ac9228183 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 29 Dec 2024 10:05:15 -0800 Subject: [PATCH 079/121] Check for redundant parens in permutations test --- tests/common/visit.rs | 23 +++++++++++++++++++++++ tests/test_expr.rs | 10 ++++++++++ 2 files changed, 33 insertions(+) diff --git a/tests/common/visit.rs b/tests/common/visit.rs index 7330a5727e..b4ae5ecd55 100644 --- a/tests/common/visit.rs +++ b/tests/common/visit.rs @@ -1,9 +1,32 @@ +use proc_macro2::{Delimiter, Group, TokenStream, TokenTree}; use std::mem; use syn::visit_mut::{self, VisitMut}; use syn::{Expr, File, Generics, LifetimeParam, MacroDelimiter, Stmt, StmtMacro, TypeParam}; pub struct FlattenParens; +impl FlattenParens { + pub fn visit_token_stream_mut(tokens: &mut TokenStream) { + *tokens = mem::take(tokens) + .into_iter() + .flat_map(|tt| { + if let TokenTree::Group(group) = tt { + let delimiter = group.delimiter(); + let mut content = group.stream(); + Self::visit_token_stream_mut(&mut content); + if let Delimiter::Parenthesis = delimiter { + content + } else { + TokenStream::from(TokenTree::Group(Group::new(delimiter, content))) + } + } else { + TokenStream::from(tt) + } + }) + .collect(); + } +} + impl VisitMut for FlattenParens { fn visit_expr_mut(&mut self, e: &mut Expr) { while let Expr::Paren(paren) = e { diff --git a/tests/test_expr.rs b/tests/test_expr.rs index 36b0929af9..c8ad7e82bb 100644 --- a/tests/test_expr.rs +++ b/tests/test_expr.rs @@ -1191,6 +1191,16 @@ fn test_permutations() -> ExitCode { if original != parsed { fail!("before: {}\nafter: {}", tokens, parsed.to_token_stream()); } + let mut tokens_no_paren = tokens.clone(); + FlattenParens::visit_token_stream_mut(&mut tokens_no_paren); + if tokens.to_string() != tokens_no_paren.to_string() { + if let Ok(mut parsed2) = syn::parse2::(tokens_no_paren) { + FlattenParens.visit_expr_mut(&mut parsed2); + if original == parsed2 { + fail!("redundant parens: {}", tokens); + } + } + } }; iter(2, &mut assert); From ed4d1442638f06cc3686161b29e90acdffe048ac Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 29 Dec 2024 22:02:04 -0800 Subject: [PATCH 080/121] Reduce redundant testing of grammatically similar assignment binops --- tests/test_expr.rs | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/tests/test_expr.rs b/tests/test_expr.rs index c8ad7e82bb..652e66ed96 100644 --- a/tests/test_expr.rs +++ b/tests/test_expr.rs @@ -805,16 +805,7 @@ fn test_permutations() -> ExitCode { BinOp::Ne(Token![!=](Span::call_site())), BinOp::Ge(Token![>=](Span::call_site())), BinOp::Gt(Token![>](Span::call_site())), - BinOp::AddAssign(Token![+=](Span::call_site())), - BinOp::SubAssign(Token![-=](Span::call_site())), - BinOp::MulAssign(Token![*=](Span::call_site())), - BinOp::DivAssign(Token![/=](Span::call_site())), - BinOp::RemAssign(Token![%=](Span::call_site())), - BinOp::BitXorAssign(Token![^=](Span::call_site())), - BinOp::BitAndAssign(Token![&=](Span::call_site())), - BinOp::BitOrAssign(Token![|=](Span::call_site())), BinOp::ShlAssign(Token![<<=](Span::call_site())), - BinOp::ShrAssign(Token![>>=](Span::call_site())), ] { f(Expr::Binary(ExprBinary { attrs: Vec::new(), @@ -1204,6 +1195,6 @@ fn test_permutations() -> ExitCode { }; iter(2, &mut assert); - assert_eq!(count, 133539); + assert_eq!(count, 73923); status } From 6b88946a96962cd43fe3c7b44469b5cda3eb971e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 30 Dec 2024 09:03:56 -0800 Subject: [PATCH 081/121] Add tests of bailout involving ranges and dots, and fix for question --- src/expr.rs | 8 +++++- tests/test_expr.rs | 71 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 1 deletion(-) diff --git a/src/expr.rs b/src/expr.rs index b5661fed16..cde51cdebb 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -1673,7 +1673,12 @@ pub(crate) mod parsing { bracket_token: bracketed!(content in input), index: content.parse()?, }); - } else if input.peek(Token![?]) { + } else if input.peek(Token![?]) + && match e { + Expr::Range(_) => false, + _ => true, + } + { e = Expr::Try(ExprTry { attrs: Vec::new(), expr: Box::new(e), @@ -2867,6 +2872,7 @@ pub(crate) mod parsing { || input.peek(Token![,]) || input.peek(Token![;]) || input.peek(Token![.]) && !input.peek(Token![..]) + || input.peek(Token![?]) || input.peek(Token![=>]) || !allow_struct.0 && input.peek(token::Brace)) { diff --git a/tests/test_expr.rs b/tests/test_expr.rs index 652e66ed96..5783c778b2 100644 --- a/tests/test_expr.rs +++ b/tests/test_expr.rs @@ -407,6 +407,77 @@ fn test_range_precedence() { syn::parse_str::("x .. x ..").unwrap_err(); } +#[test] +fn test_ranges_dots_bailout() { + syn::parse_str::(".. ?").unwrap_err(); + syn::parse_str::(".. .field").unwrap_err(); + + snapshot!("return .. ?" as Expr, @r" + Expr::Try { + expr: Expr::Return { + expr: Some(Expr::Range { + limits: RangeLimits::HalfOpen, + }), + }, + } + "); + + snapshot!("break .. ?" as Expr, @r" + Expr::Try { + expr: Expr::Break { + expr: Some(Expr::Range { + limits: RangeLimits::HalfOpen, + }), + }, + } + "); + + snapshot!("|| .. ?" as Expr, @r" + Expr::Try { + expr: Expr::Closure { + output: ReturnType::Default, + body: Expr::Range { + limits: RangeLimits::HalfOpen, + }, + }, + } + "); + + snapshot!("return .. .field" as Expr, @r#" + Expr::Field { + base: Expr::Return { + expr: Some(Expr::Range { + limits: RangeLimits::HalfOpen, + }), + }, + member: Member::Named("field"), + } + "#); + + snapshot!("break .. .field" as Expr, @r#" + Expr::Field { + base: Expr::Break { + expr: Some(Expr::Range { + limits: RangeLimits::HalfOpen, + }), + }, + member: Member::Named("field"), + } + "#); + + snapshot!("|| .. .field" as Expr, @r#" + Expr::Field { + base: Expr::Closure { + output: ReturnType::Default, + body: Expr::Range { + limits: RangeLimits::HalfOpen, + }, + }, + member: Member::Named("field"), + } + "#); +} + #[test] fn test_ambiguous_label() { for stmt in [ From a08dedf76bdde8340b7fca0dcc01420338cb7afd Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 30 Dec 2024 19:43:23 -0800 Subject: [PATCH 082/121] Fix ranges bailout edge cases with assignment --- src/expr.rs | 26 ++++++++++++++++++++++++-- tests/test_expr.rs | 25 ++++++++++++++++++++++++- 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/src/expr.rs b/src/expr.rs index cde51cdebb..49f050e2a6 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -1324,6 +1324,11 @@ pub(crate) mod parsing { if precedence < base { break; } + if precedence == Precedence::Assign { + if let Expr::Range(_) = lhs { + break; + } + } if precedence == Precedence::Compare { if let Expr::Binary(lhs) = &lhs { if Precedence::of_binop(&lhs.op) == Precedence::Compare { @@ -1339,7 +1344,13 @@ pub(crate) mod parsing { op, right, }); - } else if Precedence::Assign >= base && input.peek(Token![=]) && !input.peek(Token![=>]) + } else if Precedence::Assign >= base + && input.peek(Token![=]) + && !input.peek(Token![=>]) + && match lhs { + Expr::Range(_) => false, + _ => true, + } { let eq_token: Token![=] = input.parse()?; let right = parse_binop_rhs(input, allow_struct, Precedence::Assign)?; @@ -2874,7 +2885,18 @@ pub(crate) mod parsing { || input.peek(Token![.]) && !input.peek(Token![..]) || input.peek(Token![?]) || input.peek(Token![=>]) - || !allow_struct.0 && input.peek(token::Brace)) + || !allow_struct.0 && input.peek(token::Brace) + || input.peek(Token![=]) && !input.peek(Token![==]) + || input.peek(Token![+=]) + || input.peek(Token![-=]) + || input.peek(Token![*=]) + || input.peek(Token![/=]) + || input.peek(Token![%=]) + || input.peek(Token![^=]) + || input.peek(Token![&=]) + || input.peek(Token![|=]) + || input.peek(Token![<<=]) + || input.peek(Token![>>=])) { Ok(None) } else { diff --git a/tests/test_expr.rs b/tests/test_expr.rs index 5783c778b2..1bcc0941fa 100644 --- a/tests/test_expr.rs +++ b/tests/test_expr.rs @@ -408,7 +408,7 @@ fn test_range_precedence() { } #[test] -fn test_ranges_dots_bailout() { +fn test_ranges_bailout() { syn::parse_str::(".. ?").unwrap_err(); syn::parse_str::(".. .field").unwrap_err(); @@ -476,6 +476,29 @@ fn test_ranges_dots_bailout() { member: Member::Named("field"), } "#); + + snapshot!("return .. = ()" as Expr, @r" + Expr::Assign { + left: Expr::Return { + expr: Some(Expr::Range { + limits: RangeLimits::HalfOpen, + }), + }, + right: Expr::Tuple, + } + "); + + snapshot!("return .. += ()" as Expr, @r" + Expr::Binary { + left: Expr::Return { + expr: Some(Expr::Range { + limits: RangeLimits::HalfOpen, + }), + }, + op: BinOp::AddAssign, + right: Expr::Tuple, + } + "); } #[test] From cd75e1079fef5e5007565cbe7a30cf151de31346 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 30 Dec 2024 20:47:09 -0800 Subject: [PATCH 083/121] Allow binops to terminate a range endpoint --- src/expr.rs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/expr.rs b/src/expr.rs index 49f050e2a6..a36e20ff01 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -2886,17 +2886,20 @@ pub(crate) mod parsing { || input.peek(Token![?]) || input.peek(Token![=>]) || !allow_struct.0 && input.peek(token::Brace) - || input.peek(Token![=]) && !input.peek(Token![==]) - || input.peek(Token![+=]) + || input.peek(Token![=]) + || input.peek(Token![+]) + || input.peek(Token![/]) + || input.peek(Token![%]) + || input.peek(Token![^]) + || input.peek(Token![>]) + || input.peek(Token![<=]) + || input.peek(Token![!=]) || input.peek(Token![-=]) || input.peek(Token![*=]) - || input.peek(Token![/=]) - || input.peek(Token![%=]) - || input.peek(Token![^=]) || input.peek(Token![&=]) || input.peek(Token![|=]) || input.peek(Token![<<=]) - || input.peek(Token![>>=])) + || input.peek(Token![as])) { Ok(None) } else { From 223540ca48cf49b6187633c539754de3785aea94 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 30 Dec 2024 21:03:19 -0800 Subject: [PATCH 084/121] Fix unambiguous RangeFull parenthesization --- src/fixup.rs | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/src/fixup.rs b/src/fixup.rs index 03e13b09bc..e4c726e41c 100644 --- a/src/fixup.rs +++ b/src/fixup.rs @@ -81,6 +81,15 @@ pub(crate) struct FixupContext { #[cfg(feature = "full")] leftmost_subexpression_in_match_arm: bool, + // This is the difference between: + // + // (..) + x // parens because range cannot be lhs of a binop + // + // &.. + x // no parens because range is a right subexpression + // + #[cfg(feature = "full")] + rightmost_subexpression: bool, + // This is the difference between: // // if let _ = (Struct {}) {} // needs parens @@ -133,6 +142,8 @@ impl FixupContext { #[cfg(feature = "full")] leftmost_subexpression_in_match_arm: false, #[cfg(feature = "full")] + rightmost_subexpression: false, + #[cfg(feature = "full")] parenthesize_exterior_struct_lit: false, #[cfg(feature = "full")] next_operator_can_begin_expr: false, @@ -196,6 +207,8 @@ impl FixupContext { leftmost_subexpression_in_match_arm: self.match_arm || self.leftmost_subexpression_in_match_arm, #[cfg(feature = "full")] + rightmost_subexpression: false, + #[cfg(feature = "full")] next_operator_can_begin_expr: false, #[cfg(feature = "full")] next_operator_can_continue_expr: true, @@ -219,6 +232,8 @@ impl FixupContext { #[cfg(feature = "full")] leftmost_subexpression_in_match_arm: false, #[cfg(feature = "full")] + rightmost_subexpression: false, + #[cfg(feature = "full")] next_operator_can_begin_expr: false, #[cfg(feature = "full")] next_operator_can_continue_expr: true, @@ -264,6 +279,8 @@ impl FixupContext { match_arm: false, #[cfg(feature = "full")] leftmost_subexpression_in_match_arm: false, + #[cfg(feature = "full")] + rightmost_subexpression: true, ..self } } @@ -309,6 +326,12 @@ impl FixupContext { if let Expr::Break(_) | Expr::Return(_) | Expr::Yield(_) = expr { return Precedence::Jump; } + } else if self.rightmost_subexpression { + if let Expr::Range(range) = expr { + if range.start.is_none() && range.end.is_none() { + return Precedence::Unambiguous; + } + } } #[cfg(feature = "full")] if !self.next_operator_can_continue_expr { @@ -348,7 +371,7 @@ impl Clone for FixupContext { #[cfg(feature = "full")] #[test] fn test_leftmost_rightmost_invariant() { - const BITS: usize = 8; + const BITS: usize = 9; for bits in 0u16..1 << BITS { let mut i = 0; @@ -362,6 +385,7 @@ fn test_leftmost_rightmost_invariant() { leftmost_subexpression_in_stmt: bit(), match_arm: bit(), leftmost_subexpression_in_match_arm: bit(), + rightmost_subexpression: bit(), parenthesize_exterior_struct_lit: bit(), next_operator_can_begin_expr: bit(), next_operator_can_continue_expr: bit(), @@ -370,7 +394,10 @@ fn test_leftmost_rightmost_invariant() { assert_eq!(i, BITS); assert_eq!( fixup.leftmost_subexpression().rightmost_subexpression(), - fixup.rightmost_subexpression().leftmost_subexpression(), + FixupContext { + rightmost_subexpression: true, + ..fixup.rightmost_subexpression().leftmost_subexpression() + }, ); } } From f3d8f8d9b56c24705c396b392ab92d31e07f0dc3 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 30 Dec 2024 21:06:44 -0800 Subject: [PATCH 085/121] Add test of range precedence with binop --- tests/test_expr.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/test_expr.rs b/tests/test_expr.rs index 1bcc0941fa..6943c3fba9 100644 --- a/tests/test_expr.rs +++ b/tests/test_expr.rs @@ -400,6 +400,19 @@ fn test_range_precedence() { } "#); + snapshot!("() = .. + ()" as Expr, @r" + Expr::Assign { + left: Expr::Tuple, + right: Expr::Binary { + left: Expr::Range { + limits: RangeLimits::HalfOpen, + }, + op: BinOp::Add, + right: Expr::Tuple, + }, + } + "); + // A range with a lower bound cannot be the upper bound of another range, // and a range with an upper bound cannot be the lower bound of another // range. From f7b0d726026c6692828ad9f19908981588ccbc98 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 30 Dec 2024 21:07:36 -0800 Subject: [PATCH 086/121] Disallow binop with a range lhs --- src/expr.rs | 5 ++--- tests/test_expr.rs | 12 ++++++------ 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/expr.rs b/src/expr.rs index a36e20ff01..2fc9ef516d 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -1315,9 +1315,8 @@ pub(crate) mod parsing { ) -> Result { loop { let ahead = input.fork(); - if let Expr::Range(ExprRange { end: Some(_), .. }) = lhs { - // A range with an upper bound cannot be the left-hand side of - // another binary operator. + if let Expr::Range(_) = lhs { + // A range cannot be the left-hand side of another binary operator. break; } else if let Ok(op) = ahead.parse::() { let precedence = Precedence::of_binop(&op); diff --git a/tests/test_expr.rs b/tests/test_expr.rs index 6943c3fba9..70b6f520c5 100644 --- a/tests/test_expr.rs +++ b/tests/test_expr.rs @@ -401,15 +401,15 @@ fn test_range_precedence() { "#); snapshot!("() = .. + ()" as Expr, @r" - Expr::Assign { - left: Expr::Tuple, - right: Expr::Binary { - left: Expr::Range { + Expr::Binary { + left: Expr::Assign { + left: Expr::Tuple, + right: Expr::Range { limits: RangeLimits::HalfOpen, }, - op: BinOp::Add, - right: Expr::Tuple, }, + op: BinOp::Add, + right: Expr::Tuple, } "); From f0329a5660dbad00a11ea5fe002df56ee9d4a0f2 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 31 Dec 2024 18:41:30 -0800 Subject: [PATCH 087/121] Update test suite to nightly-2025-01-01 --- tests/common/eq.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/common/eq.rs b/tests/common/eq.rs index f857f64853..6a74c0263b 100644 --- a/tests/common/eq.rs +++ b/tests/common/eq.rs @@ -503,7 +503,7 @@ spanless_eq_struct!(FnHeader; constness coroutine_kind safety ext); spanless_eq_struct!(FnSig; header decl span); spanless_eq_struct!(ForeignMod; extern_span safety abi items); spanless_eq_struct!(FormatArgPosition; index kind span); -spanless_eq_struct!(FormatArgs; span template arguments); +spanless_eq_struct!(FormatArgs; span template arguments uncooked_fmt_str); spanless_eq_struct!(FormatArgument; kind expr); spanless_eq_struct!(FormatOptions; width precision alignment fill sign alternate zero_pad debug_hex); spanless_eq_struct!(FormatPlaceholder; argument span format_trait format_options); From 5b8bb10f6cf98dd98d2eddcbdfe3838527f24b2b Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 31 Dec 2024 17:27:22 -0800 Subject: [PATCH 088/121] Factor out test_permutations's single call site span --- tests/test_expr.rs | 145 ++++++++++++++++++++++----------------------- 1 file changed, 72 insertions(+), 73 deletions(-) diff --git a/tests/test_expr.rs b/tests/test_expr.rs index 70b6f520c5..5b59fa0897 100644 --- a/tests/test_expr.rs +++ b/tests/test_expr.rs @@ -847,6 +847,8 @@ fn test_fixup() { #[test] fn test_permutations() -> ExitCode { fn iter(depth: usize, f: &mut dyn FnMut(Expr)) { + let span = Span::call_site(); + // Expr::Path f(parse_quote!(x)); f(parse_quote!(x::)); @@ -865,13 +867,13 @@ fn test_permutations() -> ExitCode { f(Expr::Assign(ExprAssign { attrs: Vec::new(), left: Box::new(simple.clone()), - eq_token: Token![=](Span::call_site()), + eq_token: Token![=](span), right: Box::new(expr.clone()), })); f(Expr::Assign(ExprAssign { attrs: Vec::new(), left: Box::new(expr.clone()), - eq_token: Token![=](Span::call_site()), + eq_token: Token![=](span), right: Box::new(simple), })); }); @@ -885,8 +887,8 @@ fn test_permutations() -> ExitCode { f(Expr::Await(ExprAwait { attrs: Vec::new(), base: Box::new(base), - dot_token: Token![.](Span::call_site()), - await_token: Token![await](Span::call_site()), + dot_token: Token![.](span), + await_token: Token![await](span), })); }); @@ -894,25 +896,25 @@ fn test_permutations() -> ExitCode { iter(depth, &mut |expr| { iter(0, &mut |simple| { for op in [ - BinOp::Add(Token![+](Span::call_site())), - BinOp::Sub(Token![-](Span::call_site())), - BinOp::Mul(Token![*](Span::call_site())), - BinOp::Div(Token![/](Span::call_site())), - BinOp::Rem(Token![%](Span::call_site())), - BinOp::And(Token![&&](Span::call_site())), - BinOp::Or(Token![||](Span::call_site())), - BinOp::BitXor(Token![^](Span::call_site())), - BinOp::BitAnd(Token![&](Span::call_site())), - BinOp::BitOr(Token![|](Span::call_site())), - BinOp::Shl(Token![<<](Span::call_site())), - BinOp::Shr(Token![>>](Span::call_site())), - BinOp::Eq(Token![==](Span::call_site())), - BinOp::Lt(Token![<](Span::call_site())), - BinOp::Le(Token![<=](Span::call_site())), - BinOp::Ne(Token![!=](Span::call_site())), - BinOp::Ge(Token![>=](Span::call_site())), - BinOp::Gt(Token![>](Span::call_site())), - BinOp::ShlAssign(Token![<<=](Span::call_site())), + BinOp::Add(Token![+](span)), + BinOp::Sub(Token![-](span)), + BinOp::Mul(Token![*](span)), + BinOp::Div(Token![/](span)), + BinOp::Rem(Token![%](span)), + BinOp::And(Token![&&](span)), + BinOp::Or(Token![||](span)), + BinOp::BitXor(Token![^](span)), + BinOp::BitAnd(Token![&](span)), + BinOp::BitOr(Token![|](span)), + BinOp::Shl(Token![<<](span)), + BinOp::Shr(Token![>>](span)), + BinOp::Eq(Token![==](span)), + BinOp::Lt(Token![<](span)), + BinOp::Le(Token![<=](span)), + BinOp::Ne(Token![!=](span)), + BinOp::Ge(Token![>=](span)), + BinOp::Gt(Token![>](span)), + BinOp::ShlAssign(Token![<<=](span)), ] { f(Expr::Binary(ExprBinary { attrs: Vec::new(), @@ -937,7 +939,7 @@ fn test_permutations() -> ExitCode { attrs: Vec::new(), label: None, block: Block { - brace_token: token::Brace(Span::call_site()), + brace_token: token::Brace(span), stmts: Vec::from([Stmt::Expr(expr.clone(), None)]), }, })); @@ -945,11 +947,8 @@ fn test_permutations() -> ExitCode { attrs: Vec::new(), label: None, block: Block { - brace_token: token::Brace(Span::call_site()), - stmts: Vec::from([Stmt::Expr( - expr.clone(), - Some(Token![;](Span::call_site())), - )]), + brace_token: token::Brace(span), + stmts: Vec::from([Stmt::Expr(expr.clone(), Some(Token![;](span)))]), }, })); }); @@ -960,13 +959,13 @@ fn test_permutations() -> ExitCode { iter(depth, &mut |expr| { f(Expr::Break(ExprBreak { attrs: Vec::new(), - break_token: Token![break](Span::call_site()), + break_token: Token![break](span), label: None, expr: Some(Box::new(expr.clone())), })); f(Expr::Break(ExprBreak { attrs: Vec::new(), - break_token: Token![break](Span::call_site()), + break_token: Token![break](span), label: Some(parse_quote!('a)), expr: Some(Box::new(expr)), })); @@ -977,7 +976,7 @@ fn test_permutations() -> ExitCode { f(Expr::Call(ExprCall { attrs: Vec::new(), func: Box::new(expr), - paren_token: token::Paren(Span::call_site()), + paren_token: token::Paren(span), args: Punctuated::new(), })); }); @@ -987,13 +986,13 @@ fn test_permutations() -> ExitCode { f(Expr::Cast(ExprCast { attrs: Vec::new(), expr: Box::new(expr.clone()), - as_token: Token![as](Span::call_site()), + as_token: Token![as](span), ty: parse_quote!(T), })); f(Expr::Cast(ExprCast { attrs: Vec::new(), expr: Box::new(expr), - as_token: Token![as](Span::call_site()), + as_token: Token![as](span), ty: parse_quote!(Thing), })); }); @@ -1008,9 +1007,9 @@ fn test_permutations() -> ExitCode { movability: None, asyncness: None, capture: None, - or1_token: Token![|](Span::call_site()), + or1_token: Token![|](span), inputs: Punctuated::new(), - or2_token: Token![|](Span::call_site()), + or2_token: Token![|](span), output: ReturnType::Default, body: Box::new(expr.clone()), })); @@ -1028,13 +1027,13 @@ fn test_permutations() -> ExitCode { f(Expr::Field(ExprField { attrs: Vec::new(), base: Box::new(expr.clone()), - dot_token: Token![.](Span::call_site()), + dot_token: Token![.](span), member: parse_quote!(field), })); f(Expr::Field(ExprField { attrs: Vec::new(), base: Box::new(expr), - dot_token: Token![.](Span::call_site()), + dot_token: Token![.](span), member: parse_quote!(0), })); }); @@ -1044,18 +1043,18 @@ fn test_permutations() -> ExitCode { f(Expr::ForLoop(ExprForLoop { attrs: Vec::new(), label: None, - for_token: Token![for](Span::call_site()), + for_token: Token![for](span), pat: parse_quote!(_), - in_token: Token![in](Span::call_site()), + in_token: Token![in](span), expr: Box::new(expr.clone()), body: parse_quote!({}), })); f(Expr::ForLoop(ExprForLoop { attrs: Vec::new(), label: Some(parse_quote!('a:)), - for_token: Token![for](Span::call_site()), + for_token: Token![for](span), pat: parse_quote!(_), - in_token: Token![in](Span::call_site()), + in_token: Token![in](span), expr: Box::new(expr), body: parse_quote!({}), })); @@ -1065,7 +1064,7 @@ fn test_permutations() -> ExitCode { iter(depth, &mut |expr| { f(Expr::If(ExprIf { attrs: Vec::new(), - if_token: Token![if](Span::call_site()), + if_token: Token![if](span), cond: Box::new(expr), then_branch: parse_quote!({}), else_branch: None, @@ -1077,7 +1076,7 @@ fn test_permutations() -> ExitCode { f(Expr::Index(ExprIndex { attrs: Vec::new(), expr: Box::new(expr), - bracket_token: token::Bracket(Span::call_site()), + bracket_token: token::Bracket(span), index: parse_quote!(0), })); }); @@ -1094,35 +1093,35 @@ fn test_permutations() -> ExitCode { iter(depth, &mut |expr| { f(Expr::Match(ExprMatch { attrs: Vec::new(), - match_token: Token![match](Span::call_site()), + match_token: Token![match](span), expr: Box::new(expr.clone()), - brace_token: token::Brace(Span::call_site()), + brace_token: token::Brace(span), arms: Vec::new(), })); f(Expr::Match(ExprMatch { attrs: Vec::new(), - match_token: Token![match](Span::call_site()), + match_token: Token![match](span), expr: parse_quote!(x), - brace_token: token::Brace(Span::call_site()), + brace_token: token::Brace(span), arms: Vec::from([Arm { attrs: Vec::new(), pat: parse_quote!(_), guard: None, - fat_arrow_token: Token![=>](Span::call_site()), + fat_arrow_token: Token![=>](span), body: Box::new(expr.clone()), comma: None, }]), })); f(Expr::Match(ExprMatch { attrs: Vec::new(), - match_token: Token![match](Span::call_site()), + match_token: Token![match](span), expr: parse_quote!(x), - brace_token: token::Brace(Span::call_site()), + brace_token: token::Brace(span), arms: Vec::from([Arm { attrs: Vec::new(), pat: parse_quote!(_), - guard: Some((Token![if](Span::call_site()), Box::new(expr))), - fat_arrow_token: Token![=>](Span::call_site()), + guard: Some((Token![if](span), Box::new(expr))), + fat_arrow_token: Token![=>](span), body: parse_quote!({}), comma: None, }]), @@ -1134,19 +1133,19 @@ fn test_permutations() -> ExitCode { f(Expr::MethodCall(ExprMethodCall { attrs: Vec::new(), receiver: Box::new(expr.clone()), - dot_token: Token![.](Span::call_site()), + dot_token: Token![.](span), method: parse_quote!(method), turbofish: None, - paren_token: token::Paren(Span::call_site()), + paren_token: token::Paren(span), args: Punctuated::new(), })); f(Expr::MethodCall(ExprMethodCall { attrs: Vec::new(), receiver: Box::new(expr), - dot_token: Token![.](Span::call_site()), + dot_token: Token![.](span), method: parse_quote!(method), turbofish: Some(parse_quote!(::)), - paren_token: token::Paren(Span::call_site()), + paren_token: token::Paren(span), args: Punctuated::new(), })); }); @@ -1159,13 +1158,13 @@ fn test_permutations() -> ExitCode { f(Expr::Range(ExprRange { attrs: Vec::new(), start: None, - limits: RangeLimits::HalfOpen(Token![..](Span::call_site())), + limits: RangeLimits::HalfOpen(Token![..](span)), end: Some(Box::new(expr.clone())), })); f(Expr::Range(ExprRange { attrs: Vec::new(), start: Some(Box::new(expr.clone())), - limits: RangeLimits::HalfOpen(Token![..](Span::call_site())), + limits: RangeLimits::HalfOpen(Token![..](span)), end: None, })); }); @@ -1174,9 +1173,9 @@ fn test_permutations() -> ExitCode { iter(depth, &mut |expr| { f(Expr::RawAddr(ExprRawAddr { attrs: Vec::new(), - and_token: Token![&](Span::call_site()), - raw: Token![raw](Span::call_site()), - mutability: PointerMutability::Const(Token![const](Span::call_site())), + and_token: Token![&](span), + raw: Token![raw](span), + mutability: PointerMutability::Const(Token![const](span)), expr: Box::new(expr), })); }); @@ -1185,14 +1184,14 @@ fn test_permutations() -> ExitCode { iter(depth, &mut |expr| { f(Expr::Reference(ExprReference { attrs: Vec::new(), - and_token: Token![&](Span::call_site()), + and_token: Token![&](span), mutability: None, expr: Box::new(expr.clone()), })); f(Expr::Reference(ExprReference { attrs: Vec::new(), - and_token: Token![&](Span::call_site()), - mutability: Some(Token![mut](Span::call_site())), + and_token: Token![&](span), + mutability: Some(Token![mut](span)), expr: Box::new(expr), })); }); @@ -1202,7 +1201,7 @@ fn test_permutations() -> ExitCode { iter(depth, &mut |expr| { f(Expr::Return(ExprReturn { attrs: Vec::new(), - return_token: Token![return](Span::call_site()), + return_token: Token![return](span), expr: Some(Box::new(expr)), })); }); @@ -1215,7 +1214,7 @@ fn test_permutations() -> ExitCode { f(Expr::Try(ExprTry { attrs: Vec::new(), expr: Box::new(expr), - question_token: Token![?](Span::call_site()), + question_token: Token![?](span), })); }); @@ -1225,9 +1224,9 @@ fn test_permutations() -> ExitCode { // Expr::Unary iter(depth, &mut |expr| { for op in [ - UnOp::Deref(Token![*](Span::call_site())), - UnOp::Not(Token![!](Span::call_site())), - UnOp::Neg(Token![-](Span::call_site())), + UnOp::Deref(Token![*](span)), + UnOp::Not(Token![!](span)), + UnOp::Neg(Token![-](span)), ] { f(Expr::Unary(ExprUnary { attrs: Vec::new(), @@ -1245,14 +1244,14 @@ fn test_permutations() -> ExitCode { f(Expr::While(ExprWhile { attrs: Vec::new(), label: None, - while_token: Token![while](Span::call_site()), + while_token: Token![while](span), cond: Box::new(expr.clone()), body: parse_quote!({}), })); f(Expr::While(ExprWhile { attrs: Vec::new(), label: Some(parse_quote!('a:)), - while_token: Token![while](Span::call_site()), + while_token: Token![while](span), cond: Box::new(expr), body: parse_quote!({}), })); @@ -1263,7 +1262,7 @@ fn test_permutations() -> ExitCode { iter(depth, &mut |expr| { f(Expr::Yield(ExprYield { attrs: Vec::new(), - yield_token: Token![yield](Span::call_site()), + yield_token: Token![yield](span), expr: Some(Box::new(expr)), })); }); From 6226fed911c738dbd893377df9578ed4b7499a87 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 31 Dec 2024 17:45:23 -0800 Subject: [PATCH 089/121] Comment the expressions being constructed in test_permutations --- tests/test_expr.rs | 39 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/tests/test_expr.rs b/tests/test_expr.rs index 5b59fa0897..b091667124 100644 --- a/tests/test_expr.rs +++ b/tests/test_expr.rs @@ -865,12 +865,14 @@ fn test_permutations() -> ExitCode { iter(depth, &mut |expr| { iter(0, &mut |simple| { f(Expr::Assign(ExprAssign { + // `x = $expr` attrs: Vec::new(), left: Box::new(simple.clone()), eq_token: Token![=](span), right: Box::new(expr.clone()), })); f(Expr::Assign(ExprAssign { + // `$expr = x` attrs: Vec::new(), left: Box::new(expr.clone()), eq_token: Token![=](span), @@ -883,10 +885,11 @@ fn test_permutations() -> ExitCode { f(parse_quote!(async {})); // Expr::Await - iter(depth, &mut |base| { + iter(depth, &mut |expr| { f(Expr::Await(ExprAwait { + // `$expr.await` attrs: Vec::new(), - base: Box::new(base), + base: Box::new(expr), dot_token: Token![.](span), await_token: Token![await](span), })); @@ -917,12 +920,14 @@ fn test_permutations() -> ExitCode { BinOp::ShlAssign(Token![<<=](span)), ] { f(Expr::Binary(ExprBinary { + // `x + $expr` attrs: Vec::new(), left: Box::new(simple.clone()), op, right: Box::new(expr.clone()), })); f(Expr::Binary(ExprBinary { + // `$expr + x` attrs: Vec::new(), left: Box::new(expr.clone()), op, @@ -936,6 +941,7 @@ fn test_permutations() -> ExitCode { f(parse_quote!('a: {})); iter(depth, &mut |expr| { f(Expr::Block(ExprBlock { + // `{ $expr }` attrs: Vec::new(), label: None, block: Block { @@ -944,6 +950,7 @@ fn test_permutations() -> ExitCode { }, })); f(Expr::Block(ExprBlock { + // `{ $expr; }` attrs: Vec::new(), label: None, block: Block { @@ -958,12 +965,14 @@ fn test_permutations() -> ExitCode { f(parse_quote!(break 'a)); iter(depth, &mut |expr| { f(Expr::Break(ExprBreak { + // `break $expr` attrs: Vec::new(), break_token: Token![break](span), label: None, expr: Some(Box::new(expr.clone())), })); f(Expr::Break(ExprBreak { + // `break 'a $expr` attrs: Vec::new(), break_token: Token![break](span), label: Some(parse_quote!('a)), @@ -974,6 +983,7 @@ fn test_permutations() -> ExitCode { // Expr::Call iter(depth, &mut |expr| { f(Expr::Call(ExprCall { + // `$expr()` attrs: Vec::new(), func: Box::new(expr), paren_token: token::Paren(span), @@ -984,12 +994,14 @@ fn test_permutations() -> ExitCode { // Expr::Cast iter(depth, &mut |expr| { f(Expr::Cast(ExprCast { + // `$expr as T` attrs: Vec::new(), expr: Box::new(expr.clone()), as_token: Token![as](span), ty: parse_quote!(T), })); f(Expr::Cast(ExprCast { + // `$expr as Thing` attrs: Vec::new(), expr: Box::new(expr), as_token: Token![as](span), @@ -1001,6 +1013,7 @@ fn test_permutations() -> ExitCode { f(parse_quote!(|| -> T {})); iter(depth, &mut |expr| { f(Expr::Closure(ExprClosure { + // `|| $expr` attrs: Vec::new(), lifetimes: None, constness: None, @@ -1025,12 +1038,14 @@ fn test_permutations() -> ExitCode { // Expr::Field iter(depth, &mut |expr| { f(Expr::Field(ExprField { + // `$expr.field` attrs: Vec::new(), base: Box::new(expr.clone()), dot_token: Token![.](span), member: parse_quote!(field), })); f(Expr::Field(ExprField { + // `$expr.0` attrs: Vec::new(), base: Box::new(expr), dot_token: Token![.](span), @@ -1041,6 +1056,7 @@ fn test_permutations() -> ExitCode { // Expr::ForLoop iter(depth, &mut |expr| { f(Expr::ForLoop(ExprForLoop { + // `for _ in $expr {}` attrs: Vec::new(), label: None, for_token: Token![for](span), @@ -1050,6 +1066,7 @@ fn test_permutations() -> ExitCode { body: parse_quote!({}), })); f(Expr::ForLoop(ExprForLoop { + // `'a: for _ in $expr {}` attrs: Vec::new(), label: Some(parse_quote!('a:)), for_token: Token![for](span), @@ -1063,6 +1080,7 @@ fn test_permutations() -> ExitCode { // Expr::If iter(depth, &mut |expr| { f(Expr::If(ExprIf { + // `if $expr {}` attrs: Vec::new(), if_token: Token![if](span), cond: Box::new(expr), @@ -1074,6 +1092,7 @@ fn test_permutations() -> ExitCode { // Expr::Index iter(depth, &mut |expr| { f(Expr::Index(ExprIndex { + // `$expr[0]` attrs: Vec::new(), expr: Box::new(expr), bracket_token: token::Bracket(span), @@ -1092,6 +1111,7 @@ fn test_permutations() -> ExitCode { // Expr::Match iter(depth, &mut |expr| { f(Expr::Match(ExprMatch { + // `match $expr {}` attrs: Vec::new(), match_token: Token![match](span), expr: Box::new(expr.clone()), @@ -1099,6 +1119,7 @@ fn test_permutations() -> ExitCode { arms: Vec::new(), })); f(Expr::Match(ExprMatch { + // `match x { _ => $expr }` attrs: Vec::new(), match_token: Token![match](span), expr: parse_quote!(x), @@ -1113,6 +1134,7 @@ fn test_permutations() -> ExitCode { }]), })); f(Expr::Match(ExprMatch { + // `match x { _ if $expr => {} }` attrs: Vec::new(), match_token: Token![match](span), expr: parse_quote!(x), @@ -1131,6 +1153,7 @@ fn test_permutations() -> ExitCode { // Expr::MethodCall iter(depth, &mut |expr| { f(Expr::MethodCall(ExprMethodCall { + // `$expr.method()` attrs: Vec::new(), receiver: Box::new(expr.clone()), dot_token: Token![.](span), @@ -1140,6 +1163,7 @@ fn test_permutations() -> ExitCode { args: Punctuated::new(), })); f(Expr::MethodCall(ExprMethodCall { + // `$expr.method::()` attrs: Vec::new(), receiver: Box::new(expr), dot_token: Token![.](span), @@ -1156,12 +1180,14 @@ fn test_permutations() -> ExitCode { f(parse_quote!(..0)); iter(depth, &mut |expr| { f(Expr::Range(ExprRange { + // `..$expr` attrs: Vec::new(), start: None, limits: RangeLimits::HalfOpen(Token![..](span)), end: Some(Box::new(expr.clone())), })); f(Expr::Range(ExprRange { + // `$expr..` attrs: Vec::new(), start: Some(Box::new(expr.clone())), limits: RangeLimits::HalfOpen(Token![..](span)), @@ -1172,6 +1198,7 @@ fn test_permutations() -> ExitCode { // Expr::RawAddr iter(depth, &mut |expr| { f(Expr::RawAddr(ExprRawAddr { + // `&raw const $expr` attrs: Vec::new(), and_token: Token![&](span), raw: Token![raw](span), @@ -1183,12 +1210,14 @@ fn test_permutations() -> ExitCode { // Expr::Reference iter(depth, &mut |expr| { f(Expr::Reference(ExprReference { + // `&$expr` attrs: Vec::new(), and_token: Token![&](span), mutability: None, expr: Box::new(expr.clone()), })); f(Expr::Reference(ExprReference { + // `&mut $expr` attrs: Vec::new(), and_token: Token![&](span), mutability: Some(Token![mut](span)), @@ -1200,6 +1229,7 @@ fn test_permutations() -> ExitCode { f(parse_quote!(return)); iter(depth, &mut |expr| { f(Expr::Return(ExprReturn { + // `return $expr` attrs: Vec::new(), return_token: Token![return](span), expr: Some(Box::new(expr)), @@ -1212,6 +1242,7 @@ fn test_permutations() -> ExitCode { // Expr::Try iter(depth, &mut |expr| { f(Expr::Try(ExprTry { + // `$expr?` attrs: Vec::new(), expr: Box::new(expr), question_token: Token![?](span), @@ -1229,6 +1260,7 @@ fn test_permutations() -> ExitCode { UnOp::Neg(Token![-](span)), ] { f(Expr::Unary(ExprUnary { + // `*$expr` attrs: Vec::new(), op, expr: Box::new(expr.clone()), @@ -1242,6 +1274,7 @@ fn test_permutations() -> ExitCode { // Expr::While iter(depth, &mut |expr| { f(Expr::While(ExprWhile { + // `while $expr {}` attrs: Vec::new(), label: None, while_token: Token![while](span), @@ -1249,6 +1282,7 @@ fn test_permutations() -> ExitCode { body: parse_quote!({}), })); f(Expr::While(ExprWhile { + // `'a: while $expr {}` attrs: Vec::new(), label: Some(parse_quote!('a:)), while_token: Token![while](span), @@ -1261,6 +1295,7 @@ fn test_permutations() -> ExitCode { f(parse_quote!(yield)); iter(depth, &mut |expr| { f(Expr::Yield(ExprYield { + // `yield $expr` attrs: Vec::new(), yield_token: Token![yield](span), expr: Some(Box::new(expr)), From 5392a0ec5ab419c52bba0d3857ed6e1c18f05332 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 31 Dec 2024 17:29:33 -0800 Subject: [PATCH 090/121] Speed up test_permutations by bypassing parsing This makes a difference for testing larger expression depths. --- tests/test_expr.rs | 417 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 364 insertions(+), 53 deletions(-) diff --git a/tests/test_expr.rs b/tests/test_expr.rs index b091667124..5ce9d1f293 100644 --- a/tests/test_expr.rs +++ b/tests/test_expr.rs @@ -16,17 +16,20 @@ mod macros; mod common; use crate::common::visit::{AsIfPrinted, FlattenParens}; -use proc_macro2::{Delimiter, Group, Span}; +use proc_macro2::{Delimiter, Group, Ident, Span, TokenStream}; use quote::{quote, ToTokens as _}; use std::process::ExitCode; use syn::punctuated::Punctuated; use syn::visit_mut::VisitMut as _; use syn::{ - parse_quote, token, Arm, BinOp, Block, Expr, ExprAssign, ExprAwait, ExprBinary, ExprBlock, - ExprBreak, ExprCall, ExprCast, ExprClosure, ExprField, ExprForLoop, ExprIf, ExprIndex, - ExprMatch, ExprMethodCall, ExprRange, ExprRawAddr, ExprReference, ExprReturn, ExprTry, - ExprTuple, ExprUnary, ExprWhile, ExprYield, PointerMutability, RangeLimits, ReturnType, Stmt, - Token, UnOp, + parse_quote, token, AngleBracketedGenericArguments, Arm, BinOp, Block, Expr, ExprArray, + ExprAssign, ExprAsync, ExprAwait, ExprBinary, ExprBlock, ExprBreak, ExprCall, ExprCast, + ExprClosure, ExprConst, ExprContinue, ExprField, ExprForLoop, ExprIf, ExprIndex, ExprLit, + ExprLoop, ExprMacro, ExprMatch, ExprMethodCall, ExprPath, ExprRange, ExprRawAddr, + ExprReference, ExprReturn, ExprStruct, ExprTry, ExprTryBlock, ExprTuple, ExprUnary, ExprUnsafe, + ExprWhile, ExprYield, GenericArgument, Index, Label, Lifetime, Lit, LitInt, Macro, + MacroDelimiter, Member, Pat, PatWild, Path, PathArguments, PathSegment, PointerMutability, + QSelf, RangeLimits, ReturnType, Stmt, Token, Type, TypePath, UnOp, }; #[test] @@ -850,16 +853,67 @@ fn test_permutations() -> ExitCode { let span = Span::call_site(); // Expr::Path - f(parse_quote!(x)); - f(parse_quote!(x::)); - f(parse_quote!(::CONST)); + f(Expr::Path(ExprPath { + // `x` + attrs: Vec::new(), + qself: None, + path: Path::from(Ident::new("x", span)), + })); + f(Expr::Path(ExprPath { + // `x::` + attrs: Vec::new(), + qself: None, + path: Path { + leading_colon: None, + segments: Punctuated::from_iter([PathSegment { + ident: Ident::new("x", span), + arguments: PathArguments::AngleBracketed(AngleBracketedGenericArguments { + colon2_token: Some(Token![::](span)), + lt_token: Token![<](span), + args: Punctuated::from_iter([GenericArgument::Type(Type::Path( + TypePath { + qself: None, + path: Path::from(Ident::new("T", span)), + }, + ))]), + gt_token: Token![>](span), + }), + }]), + }, + })); + f(Expr::Path(ExprPath { + // `::CONST` + attrs: Vec::new(), + qself: Some(QSelf { + lt_token: Token![<](span), + ty: Box::new(Type::Path(TypePath { + qself: None, + path: Path::from(Ident::new("T", span)), + })), + position: 1, + as_token: Some(Token![as](span)), + gt_token: Token![>](span), + }), + path: Path { + leading_colon: None, + segments: Punctuated::from_iter([ + PathSegment::from(Ident::new("Trait", span)), + PathSegment::from(Ident::new("CONST", span)), + ]), + }, + })); let Some(depth) = depth.checked_sub(1) else { return; }; // Expr::Array - f(parse_quote!([])); + f(Expr::Array(ExprArray { + // `[]` + attrs: Vec::new(), + bracket_token: token::Bracket(span), + elems: Punctuated::new(), + })); // Expr::Assign iter(depth, &mut |expr| { @@ -882,7 +936,16 @@ fn test_permutations() -> ExitCode { }); // Expr::Async - f(parse_quote!(async {})); + f(Expr::Async(ExprAsync { + // `async {}` + attrs: Vec::new(), + async_token: Token![async](span), + capture: None, + block: Block { + brace_token: token::Brace(span), + stmts: Vec::new(), + }, + })); // Expr::Await iter(depth, &mut |expr| { @@ -938,7 +1001,18 @@ fn test_permutations() -> ExitCode { }); // Expr::Block - f(parse_quote!('a: {})); + f(Expr::Block(ExprBlock { + // `'a: {}` + attrs: Vec::new(), + label: Some(Label { + name: Lifetime::new("'a", span), + colon_token: Token![:](span), + }), + block: Block { + brace_token: token::Brace(span), + stmts: Vec::new(), + }, + })); iter(depth, &mut |expr| { f(Expr::Block(ExprBlock { // `{ $expr }` @@ -961,8 +1035,20 @@ fn test_permutations() -> ExitCode { }); // Expr::Break - f(parse_quote!(break)); - f(parse_quote!(break 'a)); + f(Expr::Break(ExprBreak { + // `break` + attrs: Vec::new(), + break_token: Token![break](span), + label: None, + expr: None, + })); + f(Expr::Break(ExprBreak { + // `break 'a` + attrs: Vec::new(), + break_token: Token![break](span), + label: Some(Lifetime::new("'a", span)), + expr: None, + })); iter(depth, &mut |expr| { f(Expr::Break(ExprBreak { // `break $expr` @@ -975,7 +1061,7 @@ fn test_permutations() -> ExitCode { // `break 'a $expr` attrs: Vec::new(), break_token: Token![break](span), - label: Some(parse_quote!('a)), + label: Some(Lifetime::new("'a", span)), expr: Some(Box::new(expr)), })); }); @@ -998,19 +1084,69 @@ fn test_permutations() -> ExitCode { attrs: Vec::new(), expr: Box::new(expr.clone()), as_token: Token![as](span), - ty: parse_quote!(T), + ty: Box::new(Type::Path(TypePath { + qself: None, + path: Path::from(Ident::new("T", span)), + })), })); f(Expr::Cast(ExprCast { // `$expr as Thing` attrs: Vec::new(), expr: Box::new(expr), as_token: Token![as](span), - ty: parse_quote!(Thing), + ty: Box::new(Type::Path(TypePath { + qself: None, + path: Path { + leading_colon: None, + segments: Punctuated::from_iter([PathSegment { + ident: Ident::new("Thing", span), + arguments: PathArguments::AngleBracketed( + AngleBracketedGenericArguments { + colon2_token: None, + lt_token: Token![<](span), + args: Punctuated::from_iter([GenericArgument::Type( + Type::Path(TypePath { + qself: None, + path: Path::from(Ident::new("T", span)), + }), + )]), + gt_token: Token![>](span), + }, + ), + }]), + }, + })), })); }); // Expr::Closure - f(parse_quote!(|| -> T {})); + f(Expr::Closure(ExprClosure { + // `|| -> T {}` + attrs: Vec::new(), + lifetimes: None, + constness: None, + movability: None, + asyncness: None, + capture: None, + or1_token: Token![|](span), + inputs: Punctuated::new(), + or2_token: Token![|](span), + output: ReturnType::Type( + Token![->](span), + Box::new(Type::Path(TypePath { + qself: None, + path: Path::from(Ident::new("T", span)), + })), + ), + body: Box::new(Expr::Block(ExprBlock { + attrs: Vec::new(), + label: None, + block: Block { + brace_token: token::Brace(span), + stmts: Vec::new(), + }, + })), + })); iter(depth, &mut |expr| { f(Expr::Closure(ExprClosure { // `|| $expr` @@ -1029,11 +1165,29 @@ fn test_permutations() -> ExitCode { }); // Expr::Const - f(parse_quote!(const {})); + f(Expr::Const(ExprConst { + // `const {}` + attrs: Vec::new(), + const_token: Token![const](span), + block: Block { + brace_token: token::Brace(span), + stmts: Vec::new(), + }, + })); // Expr::Continue - f(parse_quote!(continue)); - f(parse_quote!(continue 'a)); + f(Expr::Continue(ExprContinue { + // `continue` + attrs: Vec::new(), + continue_token: Token![continue](span), + label: None, + })); + f(Expr::Continue(ExprContinue { + // `continue 'a` + attrs: Vec::new(), + continue_token: Token![continue](span), + label: Some(Lifetime::new("'a", span)), + })); // Expr::Field iter(depth, &mut |expr| { @@ -1042,14 +1196,14 @@ fn test_permutations() -> ExitCode { attrs: Vec::new(), base: Box::new(expr.clone()), dot_token: Token![.](span), - member: parse_quote!(field), + member: Member::Named(Ident::new("field", span)), })); f(Expr::Field(ExprField { // `$expr.0` attrs: Vec::new(), base: Box::new(expr), dot_token: Token![.](span), - member: parse_quote!(0), + member: Member::Unnamed(Index { index: 0, span }), })); }); @@ -1060,20 +1214,35 @@ fn test_permutations() -> ExitCode { attrs: Vec::new(), label: None, for_token: Token![for](span), - pat: parse_quote!(_), + pat: Box::new(Pat::Wild(PatWild { + attrs: Vec::new(), + underscore_token: Token![_](span), + })), in_token: Token![in](span), expr: Box::new(expr.clone()), - body: parse_quote!({}), + body: Block { + brace_token: token::Brace(span), + stmts: Vec::new(), + }, })); f(Expr::ForLoop(ExprForLoop { // `'a: for _ in $expr {}` attrs: Vec::new(), - label: Some(parse_quote!('a:)), + label: Some(Label { + name: Lifetime::new("'a", span), + colon_token: Token![:](span), + }), for_token: Token![for](span), - pat: parse_quote!(_), + pat: Box::new(Pat::Wild(PatWild { + attrs: Vec::new(), + underscore_token: Token![_](span), + })), in_token: Token![in](span), expr: Box::new(expr), - body: parse_quote!({}), + body: Block { + brace_token: token::Brace(span), + stmts: Vec::new(), + }, })); }); @@ -1084,7 +1253,10 @@ fn test_permutations() -> ExitCode { attrs: Vec::new(), if_token: Token![if](span), cond: Box::new(expr), - then_branch: parse_quote!({}), + then_branch: Block { + brace_token: token::Brace(span), + stmts: Vec::new(), + }, else_branch: None, })); }); @@ -1096,17 +1268,59 @@ fn test_permutations() -> ExitCode { attrs: Vec::new(), expr: Box::new(expr), bracket_token: token::Bracket(span), - index: parse_quote!(0), + index: Box::new(Expr::Lit(ExprLit { + attrs: Vec::new(), + lit: Lit::Int(LitInt::new("0", span)), + })), })); }); // Expr::Loop - f(parse_quote!(loop {})); - f(parse_quote!('a: loop {})); + f(Expr::Loop(ExprLoop { + // `loop {}` + attrs: Vec::new(), + label: None, + loop_token: Token![loop](span), + body: Block { + brace_token: token::Brace(span), + stmts: Vec::new(), + }, + })); + f(Expr::Loop(ExprLoop { + // `'a: loop {}` + attrs: Vec::new(), + label: Some(Label { + name: Lifetime::new("'a", span), + colon_token: Token![:](span), + }), + loop_token: Token![loop](span), + body: Block { + brace_token: token::Brace(span), + stmts: Vec::new(), + }, + })); // Expr::Macro - f(parse_quote!(m!())); - f(parse_quote!(m! {})); + f(Expr::Macro(ExprMacro { + // `m!()` + attrs: Vec::new(), + mac: Macro { + path: Path::from(Ident::new("m", span)), + bang_token: Token![!](span), + delimiter: MacroDelimiter::Paren(token::Paren(span)), + tokens: TokenStream::new(), + }, + })); + f(Expr::Macro(ExprMacro { + // `m! {}` + attrs: Vec::new(), + mac: Macro { + path: Path::from(Ident::new("m", span)), + bang_token: Token![!](span), + delimiter: MacroDelimiter::Brace(token::Brace(span)), + tokens: TokenStream::new(), + }, + })); // Expr::Match iter(depth, &mut |expr| { @@ -1122,11 +1336,18 @@ fn test_permutations() -> ExitCode { // `match x { _ => $expr }` attrs: Vec::new(), match_token: Token![match](span), - expr: parse_quote!(x), + expr: Box::new(Expr::Path(ExprPath { + attrs: Vec::new(), + qself: None, + path: Path::from(Ident::new("x", span)), + })), brace_token: token::Brace(span), arms: Vec::from([Arm { attrs: Vec::new(), - pat: parse_quote!(_), + pat: Pat::Wild(PatWild { + attrs: Vec::new(), + underscore_token: Token![_](span), + }), guard: None, fat_arrow_token: Token![=>](span), body: Box::new(expr.clone()), @@ -1137,14 +1358,28 @@ fn test_permutations() -> ExitCode { // `match x { _ if $expr => {} }` attrs: Vec::new(), match_token: Token![match](span), - expr: parse_quote!(x), + expr: Box::new(Expr::Path(ExprPath { + attrs: Vec::new(), + qself: None, + path: Path::from(Ident::new("x", span)), + })), brace_token: token::Brace(span), arms: Vec::from([Arm { attrs: Vec::new(), - pat: parse_quote!(_), + pat: Pat::Wild(PatWild { + attrs: Vec::new(), + underscore_token: Token![_](span), + }), guard: Some((Token![if](span), Box::new(expr))), fat_arrow_token: Token![=>](span), - body: parse_quote!({}), + body: Box::new(Expr::Block(ExprBlock { + attrs: Vec::new(), + label: None, + block: Block { + brace_token: token::Brace(span), + stmts: Vec::new(), + }, + })), comma: None, }]), })); @@ -1157,7 +1392,7 @@ fn test_permutations() -> ExitCode { attrs: Vec::new(), receiver: Box::new(expr.clone()), dot_token: Token![.](span), - method: parse_quote!(method), + method: Ident::new("method", span), turbofish: None, paren_token: token::Paren(span), args: Punctuated::new(), @@ -1167,17 +1402,49 @@ fn test_permutations() -> ExitCode { attrs: Vec::new(), receiver: Box::new(expr), dot_token: Token![.](span), - method: parse_quote!(method), - turbofish: Some(parse_quote!(::)), + method: Ident::new("method", span), + turbofish: Some(AngleBracketedGenericArguments { + colon2_token: Some(Token![::](span)), + lt_token: Token![<](span), + args: Punctuated::from_iter([GenericArgument::Type(Type::Path(TypePath { + qself: None, + path: Path::from(Ident::new("T", span)), + }))]), + gt_token: Token![>](span), + }), paren_token: token::Paren(span), args: Punctuated::new(), })); }); // Expr::Range - f(parse_quote!(..)); - f(parse_quote!(0..)); - f(parse_quote!(..0)); + f(Expr::Range(ExprRange { + // `..` + attrs: Vec::new(), + start: None, + limits: RangeLimits::HalfOpen(Token![..](span)), + end: None, + })); + f(Expr::Range(ExprRange { + // `0..` + attrs: Vec::new(), + start: Some(Box::new(Expr::Lit(ExprLit { + attrs: Vec::new(), + lit: Lit::Int(LitInt::new("0", span)), + }))), + limits: RangeLimits::HalfOpen(Token![..](span)), + end: None, + })); + f(Expr::Range(ExprRange { + // `..0` + attrs: Vec::new(), + start: None, + limits: RangeLimits::HalfOpen(Token![..](span)), + end: Some(Box::new(Expr::Lit(ExprLit { + attrs: Vec::new(), + lit: Lit::Int(LitInt::new("0", span)), + }))), + })); iter(depth, &mut |expr| { f(Expr::Range(ExprRange { // `..$expr` @@ -1226,7 +1493,12 @@ fn test_permutations() -> ExitCode { }); // Expr::Return - f(parse_quote!(return)); + f(Expr::Return(ExprReturn { + // `return` + attrs: Vec::new(), + return_token: Token![return](span), + expr: None, + })); iter(depth, &mut |expr| { f(Expr::Return(ExprReturn { // `return $expr` @@ -1237,7 +1509,16 @@ fn test_permutations() -> ExitCode { }); // Expr::Struct - f(parse_quote!(Struct {})); + f(Expr::Struct(ExprStruct { + // `Struct {}` + attrs: Vec::new(), + qself: None, + path: Path::from(Ident::new("Struct", span)), + brace_token: token::Brace(span), + fields: Punctuated::new(), + dot2_token: None, + rest: None, + })); // Expr::Try iter(depth, &mut |expr| { @@ -1250,7 +1531,15 @@ fn test_permutations() -> ExitCode { }); // Expr::TryBlock - f(parse_quote!(try {})); + f(Expr::TryBlock(ExprTryBlock { + // `try {}` + attrs: Vec::new(), + try_token: Token![try](span), + block: Block { + brace_token: token::Brace(span), + stmts: Vec::new(), + }, + })); // Expr::Unary iter(depth, &mut |expr| { @@ -1269,7 +1558,15 @@ fn test_permutations() -> ExitCode { }); // Expr::Unsafe - f(parse_quote!(unsafe {})); + f(Expr::Unsafe(ExprUnsafe { + // `unsafe {}` + attrs: Vec::new(), + unsafe_token: Token![unsafe](span), + block: Block { + brace_token: token::Brace(span), + stmts: Vec::new(), + }, + })); // Expr::While iter(depth, &mut |expr| { @@ -1279,20 +1576,34 @@ fn test_permutations() -> ExitCode { label: None, while_token: Token![while](span), cond: Box::new(expr.clone()), - body: parse_quote!({}), + body: Block { + brace_token: token::Brace(span), + stmts: Vec::new(), + }, })); f(Expr::While(ExprWhile { // `'a: while $expr {}` attrs: Vec::new(), - label: Some(parse_quote!('a:)), + label: Some(Label { + name: Lifetime::new("'a", span), + colon_token: Token![:](span), + }), while_token: Token![while](span), cond: Box::new(expr), - body: parse_quote!({}), + body: Block { + brace_token: token::Brace(span), + stmts: Vec::new(), + }, })); }); // Expr::Yield - f(parse_quote!(yield)); + f(Expr::Yield(ExprYield { + // `yield` + attrs: Vec::new(), + yield_token: Token![yield](span), + expr: None, + })); iter(depth, &mut |expr| { f(Expr::Yield(ExprYield { // `yield $expr` From df02b0f945aa4ef118176e8ef2b7dddfaa4de438 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 31 Dec 2024 21:13:40 -0800 Subject: [PATCH 091/121] Add count of number of test_permutations failures --- tests/test_expr.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/tests/test_expr.rs b/tests/test_expr.rs index 5ce9d1f293..1f9d48479e 100644 --- a/tests/test_expr.rs +++ b/tests/test_expr.rs @@ -1615,11 +1615,11 @@ fn test_permutations() -> ExitCode { } let mut count = 0; - let mut status = ExitCode::SUCCESS; + let mut failures = 0; macro_rules! fail { ($($message:tt)*) => {{ eprintln!($($message)*); - status = ExitCode::FAILURE; + failures += 1; return; }}; } @@ -1648,5 +1648,10 @@ fn test_permutations() -> ExitCode { iter(2, &mut assert); assert_eq!(count, 73923); - status + if failures > 0 { + eprintln!("FAILURES: {failures}"); + ExitCode::FAILURE + } else { + ExitCode::SUCCESS + } } From 8bbe8e5e0ff954fe87aa67ebde80e31aa8eafd06 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 31 Dec 2024 21:26:38 -0800 Subject: [PATCH 092/121] Include syntax tree in test failure --- tests/test_expr.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/tests/test_expr.rs b/tests/test_expr.rs index 1f9d48479e..9e0d063419 100644 --- a/tests/test_expr.rs +++ b/tests/test_expr.rs @@ -1627,12 +1627,22 @@ fn test_permutations() -> ExitCode { count += 1; let tokens = original.to_token_stream(); let Ok(mut parsed) = syn::parse2::(tokens.clone()) else { - fail!("failed to parse: {}", tokens); + fail!( + "failed to parse: {}\n{:#?}", + tokens, + crate::macros::debug::Lite(&original), + ); }; AsIfPrinted.visit_expr_mut(&mut original); FlattenParens.visit_expr_mut(&mut parsed); if original != parsed { - fail!("before: {}\nafter: {}", tokens, parsed.to_token_stream()); + fail!( + "before: {}\n{:#?}\nafter: {}\n{:#?}", + tokens, + crate::macros::debug::Lite(&original), + parsed.to_token_stream(), + crate::macros::debug::Lite(&parsed), + ); } let mut tokens_no_paren = tokens.clone(); FlattenParens::visit_token_stream_mut(&mut tokens_no_paren); From 668d52e80579496291e334d69fa5063c6207b0af Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 31 Dec 2024 21:43:35 -0800 Subject: [PATCH 093/121] Reorganize lower value precedence tests --- tests/test_expr.rs | 991 ++++++++++++++++++++++----------------------- 1 file changed, 476 insertions(+), 515 deletions(-) diff --git a/tests/test_expr.rs b/tests/test_expr.rs index 9e0d063419..3a8f6d412d 100644 --- a/tests/test_expr.rs +++ b/tests/test_expr.rs @@ -27,9 +27,9 @@ use syn::{ ExprClosure, ExprConst, ExprContinue, ExprField, ExprForLoop, ExprIf, ExprIndex, ExprLit, ExprLoop, ExprMacro, ExprMatch, ExprMethodCall, ExprPath, ExprRange, ExprRawAddr, ExprReference, ExprReturn, ExprStruct, ExprTry, ExprTryBlock, ExprTuple, ExprUnary, ExprUnsafe, - ExprWhile, ExprYield, GenericArgument, Index, Label, Lifetime, Lit, LitInt, Macro, - MacroDelimiter, Member, Pat, PatWild, Path, PathArguments, PathSegment, PointerMutability, - QSelf, RangeLimits, ReturnType, Stmt, Token, Type, TypePath, UnOp, + ExprWhile, ExprYield, GenericArgument, Label, Lifetime, Lit, LitInt, Macro, MacroDelimiter, + Member, Pat, PatWild, Path, PathArguments, PathSegment, PointerMutability, QSelf, RangeLimits, + ReturnType, Stmt, Token, Type, TypePath, UnOp, }; #[test] @@ -859,62 +859,56 @@ fn test_permutations() -> ExitCode { qself: None, path: Path::from(Ident::new("x", span)), })); - f(Expr::Path(ExprPath { - // `x::` - attrs: Vec::new(), - qself: None, - path: Path { - leading_colon: None, - segments: Punctuated::from_iter([PathSegment { - ident: Ident::new("x", span), - arguments: PathArguments::AngleBracketed(AngleBracketedGenericArguments { - colon2_token: Some(Token![::](span)), - lt_token: Token![<](span), - args: Punctuated::from_iter([GenericArgument::Type(Type::Path( - TypePath { - qself: None, - path: Path::from(Ident::new("T", span)), - }, - ))]), - gt_token: Token![>](span), - }), - }]), - }, - })); - f(Expr::Path(ExprPath { - // `::CONST` - attrs: Vec::new(), - qself: Some(QSelf { - lt_token: Token![<](span), - ty: Box::new(Type::Path(TypePath { - qself: None, - path: Path::from(Ident::new("T", span)), - })), - position: 1, - as_token: Some(Token![as](span)), - gt_token: Token![>](span), - }), - path: Path { - leading_colon: None, - segments: Punctuated::from_iter([ - PathSegment::from(Ident::new("Trait", span)), - PathSegment::from(Ident::new("CONST", span)), - ]), - }, - })); + if false { + f(Expr::Path(ExprPath { + // `x::` + attrs: Vec::new(), + qself: None, + path: Path { + leading_colon: None, + segments: Punctuated::from_iter([PathSegment { + ident: Ident::new("x", span), + arguments: PathArguments::AngleBracketed(AngleBracketedGenericArguments { + colon2_token: Some(Token![::](span)), + lt_token: Token![<](span), + args: Punctuated::from_iter([GenericArgument::Type(Type::Path( + TypePath { + qself: None, + path: Path::from(Ident::new("T", span)), + }, + ))]), + gt_token: Token![>](span), + }), + }]), + }, + })); + f(Expr::Path(ExprPath { + // `::CONST` + attrs: Vec::new(), + qself: Some(QSelf { + lt_token: Token![<](span), + ty: Box::new(Type::Path(TypePath { + qself: None, + path: Path::from(Ident::new("T", span)), + })), + position: 1, + as_token: Some(Token![as](span)), + gt_token: Token![>](span), + }), + path: Path { + leading_colon: None, + segments: Punctuated::from_iter([ + PathSegment::from(Ident::new("Trait", span)), + PathSegment::from(Ident::new("CONST", span)), + ]), + }, + })); + } let Some(depth) = depth.checked_sub(1) else { return; }; - // Expr::Array - f(Expr::Array(ExprArray { - // `[]` - attrs: Vec::new(), - bracket_token: token::Bracket(span), - elems: Punctuated::new(), - })); - // Expr::Assign iter(depth, &mut |expr| { iter(0, &mut |simple| { @@ -935,51 +929,28 @@ fn test_permutations() -> ExitCode { }); }); - // Expr::Async - f(Expr::Async(ExprAsync { - // `async {}` - attrs: Vec::new(), - async_token: Token![async](span), - capture: None, - block: Block { - brace_token: token::Brace(span), - stmts: Vec::new(), - }, - })); - - // Expr::Await - iter(depth, &mut |expr| { - f(Expr::Await(ExprAwait { - // `$expr.await` - attrs: Vec::new(), - base: Box::new(expr), - dot_token: Token![.](span), - await_token: Token![await](span), - })); - }); - // Expr::Binary iter(depth, &mut |expr| { iter(0, &mut |simple| { for op in [ BinOp::Add(Token![+](span)), - BinOp::Sub(Token![-](span)), - BinOp::Mul(Token![*](span)), - BinOp::Div(Token![/](span)), - BinOp::Rem(Token![%](span)), - BinOp::And(Token![&&](span)), - BinOp::Or(Token![||](span)), - BinOp::BitXor(Token![^](span)), - BinOp::BitAnd(Token![&](span)), - BinOp::BitOr(Token![|](span)), - BinOp::Shl(Token![<<](span)), - BinOp::Shr(Token![>>](span)), - BinOp::Eq(Token![==](span)), + //BinOp::Sub(Token![-](span)), + //BinOp::Mul(Token![*](span)), + //BinOp::Div(Token![/](span)), + //BinOp::Rem(Token![%](span)), + //BinOp::And(Token![&&](span)), + //BinOp::Or(Token![||](span)), + //BinOp::BitXor(Token![^](span)), + //BinOp::BitAnd(Token![&](span)), + //BinOp::BitOr(Token![|](span)), + //BinOp::Shl(Token![<<](span)), + //BinOp::Shr(Token![>>](span)), + //BinOp::Eq(Token![==](span)), BinOp::Lt(Token![<](span)), - BinOp::Le(Token![<=](span)), - BinOp::Ne(Token![!=](span)), - BinOp::Ge(Token![>=](span)), - BinOp::Gt(Token![>](span)), + //BinOp::Le(Token![<=](span)), + //BinOp::Ne(Token![!=](span)), + //BinOp::Ge(Token![>=](span)), + //BinOp::Gt(Token![>](span)), BinOp::ShlAssign(Token![<<=](span)), ] { f(Expr::Binary(ExprBinary { @@ -1002,37 +973,14 @@ fn test_permutations() -> ExitCode { // Expr::Block f(Expr::Block(ExprBlock { - // `'a: {}` + // `{}` attrs: Vec::new(), - label: Some(Label { - name: Lifetime::new("'a", span), - colon_token: Token![:](span), - }), + label: None, block: Block { brace_token: token::Brace(span), stmts: Vec::new(), }, })); - iter(depth, &mut |expr| { - f(Expr::Block(ExprBlock { - // `{ $expr }` - attrs: Vec::new(), - label: None, - block: Block { - brace_token: token::Brace(span), - stmts: Vec::from([Stmt::Expr(expr.clone(), None)]), - }, - })); - f(Expr::Block(ExprBlock { - // `{ $expr; }` - attrs: Vec::new(), - label: None, - block: Block { - brace_token: token::Brace(span), - stmts: Vec::from([Stmt::Expr(expr.clone(), Some(Token![;](span)))]), - }, - })); - }); // Expr::Break f(Expr::Break(ExprBreak { @@ -1042,26 +990,12 @@ fn test_permutations() -> ExitCode { label: None, expr: None, })); - f(Expr::Break(ExprBreak { - // `break 'a` - attrs: Vec::new(), - break_token: Token![break](span), - label: Some(Lifetime::new("'a", span)), - expr: None, - })); iter(depth, &mut |expr| { f(Expr::Break(ExprBreak { // `break $expr` attrs: Vec::new(), break_token: Token![break](span), label: None, - expr: Some(Box::new(expr.clone())), - })); - f(Expr::Break(ExprBreak { - // `break 'a $expr` - attrs: Vec::new(), - break_token: Token![break](span), - label: Some(Lifetime::new("'a", span)), expr: Some(Box::new(expr)), })); }); @@ -1082,71 +1016,16 @@ fn test_permutations() -> ExitCode { f(Expr::Cast(ExprCast { // `$expr as T` attrs: Vec::new(), - expr: Box::new(expr.clone()), - as_token: Token![as](span), - ty: Box::new(Type::Path(TypePath { - qself: None, - path: Path::from(Ident::new("T", span)), - })), - })); - f(Expr::Cast(ExprCast { - // `$expr as Thing` - attrs: Vec::new(), expr: Box::new(expr), as_token: Token![as](span), ty: Box::new(Type::Path(TypePath { qself: None, - path: Path { - leading_colon: None, - segments: Punctuated::from_iter([PathSegment { - ident: Ident::new("Thing", span), - arguments: PathArguments::AngleBracketed( - AngleBracketedGenericArguments { - colon2_token: None, - lt_token: Token![<](span), - args: Punctuated::from_iter([GenericArgument::Type( - Type::Path(TypePath { - qself: None, - path: Path::from(Ident::new("T", span)), - }), - )]), - gt_token: Token![>](span), - }, - ), - }]), - }, + path: Path::from(Ident::new("T", span)), })), })); }); // Expr::Closure - f(Expr::Closure(ExprClosure { - // `|| -> T {}` - attrs: Vec::new(), - lifetimes: None, - constness: None, - movability: None, - asyncness: None, - capture: None, - or1_token: Token![|](span), - inputs: Punctuated::new(), - or2_token: Token![|](span), - output: ReturnType::Type( - Token![->](span), - Box::new(Type::Path(TypePath { - qself: None, - path: Path::from(Ident::new("T", span)), - })), - ), - body: Box::new(Expr::Block(ExprBlock { - attrs: Vec::new(), - label: None, - block: Block { - brace_token: token::Brace(span), - stmts: Vec::new(), - }, - })), - })); iter(depth, &mut |expr| { f(Expr::Closure(ExprClosure { // `|| $expr` @@ -1160,89 +1039,18 @@ fn test_permutations() -> ExitCode { inputs: Punctuated::new(), or2_token: Token![|](span), output: ReturnType::Default, - body: Box::new(expr.clone()), + body: Box::new(expr), })); }); - // Expr::Const - f(Expr::Const(ExprConst { - // `const {}` - attrs: Vec::new(), - const_token: Token![const](span), - block: Block { - brace_token: token::Brace(span), - stmts: Vec::new(), - }, - })); - - // Expr::Continue - f(Expr::Continue(ExprContinue { - // `continue` - attrs: Vec::new(), - continue_token: Token![continue](span), - label: None, - })); - f(Expr::Continue(ExprContinue { - // `continue 'a` - attrs: Vec::new(), - continue_token: Token![continue](span), - label: Some(Lifetime::new("'a", span)), - })); - // Expr::Field iter(depth, &mut |expr| { f(Expr::Field(ExprField { // `$expr.field` attrs: Vec::new(), - base: Box::new(expr.clone()), - dot_token: Token![.](span), - member: Member::Named(Ident::new("field", span)), - })); - f(Expr::Field(ExprField { - // `$expr.0` - attrs: Vec::new(), base: Box::new(expr), dot_token: Token![.](span), - member: Member::Unnamed(Index { index: 0, span }), - })); - }); - - // Expr::ForLoop - iter(depth, &mut |expr| { - f(Expr::ForLoop(ExprForLoop { - // `for _ in $expr {}` - attrs: Vec::new(), - label: None, - for_token: Token![for](span), - pat: Box::new(Pat::Wild(PatWild { - attrs: Vec::new(), - underscore_token: Token![_](span), - })), - in_token: Token![in](span), - expr: Box::new(expr.clone()), - body: Block { - brace_token: token::Brace(span), - stmts: Vec::new(), - }, - })); - f(Expr::ForLoop(ExprForLoop { - // `'a: for _ in $expr {}` - attrs: Vec::new(), - label: Some(Label { - name: Lifetime::new("'a", span), - colon_token: Token![:](span), - }), - for_token: Token![for](span), - pat: Box::new(Pat::Wild(PatWild { - attrs: Vec::new(), - underscore_token: Token![_](span), - })), - in_token: Token![in](span), - expr: Box::new(expr), - body: Block { - brace_token: token::Brace(span), - stmts: Vec::new(), - }, + member: Member::Named(Ident::new("field", span)), })); }); @@ -1261,162 +1069,6 @@ fn test_permutations() -> ExitCode { })); }); - // Expr::Index - iter(depth, &mut |expr| { - f(Expr::Index(ExprIndex { - // `$expr[0]` - attrs: Vec::new(), - expr: Box::new(expr), - bracket_token: token::Bracket(span), - index: Box::new(Expr::Lit(ExprLit { - attrs: Vec::new(), - lit: Lit::Int(LitInt::new("0", span)), - })), - })); - }); - - // Expr::Loop - f(Expr::Loop(ExprLoop { - // `loop {}` - attrs: Vec::new(), - label: None, - loop_token: Token![loop](span), - body: Block { - brace_token: token::Brace(span), - stmts: Vec::new(), - }, - })); - f(Expr::Loop(ExprLoop { - // `'a: loop {}` - attrs: Vec::new(), - label: Some(Label { - name: Lifetime::new("'a", span), - colon_token: Token![:](span), - }), - loop_token: Token![loop](span), - body: Block { - brace_token: token::Brace(span), - stmts: Vec::new(), - }, - })); - - // Expr::Macro - f(Expr::Macro(ExprMacro { - // `m!()` - attrs: Vec::new(), - mac: Macro { - path: Path::from(Ident::new("m", span)), - bang_token: Token![!](span), - delimiter: MacroDelimiter::Paren(token::Paren(span)), - tokens: TokenStream::new(), - }, - })); - f(Expr::Macro(ExprMacro { - // `m! {}` - attrs: Vec::new(), - mac: Macro { - path: Path::from(Ident::new("m", span)), - bang_token: Token![!](span), - delimiter: MacroDelimiter::Brace(token::Brace(span)), - tokens: TokenStream::new(), - }, - })); - - // Expr::Match - iter(depth, &mut |expr| { - f(Expr::Match(ExprMatch { - // `match $expr {}` - attrs: Vec::new(), - match_token: Token![match](span), - expr: Box::new(expr.clone()), - brace_token: token::Brace(span), - arms: Vec::new(), - })); - f(Expr::Match(ExprMatch { - // `match x { _ => $expr }` - attrs: Vec::new(), - match_token: Token![match](span), - expr: Box::new(Expr::Path(ExprPath { - attrs: Vec::new(), - qself: None, - path: Path::from(Ident::new("x", span)), - })), - brace_token: token::Brace(span), - arms: Vec::from([Arm { - attrs: Vec::new(), - pat: Pat::Wild(PatWild { - attrs: Vec::new(), - underscore_token: Token![_](span), - }), - guard: None, - fat_arrow_token: Token![=>](span), - body: Box::new(expr.clone()), - comma: None, - }]), - })); - f(Expr::Match(ExprMatch { - // `match x { _ if $expr => {} }` - attrs: Vec::new(), - match_token: Token![match](span), - expr: Box::new(Expr::Path(ExprPath { - attrs: Vec::new(), - qself: None, - path: Path::from(Ident::new("x", span)), - })), - brace_token: token::Brace(span), - arms: Vec::from([Arm { - attrs: Vec::new(), - pat: Pat::Wild(PatWild { - attrs: Vec::new(), - underscore_token: Token![_](span), - }), - guard: Some((Token![if](span), Box::new(expr))), - fat_arrow_token: Token![=>](span), - body: Box::new(Expr::Block(ExprBlock { - attrs: Vec::new(), - label: None, - block: Block { - brace_token: token::Brace(span), - stmts: Vec::new(), - }, - })), - comma: None, - }]), - })); - }); - - // Expr::MethodCall - iter(depth, &mut |expr| { - f(Expr::MethodCall(ExprMethodCall { - // `$expr.method()` - attrs: Vec::new(), - receiver: Box::new(expr.clone()), - dot_token: Token![.](span), - method: Ident::new("method", span), - turbofish: None, - paren_token: token::Paren(span), - args: Punctuated::new(), - })); - f(Expr::MethodCall(ExprMethodCall { - // `$expr.method::()` - attrs: Vec::new(), - receiver: Box::new(expr), - dot_token: Token![.](span), - method: Ident::new("method", span), - turbofish: Some(AngleBracketedGenericArguments { - colon2_token: Some(Token![::](span)), - lt_token: Token![<](span), - args: Punctuated::from_iter([GenericArgument::Type(Type::Path(TypePath { - qself: None, - path: Path::from(Ident::new("T", span)), - }))]), - gt_token: Token![>](span), - }), - paren_token: token::Paren(span), - args: Punctuated::new(), - })); - }); - // Expr::Range f(Expr::Range(ExprRange { // `..` @@ -1425,26 +1077,6 @@ fn test_permutations() -> ExitCode { limits: RangeLimits::HalfOpen(Token![..](span)), end: None, })); - f(Expr::Range(ExprRange { - // `0..` - attrs: Vec::new(), - start: Some(Box::new(Expr::Lit(ExprLit { - attrs: Vec::new(), - lit: Lit::Int(LitInt::new("0", span)), - }))), - limits: RangeLimits::HalfOpen(Token![..](span)), - end: None, - })); - f(Expr::Range(ExprRange { - // `..0` - attrs: Vec::new(), - start: None, - limits: RangeLimits::HalfOpen(Token![..](span)), - end: Some(Box::new(Expr::Lit(ExprLit { - attrs: Vec::new(), - lit: Lit::Int(LitInt::new("0", span)), - }))), - })); iter(depth, &mut |expr| { f(Expr::Range(ExprRange { // `..$expr` @@ -1456,24 +1088,12 @@ fn test_permutations() -> ExitCode { f(Expr::Range(ExprRange { // `$expr..` attrs: Vec::new(), - start: Some(Box::new(expr.clone())), + start: Some(Box::new(expr)), limits: RangeLimits::HalfOpen(Token![..](span)), end: None, })); }); - // Expr::RawAddr - iter(depth, &mut |expr| { - f(Expr::RawAddr(ExprRawAddr { - // `&raw const $expr` - attrs: Vec::new(), - and_token: Token![&](span), - raw: Token![raw](span), - mutability: PointerMutability::Const(Token![const](span)), - expr: Box::new(expr), - })); - }); - // Expr::Reference iter(depth, &mut |expr| { f(Expr::Reference(ExprReference { @@ -1481,13 +1101,6 @@ fn test_permutations() -> ExitCode { attrs: Vec::new(), and_token: Token![&](span), mutability: None, - expr: Box::new(expr.clone()), - })); - f(Expr::Reference(ExprReference { - // `&mut $expr` - attrs: Vec::new(), - and_token: Token![&](span), - mutability: Some(Token![mut](span)), expr: Box::new(expr), })); }); @@ -1508,18 +1121,6 @@ fn test_permutations() -> ExitCode { })); }); - // Expr::Struct - f(Expr::Struct(ExprStruct { - // `Struct {}` - attrs: Vec::new(), - qself: None, - path: Path::from(Ident::new("Struct", span)), - brace_token: token::Brace(span), - fields: Punctuated::new(), - dot2_token: None, - rest: None, - })); - // Expr::Try iter(depth, &mut |expr| { f(Expr::Try(ExprTry { @@ -1530,23 +1131,12 @@ fn test_permutations() -> ExitCode { })); }); - // Expr::TryBlock - f(Expr::TryBlock(ExprTryBlock { - // `try {}` - attrs: Vec::new(), - try_token: Token![try](span), - block: Block { - brace_token: token::Brace(span), - stmts: Vec::new(), - }, - })); - // Expr::Unary iter(depth, &mut |expr| { for op in [ UnOp::Deref(Token![*](span)), - UnOp::Not(Token![!](span)), - UnOp::Neg(Token![-](span)), + //UnOp::Not(Token![!](span)), + //UnOp::Neg(Token![-](span)), ] { f(Expr::Unary(ExprUnary { // `*$expr` @@ -1557,61 +1147,432 @@ fn test_permutations() -> ExitCode { } }); - // Expr::Unsafe - f(Expr::Unsafe(ExprUnsafe { - // `unsafe {}` - attrs: Vec::new(), - unsafe_token: Token![unsafe](span), - block: Block { - brace_token: token::Brace(span), - stmts: Vec::new(), - }, - })); + if false { + // Expr::Array + f(Expr::Array(ExprArray { + // `[]` + attrs: Vec::new(), + bracket_token: token::Bracket(span), + elems: Punctuated::new(), + })); - // Expr::While - iter(depth, &mut |expr| { - f(Expr::While(ExprWhile { - // `while $expr {}` + // Expr::Async + f(Expr::Async(ExprAsync { + // `async {}` + attrs: Vec::new(), + async_token: Token![async](span), + capture: None, + block: Block { + brace_token: token::Brace(span), + stmts: Vec::new(), + }, + })); + + // Expr::Await + iter(depth, &mut |expr| { + f(Expr::Await(ExprAwait { + // `$expr.await` + attrs: Vec::new(), + base: Box::new(expr), + dot_token: Token![.](span), + await_token: Token![await](span), + })); + }); + + // Expr::Block + f(Expr::Block(ExprBlock { + // `'a: {}` attrs: Vec::new(), + label: Some(Label { + name: Lifetime::new("'a", span), + colon_token: Token![:](span), + }), + block: Block { + brace_token: token::Brace(span), + stmts: Vec::new(), + }, + })); + iter(depth, &mut |expr| { + f(Expr::Block(ExprBlock { + // `{ $expr }` + attrs: Vec::new(), + label: None, + block: Block { + brace_token: token::Brace(span), + stmts: Vec::from([Stmt::Expr(expr.clone(), None)]), + }, + })); + f(Expr::Block(ExprBlock { + // `{ $expr; }` + attrs: Vec::new(), + label: None, + block: Block { + brace_token: token::Brace(span), + stmts: Vec::from([Stmt::Expr(expr, Some(Token![;](span)))]), + }, + })); + }); + + // Expr::Break + f(Expr::Break(ExprBreak { + // `break 'a` + attrs: Vec::new(), + break_token: Token![break](span), + label: Some(Lifetime::new("'a", span)), + expr: None, + })); + iter(depth, &mut |expr| { + f(Expr::Break(ExprBreak { + // `break 'a $expr` + attrs: Vec::new(), + break_token: Token![break](span), + label: Some(Lifetime::new("'a", span)), + expr: Some(Box::new(expr)), + })); + }); + + // Expr::Closure + f(Expr::Closure(ExprClosure { + // `|| -> T {}` + attrs: Vec::new(), + lifetimes: None, + constness: None, + movability: None, + asyncness: None, + capture: None, + or1_token: Token![|](span), + inputs: Punctuated::new(), + or2_token: Token![|](span), + output: ReturnType::Type( + Token![->](span), + Box::new(Type::Path(TypePath { + qself: None, + path: Path::from(Ident::new("T", span)), + })), + ), + body: Box::new(Expr::Block(ExprBlock { + attrs: Vec::new(), + label: None, + block: Block { + brace_token: token::Brace(span), + stmts: Vec::new(), + }, + })), + })); + + // Expr::Const + f(Expr::Const(ExprConst { + // `const {}` + attrs: Vec::new(), + const_token: Token![const](span), + block: Block { + brace_token: token::Brace(span), + stmts: Vec::new(), + }, + })); + + // Expr::Continue + f(Expr::Continue(ExprContinue { + // `continue` + attrs: Vec::new(), + continue_token: Token![continue](span), label: None, - while_token: Token![while](span), - cond: Box::new(expr.clone()), + })); + f(Expr::Continue(ExprContinue { + // `continue 'a` + attrs: Vec::new(), + continue_token: Token![continue](span), + label: Some(Lifetime::new("'a", span)), + })); + + // Expr::ForLoop + iter(depth, &mut |expr| { + f(Expr::ForLoop(ExprForLoop { + // `for _ in $expr {}` + attrs: Vec::new(), + label: None, + for_token: Token![for](span), + pat: Box::new(Pat::Wild(PatWild { + attrs: Vec::new(), + underscore_token: Token![_](span), + })), + in_token: Token![in](span), + expr: Box::new(expr.clone()), + body: Block { + brace_token: token::Brace(span), + stmts: Vec::new(), + }, + })); + f(Expr::ForLoop(ExprForLoop { + // `'a: for _ in $expr {}` + attrs: Vec::new(), + label: Some(Label { + name: Lifetime::new("'a", span), + colon_token: Token![:](span), + }), + for_token: Token![for](span), + pat: Box::new(Pat::Wild(PatWild { + attrs: Vec::new(), + underscore_token: Token![_](span), + })), + in_token: Token![in](span), + expr: Box::new(expr), + body: Block { + brace_token: token::Brace(span), + stmts: Vec::new(), + }, + })); + }); + + // Expr::Index + iter(depth, &mut |expr| { + f(Expr::Index(ExprIndex { + // `$expr[0]` + attrs: Vec::new(), + expr: Box::new(expr), + bracket_token: token::Bracket(span), + index: Box::new(Expr::Lit(ExprLit { + attrs: Vec::new(), + lit: Lit::Int(LitInt::new("0", span)), + })), + })); + }); + + // Expr::Loop + f(Expr::Loop(ExprLoop { + // `loop {}` + attrs: Vec::new(), + label: None, + loop_token: Token![loop](span), body: Block { brace_token: token::Brace(span), stmts: Vec::new(), }, })); - f(Expr::While(ExprWhile { - // `'a: while $expr {}` + f(Expr::Loop(ExprLoop { + // `'a: loop {}` attrs: Vec::new(), label: Some(Label { name: Lifetime::new("'a", span), colon_token: Token![:](span), }), - while_token: Token![while](span), - cond: Box::new(expr), + loop_token: Token![loop](span), body: Block { brace_token: token::Brace(span), stmts: Vec::new(), }, })); - }); - // Expr::Yield - f(Expr::Yield(ExprYield { - // `yield` - attrs: Vec::new(), - yield_token: Token![yield](span), - expr: None, - })); - iter(depth, &mut |expr| { + // Expr::Macro + f(Expr::Macro(ExprMacro { + // `m!()` + attrs: Vec::new(), + mac: Macro { + path: Path::from(Ident::new("m", span)), + bang_token: Token![!](span), + delimiter: MacroDelimiter::Paren(token::Paren(span)), + tokens: TokenStream::new(), + }, + })); + f(Expr::Macro(ExprMacro { + // `m! {}` + attrs: Vec::new(), + mac: Macro { + path: Path::from(Ident::new("m", span)), + bang_token: Token![!](span), + delimiter: MacroDelimiter::Brace(token::Brace(span)), + tokens: TokenStream::new(), + }, + })); + + // Expr::Match + iter(depth, &mut |expr| { + f(Expr::Match(ExprMatch { + // `match $expr {}` + attrs: Vec::new(), + match_token: Token![match](span), + expr: Box::new(expr.clone()), + brace_token: token::Brace(span), + arms: Vec::new(), + })); + f(Expr::Match(ExprMatch { + // `match x { _ => $expr }` + attrs: Vec::new(), + match_token: Token![match](span), + expr: Box::new(Expr::Path(ExprPath { + attrs: Vec::new(), + qself: None, + path: Path::from(Ident::new("x", span)), + })), + brace_token: token::Brace(span), + arms: Vec::from([Arm { + attrs: Vec::new(), + pat: Pat::Wild(PatWild { + attrs: Vec::new(), + underscore_token: Token![_](span), + }), + guard: None, + fat_arrow_token: Token![=>](span), + body: Box::new(expr.clone()), + comma: None, + }]), + })); + f(Expr::Match(ExprMatch { + // `match x { _ if $expr => {} }` + attrs: Vec::new(), + match_token: Token![match](span), + expr: Box::new(Expr::Path(ExprPath { + attrs: Vec::new(), + qself: None, + path: Path::from(Ident::new("x", span)), + })), + brace_token: token::Brace(span), + arms: Vec::from([Arm { + attrs: Vec::new(), + pat: Pat::Wild(PatWild { + attrs: Vec::new(), + underscore_token: Token![_](span), + }), + guard: Some((Token![if](span), Box::new(expr))), + fat_arrow_token: Token![=>](span), + body: Box::new(Expr::Block(ExprBlock { + attrs: Vec::new(), + label: None, + block: Block { + brace_token: token::Brace(span), + stmts: Vec::new(), + }, + })), + comma: None, + }]), + })); + }); + + // Expr::MethodCall + iter(depth, &mut |expr| { + f(Expr::MethodCall(ExprMethodCall { + // `$expr.method()` + attrs: Vec::new(), + receiver: Box::new(expr.clone()), + dot_token: Token![.](span), + method: Ident::new("method", span), + turbofish: None, + paren_token: token::Paren(span), + args: Punctuated::new(), + })); + f(Expr::MethodCall(ExprMethodCall { + // `$expr.method::()` + attrs: Vec::new(), + receiver: Box::new(expr), + dot_token: Token![.](span), + method: Ident::new("method", span), + turbofish: Some(AngleBracketedGenericArguments { + colon2_token: Some(Token![::](span)), + lt_token: Token![<](span), + args: Punctuated::from_iter([GenericArgument::Type(Type::Path( + TypePath { + qself: None, + path: Path::from(Ident::new("T", span)), + }, + ))]), + gt_token: Token![>](span), + }), + paren_token: token::Paren(span), + args: Punctuated::new(), + })); + }); + + // Expr::RawAddr + iter(depth, &mut |expr| { + f(Expr::RawAddr(ExprRawAddr { + // `&raw const $expr` + attrs: Vec::new(), + and_token: Token![&](span), + raw: Token![raw](span), + mutability: PointerMutability::Const(Token![const](span)), + expr: Box::new(expr), + })); + }); + + // Expr::Struct + f(Expr::Struct(ExprStruct { + // `Struct {}` + attrs: Vec::new(), + qself: None, + path: Path::from(Ident::new("Struct", span)), + brace_token: token::Brace(span), + fields: Punctuated::new(), + dot2_token: None, + rest: None, + })); + + // Expr::TryBlock + f(Expr::TryBlock(ExprTryBlock { + // `try {}` + attrs: Vec::new(), + try_token: Token![try](span), + block: Block { + brace_token: token::Brace(span), + stmts: Vec::new(), + }, + })); + + // Expr::Unsafe + f(Expr::Unsafe(ExprUnsafe { + // `unsafe {}` + attrs: Vec::new(), + unsafe_token: Token![unsafe](span), + block: Block { + brace_token: token::Brace(span), + stmts: Vec::new(), + }, + })); + + // Expr::While + iter(depth, &mut |expr| { + f(Expr::While(ExprWhile { + // `while $expr {}` + attrs: Vec::new(), + label: None, + while_token: Token![while](span), + cond: Box::new(expr.clone()), + body: Block { + brace_token: token::Brace(span), + stmts: Vec::new(), + }, + })); + f(Expr::While(ExprWhile { + // `'a: while $expr {}` + attrs: Vec::new(), + label: Some(Label { + name: Lifetime::new("'a", span), + colon_token: Token![:](span), + }), + while_token: Token![while](span), + cond: Box::new(expr), + body: Block { + brace_token: token::Brace(span), + stmts: Vec::new(), + }, + })); + }); + + // Expr::Yield f(Expr::Yield(ExprYield { - // `yield $expr` + // `yield` attrs: Vec::new(), yield_token: Token![yield](span), - expr: Some(Box::new(expr)), + expr: None, })); - }); + iter(depth, &mut |expr| { + f(Expr::Yield(ExprYield { + // `yield $expr` + attrs: Vec::new(), + yield_token: Token![yield](span), + expr: Some(Box::new(expr)), + })); + }); + } } let mut count = 0; @@ -1657,7 +1618,7 @@ fn test_permutations() -> ExitCode { }; iter(2, &mut assert); - assert_eq!(count, 73923); + assert_eq!(count, 505); if failures > 0 { eprintln!("FAILURES: {failures}"); ExitCode::FAILURE From c7403e534ebb9b1586ea01abc93ee9bffb118117 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 1 Jan 2025 11:22:03 -0800 Subject: [PATCH 094/121] Preserve FixupContext when printing closure body without brace --- src/expr.rs | 45 +++++++++++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/src/expr.rs b/src/expr.rs index 2fc9ef516d..8efb1f04d9 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -3218,7 +3218,7 @@ pub(crate) mod printing { Expr::Call(e) => print_expr_call(e, tokens, fixup), Expr::Cast(e) => print_expr_cast(e, tokens, fixup), #[cfg(feature = "full")] - Expr::Closure(e) => e.to_tokens(tokens), + Expr::Closure(e) => print_expr_closure(e, tokens, fixup), #[cfg(feature = "full")] Expr::Const(e) => e.to_tokens(tokens), #[cfg(feature = "full")] @@ -3485,25 +3485,30 @@ pub(crate) mod printing { #[cfg_attr(docsrs, doc(cfg(feature = "printing")))] impl ToTokens for ExprClosure { fn to_tokens(&self, tokens: &mut TokenStream) { - outer_attrs_to_tokens(&self.attrs, tokens); - self.lifetimes.to_tokens(tokens); - self.constness.to_tokens(tokens); - self.movability.to_tokens(tokens); - self.asyncness.to_tokens(tokens); - self.capture.to_tokens(tokens); - self.or1_token.to_tokens(tokens); - self.inputs.to_tokens(tokens); - self.or2_token.to_tokens(tokens); - self.output.to_tokens(tokens); - if matches!(self.output, ReturnType::Default) - || matches!(&*self.body, Expr::Block(body) if body.attrs.is_empty() && body.label.is_none()) - { - self.body.to_tokens(tokens); - } else { - token::Brace::default().surround(tokens, |tokens| { - print_expr(&self.body, tokens, FixupContext::new_stmt()); - }); - } + print_expr_closure(self, tokens, FixupContext::NONE); + } + } + + #[cfg(feature = "full")] + fn print_expr_closure(e: &ExprClosure, tokens: &mut TokenStream, fixup: FixupContext) { + outer_attrs_to_tokens(&e.attrs, tokens); + e.lifetimes.to_tokens(tokens); + e.constness.to_tokens(tokens); + e.movability.to_tokens(tokens); + e.asyncness.to_tokens(tokens); + e.capture.to_tokens(tokens); + e.or1_token.to_tokens(tokens); + e.inputs.to_tokens(tokens); + e.or2_token.to_tokens(tokens); + e.output.to_tokens(tokens); + if matches!(e.output, ReturnType::Default) + || matches!(&*e.body, Expr::Block(body) if body.attrs.is_empty() && body.label.is_none()) + { + print_expr(&e.body, tokens, fixup.rightmost_subexpression()); + } else { + token::Brace::default().surround(tokens, |tokens| { + print_expr(&e.body, tokens, FixupContext::new_stmt()); + }); } } From 19d93ba852c96886679d5a57108bf83c13c43635 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 1 Jan 2025 11:29:20 -0800 Subject: [PATCH 095/121] Merge leftmost_subexpression_with_operator and leftmost_subexpression into one method --- src/expr.rs | 8 ++++++-- src/fixup.rs | 34 +++++++++++++--------------------- 2 files changed, 19 insertions(+), 23 deletions(-) diff --git a/src/expr.rs b/src/expr.rs index 8efb1f04d9..060a205a7f 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -3306,7 +3306,7 @@ pub(crate) mod printing { &e.left, Precedence::of(&e.left) <= Precedence::Range, tokens, - fixup.leftmost_subexpression(), + fixup.leftmost_subexpression_with_operator(false, false), ); e.eq_token.to_tokens(tokens); print_subexpression( @@ -3475,7 +3475,11 @@ pub(crate) mod printing { &e.expr, Precedence::of(&e.expr) < Precedence::Cast, tokens, - fixup.leftmost_subexpression(), + fixup.leftmost_subexpression_with_operator( + #[cfg(feature = "full")] + false, + false, + ), ); e.as_token.to_tokens(tokens); e.ty.to_tokens(tokens); diff --git a/src/fixup.rs b/src/fixup.rs index e4c726e41c..f8ba0d02f1 100644 --- a/src/fixup.rs +++ b/src/fixup.rs @@ -195,7 +195,11 @@ impl FixupContext { /// /// Not every expression has a leftmost subexpression. For example neither /// `-$a` nor `[$a]` have one. - pub fn leftmost_subexpression(self) -> Self { + pub fn leftmost_subexpression_with_operator( + self, + #[cfg(feature = "full")] next_operator_can_begin_expr: bool, + next_operator_can_begin_generics: bool, + ) -> Self { FixupContext { #[cfg(feature = "full")] stmt: false, @@ -209,10 +213,10 @@ impl FixupContext { #[cfg(feature = "full")] rightmost_subexpression: false, #[cfg(feature = "full")] - next_operator_can_begin_expr: false, + next_operator_can_begin_expr, #[cfg(feature = "full")] next_operator_can_continue_expr: true, - next_operator_can_begin_generics: false, + next_operator_can_begin_generics, ..self } } @@ -242,22 +246,6 @@ impl FixupContext { } } - /// Transform this fixup into the one that should apply when printing a - /// leftmost subexpression followed by punctuation that is legal as the - /// first token of an expression. - pub fn leftmost_subexpression_with_operator( - self, - #[cfg(feature = "full")] next_operator_can_begin_expr: bool, - next_operator_can_begin_generics: bool, - ) -> Self { - FixupContext { - #[cfg(feature = "full")] - next_operator_can_begin_expr, - next_operator_can_begin_generics, - ..self.leftmost_subexpression() - } - } - /// Transform this fixup into the one that should apply when printing the /// rightmost subexpression of the current expression. /// @@ -393,10 +381,14 @@ fn test_leftmost_rightmost_invariant() { }; assert_eq!(i, BITS); assert_eq!( - fixup.leftmost_subexpression().rightmost_subexpression(), + fixup + .leftmost_subexpression_with_operator(false, false) + .rightmost_subexpression(), FixupContext { rightmost_subexpression: true, - ..fixup.rightmost_subexpression().leftmost_subexpression() + ..fixup + .rightmost_subexpression() + .leftmost_subexpression_with_operator(false, false) }, ); } From 4b1cf00ff14e955a15c3921c690135efeb0456f0 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 1 Jan 2025 11:35:03 -0800 Subject: [PATCH 096/121] Delete test_leftmost_rightmost_invariant For an upcoming more-complicated FixupContext this test is no longer useful. --- src/fixup.rs | 39 --------------------------------------- 1 file changed, 39 deletions(-) diff --git a/src/fixup.rs b/src/fixup.rs index f8ba0d02f1..6e8e277351 100644 --- a/src/fixup.rs +++ b/src/fixup.rs @@ -2,7 +2,6 @@ use crate::classify; use crate::expr::Expr; use crate::precedence::Precedence; -#[cfg_attr(test, derive(PartialEq, Debug))] pub(crate) struct FixupContext { // Print expression such that it can be parsed back as a statement // consisting of the original expression. @@ -355,41 +354,3 @@ impl Clone for FixupContext { *self } } - -#[cfg(feature = "full")] -#[test] -fn test_leftmost_rightmost_invariant() { - const BITS: usize = 9; - - for bits in 0u16..1 << BITS { - let mut i = 0; - let mut bit = || { - let mask = 1 << i; - i += 1; - (bits & mask) != 0 - }; - let fixup = FixupContext { - stmt: bit(), - leftmost_subexpression_in_stmt: bit(), - match_arm: bit(), - leftmost_subexpression_in_match_arm: bit(), - rightmost_subexpression: bit(), - parenthesize_exterior_struct_lit: bit(), - next_operator_can_begin_expr: bit(), - next_operator_can_continue_expr: bit(), - next_operator_can_begin_generics: bit(), - }; - assert_eq!(i, BITS); - assert_eq!( - fixup - .leftmost_subexpression_with_operator(false, false) - .rightmost_subexpression(), - FixupContext { - rightmost_subexpression: true, - ..fixup - .rightmost_subexpression() - .leftmost_subexpression_with_operator(false, false) - }, - ); - } -} From eb9d807b6e122aebcbd0f91d3124d36b6ef084b4 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 1 Jan 2025 11:52:03 -0800 Subject: [PATCH 097/121] Move precedence computation exclusively into subexpression helpers --- src/expr.rs | 118 +++++++++++++++++++++++---------------------------- src/fixup.rs | 26 ++++++++---- 2 files changed, 72 insertions(+), 72 deletions(-) diff --git a/src/expr.rs b/src/expr.rs index 060a205a7f..e266cef1cd 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -3302,18 +3302,16 @@ pub(crate) mod printing { #[cfg(feature = "full")] fn print_expr_assign(e: &ExprAssign, tokens: &mut TokenStream, fixup: FixupContext) { outer_attrs_to_tokens(&e.attrs, tokens); - print_subexpression( - &e.left, - Precedence::of(&e.left) <= Precedence::Range, - tokens, - fixup.leftmost_subexpression_with_operator(false, false), - ); + let (left_prec, left_fixup) = + fixup.leftmost_subexpression_with_operator(&e.left, false, false); + print_subexpression(&e.left, left_prec <= Precedence::Range, tokens, left_fixup); e.eq_token.to_tokens(tokens); + let (right_prec, right_fixup) = fixup.rightmost_subexpression(&e.right); print_subexpression( &e.right, - fixup.precedence(&e.right) < Precedence::Assign, + right_prec < Precedence::Assign, tokens, - fixup.rightmost_subexpression(), + right_fixup, ); } @@ -3339,11 +3337,12 @@ pub(crate) mod printing { #[cfg(feature = "full")] fn print_expr_await(e: &ExprAwait, tokens: &mut TokenStream, fixup: FixupContext) { outer_attrs_to_tokens(&e.attrs, tokens); + let (left_prec, left_fixup) = fixup.leftmost_subexpression_with_dot(&e.base); print_subexpression( &e.base, - Precedence::of(&e.base) < Precedence::Unambiguous, + left_prec < Precedence::Unambiguous, tokens, - fixup.leftmost_subexpression_with_dot(), + left_fixup, ); e.dot_token.to_tokens(tokens); e.await_token.to_tokens(tokens); @@ -3359,7 +3358,9 @@ pub(crate) mod printing { fn print_expr_binary(e: &ExprBinary, tokens: &mut TokenStream, fixup: FixupContext) { outer_attrs_to_tokens(&e.attrs, tokens); - let left_fixup = fixup.leftmost_subexpression_with_operator( + let binop_prec = Precedence::of_binop(&e.op); + let (left_prec, left_fixup) = fixup.leftmost_subexpression_with_operator( + &e.left, #[cfg(feature = "full")] match &e.op { BinOp::Sub(_) @@ -3377,10 +3378,7 @@ pub(crate) mod printing { _ => false, }, ); - - let binop_prec = Precedence::of_binop(&e.op); - let left_prec = left_fixup.precedence(&e.left); - let right_prec = fixup.precedence(&e.right); + let (right_prec, right_fixup) = fixup.rightmost_subexpression(&e.right); let (left_needs_group, right_needs_group) = match binop_prec { Precedence::Assign => (left_prec <= Precedence::Range, right_prec < binop_prec), Precedence::Compare => (left_prec <= binop_prec, right_prec <= binop_prec), @@ -3389,12 +3387,7 @@ pub(crate) mod printing { print_subexpression(&e.left, left_needs_group, tokens, left_fixup); e.op.to_tokens(tokens); - print_subexpression( - &e.right, - right_needs_group, - tokens, - fixup.rightmost_subexpression(), - ); + print_subexpression(&e.right, right_needs_group, tokens, right_fixup); } #[cfg(feature = "full")] @@ -3430,7 +3423,7 @@ pub(crate) mod printing { // ^---------------------------------^ e.label.is_none() && classify::expr_leading_label(value), tokens, - fixup.rightmost_subexpression(), + fixup.rightmost_subexpression_fixup(), ); } } @@ -3445,7 +3438,8 @@ pub(crate) mod printing { fn print_expr_call(e: &ExprCall, tokens: &mut TokenStream, fixup: FixupContext) { outer_attrs_to_tokens(&e.attrs, tokens); - let func_fixup = fixup.leftmost_subexpression_with_operator( + let (left_prec, left_fixup) = fixup.leftmost_subexpression_with_operator( + &e.func, #[cfg(feature = "full")] true, false, @@ -3453,9 +3447,9 @@ pub(crate) mod printing { let needs_group = if let Expr::Field(func) = &*e.func { func.member.is_named() } else { - func_fixup.precedence(&e.func) < Precedence::Unambiguous + left_prec < Precedence::Unambiguous }; - print_subexpression(&e.func, needs_group, tokens, func_fixup); + print_subexpression(&e.func, needs_group, tokens, left_fixup); e.paren_token.surround(tokens, |tokens| { e.args.to_tokens(tokens); @@ -3471,16 +3465,13 @@ pub(crate) mod printing { fn print_expr_cast(e: &ExprCast, tokens: &mut TokenStream, fixup: FixupContext) { outer_attrs_to_tokens(&e.attrs, tokens); - print_subexpression( + let (left_prec, left_fixup) = fixup.leftmost_subexpression_with_operator( &e.expr, - Precedence::of(&e.expr) < Precedence::Cast, - tokens, - fixup.leftmost_subexpression_with_operator( - #[cfg(feature = "full")] - false, - false, - ), + #[cfg(feature = "full")] + false, + false, ); + print_subexpression(&e.expr, left_prec < Precedence::Cast, tokens, left_fixup); e.as_token.to_tokens(tokens); e.ty.to_tokens(tokens); } @@ -3508,7 +3499,7 @@ pub(crate) mod printing { if matches!(e.output, ReturnType::Default) || matches!(&*e.body, Expr::Block(body) if body.attrs.is_empty() && body.label.is_none()) { - print_expr(&e.body, tokens, fixup.rightmost_subexpression()); + print_expr(&e.body, tokens, fixup.rightmost_subexpression_fixup()); } else { token::Brace::default().surround(tokens, |tokens| { print_expr(&e.body, tokens, FixupContext::new_stmt()); @@ -3548,11 +3539,12 @@ pub(crate) mod printing { fn print_expr_field(e: &ExprField, tokens: &mut TokenStream, fixup: FixupContext) { outer_attrs_to_tokens(&e.attrs, tokens); + let (left_prec, left_fixup) = fixup.leftmost_subexpression_with_dot(&e.base); print_subexpression( &e.base, - Precedence::of(&e.base) < Precedence::Unambiguous, + left_prec < Precedence::Unambiguous, tokens, - fixup.leftmost_subexpression_with_dot(), + left_fixup, ); e.dot_token.to_tokens(tokens); e.member.to_tokens(tokens); @@ -3633,16 +3625,17 @@ pub(crate) mod printing { fn print_expr_index(e: &ExprIndex, tokens: &mut TokenStream, fixup: FixupContext) { outer_attrs_to_tokens(&e.attrs, tokens); - let obj_fixup = fixup.leftmost_subexpression_with_operator( + let (left_prec, left_fixup) = fixup.leftmost_subexpression_with_operator( + &e.expr, #[cfg(feature = "full")] true, false, ); print_subexpression( &e.expr, - obj_fixup.precedence(&e.expr) < Precedence::Unambiguous, + left_prec < Precedence::Unambiguous, tokens, - obj_fixup, + left_fixup, ); e.bracket_token.surround(tokens, |tokens| { e.index.to_tokens(tokens); @@ -3744,11 +3737,12 @@ pub(crate) mod printing { fn print_expr_method_call(e: &ExprMethodCall, tokens: &mut TokenStream, fixup: FixupContext) { outer_attrs_to_tokens(&e.attrs, tokens); + let (left_prec, left_fixup) = fixup.leftmost_subexpression_with_dot(&e.receiver); print_subexpression( &e.receiver, - Precedence::of(&e.receiver) < Precedence::Unambiguous, + left_prec < Precedence::Unambiguous, tokens, - fixup.leftmost_subexpression_with_dot(), + left_fixup, ); e.dot_token.to_tokens(tokens); e.method.to_tokens(tokens); @@ -3794,22 +3788,14 @@ pub(crate) mod printing { fn print_expr_range(e: &ExprRange, tokens: &mut TokenStream, fixup: FixupContext) { outer_attrs_to_tokens(&e.attrs, tokens); if let Some(start) = &e.start { - let start_fixup = fixup.leftmost_subexpression_with_operator(true, false); - print_subexpression( - start, - start_fixup.precedence(start) <= Precedence::Range, - tokens, - start_fixup, - ); + let (left_prec, left_fixup) = + fixup.leftmost_subexpression_with_operator(start, true, false); + print_subexpression(start, left_prec <= Precedence::Range, tokens, left_fixup); } e.limits.to_tokens(tokens); if let Some(end) = &e.end { - print_subexpression( - end, - fixup.precedence(end) <= Precedence::Range, - tokens, - fixup.rightmost_subexpression(), - ); + let (right_prec, right_fixup) = fixup.rightmost_subexpression(end); + print_subexpression(end, right_prec <= Precedence::Range, tokens, right_fixup); } } @@ -3827,11 +3813,12 @@ pub(crate) mod printing { e.and_token.to_tokens(tokens); e.raw.to_tokens(tokens); e.mutability.to_tokens(tokens); + let (right_prec, right_fixup) = fixup.rightmost_subexpression(&e.expr); print_subexpression( &e.expr, - fixup.precedence(&e.expr) < Precedence::Prefix, + right_prec < Precedence::Prefix, tokens, - fixup.rightmost_subexpression(), + right_fixup, ); } @@ -3846,11 +3833,12 @@ pub(crate) mod printing { outer_attrs_to_tokens(&e.attrs, tokens); e.and_token.to_tokens(tokens); e.mutability.to_tokens(tokens); + let (right_prec, right_fixup) = fixup.rightmost_subexpression(&e.expr); print_subexpression( &e.expr, - fixup.precedence(&e.expr) < Precedence::Prefix, + right_prec < Precedence::Prefix, tokens, - fixup.rightmost_subexpression(), + right_fixup, ); } @@ -3880,7 +3868,7 @@ pub(crate) mod printing { outer_attrs_to_tokens(&e.attrs, tokens); e.return_token.to_tokens(tokens); if let Some(expr) = &e.expr { - print_expr(expr, tokens, fixup.rightmost_subexpression()); + print_expr(expr, tokens, fixup.rightmost_subexpression_fixup()); } } @@ -3912,11 +3900,12 @@ pub(crate) mod printing { #[cfg(feature = "full")] fn print_expr_try(e: &ExprTry, tokens: &mut TokenStream, fixup: FixupContext) { outer_attrs_to_tokens(&e.attrs, tokens); + let (left_prec, left_fixup) = fixup.leftmost_subexpression_with_dot(&e.expr); print_subexpression( &e.expr, - Precedence::of(&e.expr) < Precedence::Unambiguous, + left_prec < Precedence::Unambiguous, tokens, - fixup.leftmost_subexpression_with_dot(), + left_fixup, ); e.question_token.to_tokens(tokens); } @@ -3956,11 +3945,12 @@ pub(crate) mod printing { fn print_expr_unary(e: &ExprUnary, tokens: &mut TokenStream, fixup: FixupContext) { outer_attrs_to_tokens(&e.attrs, tokens); e.op.to_tokens(tokens); + let (right_prec, right_fixup) = fixup.rightmost_subexpression(&e.expr); print_subexpression( &e.expr, - fixup.precedence(&e.expr) < Precedence::Prefix, + right_prec < Precedence::Prefix, tokens, - fixup.rightmost_subexpression(), + right_fixup, ); } @@ -4005,7 +3995,7 @@ pub(crate) mod printing { outer_attrs_to_tokens(&e.attrs, tokens); e.yield_token.to_tokens(tokens); if let Some(expr) = &e.expr { - print_expr(expr, tokens, fixup.rightmost_subexpression()); + print_expr(expr, tokens, fixup.rightmost_subexpression_fixup()); } } diff --git a/src/fixup.rs b/src/fixup.rs index 6e8e277351..85f0166d0d 100644 --- a/src/fixup.rs +++ b/src/fixup.rs @@ -196,10 +196,11 @@ impl FixupContext { /// `-$a` nor `[$a]` have one. pub fn leftmost_subexpression_with_operator( self, + expr: &Expr, #[cfg(feature = "full")] next_operator_can_begin_expr: bool, next_operator_can_begin_generics: bool, - ) -> Self { - FixupContext { + ) -> (Precedence, Self) { + let fixup = FixupContext { #[cfg(feature = "full")] stmt: false, #[cfg(feature = "full")] @@ -217,15 +218,17 @@ impl FixupContext { next_operator_can_continue_expr: true, next_operator_can_begin_generics, ..self - } + }; + + (fixup.precedence(expr), fixup) } /// Transform this fixup into the one that should apply when printing a /// leftmost subexpression followed by a `.` or `?` token, which confer /// different statement boundary rules compared to other leftmost /// subexpressions. - pub fn leftmost_subexpression_with_dot(self) -> Self { - FixupContext { + pub fn leftmost_subexpression_with_dot(self, expr: &Expr) -> (Precedence, Self) { + let fixup = FixupContext { #[cfg(feature = "full")] stmt: self.stmt || self.leftmost_subexpression_in_stmt, #[cfg(feature = "full")] @@ -242,7 +245,9 @@ impl FixupContext { next_operator_can_continue_expr: true, next_operator_can_begin_generics: false, ..self - } + }; + + (fixup.precedence(expr), fixup) } /// Transform this fixup into the one that should apply when printing the @@ -256,7 +261,12 @@ impl FixupContext { /// /// Not every expression has a rightmost subexpression. For example neither /// `[$b]` nor `$a.f($b)` have one. - pub fn rightmost_subexpression(self) -> Self { + pub fn rightmost_subexpression(self, expr: &Expr) -> (Precedence, Self) { + let fixup = self.rightmost_subexpression_fixup(); + (fixup.precedence(expr), fixup) + } + + pub fn rightmost_subexpression_fixup(self) -> Self { FixupContext { #[cfg(feature = "full")] stmt: false, @@ -304,7 +314,7 @@ impl FixupContext { /// Determines the effective precedence of a subexpression. Some expressions /// have higher or lower precedence when adjacent to particular operators. - pub fn precedence(self, expr: &Expr) -> Precedence { + fn precedence(self, expr: &Expr) -> Precedence { #[cfg(feature = "full")] if self.next_operator_can_begin_expr { // Decrease precedence of value-less jumps when followed by an From 7e8a56a7e03ceb4cf1a5d2de2d7a0d86b4933e95 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 1 Jan 2025 12:09:32 -0800 Subject: [PATCH 098/121] Track previous and next operator's precedence in fixup context --- src/expr.rs | 57 +++++++++++++++++++++++++++++++++++++++++----------- src/fixup.rs | 34 ++++++++++++++++++++++++++++--- 2 files changed, 76 insertions(+), 15 deletions(-) diff --git a/src/expr.rs b/src/expr.rs index e266cef1cd..6c244e7d3b 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -3303,10 +3303,10 @@ pub(crate) mod printing { fn print_expr_assign(e: &ExprAssign, tokens: &mut TokenStream, fixup: FixupContext) { outer_attrs_to_tokens(&e.attrs, tokens); let (left_prec, left_fixup) = - fixup.leftmost_subexpression_with_operator(&e.left, false, false); + fixup.leftmost_subexpression_with_operator(&e.left, false, false, Precedence::Assign); print_subexpression(&e.left, left_prec <= Precedence::Range, tokens, left_fixup); e.eq_token.to_tokens(tokens); - let (right_prec, right_fixup) = fixup.rightmost_subexpression(&e.right); + let (right_prec, right_fixup) = fixup.rightmost_subexpression(&e.right, Precedence::Assign); print_subexpression( &e.right, right_prec < Precedence::Assign, @@ -3377,8 +3377,15 @@ pub(crate) mod printing { BinOp::Shl(_) | BinOp::Lt(_) => true, _ => false, }, + #[cfg(feature = "full")] + binop_prec, + ); + + let (right_prec, right_fixup) = fixup.rightmost_subexpression( + &e.right, + #[cfg(feature = "full")] + binop_prec, ); - let (right_prec, right_fixup) = fixup.rightmost_subexpression(&e.right); let (left_needs_group, right_needs_group) = match binop_prec { Precedence::Assign => (left_prec <= Precedence::Range, right_prec < binop_prec), Precedence::Compare => (left_prec <= binop_prec, right_prec <= binop_prec), @@ -3423,7 +3430,7 @@ pub(crate) mod printing { // ^---------------------------------^ e.label.is_none() && classify::expr_leading_label(value), tokens, - fixup.rightmost_subexpression_fixup(), + fixup.rightmost_subexpression_fixup(Precedence::Jump), ); } } @@ -3443,6 +3450,8 @@ pub(crate) mod printing { #[cfg(feature = "full")] true, false, + #[cfg(feature = "full")] + Precedence::Unambiguous, ); let needs_group = if let Expr::Field(func) = &*e.func { func.member.is_named() @@ -3470,6 +3479,8 @@ pub(crate) mod printing { #[cfg(feature = "full")] false, false, + #[cfg(feature = "full")] + Precedence::Cast, ); print_subexpression(&e.expr, left_prec < Precedence::Cast, tokens, left_fixup); e.as_token.to_tokens(tokens); @@ -3499,7 +3510,11 @@ pub(crate) mod printing { if matches!(e.output, ReturnType::Default) || matches!(&*e.body, Expr::Block(body) if body.attrs.is_empty() && body.label.is_none()) { - print_expr(&e.body, tokens, fixup.rightmost_subexpression_fixup()); + print_expr( + &e.body, + tokens, + fixup.rightmost_subexpression_fixup(Precedence::Jump), + ); } else { token::Brace::default().surround(tokens, |tokens| { print_expr(&e.body, tokens, FixupContext::new_stmt()); @@ -3630,6 +3645,8 @@ pub(crate) mod printing { #[cfg(feature = "full")] true, false, + #[cfg(feature = "full")] + Precedence::Unambiguous, ); print_subexpression( &e.expr, @@ -3789,12 +3806,12 @@ pub(crate) mod printing { outer_attrs_to_tokens(&e.attrs, tokens); if let Some(start) = &e.start { let (left_prec, left_fixup) = - fixup.leftmost_subexpression_with_operator(start, true, false); + fixup.leftmost_subexpression_with_operator(start, true, false, Precedence::Range); print_subexpression(start, left_prec <= Precedence::Range, tokens, left_fixup); } e.limits.to_tokens(tokens); if let Some(end) = &e.end { - let (right_prec, right_fixup) = fixup.rightmost_subexpression(end); + let (right_prec, right_fixup) = fixup.rightmost_subexpression(end, Precedence::Range); print_subexpression(end, right_prec <= Precedence::Range, tokens, right_fixup); } } @@ -3813,7 +3830,7 @@ pub(crate) mod printing { e.and_token.to_tokens(tokens); e.raw.to_tokens(tokens); e.mutability.to_tokens(tokens); - let (right_prec, right_fixup) = fixup.rightmost_subexpression(&e.expr); + let (right_prec, right_fixup) = fixup.rightmost_subexpression(&e.expr, Precedence::Prefix); print_subexpression( &e.expr, right_prec < Precedence::Prefix, @@ -3833,7 +3850,11 @@ pub(crate) mod printing { outer_attrs_to_tokens(&e.attrs, tokens); e.and_token.to_tokens(tokens); e.mutability.to_tokens(tokens); - let (right_prec, right_fixup) = fixup.rightmost_subexpression(&e.expr); + let (right_prec, right_fixup) = fixup.rightmost_subexpression( + &e.expr, + #[cfg(feature = "full")] + Precedence::Prefix, + ); print_subexpression( &e.expr, right_prec < Precedence::Prefix, @@ -3868,7 +3889,11 @@ pub(crate) mod printing { outer_attrs_to_tokens(&e.attrs, tokens); e.return_token.to_tokens(tokens); if let Some(expr) = &e.expr { - print_expr(expr, tokens, fixup.rightmost_subexpression_fixup()); + print_expr( + expr, + tokens, + fixup.rightmost_subexpression_fixup(Precedence::Jump), + ); } } @@ -3945,7 +3970,11 @@ pub(crate) mod printing { fn print_expr_unary(e: &ExprUnary, tokens: &mut TokenStream, fixup: FixupContext) { outer_attrs_to_tokens(&e.attrs, tokens); e.op.to_tokens(tokens); - let (right_prec, right_fixup) = fixup.rightmost_subexpression(&e.expr); + let (right_prec, right_fixup) = fixup.rightmost_subexpression( + &e.expr, + #[cfg(feature = "full")] + Precedence::Prefix, + ); print_subexpression( &e.expr, right_prec < Precedence::Prefix, @@ -3995,7 +4024,11 @@ pub(crate) mod printing { outer_attrs_to_tokens(&e.attrs, tokens); e.yield_token.to_tokens(tokens); if let Some(expr) = &e.expr { - print_expr(expr, tokens, fixup.rightmost_subexpression_fixup()); + print_expr( + expr, + tokens, + fixup.rightmost_subexpression_fixup(Precedence::Jump), + ); } } diff --git a/src/fixup.rs b/src/fixup.rs index 85f0166d0d..2243c06422 100644 --- a/src/fixup.rs +++ b/src/fixup.rs @@ -3,6 +3,13 @@ use crate::expr::Expr; use crate::precedence::Precedence; pub(crate) struct FixupContext { + #[cfg(feature = "full")] + #[allow(dead_code)] + previous_operator: Precedence, + #[cfg(feature = "full")] + #[allow(dead_code)] + next_operator: Precedence, + // Print expression such that it can be parsed back as a statement // consisting of the original expression. // @@ -132,6 +139,10 @@ impl FixupContext { /// The default amount of fixing is minimal fixing. Fixups should be turned /// on in a targeted fashion where needed. pub const NONE: Self = FixupContext { + #[cfg(feature = "full")] + previous_operator: Precedence::MIN, + #[cfg(feature = "full")] + next_operator: Precedence::MIN, #[cfg(feature = "full")] stmt: false, #[cfg(feature = "full")] @@ -199,8 +210,11 @@ impl FixupContext { expr: &Expr, #[cfg(feature = "full")] next_operator_can_begin_expr: bool, next_operator_can_begin_generics: bool, + #[cfg(feature = "full")] precedence: Precedence, ) -> (Precedence, Self) { let fixup = FixupContext { + #[cfg(feature = "full")] + next_operator: precedence, #[cfg(feature = "full")] stmt: false, #[cfg(feature = "full")] @@ -229,6 +243,8 @@ impl FixupContext { /// subexpressions. pub fn leftmost_subexpression_with_dot(self, expr: &Expr) -> (Precedence, Self) { let fixup = FixupContext { + #[cfg(feature = "full")] + next_operator: Precedence::Unambiguous, #[cfg(feature = "full")] stmt: self.stmt || self.leftmost_subexpression_in_stmt, #[cfg(feature = "full")] @@ -261,13 +277,25 @@ impl FixupContext { /// /// Not every expression has a rightmost subexpression. For example neither /// `[$b]` nor `$a.f($b)` have one. - pub fn rightmost_subexpression(self, expr: &Expr) -> (Precedence, Self) { - let fixup = self.rightmost_subexpression_fixup(); + pub fn rightmost_subexpression( + self, + expr: &Expr, + #[cfg(feature = "full")] precedence: Precedence, + ) -> (Precedence, Self) { + let fixup = self.rightmost_subexpression_fixup( + #[cfg(feature = "full")] + precedence, + ); (fixup.precedence(expr), fixup) } - pub fn rightmost_subexpression_fixup(self) -> Self { + pub fn rightmost_subexpression_fixup( + self, + #[cfg(feature = "full")] precedence: Precedence, + ) -> Self { FixupContext { + #[cfg(feature = "full")] + previous_operator: precedence, #[cfg(feature = "full")] stmt: false, #[cfg(feature = "full")] From 977a524bccf424ec9ae26391a1d7fb3107be2aca Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 1 Jan 2025 12:12:46 -0800 Subject: [PATCH 099/121] Add test of second-level break confusable --- tests/test_expr.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test_expr.rs b/tests/test_expr.rs index 3a8f6d412d..0c210019fe 100644 --- a/tests/test_expr.rs +++ b/tests/test_expr.rs @@ -812,7 +812,9 @@ fn test_fixup() { quote! { if let _ = ((break) - 1 || true) {} }, quote! { if let _ = (break + 1 || true) {} }, quote! { if (break break) {} }, + quote! { if break break {} {} }, quote! { if (return ..) {} }, + quote! { if return .. {} {} }, quote! { if (|| Struct {}) {} }, quote! { if (|| Struct {}.await) {} }, quote! { if break || Struct {}.await {} }, From 4d1f6c01d51f9f7cd464762aef38fb2d5dff65f6 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 1 Jan 2025 12:13:44 -0800 Subject: [PATCH 100/121] Fix classification of nested breaks/ranges inside condition --- src/classify.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/classify.rs b/src/classify.rs index 53bd6722e6..458bbdc139 100644 --- a/src/classify.rs +++ b/src/classify.rs @@ -113,7 +113,7 @@ pub(crate) fn confusable_with_adjacent_block(expr: &Expr) -> bool { } Expr::Break(e) => { if let Some(value) = &e.expr { - confusable(value, true, true, rightmost_subexpression) + confusable(value, true, !allow_struct, rightmost_subexpression) } else { allow_struct && rightmost_subexpression } @@ -157,7 +157,9 @@ pub(crate) fn confusable_with_adjacent_block(expr: &Expr) -> bool { } None => false, } || match &e.end { - Some(end) => confusable(end, allow_struct, true, rightmost_subexpression), + Some(end) => { + confusable(end, allow_struct, !allow_struct, rightmost_subexpression) + } None => allow_struct && rightmost_subexpression, }) } From 215b57df93e09f144cb3ca1c8e8aa26e3ca83df6 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 1 Jan 2025 12:16:24 -0800 Subject: [PATCH 101/121] Implement a parser simulator to determine true precedence levels --- src/fixup.rs | 347 ++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 318 insertions(+), 29 deletions(-) diff --git a/src/fixup.rs b/src/fixup.rs index 2243c06422..2604fd86d8 100644 --- a/src/fixup.rs +++ b/src/fixup.rs @@ -1,13 +1,15 @@ use crate::classify; use crate::expr::Expr; +#[cfg(feature = "full")] +use crate::expr::{ExprBreak, ExprRawAddr, ExprReference, ExprReturn, ExprUnary, ExprYield}; use crate::precedence::Precedence; +#[cfg(feature = "full")] +use crate::ty::ReturnType; pub(crate) struct FixupContext { #[cfg(feature = "full")] - #[allow(dead_code)] previous_operator: Precedence, #[cfg(feature = "full")] - #[allow(dead_code)] next_operator: Precedence, // Print expression such that it can be parsed back as a statement @@ -87,15 +89,6 @@ pub(crate) struct FixupContext { #[cfg(feature = "full")] leftmost_subexpression_in_match_arm: bool, - // This is the difference between: - // - // (..) + x // parens because range cannot be lhs of a binop - // - // &.. + x // no parens because range is a right subexpression - // - #[cfg(feature = "full")] - rightmost_subexpression: bool, - // This is the difference between: // // if let _ = (Struct {}) {} // needs parens @@ -152,8 +145,6 @@ impl FixupContext { #[cfg(feature = "full")] leftmost_subexpression_in_match_arm: false, #[cfg(feature = "full")] - rightmost_subexpression: false, - #[cfg(feature = "full")] parenthesize_exterior_struct_lit: false, #[cfg(feature = "full")] next_operator_can_begin_expr: false, @@ -225,8 +216,6 @@ impl FixupContext { leftmost_subexpression_in_match_arm: self.match_arm || self.leftmost_subexpression_in_match_arm, #[cfg(feature = "full")] - rightmost_subexpression: false, - #[cfg(feature = "full")] next_operator_can_begin_expr, #[cfg(feature = "full")] next_operator_can_continue_expr: true, @@ -234,7 +223,7 @@ impl FixupContext { ..self }; - (fixup.precedence(expr), fixup) + (fixup.leftmost_subexpression_precedence(expr), fixup) } /// Transform this fixup into the one that should apply when printing a @@ -254,8 +243,6 @@ impl FixupContext { #[cfg(feature = "full")] leftmost_subexpression_in_match_arm: false, #[cfg(feature = "full")] - rightmost_subexpression: false, - #[cfg(feature = "full")] next_operator_can_begin_expr: false, #[cfg(feature = "full")] next_operator_can_continue_expr: true, @@ -263,7 +250,20 @@ impl FixupContext { ..self }; - (fixup.precedence(expr), fixup) + (fixup.leftmost_subexpression_precedence(expr), fixup) + } + + fn leftmost_subexpression_precedence(self, expr: &Expr) -> Precedence { + #[cfg(feature = "full")] + if !self.next_operator_can_begin_expr || self.next_operator == Precedence::Range { + if let Scan::Bailout = scan_right(expr, self, false, 0, 0) { + if scan_left(expr, self) { + return Precedence::Unambiguous; + } + } + } + + self.precedence(expr) } /// Transform this fixup into the one that should apply when printing the @@ -286,7 +286,7 @@ impl FixupContext { #[cfg(feature = "full")] precedence, ); - (fixup.precedence(expr), fixup) + (fixup.rightmost_subexpression_precedence(expr), fixup) } pub fn rightmost_subexpression_fixup( @@ -304,12 +304,33 @@ impl FixupContext { match_arm: false, #[cfg(feature = "full")] leftmost_subexpression_in_match_arm: false, - #[cfg(feature = "full")] - rightmost_subexpression: true, ..self } } + fn rightmost_subexpression_precedence(self, expr: &Expr) -> Precedence { + let default_prec = self.precedence(expr); + + #[cfg(feature = "full")] + if default_prec < Precedence::Prefix + && (!self.next_operator_can_begin_expr || self.next_operator == Precedence::Range) + { + if let Scan::Bailout | Scan::Fail = scan_right( + expr, + self, + self.previous_operator == Precedence::Range, + 1, + 0, + ) { + if scan_left(expr, self) { + return Precedence::Prefix; + } + } + } + + default_prec + } + /// Determine whether parentheses are needed around the given expression to /// head off an unintended statement boundary. /// @@ -348,16 +369,14 @@ impl FixupContext { // Decrease precedence of value-less jumps when followed by an // operator that would otherwise get interpreted as beginning a // value for the jump. - if let Expr::Break(_) | Expr::Return(_) | Expr::Yield(_) = expr { + if let Expr::Break(ExprBreak { expr: None, .. }) + | Expr::Return(ExprReturn { expr: None, .. }) + | Expr::Yield(ExprYield { expr: None, .. }) = expr + { return Precedence::Jump; } - } else if self.rightmost_subexpression { - if let Expr::Range(range) = expr { - if range.start.is_none() && range.end.is_none() { - return Precedence::Unambiguous; - } - } } + #[cfg(feature = "full")] if !self.next_operator_can_continue_expr { match expr { @@ -374,6 +393,7 @@ impl FixupContext { _ => {} } } + if self.next_operator_can_begin_generics { if let Expr::Cast(cast) = expr { if classify::trailing_unparameterized_path(&cast.ty) { @@ -381,6 +401,7 @@ impl FixupContext { } } } + Precedence::of(expr) } } @@ -392,3 +413,271 @@ impl Clone for FixupContext { *self } } + +#[cfg(feature = "full")] +enum Scan { + Fail, + Bailout, + Consume, +} + +#[cfg(feature = "full")] +impl Copy for Scan {} + +#[cfg(feature = "full")] +impl Clone for Scan { + fn clone(&self) -> Self { + *self + } +} + +#[cfg(feature = "full")] +fn scan_left(expr: &Expr, fixup: FixupContext) -> bool { + match expr { + Expr::Assign(_) => fixup.previous_operator <= Precedence::Assign, + Expr::Binary(e) => match Precedence::of_binop(&e.op) { + Precedence::Assign => fixup.previous_operator <= Precedence::Assign, + binop_prec => fixup.previous_operator < binop_prec, + }, + Expr::Range(e) => e.start.is_none() || fixup.previous_operator < Precedence::Assign, + _ => true, + } +} + +#[cfg(feature = "full")] +fn scan_right( + expr: &Expr, + fixup: FixupContext, + range: bool, + fail_offset: u8, + bailout_offset: u8, +) -> Scan { + match expr { + Expr::Assign(e) => { + if match fixup.next_operator { + Precedence::Unambiguous => fail_offset >= 2, + _ => bailout_offset >= 1, + } { + return Scan::Consume; + } + let right_fixup = fixup.rightmost_subexpression_fixup(Precedence::Assign); + let scan = scan_right( + &e.right, + right_fixup, + false, + match fixup.next_operator { + Precedence::Unambiguous => fail_offset, + _ => 1, + }, + 1, + ); + if let Scan::Bailout | Scan::Consume = scan { + return Scan::Consume; + } + if right_fixup.rightmost_subexpression_precedence(&e.right) < Precedence::Assign { + Scan::Consume + } else if let Precedence::Unambiguous = fixup.next_operator { + Scan::Fail + } else { + Scan::Bailout + } + } + Expr::Binary(e) => { + if match fixup.next_operator { + Precedence::Unambiguous => fail_offset >= 2, + _ => bailout_offset >= 1, + } { + return Scan::Consume; + } + let binop_prec = Precedence::of_binop(&e.op); + let right_fixup = fixup.rightmost_subexpression_fixup(binop_prec); + let scan = scan_right( + &e.right, + right_fixup, + range && binop_prec != Precedence::Assign, + match fixup.next_operator { + Precedence::Unambiguous => fail_offset, + _ => 1, + }, + match (binop_prec, fixup.next_operator) { + (Precedence::Assign, _) => 1, + (_, Precedence::Assign | Precedence::Range) if range => 0, + _ => 1, + }, + ); + if match (scan, fixup.next_operator) { + (Scan::Fail, _) => false, + (Scan::Bailout, _) if binop_prec == Precedence::Assign => true, + (Scan::Bailout, Precedence::Assign | Precedence::Range) => !range, + (Scan::Bailout | Scan::Consume, _) => true, + } { + return Scan::Consume; + } + let right_prec = right_fixup.rightmost_subexpression_precedence(&e.right); + let right_needs_group = match binop_prec { + Precedence::Assign => right_prec < binop_prec, + _ => right_prec <= binop_prec, + }; + if right_needs_group { + Scan::Consume + } else if let (Scan::Fail, Precedence::Unambiguous) = (scan, fixup.next_operator) { + Scan::Fail + } else { + Scan::Bailout + } + } + Expr::RawAddr(ExprRawAddr { expr, .. }) + | Expr::Reference(ExprReference { expr, .. }) + | Expr::Unary(ExprUnary { expr, .. }) => { + if match fixup.next_operator { + Precedence::Unambiguous => fail_offset >= 2, + _ => bailout_offset >= 1, + } { + return Scan::Consume; + } + let right_fixup = fixup.rightmost_subexpression_fixup(Precedence::Prefix); + let scan = scan_right( + expr, + right_fixup, + range, + match fixup.next_operator { + Precedence::Unambiguous => fail_offset, + _ => 1, + }, + match fixup.next_operator { + Precedence::Assign | Precedence::Range if range => 0, + _ => 1, + }, + ); + if match (scan, fixup.next_operator) { + (Scan::Fail, _) => false, + (Scan::Bailout, Precedence::Assign | Precedence::Range) => !range, + (Scan::Bailout | Scan::Consume, _) => true, + } { + return Scan::Consume; + } + if right_fixup.rightmost_subexpression_precedence(expr) < Precedence::Prefix { + Scan::Consume + } else if let (Scan::Fail, Precedence::Unambiguous) = (scan, fixup.next_operator) { + Scan::Fail + } else { + Scan::Bailout + } + } + Expr::Range(e) => match &e.end { + Some(end) => { + if fail_offset >= 2 { + return Scan::Consume; + } + let right_fixup = fixup.rightmost_subexpression_fixup(Precedence::Range); + let scan = scan_right( + end, + right_fixup, + true, + fail_offset, + match fixup.next_operator { + Precedence::Assign | Precedence::Range => 0, + _ => 1, + }, + ); + if match (scan, fixup.next_operator) { + (Scan::Fail, _) => false, + (Scan::Bailout, Precedence::Assign | Precedence::Range) => false, + (Scan::Bailout | Scan::Consume, _) => true, + } { + return Scan::Consume; + } + if right_fixup.rightmost_subexpression_precedence(end) <= Precedence::Range { + Scan::Consume + } else { + Scan::Fail + } + } + None => match fixup.next_operator { + Precedence::Range => Scan::Consume, + _ => Scan::Fail, + }, + }, + Expr::Break(e) => match &e.expr { + Some(value) => { + if bailout_offset >= 1 || e.label.is_none() && classify::expr_leading_label(value) { + return Scan::Consume; + } + let right_fixup = fixup.rightmost_subexpression_fixup(Precedence::Jump); + match scan_right(value, right_fixup, false, 1, 1) { + Scan::Fail => Scan::Bailout, + Scan::Bailout | Scan::Consume => Scan::Consume, + } + } + None => match fixup.next_operator { + Precedence::Assign if range => Scan::Fail, + _ => Scan::Consume, + }, + }, + Expr::Return(ExprReturn { expr, .. }) | Expr::Yield(ExprYield { expr, .. }) => match expr { + Some(e) => { + if bailout_offset >= 1 { + return Scan::Consume; + } + let right_fixup = fixup.rightmost_subexpression_fixup(Precedence::Jump); + match scan_right(e, right_fixup, false, 1, 1) { + Scan::Fail => Scan::Bailout, + Scan::Bailout | Scan::Consume => Scan::Consume, + } + } + None => match fixup.next_operator { + Precedence::Assign if range => Scan::Fail, + _ => Scan::Consume, + }, + }, + Expr::Closure(e) => { + if matches!(e.output, ReturnType::Default) + || matches!(&*e.body, Expr::Block(body) if body.attrs.is_empty() && body.label.is_none()) + { + if bailout_offset >= 1 { + return Scan::Consume; + } + let right_fixup = fixup.rightmost_subexpression_fixup(Precedence::Jump); + match scan_right(&e.body, right_fixup, false, 1, 1) { + Scan::Fail => Scan::Bailout, + Scan::Bailout | Scan::Consume => Scan::Consume, + } + } else { + Scan::Consume + } + } + Expr::Array(_) + | Expr::Async(_) + | Expr::Await(_) + | Expr::Block(_) + | Expr::Call(_) + | Expr::Cast(_) + | Expr::Const(_) + | Expr::Continue(_) + | Expr::Field(_) + | Expr::ForLoop(_) + | Expr::Group(_) + | Expr::If(_) + | Expr::Index(_) + | Expr::Infer(_) + | Expr::Let(_) + | Expr::Lit(_) + | Expr::Loop(_) + | Expr::Macro(_) + | Expr::Match(_) + | Expr::MethodCall(_) + | Expr::Paren(_) + | Expr::Path(_) + | Expr::Repeat(_) + | Expr::Struct(_) + | Expr::Try(_) + | Expr::TryBlock(_) + | Expr::Tuple(_) + | Expr::Unsafe(_) + | Expr::Verbatim(_) + | Expr::While(_) => match fixup.next_operator { + Precedence::Assign | Precedence::Range if range => Scan::Fail, + _ => Scan::Consume, + }, + } +} From bd02f07e3b7d019f023c26619beee4ec5889263a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 1 Jan 2025 12:16:31 -0800 Subject: [PATCH 102/121] Test larger expr depth --- tests/test_expr.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/test_expr.rs b/tests/test_expr.rs index 0c210019fe..d5b4232538 100644 --- a/tests/test_expr.rs +++ b/tests/test_expr.rs @@ -1577,7 +1577,6 @@ fn test_permutations() -> ExitCode { } } - let mut count = 0; let mut failures = 0; macro_rules! fail { ($($message:tt)*) => {{ @@ -1587,7 +1586,6 @@ fn test_permutations() -> ExitCode { }}; } let mut assert = |mut original: Expr| { - count += 1; let tokens = original.to_token_stream(); let Ok(mut parsed) = syn::parse2::(tokens.clone()) else { fail!( @@ -1619,8 +1617,7 @@ fn test_permutations() -> ExitCode { } }; - iter(2, &mut assert); - assert_eq!(count, 505); + iter(4, &mut assert); if failures > 0 { eprintln!("FAILURES: {failures}"); ExitCode::FAILURE From b2fa2109794eab06677fb8eed6686f74ae47ac82 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 1 Jan 2025 12:17:18 -0800 Subject: [PATCH 103/121] Add test of deeply nested prefix ranges --- tests/test_expr.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_expr.rs b/tests/test_expr.rs index d5b4232538..0943c44869 100644 --- a/tests/test_expr.rs +++ b/tests/test_expr.rs @@ -830,6 +830,7 @@ fn test_fixup() { quote! { (self.f)() }, quote! { (return)..=return }, quote! { 1 + (return)..=1 + return }, + quote! { .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. }, ] { let original: Expr = syn::parse2(tokens).unwrap(); From 417ff92f3fa7d0e718cca21f5e02a2b45570e8ba Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 1 Jan 2025 12:32:50 -0800 Subject: [PATCH 104/121] Release 2.0.94 --- Cargo.toml | 2 +- src/lib.rs | 2 +- syn.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e0b6e08bfe..dcc6c20be5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "syn" -version = "2.0.93" +version = "2.0.94" authors = ["David Tolnay "] categories = ["development-tools::procedural-macro-helpers", "parser-implementations"] description = "Parser for Rust source code" diff --git a/src/lib.rs b/src/lib.rs index 18d3a5ef1d..d20500bbdb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -249,7 +249,7 @@ //! dynamic library libproc_macro from rustc toolchain. // Syn types in rustdoc of other crates get linked to here. -#![doc(html_root_url = "https://docs.rs/syn/2.0.93")] +#![doc(html_root_url = "https://docs.rs/syn/2.0.94")] #![cfg_attr(docsrs, feature(doc_cfg))] #![deny(unsafe_op_in_unsafe_fn)] #![allow(non_camel_case_types)] diff --git a/syn.json b/syn.json index a45c6e3550..cfb55a1a10 100644 --- a/syn.json +++ b/syn.json @@ -1,5 +1,5 @@ { - "version": "2.0.93", + "version": "2.0.94", "types": [ { "ident": "Abi", From 505ab0091110a607c2c32f4fc45cb7e17f908102 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 1 Jan 2025 17:01:27 -0800 Subject: [PATCH 105/121] Fix trailing comma not inserted by rustfmt --- src/classify.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/classify.rs b/src/classify.rs index 458bbdc139..32e491dc11 100644 --- a/src/classify.rs +++ b/src/classify.rs @@ -63,7 +63,7 @@ pub(crate) fn requires_comma_to_be_match_arm(expr: &Expr) -> bool { | Expr::Tuple(_) | Expr::Unary(_) | Expr::Yield(_) - | Expr::Verbatim(_) => true + | Expr::Verbatim(_) => true, } } From 5c935d2fc27129170c51d11571f55d3f65e90fde Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 4 Jan 2025 13:31:54 -0800 Subject: [PATCH 106/121] Reuse print_subexpression outside of expr.rs --- src/expr.rs | 2 +- src/stmt.rs | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/expr.rs b/src/expr.rs index 6c244e7d3b..49dcb18c3f 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -3161,7 +3161,7 @@ pub(crate) mod printing { ); } - fn print_subexpression( + pub(crate) fn print_subexpression( expr: &Expr, needs_group: bool, tokens: &mut TokenStream, diff --git a/src/stmt.rs b/src/stmt.rs index 849afd4897..6261c7b7a9 100644 --- a/src/stmt.rs +++ b/src/stmt.rs @@ -453,11 +453,12 @@ pub(crate) mod printing { self.pat.to_tokens(tokens); if let Some(init) = &self.init { init.eq_token.to_tokens(tokens); - if init.diverge.is_some() && classify::expr_trailing_brace(&init.expr) { - token::Paren::default().surround(tokens, |tokens| init.expr.to_tokens(tokens)); - } else { - init.expr.to_tokens(tokens); - } + expr::printing::print_subexpression( + &init.expr, + init.diverge.is_some() && classify::expr_trailing_brace(&init.expr), + tokens, + FixupContext::NONE, + ); if let Some((else_token, diverge)) = &init.diverge { else_token.to_tokens(tokens); match &**diverge { From 52699de1b03821953a17090ff29d241ae0ef686d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 4 Jan 2025 20:26:44 -0800 Subject: [PATCH 107/121] Print syntax trees in test_fixup failure --- tests/test_expr.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test_expr.rs b/tests/test_expr.rs index 0943c44869..edee0bdc91 100644 --- a/tests/test_expr.rs +++ b/tests/test_expr.rs @@ -843,9 +843,11 @@ fn test_fixup() { assert!( original == reconstructed, - "original: {}\nreconstructed: {}", + "original: {}\n{:#?}\nreconstructed: {}\n{:#?}", original.to_token_stream(), + crate::macros::debug::Lite(&original), reconstructed.to_token_stream(), + crate::macros::debug::Lite(&reconstructed), ); } } From 3f8bac676ef7e81823b6eaaee5217557dee6fa0f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 4 Jan 2025 15:09:16 -0800 Subject: [PATCH 108/121] Rewrite condition printing to parenthesize leafs instead of whole cond --- src/classify.rs | 139 --------------------------------------------- src/expr.rs | 39 +++++-------- src/fixup.rs | 110 ++++++++++++++++++++++++----------- tests/test_expr.rs | 13 +++-- 4 files changed, 96 insertions(+), 205 deletions(-) diff --git a/src/classify.rs b/src/classify.rs index 32e491dc11..8eab19dbc3 100644 --- a/src/classify.rs +++ b/src/classify.rs @@ -67,145 +67,6 @@ pub(crate) fn requires_comma_to_be_match_arm(expr: &Expr) -> bool { } } -#[cfg(all(feature = "printing", feature = "full"))] -pub(crate) fn confusable_with_adjacent_block(expr: &Expr) -> bool { - let allow_struct = false; - let optional_leftmost_subexpression = false; - let rightmost_subexpression = true; - return confusable( - expr, - allow_struct, - optional_leftmost_subexpression, - rightmost_subexpression, - ); - - fn confusable( - expr: &Expr, - allow_struct: bool, - optional_leftmost_subexpression: bool, - rightmost_subexpression: bool, - ) -> bool { - match expr { - Expr::Assign(e) => { - confusable( - &e.left, - allow_struct, - optional_leftmost_subexpression, - false, - ) || confusable(&e.right, allow_struct, false, rightmost_subexpression) - } - Expr::Await(e) => confusable( - &e.base, - allow_struct, - optional_leftmost_subexpression, - false, - ), - Expr::Binary(e) => { - confusable( - &e.left, - allow_struct, - optional_leftmost_subexpression, - false, - ) || confusable(&e.right, allow_struct, false, rightmost_subexpression) - } - Expr::Block(e) => { - optional_leftmost_subexpression && e.attrs.is_empty() && e.label.is_none() - } - Expr::Break(e) => { - if let Some(value) = &e.expr { - confusable(value, true, !allow_struct, rightmost_subexpression) - } else { - allow_struct && rightmost_subexpression - } - } - Expr::Call(e) => confusable( - &e.func, - allow_struct, - optional_leftmost_subexpression, - false, - ), - Expr::Cast(e) => confusable( - &e.expr, - allow_struct, - optional_leftmost_subexpression, - false, - ), - Expr::Closure(e) => confusable(&e.body, allow_struct, false, rightmost_subexpression), - Expr::Field(e) => confusable( - &e.base, - allow_struct, - optional_leftmost_subexpression, - false, - ), - Expr::Index(e) => confusable( - &e.expr, - allow_struct, - optional_leftmost_subexpression, - false, - ), - Expr::MethodCall(e) => confusable( - &e.receiver, - allow_struct, - optional_leftmost_subexpression, - false, - ), - Expr::Path(_) => allow_struct && rightmost_subexpression, - Expr::Range(e) => { - (match &e.start { - Some(start) => { - confusable(start, allow_struct, optional_leftmost_subexpression, false) - } - None => false, - } || match &e.end { - Some(end) => { - confusable(end, allow_struct, !allow_struct, rightmost_subexpression) - } - None => allow_struct && rightmost_subexpression, - }) - } - Expr::RawAddr(e) => confusable(&e.expr, allow_struct, false, rightmost_subexpression), - Expr::Reference(e) => confusable(&e.expr, allow_struct, false, rightmost_subexpression), - Expr::Return(e) => match &e.expr { - Some(expr) => confusable(expr, true, false, rightmost_subexpression), - None => rightmost_subexpression, - }, - Expr::Struct(_) => !allow_struct, - Expr::Try(e) => confusable( - &e.expr, - allow_struct, - optional_leftmost_subexpression, - false, - ), - Expr::Unary(e) => confusable(&e.expr, allow_struct, false, rightmost_subexpression), - Expr::Yield(e) => match &e.expr { - Some(expr) => confusable(expr, true, false, rightmost_subexpression), - None => rightmost_subexpression, - }, - - Expr::Array(_) - | Expr::Async(_) - | Expr::Const(_) - | Expr::Continue(_) - | Expr::ForLoop(_) - | Expr::Group(_) - | Expr::If(_) - | Expr::Infer(_) - | Expr::Let(_) - | Expr::Lit(_) - | Expr::Loop(_) - | Expr::Macro(_) - | Expr::Match(_) - | Expr::Paren(_) - | Expr::Repeat(_) - | Expr::TryBlock(_) - | Expr::Tuple(_) - | Expr::Unsafe(_) - | Expr::Verbatim(_) - | Expr::While(_) => false, - } - } -} - #[cfg(feature = "printing")] pub(crate) fn trailing_unparameterized_path(mut ty: &Type) -> bool { loop { diff --git a/src/expr.rs b/src/expr.rs index 49dcb18c3f..03f243a057 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -3151,16 +3151,6 @@ pub(crate) mod printing { #[cfg(not(feature = "full"))] pub(crate) fn outer_attrs_to_tokens(_attrs: &[Attribute], _tokens: &mut TokenStream) {} - #[cfg(feature = "full")] - fn print_condition(expr: &Expr, tokens: &mut TokenStream) { - print_subexpression( - expr, - classify::confusable_with_adjacent_block(expr), - tokens, - FixupContext::new_condition(), - ); - } - pub(crate) fn print_subexpression( expr: &Expr, needs_group: bool, @@ -3193,7 +3183,7 @@ pub(crate) mod printing { pub(crate) fn print_expr(expr: &Expr, tokens: &mut TokenStream, mut fixup: FixupContext) { #[cfg(feature = "full")] - let needs_group = fixup.would_cause_statement_boundary(expr); + let needs_group = fixup.parenthesize(expr); #[cfg(not(feature = "full"))] let needs_group = false; @@ -3430,7 +3420,7 @@ pub(crate) mod printing { // ^---------------------------------^ e.label.is_none() && classify::expr_leading_label(value), tokens, - fixup.rightmost_subexpression_fixup(Precedence::Jump), + fixup.rightmost_subexpression_fixup(true, true, Precedence::Jump), ); } } @@ -3513,7 +3503,7 @@ pub(crate) mod printing { print_expr( &e.body, tokens, - fixup.rightmost_subexpression_fixup(Precedence::Jump), + fixup.rightmost_subexpression_fixup(false, false, Precedence::Jump), ); } else { token::Brace::default().surround(tokens, |tokens| { @@ -3574,7 +3564,7 @@ pub(crate) mod printing { self.for_token.to_tokens(tokens); self.pat.to_tokens(tokens); self.in_token.to_tokens(tokens); - print_condition(&self.expr, tokens); + print_expr(&self.expr, tokens, FixupContext::new_condition()); self.body.brace_token.surround(tokens, |tokens| { inner_attrs_to_tokens(&self.attrs, tokens); tokens.append_all(&self.body.stmts); @@ -3601,7 +3591,7 @@ pub(crate) mod printing { let mut expr = self; loop { expr.if_token.to_tokens(tokens); - print_condition(&expr.cond, tokens); + print_expr(&expr.cond, tokens, FixupContext::new_condition()); expr.then_branch.to_tokens(tokens); let (else_token, else_) = match &expr.else_branch { @@ -3682,12 +3672,8 @@ pub(crate) mod printing { e.let_token.to_tokens(tokens); e.pat.to_tokens(tokens); e.eq_token.to_tokens(tokens); - print_subexpression( - &e.expr, - fixup.needs_group_as_let_scrutinee(&e.expr), - tokens, - FixupContext::NONE, - ); + let (right_prec, right_fixup) = fixup.rightmost_subexpression(&e.expr, Precedence::Let); + print_subexpression(&e.expr, right_prec < Precedence::Let, tokens, right_fixup); } #[cfg_attr(docsrs, doc(cfg(feature = "printing")))] @@ -3726,7 +3712,7 @@ pub(crate) mod printing { fn to_tokens(&self, tokens: &mut TokenStream) { outer_attrs_to_tokens(&self.attrs, tokens); self.match_token.to_tokens(tokens); - print_condition(&self.expr, tokens); + print_expr(&self.expr, tokens, FixupContext::new_condition()); self.brace_token.surround(tokens, |tokens| { inner_attrs_to_tokens(&self.attrs, tokens); for (i, arm) in self.arms.iter().enumerate() { @@ -3811,7 +3797,8 @@ pub(crate) mod printing { } e.limits.to_tokens(tokens); if let Some(end) = &e.end { - let (right_prec, right_fixup) = fixup.rightmost_subexpression(end, Precedence::Range); + let right_fixup = fixup.rightmost_subexpression_fixup(false, true, Precedence::Range); + let right_prec = right_fixup.rightmost_subexpression_precedence(end); print_subexpression(end, right_prec <= Precedence::Range, tokens, right_fixup); } } @@ -3892,7 +3879,7 @@ pub(crate) mod printing { print_expr( expr, tokens, - fixup.rightmost_subexpression_fixup(Precedence::Jump), + fixup.rightmost_subexpression_fixup(true, false, Precedence::Jump), ); } } @@ -4003,7 +3990,7 @@ pub(crate) mod printing { outer_attrs_to_tokens(&self.attrs, tokens); self.label.to_tokens(tokens); self.while_token.to_tokens(tokens); - print_condition(&self.cond, tokens); + print_expr(&self.cond, tokens, FixupContext::new_condition()); self.body.brace_token.surround(tokens, |tokens| { inner_attrs_to_tokens(&self.attrs, tokens); tokens.append_all(&self.body.stmts); @@ -4027,7 +4014,7 @@ pub(crate) mod printing { print_expr( expr, tokens, - fixup.rightmost_subexpression_fixup(Precedence::Jump), + fixup.rightmost_subexpression_fixup(true, false, Precedence::Jump), ); } } diff --git a/src/fixup.rs b/src/fixup.rs index 2604fd86d8..126700cc8e 100644 --- a/src/fixup.rs +++ b/src/fixup.rs @@ -1,7 +1,9 @@ use crate::classify; use crate::expr::Expr; #[cfg(feature = "full")] -use crate::expr::{ExprBreak, ExprRawAddr, ExprReference, ExprReturn, ExprUnary, ExprYield}; +use crate::expr::{ + ExprBreak, ExprRange, ExprRawAddr, ExprReference, ExprReturn, ExprUnary, ExprYield, +}; use crate::precedence::Precedence; #[cfg(feature = "full")] use crate::ty::ReturnType; @@ -98,7 +100,25 @@ pub(crate) struct FixupContext { // } // #[cfg(feature = "full")] - parenthesize_exterior_struct_lit: bool, + condition: bool, + + // This is the difference between: + // + // if break Struct {} == (break) {} // needs parens + // + // if break break == Struct {} {} // no parens + // + #[cfg(feature = "full")] + rightmost_subexpression_in_condition: bool, + + // This is the difference between: + // + // if break ({ x }).field + 1 {} needs parens + // + // if break 1 + { x }.field {} // no parens + // + #[cfg(feature = "full")] + leftmost_subexpression_in_optional_operand: bool, // This is the difference between: // @@ -145,7 +165,11 @@ impl FixupContext { #[cfg(feature = "full")] leftmost_subexpression_in_match_arm: false, #[cfg(feature = "full")] - parenthesize_exterior_struct_lit: false, + condition: false, + #[cfg(feature = "full")] + rightmost_subexpression_in_condition: false, + #[cfg(feature = "full")] + leftmost_subexpression_in_optional_operand: false, #[cfg(feature = "full")] next_operator_can_begin_expr: false, #[cfg(feature = "full")] @@ -180,7 +204,8 @@ impl FixupContext { #[cfg(feature = "full")] pub fn new_condition() -> Self { FixupContext { - parenthesize_exterior_struct_lit: true, + condition: true, + rightmost_subexpression_in_condition: true, ..FixupContext::NONE } } @@ -216,6 +241,8 @@ impl FixupContext { leftmost_subexpression_in_match_arm: self.match_arm || self.leftmost_subexpression_in_match_arm, #[cfg(feature = "full")] + rightmost_subexpression_in_condition: false, + #[cfg(feature = "full")] next_operator_can_begin_expr, #[cfg(feature = "full")] next_operator_can_continue_expr: true, @@ -243,6 +270,8 @@ impl FixupContext { #[cfg(feature = "full")] leftmost_subexpression_in_match_arm: false, #[cfg(feature = "full")] + rightmost_subexpression_in_condition: false, + #[cfg(feature = "full")] next_operator_can_begin_expr: false, #[cfg(feature = "full")] next_operator_can_continue_expr: true, @@ -283,6 +312,10 @@ impl FixupContext { #[cfg(feature = "full")] precedence: Precedence, ) -> (Precedence, Self) { let fixup = self.rightmost_subexpression_fixup( + #[cfg(feature = "full")] + false, + #[cfg(feature = "full")] + false, #[cfg(feature = "full")] precedence, ); @@ -291,6 +324,8 @@ impl FixupContext { pub fn rightmost_subexpression_fixup( self, + #[cfg(feature = "full")] reset_allow_struct: bool, + #[cfg(feature = "full")] optional_operand: bool, #[cfg(feature = "full")] precedence: Precedence, ) -> Self { FixupContext { @@ -304,11 +339,15 @@ impl FixupContext { match_arm: false, #[cfg(feature = "full")] leftmost_subexpression_in_match_arm: false, + #[cfg(feature = "full")] + condition: self.condition && !reset_allow_struct, + #[cfg(feature = "full")] + leftmost_subexpression_in_optional_operand: self.condition && optional_operand, ..self } } - fn rightmost_subexpression_precedence(self, expr: &Expr) -> Precedence { + pub fn rightmost_subexpression_precedence(self, expr: &Expr) -> Precedence { let default_prec = self.precedence(expr); #[cfg(feature = "full")] @@ -332,33 +371,30 @@ impl FixupContext { } /// Determine whether parentheses are needed around the given expression to - /// head off an unintended statement boundary. - /// - /// The documentation on `FixupContext::leftmost_subexpression_in_stmt` has - /// examples. + /// head off the early termination of a statement or condition. #[cfg(feature = "full")] - pub fn would_cause_statement_boundary(self, expr: &Expr) -> bool { + pub fn parenthesize(self, expr: &Expr) -> bool { (self.leftmost_subexpression_in_stmt && !classify::requires_semi_to_be_stmt(expr)) || ((self.stmt || self.leftmost_subexpression_in_stmt) && matches!(expr, Expr::Let(_))) || (self.leftmost_subexpression_in_match_arm && !classify::requires_comma_to_be_match_arm(expr)) - } - - /// Determine whether parentheses are needed around the given `let` - /// scrutinee. - /// - /// In `if let _ = $e {}`, some examples of `$e` that would need parentheses - /// are: - /// - /// - `Struct {}.f()`, because otherwise the `{` would be misinterpreted - /// as the opening of the if's then-block. - /// - /// - `true && false`, because otherwise this would be misinterpreted as a - /// "let chain". - #[cfg(feature = "full")] - pub fn needs_group_as_let_scrutinee(self, expr: &Expr) -> bool { - self.parenthesize_exterior_struct_lit && classify::confusable_with_adjacent_block(expr) - || self.precedence(expr) < Precedence::Let + || (self.condition && matches!(expr, Expr::Struct(_))) + || (self.rightmost_subexpression_in_condition + && matches!( + expr, + Expr::Return(ExprReturn { expr: None, .. }) + | Expr::Yield(ExprYield { expr: None, .. }) + )) + || (self.rightmost_subexpression_in_condition + && !self.condition + && matches!( + expr, + Expr::Break(ExprBreak { expr: None, .. }) + | Expr::Path(_) + | Expr::Range(ExprRange { end: None, .. }) + )) + || (self.leftmost_subexpression_in_optional_operand + && matches!(expr, Expr::Block(expr) if expr.attrs.is_empty() && expr.label.is_none())) } /// Determines the effective precedence of a subexpression. Some expressions @@ -452,6 +488,9 @@ fn scan_right( fail_offset: u8, bailout_offset: u8, ) -> Scan { + if fixup.parenthesize(expr) { + return Scan::Consume; + } match expr { Expr::Assign(e) => { if match fixup.next_operator { @@ -460,7 +499,7 @@ fn scan_right( } { return Scan::Consume; } - let right_fixup = fixup.rightmost_subexpression_fixup(Precedence::Assign); + let right_fixup = fixup.rightmost_subexpression_fixup(false, false, Precedence::Assign); let scan = scan_right( &e.right, right_fixup, @@ -490,7 +529,7 @@ fn scan_right( return Scan::Consume; } let binop_prec = Precedence::of_binop(&e.op); - let right_fixup = fixup.rightmost_subexpression_fixup(binop_prec); + let right_fixup = fixup.rightmost_subexpression_fixup(false, false, binop_prec); let scan = scan_right( &e.right, right_fixup, @@ -535,7 +574,7 @@ fn scan_right( } { return Scan::Consume; } - let right_fixup = fixup.rightmost_subexpression_fixup(Precedence::Prefix); + let right_fixup = fixup.rightmost_subexpression_fixup(false, false, Precedence::Prefix); let scan = scan_right( expr, right_fixup, @@ -569,7 +608,8 @@ fn scan_right( if fail_offset >= 2 { return Scan::Consume; } - let right_fixup = fixup.rightmost_subexpression_fixup(Precedence::Range); + let right_fixup = + fixup.rightmost_subexpression_fixup(false, true, Precedence::Range); let scan = scan_right( end, right_fixup, @@ -603,7 +643,7 @@ fn scan_right( if bailout_offset >= 1 || e.label.is_none() && classify::expr_leading_label(value) { return Scan::Consume; } - let right_fixup = fixup.rightmost_subexpression_fixup(Precedence::Jump); + let right_fixup = fixup.rightmost_subexpression_fixup(true, true, Precedence::Jump); match scan_right(value, right_fixup, false, 1, 1) { Scan::Fail => Scan::Bailout, Scan::Bailout | Scan::Consume => Scan::Consume, @@ -619,7 +659,8 @@ fn scan_right( if bailout_offset >= 1 { return Scan::Consume; } - let right_fixup = fixup.rightmost_subexpression_fixup(Precedence::Jump); + let right_fixup = + fixup.rightmost_subexpression_fixup(true, false, Precedence::Jump); match scan_right(e, right_fixup, false, 1, 1) { Scan::Fail => Scan::Bailout, Scan::Bailout | Scan::Consume => Scan::Consume, @@ -637,7 +678,8 @@ fn scan_right( if bailout_offset >= 1 { return Scan::Consume; } - let right_fixup = fixup.rightmost_subexpression_fixup(Precedence::Jump); + let right_fixup = + fixup.rightmost_subexpression_fixup(false, false, Precedence::Jump); match scan_right(&e.body, right_fixup, false, 1, 1) { Scan::Fail => Scan::Bailout, Scan::Bailout | Scan::Consume => Scan::Consume, diff --git a/tests/test_expr.rs b/tests/test_expr.rs index edee0bdc91..cfbf16236f 100644 --- a/tests/test_expr.rs +++ b/tests/test_expr.rs @@ -802,25 +802,26 @@ fn test_fixup() { quote! { (&mut fut).await }, quote! { &mut (x as i32) }, quote! { -(x as i32) }, - quote! { if (S {} == 1) {} }, + quote! { if (S {}) == 1 {} }, quote! { { (m! {}) - 1 } }, quote! { match m { _ => ({}) - 1 } }, quote! { if let _ = (a && b) && c {} }, quote! { if let _ = (S {}) {} }, + quote! { if (S {}) == 0 && let Some(_) = x {} }, quote! { break ('a: loop { break 'a 1 } + 1) }, quote! { a + (|| b) + c }, quote! { if let _ = ((break) - 1 || true) {} }, quote! { if let _ = (break + 1 || true) {} }, - quote! { if (break break) {} }, + quote! { if break (break) {} }, quote! { if break break {} {} }, - quote! { if (return ..) {} }, + quote! { if return (..) {} }, quote! { if return .. {} {} }, - quote! { if (|| Struct {}) {} }, - quote! { if (|| Struct {}.await) {} }, + quote! { if || (Struct {}) {} }, + quote! { if || (Struct {}).await {} }, quote! { if break || Struct {}.await {} }, quote! { if break 'outer 'block: {} {} }, quote! { if ..'block: {} {} }, - quote! { if (break {}.await) {} }, + quote! { if break ({}).await {} }, quote! { (break)() }, quote! { (..) = () }, quote! { (..) += () }, From c7b76389008415d7b7634a3265bc8453f994275b Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 4 Jan 2025 22:02:23 -0800 Subject: [PATCH 109/121] Release 2.0.95 --- Cargo.toml | 2 +- src/lib.rs | 2 +- syn.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index dcc6c20be5..b1c26c76a8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "syn" -version = "2.0.94" +version = "2.0.95" authors = ["David Tolnay "] categories = ["development-tools::procedural-macro-helpers", "parser-implementations"] description = "Parser for Rust source code" diff --git a/src/lib.rs b/src/lib.rs index d20500bbdb..98f02ed310 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -249,7 +249,7 @@ //! dynamic library libproc_macro from rustc toolchain. // Syn types in rustdoc of other crates get linked to here. -#![doc(html_root_url = "https://docs.rs/syn/2.0.94")] +#![doc(html_root_url = "https://docs.rs/syn/2.0.95")] #![cfg_attr(docsrs, feature(doc_cfg))] #![deny(unsafe_op_in_unsafe_fn)] #![allow(non_camel_case_types)] diff --git a/syn.json b/syn.json index cfb55a1a10..cf7423bd73 100644 --- a/syn.json +++ b/syn.json @@ -1,5 +1,5 @@ { - "version": "2.0.94", + "version": "2.0.95", "types": [ { "ident": "Abi", From 533caa2266f06b2e0643ceee16479b0b5ebf3d93 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 7 Jan 2025 18:21:16 -0800 Subject: [PATCH 110/121] Updated colored dependency to v3 --- examples/dump-syntax/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/dump-syntax/Cargo.toml b/examples/dump-syntax/Cargo.toml index 6239141bb7..032a374291 100644 --- a/examples/dump-syntax/Cargo.toml +++ b/examples/dump-syntax/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" publish = false [dependencies] -colored = "2" +colored = "3" proc-macro2 = { version = "1", features = ["span-locations"] } [dependencies.syn] From ea9f750b31a83ebfd33c7a712c0e395bad5d85fb Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 8 Jan 2025 22:49:11 -0800 Subject: [PATCH 111/121] Right side of assignment can never require synthetic paren --- src/expr.rs | 25 ++++++++++++++----------- src/fixup.rs | 10 ++-------- 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/src/expr.rs b/src/expr.rs index 03f243a057..0184821a4e 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -3296,12 +3296,10 @@ pub(crate) mod printing { fixup.leftmost_subexpression_with_operator(&e.left, false, false, Precedence::Assign); print_subexpression(&e.left, left_prec <= Precedence::Range, tokens, left_fixup); e.eq_token.to_tokens(tokens); - let (right_prec, right_fixup) = fixup.rightmost_subexpression(&e.right, Precedence::Assign); - print_subexpression( + print_expr( &e.right, - right_prec < Precedence::Assign, tokens, - right_fixup, + fixup.rightmost_subexpression_fixup(false, false, Precedence::Assign), ); } @@ -3370,17 +3368,22 @@ pub(crate) mod printing { #[cfg(feature = "full")] binop_prec, ); + let left_needs_group = match binop_prec { + Precedence::Assign => left_prec <= Precedence::Range, + Precedence::Compare => left_prec <= binop_prec, + _ => left_prec < binop_prec, + }; - let (right_prec, right_fixup) = fixup.rightmost_subexpression( - &e.right, + let right_fixup = fixup.rightmost_subexpression_fixup( + #[cfg(feature = "full")] + false, + #[cfg(feature = "full")] + false, #[cfg(feature = "full")] binop_prec, ); - let (left_needs_group, right_needs_group) = match binop_prec { - Precedence::Assign => (left_prec <= Precedence::Range, right_prec < binop_prec), - Precedence::Compare => (left_prec <= binop_prec, right_prec <= binop_prec), - _ => (left_prec < binop_prec, right_prec <= binop_prec), - }; + let right_needs_group = binop_prec != Precedence::Assign + && right_fixup.rightmost_subexpression_precedence(&e.right) <= binop_prec; print_subexpression(&e.left, left_needs_group, tokens, left_fixup); e.op.to_tokens(tokens); diff --git a/src/fixup.rs b/src/fixup.rs index 126700cc8e..648fbab4dd 100644 --- a/src/fixup.rs +++ b/src/fixup.rs @@ -511,9 +511,6 @@ fn scan_right( 1, ); if let Scan::Bailout | Scan::Consume = scan { - return Scan::Consume; - } - if right_fixup.rightmost_subexpression_precedence(&e.right) < Precedence::Assign { Scan::Consume } else if let Precedence::Unambiguous = fixup.next_operator { Scan::Fail @@ -552,11 +549,8 @@ fn scan_right( } { return Scan::Consume; } - let right_prec = right_fixup.rightmost_subexpression_precedence(&e.right); - let right_needs_group = match binop_prec { - Precedence::Assign => right_prec < binop_prec, - _ => right_prec <= binop_prec, - }; + let right_needs_group = binop_prec != Precedence::Assign + && right_fixup.rightmost_subexpression_precedence(&e.right) <= binop_prec; if right_needs_group { Scan::Consume } else if let (Scan::Fail, Precedence::Unambiguous) = (scan, fixup.next_operator) { From 24ca4ddee3067aa691d92c2ab25c0cd7b8804068 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 9 Jan 2025 16:29:11 -0800 Subject: [PATCH 112/121] Disambiguate Expr::Cast precedence in scan_left --- src/fixup.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/fixup.rs b/src/fixup.rs index 648fbab4dd..e1ee2d78c8 100644 --- a/src/fixup.rs +++ b/src/fixup.rs @@ -475,6 +475,7 @@ fn scan_left(expr: &Expr, fixup: FixupContext) -> bool { Precedence::Assign => fixup.previous_operator <= Precedence::Assign, binop_prec => fixup.previous_operator < binop_prec, }, + Expr::Cast(_) => fixup.previous_operator < Precedence::Cast, Expr::Range(e) => e.start.is_none() || fixup.previous_operator < Precedence::Assign, _ => true, } From aaca8b8d11381fba8cd91ea102f41a97bdb4d0c5 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 9 Jan 2025 16:18:59 -0800 Subject: [PATCH 113/121] Choose range bailouts based on precedence --- src/fixup.rs | 99 +++++++++++++++++++++++++++------------------------- 1 file changed, 52 insertions(+), 47 deletions(-) diff --git a/src/fixup.rs b/src/fixup.rs index e1ee2d78c8..ea382657d0 100644 --- a/src/fixup.rs +++ b/src/fixup.rs @@ -285,7 +285,7 @@ impl FixupContext { fn leftmost_subexpression_precedence(self, expr: &Expr) -> Precedence { #[cfg(feature = "full")] if !self.next_operator_can_begin_expr || self.next_operator == Precedence::Range { - if let Scan::Bailout = scan_right(expr, self, false, 0, 0) { + if let Scan::Bailout = scan_right(expr, self, Precedence::MIN, 0, 0) { if scan_left(expr, self) { return Precedence::Unambiguous; } @@ -354,13 +354,8 @@ impl FixupContext { if default_prec < Precedence::Prefix && (!self.next_operator_can_begin_expr || self.next_operator == Precedence::Range) { - if let Scan::Bailout | Scan::Fail = scan_right( - expr, - self, - self.previous_operator == Precedence::Range, - 1, - 0, - ) { + if let Scan::Bailout | Scan::Fail = scan_right(expr, self, self.previous_operator, 1, 0) + { if scan_left(expr, self) { return Precedence::Prefix; } @@ -467,6 +462,13 @@ impl Clone for Scan { } } +#[cfg(feature = "full")] +impl PartialEq for Scan { + fn eq(&self, other: &Self) -> bool { + *self as u8 == *other as u8 + } +} + #[cfg(feature = "full")] fn scan_left(expr: &Expr, fixup: FixupContext) -> bool { match expr { @@ -485,12 +487,21 @@ fn scan_left(expr: &Expr, fixup: FixupContext) -> bool { fn scan_right( expr: &Expr, fixup: FixupContext, - range: bool, + precedence: Precedence, fail_offset: u8, bailout_offset: u8, ) -> Scan { + let consume_by_precedence = if match precedence { + Precedence::Assign | Precedence::Compare => precedence <= fixup.next_operator, + _ => precedence < fixup.next_operator, + } || fixup.next_operator == Precedence::MIN + { + Scan::Consume + } else { + Scan::Bailout + }; if fixup.parenthesize(expr) { - return Scan::Consume; + return consume_by_precedence; } match expr { Expr::Assign(e) => { @@ -504,7 +515,7 @@ fn scan_right( let scan = scan_right( &e.right, right_fixup, - false, + Precedence::Assign, match fixup.next_operator { Precedence::Unambiguous => fail_offset, _ => 1, @@ -521,7 +532,10 @@ fn scan_right( } Expr::Binary(e) => { if match fixup.next_operator { - Precedence::Unambiguous => fail_offset >= 2, + Precedence::Unambiguous => { + fail_offset >= 2 + && (consume_by_precedence == Scan::Consume || bailout_offset >= 1) + } _ => bailout_offset >= 1, } { return Scan::Consume; @@ -531,29 +545,22 @@ fn scan_right( let scan = scan_right( &e.right, right_fixup, - range && binop_prec != Precedence::Assign, + binop_prec, match fixup.next_operator { Precedence::Unambiguous => fail_offset, _ => 1, }, - match (binop_prec, fixup.next_operator) { - (Precedence::Assign, _) => 1, - (_, Precedence::Assign | Precedence::Range) if range => 0, - _ => 1, - }, + consume_by_precedence as u8 - Scan::Bailout as u8, ); - if match (scan, fixup.next_operator) { - (Scan::Fail, _) => false, - (Scan::Bailout, _) if binop_prec == Precedence::Assign => true, - (Scan::Bailout, Precedence::Assign | Precedence::Range) => !range, - (Scan::Bailout | Scan::Consume, _) => true, - } { - return Scan::Consume; + match scan { + Scan::Fail => {} + Scan::Bailout => return consume_by_precedence, + Scan::Consume => return Scan::Consume, } let right_needs_group = binop_prec != Precedence::Assign && right_fixup.rightmost_subexpression_precedence(&e.right) <= binop_prec; if right_needs_group { - Scan::Consume + consume_by_precedence } else if let (Scan::Fail, Precedence::Unambiguous) = (scan, fixup.next_operator) { Scan::Fail } else { @@ -564,7 +571,10 @@ fn scan_right( | Expr::Reference(ExprReference { expr, .. }) | Expr::Unary(ExprUnary { expr, .. }) => { if match fixup.next_operator { - Precedence::Unambiguous => fail_offset >= 2, + Precedence::Unambiguous => { + fail_offset >= 2 + && (consume_by_precedence == Scan::Consume || bailout_offset >= 1) + } _ => bailout_offset >= 1, } { return Scan::Consume; @@ -573,25 +583,20 @@ fn scan_right( let scan = scan_right( expr, right_fixup, - range, + precedence, match fixup.next_operator { Precedence::Unambiguous => fail_offset, _ => 1, }, - match fixup.next_operator { - Precedence::Assign | Precedence::Range if range => 0, - _ => 1, - }, + consume_by_precedence as u8 - Scan::Bailout as u8, ); - if match (scan, fixup.next_operator) { - (Scan::Fail, _) => false, - (Scan::Bailout, Precedence::Assign | Precedence::Range) => !range, - (Scan::Bailout | Scan::Consume, _) => true, - } { - return Scan::Consume; + match scan { + Scan::Fail => {} + Scan::Bailout => return consume_by_precedence, + Scan::Consume => return Scan::Consume, } if right_fixup.rightmost_subexpression_precedence(expr) < Precedence::Prefix { - Scan::Consume + consume_by_precedence } else if let (Scan::Fail, Precedence::Unambiguous) = (scan, fixup.next_operator) { Scan::Fail } else { @@ -608,7 +613,7 @@ fn scan_right( let scan = scan_right( end, right_fixup, - true, + Precedence::Range, fail_offset, match fixup.next_operator { Precedence::Assign | Precedence::Range => 0, @@ -639,13 +644,13 @@ fn scan_right( return Scan::Consume; } let right_fixup = fixup.rightmost_subexpression_fixup(true, true, Precedence::Jump); - match scan_right(value, right_fixup, false, 1, 1) { + match scan_right(value, right_fixup, Precedence::Jump, 1, 1) { Scan::Fail => Scan::Bailout, Scan::Bailout | Scan::Consume => Scan::Consume, } } None => match fixup.next_operator { - Precedence::Assign if range => Scan::Fail, + Precedence::Assign if precedence > Precedence::Assign => Scan::Fail, _ => Scan::Consume, }, }, @@ -656,13 +661,13 @@ fn scan_right( } let right_fixup = fixup.rightmost_subexpression_fixup(true, false, Precedence::Jump); - match scan_right(e, right_fixup, false, 1, 1) { + match scan_right(e, right_fixup, Precedence::Jump, 1, 1) { Scan::Fail => Scan::Bailout, Scan::Bailout | Scan::Consume => Scan::Consume, } } None => match fixup.next_operator { - Precedence::Assign if range => Scan::Fail, + Precedence::Assign if precedence > Precedence::Assign => Scan::Fail, _ => Scan::Consume, }, }, @@ -675,7 +680,7 @@ fn scan_right( } let right_fixup = fixup.rightmost_subexpression_fixup(false, false, Precedence::Jump); - match scan_right(&e.body, right_fixup, false, 1, 1) { + match scan_right(&e.body, right_fixup, Precedence::Jump, 1, 1) { Scan::Fail => Scan::Bailout, Scan::Bailout | Scan::Consume => Scan::Consume, } @@ -713,8 +718,8 @@ fn scan_right( | Expr::Unsafe(_) | Expr::Verbatim(_) | Expr::While(_) => match fixup.next_operator { - Precedence::Assign | Precedence::Range if range => Scan::Fail, - _ => Scan::Consume, + Precedence::Assign | Precedence::Range if precedence == Precedence::Range => Scan::Fail, + _ => consume_by_precedence, }, } } From 00a125eeeeb9f714bc55dcbe058985bedb636154 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 9 Jan 2025 17:02:10 -0800 Subject: [PATCH 114/121] Short-circuit precedence scan for high-precedence expressions --- src/fixup.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/fixup.rs b/src/fixup.rs index ea382657d0..9414d83845 100644 --- a/src/fixup.rs +++ b/src/fixup.rs @@ -351,9 +351,15 @@ impl FixupContext { let default_prec = self.precedence(expr); #[cfg(feature = "full")] - if default_prec < Precedence::Prefix - && (!self.next_operator_can_begin_expr || self.next_operator == Precedence::Range) - { + if match self.previous_operator { + Precedence::Assign | Precedence::Let | Precedence::Prefix => { + default_prec < self.previous_operator + } + _ => default_prec <= self.previous_operator, + } && match self.next_operator { + Precedence::Range => true, + _ => !self.next_operator_can_begin_expr, + } { if let Scan::Bailout | Scan::Fail = scan_right(expr, self, self.previous_operator, 1, 0) { if scan_left(expr, self) { From d3f2879241b90a12182d1d78a04fc91088c584e0 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 9 Jan 2025 17:09:48 -0800 Subject: [PATCH 115/121] Add regression test for chained comparisons with bailout thread 'test_fixup' panicked at tests/test_expr.rs:843:25: failed to parse `x > .. > x`: comparison operators cannot be chained note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace --- tests/test_expr.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_expr.rs b/tests/test_expr.rs index cfbf16236f..226c044db8 100644 --- a/tests/test_expr.rs +++ b/tests/test_expr.rs @@ -799,6 +799,7 @@ fn test_fixup() { quote! { (1 + 1).abs() }, quote! { (lo..hi)[..] }, quote! { (a..b)..(c..d) }, + quote! { (x > ..) > x }, quote! { (&mut fut).await }, quote! { &mut (x as i32) }, quote! { -(x as i32) }, From fd198d9b0c9cd850666618156758f78df45d73e0 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 9 Jan 2025 17:07:49 -0800 Subject: [PATCH 116/121] Generalize Expr::Range bailout to any operator that can begin expr --- src/fixup.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/fixup.rs b/src/fixup.rs index 9414d83845..2576ec365e 100644 --- a/src/fixup.rs +++ b/src/fixup.rs @@ -639,10 +639,13 @@ fn scan_right( Scan::Fail } } - None => match fixup.next_operator { - Precedence::Range => Scan::Consume, - _ => Scan::Fail, - }, + None => { + if fixup.next_operator_can_begin_expr { + Scan::Consume + } else { + Scan::Fail + } + } }, Expr::Break(e) => match &e.expr { Some(value) => { From 49443622bd2ff2986554ca58d7ee13436d784c22 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 9 Jan 2025 17:11:12 -0800 Subject: [PATCH 117/121] Fix parenthesization of chained comparisons containing bailout --- src/fixup.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/fixup.rs b/src/fixup.rs index 2576ec365e..e49f811318 100644 --- a/src/fixup.rs +++ b/src/fixup.rs @@ -547,6 +547,9 @@ fn scan_right( return Scan::Consume; } let binop_prec = Precedence::of_binop(&e.op); + if binop_prec == Precedence::Compare && fixup.next_operator == Precedence::Compare { + return Scan::Consume; + } let right_fixup = fixup.rightmost_subexpression_fixup(false, false, binop_prec); let scan = scan_right( &e.right, From 96175bea89134a416dfd7f8db93a4c157187020a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 5 Jan 2025 21:08:07 -0800 Subject: [PATCH 118/121] Integrate Expr::Let into precedence fixups --- src/fixup.rs | 37 +++++++++++++++++++++++++++++++++++-- tests/test_expr.rs | 18 ++++++++++++++++-- 2 files changed, 51 insertions(+), 4 deletions(-) diff --git a/src/fixup.rs b/src/fixup.rs index e49f811318..b0973df258 100644 --- a/src/fixup.rs +++ b/src/fixup.rs @@ -357,7 +357,7 @@ impl FixupContext { } _ => default_prec <= self.previous_operator, } && match self.next_operator { - Precedence::Range => true, + Precedence::Range | Precedence::Or | Precedence::And => true, _ => !self.next_operator_can_begin_expr, } { if let Scan::Bailout | Scan::Fail = scan_right(expr, self, self.previous_operator, 1, 0) @@ -700,6 +700,37 @@ fn scan_right( Scan::Consume } } + Expr::Let(e) => { + if bailout_offset >= 1 { + return Scan::Consume; + } + let right_fixup = fixup.rightmost_subexpression_fixup(false, false, Precedence::Let); + let scan = scan_right( + &e.expr, + right_fixup, + Precedence::Let, + 1, + if fixup.next_operator < Precedence::Let { + 0 + } else { + 1 + }, + ); + match scan { + Scan::Fail | Scan::Bailout if fixup.next_operator < Precedence::Let => { + return Scan::Bailout; + } + Scan::Consume => return Scan::Consume, + _ => {} + } + if right_fixup.rightmost_subexpression_precedence(&e.expr) < Precedence::Let { + Scan::Consume + } else if let Scan::Fail = scan { + Scan::Bailout + } else { + Scan::Consume + } + } Expr::Array(_) | Expr::Async(_) | Expr::Await(_) @@ -714,7 +745,6 @@ fn scan_right( | Expr::If(_) | Expr::Index(_) | Expr::Infer(_) - | Expr::Let(_) | Expr::Lit(_) | Expr::Loop(_) | Expr::Macro(_) @@ -731,6 +761,9 @@ fn scan_right( | Expr::Verbatim(_) | Expr::While(_) => match fixup.next_operator { Precedence::Assign | Precedence::Range if precedence == Precedence::Range => Scan::Fail, + _ if precedence == Precedence::Let && fixup.next_operator < Precedence::Let => { + Scan::Fail + } _ => consume_by_precedence, }, } diff --git a/tests/test_expr.rs b/tests/test_expr.rs index 226c044db8..67d5f0dd41 100644 --- a/tests/test_expr.rs +++ b/tests/test_expr.rs @@ -24,8 +24,8 @@ use syn::visit_mut::VisitMut as _; use syn::{ parse_quote, token, AngleBracketedGenericArguments, Arm, BinOp, Block, Expr, ExprArray, ExprAssign, ExprAsync, ExprAwait, ExprBinary, ExprBlock, ExprBreak, ExprCall, ExprCast, - ExprClosure, ExprConst, ExprContinue, ExprField, ExprForLoop, ExprIf, ExprIndex, ExprLit, - ExprLoop, ExprMacro, ExprMatch, ExprMethodCall, ExprPath, ExprRange, ExprRawAddr, + ExprClosure, ExprConst, ExprContinue, ExprField, ExprForLoop, ExprIf, ExprIndex, ExprLet, + ExprLit, ExprLoop, ExprMacro, ExprMatch, ExprMethodCall, ExprPath, ExprRange, ExprRawAddr, ExprReference, ExprReturn, ExprStruct, ExprTry, ExprTryBlock, ExprTuple, ExprUnary, ExprUnsafe, ExprWhile, ExprYield, GenericArgument, Label, Lifetime, Lit, LitInt, Macro, MacroDelimiter, Member, Pat, PatWild, Path, PathArguments, PathSegment, PointerMutability, QSelf, RangeLimits, @@ -1076,6 +1076,20 @@ fn test_permutations() -> ExitCode { })); }); + // Expr::Let + iter(depth, &mut |expr| { + f(Expr::Let(ExprLet { + attrs: Vec::new(), + let_token: Token![let](span), + pat: Box::new(Pat::Wild(PatWild { + attrs: Vec::new(), + underscore_token: Token![_](span), + })), + eq_token: Token![=](span), + expr: Box::new(expr), + })); + }); + // Expr::Range f(Expr::Range(ExprRange { // `..` From d1cbce8ef483a182c8d584ea88c248ffe89b6c96 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 9 Jan 2025 17:42:10 -0800 Subject: [PATCH 119/121] Release 2.0.96 --- Cargo.toml | 2 +- src/lib.rs | 2 +- syn.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b1c26c76a8..879ace9650 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "syn" -version = "2.0.95" +version = "2.0.96" authors = ["David Tolnay "] categories = ["development-tools::procedural-macro-helpers", "parser-implementations"] description = "Parser for Rust source code" diff --git a/src/lib.rs b/src/lib.rs index 98f02ed310..96132e785d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -249,7 +249,7 @@ //! dynamic library libproc_macro from rustc toolchain. // Syn types in rustdoc of other crates get linked to here. -#![doc(html_root_url = "https://docs.rs/syn/2.0.95")] +#![doc(html_root_url = "https://docs.rs/syn/2.0.96")] #![cfg_attr(docsrs, feature(doc_cfg))] #![deny(unsafe_op_in_unsafe_fn)] #![allow(non_camel_case_types)] diff --git a/syn.json b/syn.json index cf7423bd73..1be1b648fc 100644 --- a/syn.json +++ b/syn.json @@ -1,5 +1,5 @@ { - "version": "2.0.95", + "version": "2.0.96", "types": [ { "ident": "Abi", From dc1c0123f8989ac5fb2a80cf087361b261350fd3 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 9 Jan 2025 17:50:47 -0800 Subject: [PATCH 120/121] Update test suite to nightly-2025-01-10 --- tests/common/eq.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/common/eq.rs b/tests/common/eq.rs index 6a74c0263b..bae9b30d0a 100644 --- a/tests/common/eq.rs +++ b/tests/common/eq.rs @@ -648,7 +648,7 @@ spanless_eq_enum!(ItemKind; ExternCrate(0) Use(0) Static(0) Const(0) Fn(0) spanless_eq_enum!(LitKind; Str(0 1) ByteStr(0 1) CStr(0 1) Byte(0) Char(0) Int(0 1) Float(0 1) Bool(0) Err(0)); spanless_eq_enum!(PatKind; Wild Ident(0 1 2) Struct(0 1 2 3) TupleStruct(0 1 2) - Or(0) Path(0 1) Tuple(0) Box(0) Deref(0) Ref(0 1) Lit(0) Range(0 1 2) + Or(0) Path(0 1) Tuple(0) Box(0) Deref(0) Ref(0 1) Expr(0) Range(0 1 2) Slice(0) Rest Never Guard(0 1) Paren(0) MacCall(0) Err(0)); spanless_eq_enum!(TyKind; Slice(0) Array(0 1) Ptr(0) Ref(0 1) PinnedRef(0 1) BareFn(0) UnsafeBinder(0) Never Tup(0) Path(0 1) TraitObject(0 1) From 42601b92eb9c4d263683a9c7aa192a4f44683c6f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 10 Jan 2025 19:37:51 -0800 Subject: [PATCH 121/121] Resolve needless_continue pedantic clippy lint warning: this `continue` expression is redundant --> src/expr.rs:2246:21 | 2246 | continue; | ^^^^^^^^ | = help: consider dropping the `continue` expression = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_continue = note: `-W clippy::needless-continue` implied by `-W clippy::pedantic` = help: to override `-W clippy::pedantic` add `#[allow(clippy::needless_continue)]` --- src/expr.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/expr.rs b/src/expr.rs index 0184821a4e..07024e4ff5 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -2243,7 +2243,6 @@ pub(crate) mod parsing { if lookahead.peek(Token![if]) { expr.else_branch = Some((else_token, Box::new(Expr::PLACEHOLDER))); clauses.push(expr); - continue; } else if lookahead.peek(token::Brace) { expr.else_branch = Some(( else_token,