diff options
author | Chih-hung Hsieh <chh@google.com> | 2020-07-14 04:53:00 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2020-07-14 04:53:00 +0000 |
commit | 3aa702ba3984180742838781e236e69c3fb9f091 (patch) | |
tree | 39efb9103d47f1fd46d0b8a029861743c1b178f6 | |
parent | d8760cdd5d6bf9ab4daed1d496bfab12eb314150 (diff) | |
parent | cdfc27f09f3073e670a8b20eb6be5eba5b6bc940 (diff) | |
download | syn-3aa702ba3984180742838781e236e69c3fb9f091.tar.gz |
Merge "Upgrade rust/crates/syn to 1.0.33" am: c444e1327a am: cdfc27f09f
Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/syn/+/1360598
Change-Id: Ib8397a5521c72c5631ef006feb2276f692df1892
-rw-r--r-- | .cargo_vcs_info.json | 2 | ||||
-rw-r--r-- | Cargo.toml | 2 | ||||
-rw-r--r-- | Cargo.toml.orig | 2 | ||||
-rw-r--r-- | METADATA | 6 | ||||
-rw-r--r-- | README.md | 2 | ||||
-rw-r--r-- | src/attr.rs | 8 | ||||
-rw-r--r-- | src/error.rs | 4 | ||||
-rw-r--r-- | src/expr.rs | 253 | ||||
-rw-r--r-- | src/lib.rs | 10 | ||||
-rw-r--r-- | src/parse.rs | 17 | ||||
-rw-r--r-- | src/parse_macro_input.rs | 4 | ||||
-rw-r--r-- | src/token.rs | 60 | ||||
-rw-r--r-- | tests/.gitignore | 1 | ||||
-rw-r--r-- | tests/common/eq.rs | 6 | ||||
-rw-r--r-- | tests/test_expr.rs | 139 | ||||
-rw-r--r-- | tests/test_item.rs | 45 | ||||
-rw-r--r-- | tests/test_parse_stream.rs | 12 |
17 files changed, 442 insertions, 131 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json index 0808224b..70d33e9a 100644 --- a/.cargo_vcs_info.json +++ b/.cargo_vcs_info.json @@ -1,5 +1,5 @@ { "git": { - "sha1": "89658fc242ee17041a4e2dd74b455f8d89d5de31" + "sha1": "ab00f4841e5cdd463891fd4612cf1230bd69f613" } } @@ -13,7 +13,7 @@ [package] edition = "2018" name = "syn" -version = "1.0.30" +version = "1.0.33" authors = ["David Tolnay <dtolnay@gmail.com>"] include = ["/benches/**", "/build.rs", "/Cargo.toml", "/LICENSE-APACHE", "/LICENSE-MIT", "/README.md", "/src/**", "/tests/**"] description = "Parser for Rust source code" diff --git a/Cargo.toml.orig b/Cargo.toml.orig index 55d82eda..51d629dd 100644 --- a/Cargo.toml.orig +++ b/Cargo.toml.orig @@ -1,6 +1,6 @@ [package] name = "syn" -version = "1.0.30" # don't forget to update html_root_url and syn.json +version = "1.0.33" # don't forget to update html_root_url and syn.json authors = ["David Tolnay <dtolnay@gmail.com>"] license = "MIT OR Apache-2.0" description = "Parser for Rust source code" @@ -9,11 +9,11 @@ third_party { type: GIT value: "https://github.com/dtolnay/syn" } - version: "1.0.30" + version: "1.0.33" license_type: NOTICE last_upgrade_date { year: 2020 - month: 6 - day: 2 + month: 7 + day: 10 } } @@ -88,8 +88,6 @@ proc-macro = true ``` ```rust -extern crate proc_macro; - use proc_macro::TokenStream; use quote::quote; use syn::{parse_macro_input, DeriveInput}; diff --git a/src/attr.rs b/src/attr.rs index d8a9d877..2a4bced1 100644 --- a/src/attr.rs +++ b/src/attr.rs @@ -263,7 +263,7 @@ impl Attribute { #[cfg(feature = "parsing")] pub fn parse_outer(input: ParseStream) -> Result<Vec<Self>> { let mut attrs = Vec::new(); - while input.peek(Token![#]) && !input.peek(token::Group) { + while input.peek(Token![#]) { attrs.push(input.call(parsing::single_parse_outer)?); } Ok(attrs) @@ -276,7 +276,7 @@ impl Attribute { #[cfg(feature = "parsing")] pub fn parse_inner(input: ParseStream) -> Result<Vec<Self>> { let mut attrs = Vec::new(); - while input.peek(Token![#]) && input.peek2(Token![!]) && !input.peek(token::Group) { + while input.peek(Token![#]) && input.peek2(Token![!]) { attrs.push(input.call(parsing::single_parse_inner)?); } Ok(attrs) @@ -470,8 +470,8 @@ ast_enum_of_structs! { /// as type `AttributeArgs`. /// /// ``` -/// extern crate proc_macro; -/// +/// # extern crate proc_macro; +/// # /// use proc_macro::TokenStream; /// use syn::{parse_macro_input, AttributeArgs, ItemFn}; /// diff --git a/src/error.rs b/src/error.rs index 545c47e8..d155f984 100644 --- a/src/error.rs +++ b/src/error.rs @@ -31,8 +31,8 @@ pub type Result<T> = std::result::Result<T, Error>; /// conversion to `compile_error!` automatically. /// /// ``` -/// extern crate proc_macro; -/// +/// # extern crate proc_macro; +/// # /// use proc_macro::TokenStream; /// use syn::{parse_macro_input, AttributeArgs, ItemFn}; /// diff --git a/src/expr.rs b/src/expr.rs index d98cc612..2b51dfbc 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -1210,7 +1210,6 @@ pub(crate) fn requires_terminator(expr: &Expr) -> bool { pub(crate) mod parsing { use super::*; - use crate::parse::discouraged::Speculative; use crate::parse::{Parse, ParseStream, Result}; use crate::path; @@ -1278,9 +1277,91 @@ pub(crate) mod parsing { } } - #[cfg(feature = "full")] - fn expr_no_struct(input: ParseStream) -> Result<Expr> { - ambiguous_expr(input, AllowStruct(false)) + impl Expr { + /// An alternative to the primary `Expr::parse` parser (from the + /// [`Parse`] trait) for ambiguous syntactic positions in which a + /// trailing brace should not be taken as part of the expression. + /// + /// Rust grammar has an ambiguity where braces sometimes turn a path + /// expression into a struct initialization and sometimes do not. In the + /// following code, the expression `S {}` is one expression. Presumably + /// there is an empty struct `struct S {}` defined somewhere which it is + /// instantiating. + /// + /// ``` + /// # struct S; + /// # impl std::ops::Deref for S { + /// # type Target = bool; + /// # fn deref(&self) -> &Self::Target { + /// # &true + /// # } + /// # } + /// let _ = *S {}; + /// + /// // parsed by rustc as: `*(S {})` + /// ``` + /// + /// We would want to parse the above using `Expr::parse` after the `=` + /// token. + /// + /// But in the following, `S {}` is *not* a struct init expression. + /// + /// ``` + /// # const S: &bool = &true; + /// if *S {} {} + /// + /// // parsed by rustc as: + /// // + /// // if (*S) { + /// // /* empty block */ + /// // } + /// // { + /// // /* another empty block */ + /// // } + /// ``` + /// + /// For that reason we would want to parse if-conditions using + /// `Expr::parse_without_eager_brace` after the `if` token. Same for + /// similar syntactic positions such as the condition expr after a + /// `while` token or the expr at the top of a `match`. + /// + /// The Rust grammar's choices around which way this ambiguity is + /// resolved at various syntactic positions is fairly arbitrary. Really + /// either parse behavior could work in most positions, and language + /// designers just decide each case based on which is more likely to be + /// what the programmer had in mind most of the time. + /// + /// ``` + /// # struct S; + /// # fn doc() -> S { + /// if return S {} {} + /// # unreachable!() + /// # } + /// + /// // parsed by rustc as: + /// // + /// // if (return (S {})) { + /// // } + /// // + /// // but could equally well have been this other arbitrary choice: + /// // + /// // if (return S) { + /// // } + /// // {} + /// ``` + /// + /// Note the grammar ambiguity on trailing braces is distinct from + /// precedence and is not captured by assigning a precedence level to + /// the braced struct init expr in relation to other operators. This can + /// be illustrated by `return 0..S {}` vs `match 0..S {}`. The former + /// parses as `return (0..(S {}))` implying tighter precedence for + /// struct init than `..`, while the latter parses as `match (0..S) {}` + /// implying tighter precedence for `..` than struct init, a + /// contradiction. + #[cfg(feature = "full")] + pub fn parse_without_eager_brace(input: ParseStream) -> Result<Expr> { + ambiguous_expr(input, AllowStruct(false)) + } } #[cfg(feature = "full")] @@ -1462,6 +1543,30 @@ pub(crate) mod parsing { parse_expr(input, lhs, allow_struct, Precedence::Any) } + #[cfg(feature = "full")] + fn expr_attrs(input: ParseStream) -> Result<Vec<Attribute>> { + let mut attrs = Vec::new(); + loop { + if input.peek(token::Group) { + let ahead = input.fork(); + let group = crate::group::parse_group(&ahead)?; + if !group.content.peek(Token![#]) || group.content.peek2(Token![!]) { + break; + } + let attr = group.content.call(attr::parsing::single_parse_outer)?; + if !group.content.is_empty() { + break; + } + attrs.push(attr); + } else if input.peek(Token![#]) { + attrs.push(input.call(attr::parsing::single_parse_outer)?); + } else { + break; + } + } + Ok(attrs) + } + // <UnOp> <trailer> // & <trailer> // &mut <trailer> @@ -1469,66 +1574,53 @@ pub(crate) mod parsing { #[cfg(feature = "full")] fn unary_expr(input: ParseStream, allow_struct: AllowStruct) -> Result<Expr> { let begin = input.fork(); - let ahead = input.fork(); - let attrs = ahead.call(Attribute::parse_outer)?; - if ahead.peek(Token![&]) - || ahead.peek(Token![box]) - || ahead.peek(Token![*]) - || ahead.peek(Token![!]) - || ahead.peek(Token![-]) - { - input.advance_to(&ahead); - if input.peek(Token![&]) { - let and_token: Token![&] = input.parse()?; - let raw: Option<raw> = if input.peek(raw) - && (input.peek2(Token![mut]) || input.peek2(Token![const])) - { + let attrs = input.call(expr_attrs)?; + if input.peek(Token![&]) { + let and_token: Token![&] = input.parse()?; + let raw: Option<raw> = + if input.peek(raw) && (input.peek2(Token![mut]) || input.peek2(Token![const])) { Some(input.parse()?) } else { None }; - let mutability: Option<Token![mut]> = input.parse()?; - if raw.is_some() && mutability.is_none() { - input.parse::<Token![const]>()?; - } - let expr = Box::new(unary_expr(input, allow_struct)?); - if raw.is_some() { - Ok(Expr::Verbatim(verbatim::between(begin, input))) - } else { - Ok(Expr::Reference(ExprReference { - attrs, - and_token, - raw: Reserved::default(), - mutability, - expr, - })) - } - } else if input.peek(Token![box]) { - Ok(Expr::Box(ExprBox { - attrs, - box_token: input.parse()?, - expr: Box::new(unary_expr(input, allow_struct)?), - })) + let mutability: Option<Token![mut]> = input.parse()?; + if raw.is_some() && mutability.is_none() { + input.parse::<Token![const]>()?; + } + let expr = Box::new(unary_expr(input, allow_struct)?); + if raw.is_some() { + Ok(Expr::Verbatim(verbatim::between(begin, input))) } else { - Ok(Expr::Unary(ExprUnary { + Ok(Expr::Reference(ExprReference { attrs, - op: input.parse()?, - expr: Box::new(unary_expr(input, allow_struct)?), + and_token, + raw: Reserved::default(), + mutability, + expr, })) } + } else if input.peek(Token![box]) { + Ok(Expr::Box(ExprBox { + attrs, + box_token: input.parse()?, + expr: Box::new(unary_expr(input, allow_struct)?), + })) + } else if input.peek(Token![*]) || input.peek(Token![!]) || input.peek(Token![-]) { + Ok(Expr::Unary(ExprUnary { + attrs, + op: input.parse()?, + expr: Box::new(unary_expr(input, allow_struct)?), + })) } else { - trailer_expr(input, allow_struct) + trailer_expr(attrs, input, allow_struct) } } #[cfg(not(feature = "full"))] fn unary_expr(input: ParseStream, allow_struct: AllowStruct) -> Result<Expr> { - let ahead = input.fork(); - let attrs = ahead.call(Attribute::parse_outer)?; - if ahead.peek(Token![*]) || ahead.peek(Token![!]) || ahead.peek(Token![-]) { - input.advance_to(&ahead); + if input.peek(Token![*]) || input.peek(Token![!]) || input.peek(Token![-]) { Ok(Expr::Unary(ExprUnary { - attrs, + attrs: Vec::new(), op: input.parse()?, expr: Box::new(unary_expr(input, allow_struct)?), })) @@ -1544,9 +1636,11 @@ pub(crate) mod parsing { // <atom> [ <expr> ] ... // <atom> ? ... #[cfg(feature = "full")] - fn trailer_expr(input: ParseStream, allow_struct: AllowStruct) -> Result<Expr> { - let outer_attrs = input.call(Attribute::parse_outer)?; - + fn trailer_expr( + outer_attrs: Vec<Attribute>, + input: ParseStream, + allow_struct: AllowStruct, + ) -> Result<Expr> { let atom = atom_expr(input, allow_struct)?; let mut e = trailer_helper(input, atom)?; @@ -1570,16 +1664,23 @@ pub(crate) mod parsing { } else if input.peek(Token![.]) && !input.peek(Token![..]) { let dot_token: Token![.] = input.parse()?; - if input.peek(token::Await) { + let await_token: Option<token::Await> = input.parse()?; + if let Some(await_token) = await_token { e = Expr::Await(ExprAwait { attrs: Vec::new(), base: Box::new(e), dot_token, - await_token: input.parse()?, + await_token, }); continue; } + let float_token: Option<LitFloat> = input.parse()?; + if let Some(float_token) = float_token { + e = multi_index(e, dot_token, float_token)?; + continue; + } + let member: Member = input.parse()?; let turbofish = if member.is_named() && input.peek(Token![::]) { Some(MethodTurbofish { @@ -1665,12 +1766,18 @@ pub(crate) mod parsing { }); } else if input.peek(Token![.]) && !input.peek(Token![..]) && !input.peek2(token::Await) { - e = Expr::Field(ExprField { - attrs: Vec::new(), - base: Box::new(e), - dot_token: input.parse()?, - member: input.parse()?, - }); + let dot_token: Token![.] = input.parse()?; + let float_token: Option<LitFloat> = input.parse()?; + if let Some(float_token) = float_token { + e = multi_index(e, dot_token, float_token)?; + } else { + e = Expr::Field(ExprField { + attrs: Vec::new(), + base: Box::new(e), + dot_token: input.parse()?, + member: input.parse()?, + }); + } } else if input.peek(token::Bracket) { let content; e = Expr::Index(ExprIndex { @@ -1925,7 +2032,7 @@ pub(crate) mod parsing { #[cfg(feature = "full")] pub(crate) fn expr_early(input: ParseStream) -> Result<Expr> { - let mut attrs = input.call(Attribute::parse_outer)?; + let mut attrs = input.call(expr_attrs)?; let mut expr = if input.peek(Token![if]) { Expr::If(input.parse()?) } else if input.peek(Token![while]) { @@ -2018,7 +2125,7 @@ pub(crate) mod parsing { let_token: input.parse()?, pat: pat::parsing::multi_pat_with_leading_vert(input)?, eq_token: input.parse()?, - expr: Box::new(input.call(expr_no_struct)?), + expr: Box::new(input.call(Expr::parse_without_eager_brace)?), }) } @@ -2029,7 +2136,7 @@ pub(crate) mod parsing { Ok(ExprIf { attrs, if_token: input.parse()?, - cond: Box::new(input.call(expr_no_struct)?), + cond: Box::new(input.call(Expr::parse_without_eager_brace)?), then_branch: input.parse()?, else_branch: { if input.peek(Token![else]) { @@ -2072,7 +2179,7 @@ pub(crate) mod parsing { let pat = pat::parsing::multi_pat_with_leading_vert(input)?; let in_token: Token![in] = input.parse()?; - let expr: Expr = input.call(expr_no_struct)?; + let expr: Expr = input.call(Expr::parse_without_eager_brace)?; let content; let brace_token = braced!(content in input); @@ -2117,7 +2224,7 @@ pub(crate) mod parsing { fn parse(input: ParseStream) -> Result<Self> { let outer_attrs = input.call(Attribute::parse_outer)?; let match_token: Token![match] = input.parse()?; - let expr = expr_no_struct(input)?; + let expr = Expr::parse_without_eager_brace(input)?; let content; let brace_token = braced!(content in input); @@ -2327,7 +2434,7 @@ pub(crate) mod parsing { let outer_attrs = input.call(Attribute::parse_outer)?; let label: Option<Label> = input.parse()?; let while_token: Token![while] = input.parse()?; - let cond = expr_no_struct(input)?; + let cond = Expr::parse_without_eager_brace(input)?; let content; let brace_token = braced!(content in input); @@ -2632,6 +2739,20 @@ pub(crate) mod parsing { } } + fn multi_index(mut e: Expr, mut dot_token: Token![.], float: LitFloat) -> Result<Expr> { + for part in float.to_string().split('.') { + let index = crate::parse_str(part).map_err(|err| Error::new(float.span(), err))?; + e = Expr::Field(ExprField { + attrs: Vec::new(), + base: Box::new(e), + dot_token, + member: Member::Unnamed(index), + }); + dot_token = Token![.](float.span()); + } + Ok(e) + } + #[cfg(feature = "full")] impl Member { fn is_named(&self) -> bool { @@ -70,8 +70,8 @@ //! ``` //! //! ``` -//! extern crate proc_macro; -//! +//! # extern crate proc_macro; +//! # //! use proc_macro::TokenStream; //! use quote::quote; //! use syn::{parse_macro_input, DeriveInput}; @@ -250,7 +250,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/1.0.30")] +#![doc(html_root_url = "https://docs.rs/syn/1.0.33")] #![deny(clippy::all, clippy::pedantic)] // Ignored clippy lints. #![allow( @@ -820,8 +820,8 @@ pub use crate::error::{Error, Result}; /// # Examples /// /// ``` -/// extern crate proc_macro; -/// +/// # extern crate proc_macro; +/// # /// use proc_macro::TokenStream; /// use quote::quote; /// use syn::DeriveInput; diff --git a/src/parse.rs b/src/parse.rs index f9ef69cd..dffc2ad3 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -26,8 +26,8 @@ //! [`parse_macro_input!`]: ../macro.parse_macro_input.html //! //! ``` -//! extern crate proc_macro; -//! +//! # extern crate proc_macro; +//! # //! use proc_macro::TokenStream; //! use syn::{braced, parse_macro_input, token, Field, Ident, Result, Token}; //! use syn::parse::{Parse, ParseStream}; @@ -153,8 +153,8 @@ //! [`Parser`]: trait.Parser.html //! //! ``` -//! extern crate proc_macro; -//! +//! # extern crate proc_macro; +//! # //! use proc_macro::TokenStream; //! use syn::parse::Parser; //! use syn::punctuated::Punctuated; @@ -215,6 +215,11 @@ pub use crate::lookahead::{Lookahead1, Peek}; /// Parsing interface implemented by all types that can be parsed in a default /// way from a token stream. +/// +/// Refer to the [module documentation] for details about implementing and using +/// the `Parse` trait. +/// +/// [module documentation]: self pub trait Parse: Sized { fn parse(input: ParseStream) -> Result<Self>; } @@ -1212,8 +1217,8 @@ pub(crate) fn parse_stream<F: Parser>(f: F, input: ParseStream) -> Result<F::Out /// provided any attribute args. /// /// ``` -/// extern crate proc_macro; -/// +/// # extern crate proc_macro; +/// # /// use proc_macro::TokenStream; /// use syn::parse_macro_input; /// use syn::parse::Nothing; diff --git a/src/parse_macro_input.rs b/src/parse_macro_input.rs index 5db3ad18..fbf92701 100644 --- a/src/parse_macro_input.rs +++ b/src/parse_macro_input.rs @@ -16,8 +16,8 @@ /// #\[proc_macro_attribute\] attribute. /// /// ``` -/// extern crate proc_macro; -/// +/// # extern crate proc_macro; +/// # /// use proc_macro::TokenStream; /// use syn::{parse_macro_input, Result}; /// use syn::parse::{Parse, ParseStream}; diff --git a/src/token.rs b/src/token.rs index 0b4ad88a..d23f126b 100644 --- a/src/token.rs +++ b/src/token.rs @@ -96,13 +96,13 @@ use std::fmt::{self, Debug}; use std::hash::{Hash, Hasher}; use std::ops::{Deref, DerefMut}; -#[cfg(feature = "parsing")] -use proc_macro2::Delimiter; #[cfg(any(feature = "parsing", feature = "printing"))] use proc_macro2::Ident; use proc_macro2::Span; #[cfg(feature = "printing")] use proc_macro2::TokenStream; +#[cfg(feature = "parsing")] +use proc_macro2::{Delimiter, Literal, Punct, TokenTree}; #[cfg(feature = "printing")] use quote::{ToTokens, TokenStreamExt}; @@ -111,10 +111,8 @@ use self::private::WithSpan; use crate::buffer::Cursor; #[cfg(feature = "parsing")] use crate::error::Result; -#[cfg(any(feature = "full", feature = "derive"))] #[cfg(feature = "parsing")] use crate::lifetime::Lifetime; -#[cfg(any(feature = "full", feature = "derive"))] #[cfg(feature = "parsing")] use crate::lit::{Lit, LitBool, LitByte, LitByteStr, LitChar, LitFloat, LitInt, LitStr}; #[cfg(feature = "parsing")] @@ -154,7 +152,6 @@ mod private { #[cfg(feature = "parsing")] impl private::Sealed for Ident {} -#[cfg(any(feature = "full", feature = "derive"))] #[cfg(feature = "parsing")] fn peek_impl(cursor: Cursor, peek: fn(ParseStream) -> bool) -> bool { use crate::parse::Unexpected; @@ -167,9 +164,8 @@ fn peek_impl(cursor: Cursor, peek: fn(ParseStream) -> bool) -> bool { peek(&buffer) } -#[cfg(any(feature = "full", feature = "derive"))] macro_rules! impl_token { - ($name:ident $display:expr) => { + ($display:tt $name:ty) => { #[cfg(feature = "parsing")] impl Token for $name { fn peek(cursor: Cursor) -> bool { @@ -189,24 +185,38 @@ macro_rules! impl_token { }; } -#[cfg(any(feature = "full", feature = "derive"))] -impl_token!(Lifetime "lifetime"); -#[cfg(any(feature = "full", feature = "derive"))] -impl_token!(Lit "literal"); -#[cfg(any(feature = "full", feature = "derive"))] -impl_token!(LitStr "string literal"); -#[cfg(any(feature = "full", feature = "derive"))] -impl_token!(LitByteStr "byte string literal"); -#[cfg(any(feature = "full", feature = "derive"))] -impl_token!(LitByte "byte literal"); -#[cfg(any(feature = "full", feature = "derive"))] -impl_token!(LitChar "character literal"); -#[cfg(any(feature = "full", feature = "derive"))] -impl_token!(LitInt "integer literal"); -#[cfg(any(feature = "full", feature = "derive"))] -impl_token!(LitFloat "floating point literal"); -#[cfg(any(feature = "full", feature = "derive"))] -impl_token!(LitBool "boolean literal"); +impl_token!("lifetime" Lifetime); +impl_token!("literal" Lit); +impl_token!("string literal" LitStr); +impl_token!("byte string literal" LitByteStr); +impl_token!("byte literal" LitByte); +impl_token!("character literal" LitChar); +impl_token!("integer literal" LitInt); +impl_token!("floating point literal" LitFloat); +impl_token!("boolean literal" LitBool); +impl_token!("group token" proc_macro2::Group); + +macro_rules! impl_low_level_token { + ($display:tt $ty:ident $get:ident) => { + #[cfg(feature = "parsing")] + impl Token for $ty { + fn peek(cursor: Cursor) -> bool { + cursor.$get().is_some() + } + + fn display() -> &'static str { + $display + } + } + + #[cfg(feature = "parsing")] + impl private::Sealed for $ty {} + }; +} + +impl_low_level_token!("punctuation token" Punct punct); +impl_low_level_token!("literal" Literal literal); +impl_low_level_token!("token" TokenTree token_tree); // Not public API. #[doc(hidden)] diff --git a/tests/.gitignore b/tests/.gitignore new file mode 100644 index 00000000..291ed43a --- /dev/null +++ b/tests/.gitignore @@ -0,0 +1 @@ +/*.pending-snap diff --git a/tests/common/eq.rs b/tests/common/eq.rs index 49b132b4..794fe4a2 100644 --- a/tests/common/eq.rs +++ b/tests/common/eq.rs @@ -283,7 +283,7 @@ spanless_eq_struct!(ForeignMod; abi items); spanless_eq_struct!(GenericParam; id ident attrs bounds is_placeholder kind); spanless_eq_struct!(Generics; params where_clause span); spanless_eq_struct!(GlobalAsm; asm); -spanless_eq_struct!(InlineAsm; template operands options); +spanless_eq_struct!(InlineAsm; template operands options line_spans); spanless_eq_struct!(Item<K>; attrs id span vis ident kind !tokens); spanless_eq_struct!(Label; ident); spanless_eq_struct!(Lifetime; id ident); @@ -311,7 +311,7 @@ spanless_eq_struct!(Ty; id kind span); spanless_eq_struct!(UseTree; prefix kind span); spanless_eq_struct!(Variant; attrs id span vis ident data disr_expr is_placeholder); spanless_eq_struct!(WhereBoundPredicate; span bound_generic_params bounded_ty bounds); -spanless_eq_struct!(WhereClause; predicates span); +spanless_eq_struct!(WhereClause; has_where_token predicates span); spanless_eq_struct!(WhereEqPredicate; id span lhs_ty rhs_ty); spanless_eq_struct!(WhereRegionPredicate; span lifetime bounds); spanless_eq_enum!(AngleBracketedArg; Arg(0) Constraint(0)); @@ -364,7 +364,7 @@ spanless_eq_enum!(UseTreeKind; Simple(0 1 2) Nested(0) Glob); spanless_eq_enum!(VariantData; Struct(0 1) Tuple(0 1) Unit(0)); spanless_eq_enum!(VisibilityKind; Public Crate(0) Restricted(path id) Inherited); spanless_eq_enum!(WherePredicate; BoundPredicate(0) RegionPredicate(0) EqPredicate(0)); -spanless_eq_enum!(ExprKind; Box(0) Array(0) Call(0 1) MethodCall(0 1) Tup(0) +spanless_eq_enum!(ExprKind; Box(0) Array(0) Call(0 1) MethodCall(0 1 2) Tup(0) Binary(0 1 2) Unary(0 1) Lit(0) Cast(0 1) Type(0 1) Let(0 1) If(0 1 2) While(0 1 2) ForLoop(0 1 2 3) Loop(0 1) Match(0 1) Closure(0 1 2 3 4 5) Block(0 1) Async(0 1 2) Await(0) TryBlock(0) Assign(0 1 2) AssignOp(0 1 2) diff --git a/tests/test_expr.rs b/tests/test_expr.rs index 85d09d3c..dde72d55 100644 --- a/tests/test_expr.rs +++ b/tests/test_expr.rs @@ -1,28 +1,40 @@ #[macro_use] mod macros; -use std::str::FromStr; - -use proc_macro2::{Delimiter, Group, Punct, Spacing, TokenStream, TokenTree}; +use proc_macro2::{Delimiter, Group, Ident, Punct, Spacing, Span, TokenStream, TokenTree}; use quote::quote; use std::iter::FromIterator; use syn::{Expr, ExprRange}; #[test] fn test_expr_parse() { - let code = "..100u32"; - let tt = TokenStream::from_str(code).unwrap(); - let expr: Expr = syn::parse2(tt.clone()).unwrap(); - let expr_range: ExprRange = syn::parse2(tt).unwrap(); - assert_eq!(expr, Expr::Range(expr_range)); + let tokens = quote!(..100u32); + snapshot!(tokens as Expr, @r###" + Expr::Range { + limits: HalfOpen, + to: Some(Expr::Lit { + lit: 100u32, + }), + } + "###); + + let tokens = quote!(..100u32); + snapshot!(tokens as ExprRange, @r###" + ExprRange { + limits: HalfOpen, + to: Some(Expr::Lit { + lit: 100u32, + }), + } + "###); } #[test] fn test_await() { // Must not parse as Expr::Field. - let expr = syn::parse_str::<Expr>("fut.await").unwrap(); + let tokens = quote!(fut.await); - snapshot!(expr, @r###" + snapshot!(tokens as Expr, @r###" Expr::Await { base: Expr::Path { path: Path { @@ -39,6 +51,57 @@ fn test_await() { } #[test] +fn test_tuple_multi_index() { + let input = "tuple.0.0"; + snapshot!(input as Expr, @r###" + Expr::Field { + base: Expr::Field { + base: Expr::Path { + path: Path { + segments: [ + PathSegment { + ident: "tuple", + arguments: None, + }, + ], + }, + }, + member: Unnamed(Index { + index: 0, + }), + }, + member: Unnamed(Index { + index: 0, + }), + } + "###); + + let tokens = quote!(tuple.0.0); + snapshot!(tokens as Expr, @r###" + Expr::Field { + base: Expr::Field { + base: Expr::Path { + path: Path { + segments: [ + PathSegment { + ident: "tuple", + arguments: None, + }, + ], + }, + }, + member: Unnamed(Index { + index: 0, + }), + }, + member: Unnamed(Index { + index: 0, + }), + } + "###); +} + +#[test] fn test_macro_variable_func() { // mimics the token stream corresponding to `$fn()` let tokens = TokenStream::from_iter(vec![ @@ -164,3 +227,59 @@ fn test_macro_variable_struct() { } "###); } + +#[test] +fn test_macro_variable_match_arm() { + // mimics the token stream corresponding to `match v { _ => $expr }` + let tokens = TokenStream::from_iter(vec![ + TokenTree::Ident(Ident::new("match", Span::call_site())), + TokenTree::Ident(Ident::new("v", Span::call_site())), + TokenTree::Group(Group::new( + Delimiter::Brace, + TokenStream::from_iter(vec![ + TokenTree::Punct(Punct::new('_', Spacing::Alone)), + TokenTree::Punct(Punct::new('=', Spacing::Joint)), + TokenTree::Punct(Punct::new('>', Spacing::Alone)), + TokenTree::Group(Group::new(Delimiter::None, quote! { #[a] () })), + ]), + )), + ]); + + snapshot!(tokens as Expr, @r###" + Expr::Match { + expr: Expr::Path { + path: Path { + segments: [ + PathSegment { + ident: "v", + arguments: None, + }, + ], + }, + }, + arms: [ + Arm { + pat: Pat::Wild, + body: Expr::Group { + expr: Expr::Tuple { + attrs: [ + Attribute { + style: Outer, + path: Path { + segments: [ + PathSegment { + ident: "a", + arguments: None, + }, + ], + }, + tokens: TokenStream(``), + }, + ], + }, + }, + }, + ], + } + "###); +} diff --git a/tests/test_item.rs b/tests/test_item.rs new file mode 100644 index 00000000..74ac4bae --- /dev/null +++ b/tests/test_item.rs @@ -0,0 +1,45 @@ +#[macro_use] +mod macros; + +use proc_macro2::{Delimiter, Group, Ident, Span, TokenStream, TokenTree}; +use quote::quote; +use std::iter::FromIterator; +use syn::Item; + +#[test] +fn test_macro_variable_attr() { + // mimics the token stream corresponding to `$attr fn f() {}` + let tokens = TokenStream::from_iter(vec![ + TokenTree::Group(Group::new(Delimiter::None, quote! { #[test] })), + TokenTree::Ident(Ident::new("fn", Span::call_site())), + TokenTree::Ident(Ident::new("f", Span::call_site())), + TokenTree::Group(Group::new(Delimiter::Parenthesis, TokenStream::new())), + TokenTree::Group(Group::new(Delimiter::Brace, TokenStream::new())), + ]); + + snapshot!(tokens as Item, @r###" + Item::Fn { + attrs: [ + Attribute { + style: Outer, + path: Path { + segments: [ + PathSegment { + ident: "test", + arguments: None, + }, + ], + }, + tokens: TokenStream(``), + }, + ], + vis: Inherited, + sig: Signature { + ident: "f", + generics: Generics, + output: Default, + }, + block: Block, + } + "###); +} diff --git a/tests/test_parse_stream.rs b/tests/test_parse_stream.rs new file mode 100644 index 00000000..76bd0657 --- /dev/null +++ b/tests/test_parse_stream.rs @@ -0,0 +1,12 @@ +use syn::ext::IdentExt; +use syn::parse::ParseStream; +use syn::{Ident, Token}; + +#[test] +fn test_peek() { + let _ = |input: ParseStream| { + let _ = input.peek(Ident); + let _ = input.peek(Ident::peek_any); + let _ = input.peek(Token![::]); + }; +} |