aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChih-hung Hsieh <chh@google.com>2020-07-14 04:53:00 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2020-07-14 04:53:00 +0000
commit3aa702ba3984180742838781e236e69c3fb9f091 (patch)
tree39efb9103d47f1fd46d0b8a029861743c1b178f6
parentd8760cdd5d6bf9ab4daed1d496bfab12eb314150 (diff)
parentcdfc27f09f3073e670a8b20eb6be5eba5b6bc940 (diff)
downloadsyn-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.json2
-rw-r--r--Cargo.toml2
-rw-r--r--Cargo.toml.orig2
-rw-r--r--METADATA6
-rw-r--r--README.md2
-rw-r--r--src/attr.rs8
-rw-r--r--src/error.rs4
-rw-r--r--src/expr.rs253
-rw-r--r--src/lib.rs10
-rw-r--r--src/parse.rs17
-rw-r--r--src/parse_macro_input.rs4
-rw-r--r--src/token.rs60
-rw-r--r--tests/.gitignore1
-rw-r--r--tests/common/eq.rs6
-rw-r--r--tests/test_expr.rs139
-rw-r--r--tests/test_item.rs45
-rw-r--r--tests/test_parse_stream.rs12
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"
}
}
diff --git a/Cargo.toml b/Cargo.toml
index 7d8c8a9b..67fac010 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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"
diff --git a/METADATA b/METADATA
index 0b51e9ef..f3a01c65 100644
--- a/METADATA
+++ b/METADATA
@@ -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
}
}
diff --git a/README.md b/README.md
index 97f955b0..e24b2ce4 100644
--- a/README.md
+++ b/README.md
@@ -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 {
diff --git a/src/lib.rs b/src/lib.rs
index 33926fdf..1e941a07 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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![::]);
+ };
+}