Update to latest syn-1.0.16
* fill back missing NOTICE,METADATA,*LICENSE* files
Bug: 150877376
Test: make
Test: atest --host -c --include-subdirs external/rust/crates
Change-Id: Ib5df6b8fb97764214e701a888e44fab3a4245800
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
index c053ad6..1339f73 100644
--- a/.cargo_vcs_info.json
+++ b/.cargo_vcs_info.json
@@ -1,5 +1,5 @@
{
"git": {
- "sha1": "b06fef4bd130b74cf47b44a68e76522a45bec18d"
+ "sha1": "b76945a91ff2e6cccbab7d5dd09dbdfea300571d"
}
}
diff --git a/Android.bp b/Android.bp
index e7bff6d..10f8444 100644
--- a/Android.bp
+++ b/Android.bp
@@ -27,3 +27,8 @@
"libunicode_xid",
],
}
+
+// dependent_library ["feature_list"]
+// proc-macro2-1.0.9 "default,proc-macro"
+// quote-1.0.3 "default,proc-macro"
+// unicode-xid-0.2.0 "default"
diff --git a/Cargo.toml b/Cargo.toml
index 82f815b..e716835 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -13,7 +13,7 @@
[package]
edition = "2018"
name = "syn"
-version = "1.0.7"
+version = "1.0.16"
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"
@@ -37,7 +37,7 @@
name = "file"
required-features = ["full", "parsing"]
[dependencies.proc-macro2]
-version = "1.0"
+version = "1.0.7"
default-features = false
[dependencies.quote]
@@ -47,18 +47,31 @@
[dependencies.unicode-xid]
version = "0.2"
+[dev-dependencies.anyhow]
+version = "1.0"
+
+[dev-dependencies.flate2]
+version = "1.0"
+
[dev-dependencies.insta]
-version = "0.11"
+version = "0.12"
[dev-dependencies.rayon]
version = "1.0"
[dev-dependencies.ref-cast]
-version = "0.2"
+version = "1.0"
[dev-dependencies.regex]
version = "1.0"
+[dev-dependencies.reqwest]
+version = "0.10"
+features = ["blocking"]
+
+[dev-dependencies.tar]
+version = "0.4"
+
[dev-dependencies.termcolor]
version = "1.0"
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
index f050fac..79e5dc6 100644
--- a/Cargo.toml.orig
+++ b/Cargo.toml.orig
@@ -1,6 +1,6 @@
[package]
name = "syn"
-version = "1.0.7" # don't forget to update html_root_url and syn.json
+version = "1.0.16" # 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"
@@ -34,15 +34,19 @@
proc-macro = ["proc-macro2/proc-macro", "quote/proc-macro"]
[dependencies]
-proc-macro2 = { version = "1.0", default-features = false }
+proc-macro2 = { version = "1.0.7", default-features = false }
quote = { version = "1.0", optional = true, default-features = false }
unicode-xid = "0.2"
[dev-dependencies]
-insta = "0.11"
+anyhow = "1.0"
+flate2 = "1.0"
+insta = "0.12"
rayon = "1.0"
-ref-cast = "0.2"
+ref-cast = "1.0"
regex = "1.0"
+reqwest = { version = "0.10", features = ["blocking"] }
+tar = "0.4"
termcolor = "1.0"
walkdir = "2.1"
diff --git a/METADATA b/METADATA
index b3cf575..314c2d7 100644
--- a/METADATA
+++ b/METADATA
@@ -1,8 +1,5 @@
name: "syn"
-description:
- "Syn is a parsing library for parsing a stream of Rust tokens into a syntax "
- "tree of Rust source code."
-
+description: "Syn is a parsing library for parsing a stream of Rust tokens into a syntax tree of Rust source code."
third_party {
url {
type: HOMEPAGE
@@ -12,6 +9,10 @@
type: GIT
value: "https://github.com/dtolnay/syn"
}
- version: "1.0.7"
- last_upgrade_date { year: 2020 month: 3 day: 5 }
+ version: "1.0.16"
+ last_upgrade_date {
+ year: 2020
+ month: 3
+ day: 20
+ }
}
diff --git a/benches/file.rs b/benches/file.rs
index 08ecd90..58ab8df 100644
--- a/benches/file.rs
+++ b/benches/file.rs
@@ -1,9 +1,16 @@
// $ cargo bench --features full --bench file
#![feature(rustc_private, test)]
+#![recursion_limit = "1024"]
extern crate test;
+#[macro_use]
+#[path = "../tests/macros/mod.rs"]
+mod macros;
+
+#[path = "../tests/common/mod.rs"]
+mod common;
#[path = "../tests/repo/mod.rs"]
pub mod repo;
diff --git a/benches/rust.rs b/benches/rust.rs
index e3d9cd2..941ecb9 100644
--- a/benches/rust.rs
+++ b/benches/rust.rs
@@ -4,7 +4,14 @@
// $ RUSTFLAGS='--cfg syn_only' cargo build --release --features full --bench rust
#![cfg_attr(not(syn_only), feature(rustc_private))]
+#![recursion_limit = "1024"]
+#[macro_use]
+#[path = "../tests/macros/mod.rs"]
+mod macros;
+
+#[path = "../tests/common/mod.rs"]
+mod common;
#[path = "../tests/repo/mod.rs"]
mod repo;
@@ -30,21 +37,25 @@
#[cfg(not(syn_only))]
mod libsyntax_parse {
extern crate rustc_data_structures;
+ extern crate rustc_parse;
+ extern crate rustc_span;
extern crate syntax;
- extern crate syntax_pos;
use rustc_data_structures::sync::Lrc;
+ use rustc_span::FileName;
use syntax::edition::Edition;
- use syntax::errors::{emitter::Emitter, DiagnosticBuilder, Handler};
- use syntax::parse::ParseSess;
+ use syntax::errors::{emitter::Emitter, Diagnostic, Handler};
+ use syntax::sess::ParseSess;
use syntax::source_map::{FilePathMapping, SourceMap};
- use syntax_pos::FileName;
pub fn bench(content: &str) -> Result<(), ()> {
struct SilentEmitter;
impl Emitter for SilentEmitter {
- fn emit_diagnostic(&mut self, _db: &DiagnosticBuilder) {}
+ fn emit_diagnostic(&mut self, _diag: &Diagnostic) {}
+ fn source_map(&self) -> Option<&Lrc<SourceMap>> {
+ None
+ }
}
syntax::with_globals(Edition::Edition2018, || {
@@ -52,7 +63,7 @@
let emitter = Box::new(SilentEmitter);
let handler = Handler::with_emitter(false, None, emitter);
let sess = ParseSess::with_span_handler(handler, cm);
- if let Err(mut diagnostic) = syntax::parse::parse_crate_from_source_str(
+ if let Err(mut diagnostic) = rustc_parse::parse_crate_from_source_str(
FileName::Custom("bench".to_owned()),
content.to_owned(),
&sess,
diff --git a/build.rs b/build.rs
index c0f9ed3..c6040c1 100644
--- a/build.rs
+++ b/build.rs
@@ -57,7 +57,7 @@
};
Some(Compiler {
- minor: minor,
+ minor,
nightly: version.contains("nightly"),
})
}
diff --git a/src/attr.rs b/src/attr.rs
index 34009de..17b3793 100644
--- a/src/attr.rs
+++ b/src/attr.rs
@@ -111,6 +111,43 @@
///
/// [`parse_meta()`]: Attribute::parse_meta
/// [`parse_args()`]: Attribute::parse_args
+ ///
+ /// <p><br></p>
+ ///
+ /// # Doc comments
+ ///
+ /// The compiler transforms doc comments, such as `/// comment` and `/*!
+ /// comment */`, into attributes before macros are expanded. Each comment is
+ /// expanded into an attribute of the form `#[doc = r"comment"]`.
+ ///
+ /// As an example, the following `mod` items are expanded identically:
+ ///
+ /// ```
+ /// # use syn::{ItemMod, parse_quote};
+ /// let doc: ItemMod = parse_quote! {
+ /// /// Single line doc comments
+ /// /// We write so many!
+ /// /**
+ /// * Multi-line comments...
+ /// * May span many lines
+ /// */
+ /// mod example {
+ /// //! Of course, they can be inner too
+ /// /*! And fit in a single line */
+ /// }
+ /// };
+ /// let attr: ItemMod = parse_quote! {
+ /// #[doc = r" Single line doc comments"]
+ /// #[doc = r" We write so many!"]
+ /// #[doc = r" Multi-line comments...
+ /// May span many lines"]
+ /// mod example {
+ /// #![doc = r" Of course, they can be inner too"]
+ /// #![doc = r" And fit in a single line "]
+ /// }
+ /// };
+ /// assert_eq!(doc, attr);
+ /// ```
pub struct Attribute #manual_extra_traits {
pub pound_token: Token![#],
pub style: AttrStyle,
@@ -247,7 +284,7 @@
}
#[cfg(feature = "parsing")]
-fn error_expected_args(attr: &Attribute) -> Error {
+fn expected_parentheses(attr: &Attribute) -> String {
let style = match attr.style {
AttrStyle::Outer => "#",
AttrStyle::Inner(_) => "#!",
@@ -261,19 +298,23 @@
path += &segment.ident.to_string();
}
- let msg = format!("expected attribute arguments: {}[{}(...)]", style, path);
-
- #[cfg(feature = "printing")]
- return Error::new_spanned(attr, msg);
-
- #[cfg(not(feature = "printing"))]
- return Error::new(attr.bracket_token.span, msg);
+ format!("{}[{}(...)]", style, path)
}
#[cfg(feature = "parsing")]
fn enter_args<'a>(attr: &Attribute, input: ParseStream<'a>) -> Result<ParseBuffer<'a>> {
if input.is_empty() {
- return Err(error_expected_args(attr));
+ let expected = expected_parentheses(attr);
+ let msg = format!("expected attribute arguments in parentheses: {}", expected);
+ return Err(crate::error::new2(
+ attr.pound_token.span,
+ attr.bracket_token.span,
+ msg,
+ ));
+ } else if input.peek(Token![=]) {
+ let expected = expected_parentheses(attr);
+ let msg = format!("expected parentheses: {}", expected);
+ return Err(input.error(msg));
};
let content;
diff --git a/src/buffer.rs b/src/buffer.rs
index 551a5ac..5c2dd8a 100644
--- a/src/buffer.rs
+++ b/src/buffer.rs
@@ -221,7 +221,6 @@
/// Checks whether the cursor is currently pointing at the end of its valid
/// scope.
- #[inline]
pub fn eof(self) -> bool {
// We're at eof if we're at the end of our scope.
self.ptr == self.scope
@@ -342,6 +341,26 @@
Entry::End(..) => Span::call_site(),
}
}
+
+ /// Skip over the next token without cloning it. Returns `None` if this
+ /// cursor points to eof.
+ ///
+ /// This method treats `'lifetimes` as a single token.
+ pub(crate) fn skip(self) -> Option<Cursor<'a>> {
+ match self.entry() {
+ Entry::End(..) => None,
+
+ // Treat lifetimes as a single tt for the purposes of 'skip'.
+ Entry::Punct(op) if op.as_char() == '\'' && op.spacing() == Spacing::Joint => {
+ let next = unsafe { self.bump() };
+ match next.entry() {
+ Entry::Ident(_) => Some(unsafe { next.bump() }),
+ _ => Some(next),
+ }
+ }
+ _ => Some(unsafe { self.bump() }),
+ }
+ }
}
pub(crate) fn same_scope(a: Cursor, b: Cursor) -> bool {
diff --git a/src/custom_keyword.rs b/src/custom_keyword.rs
index 200e847..d46e277 100644
--- a/src/custom_keyword.rs
+++ b/src/custom_keyword.rs
@@ -95,7 +95,7 @@
}
#[doc(hidden)]
- #[allow(non_snake_case)]
+ #[allow(dead_code, non_snake_case)]
pub fn $ident<__S: $crate::export::IntoSpans<[$crate::export::Span; 1]>>(
span: __S,
) -> $ident {
diff --git a/src/custom_punctuation.rs b/src/custom_punctuation.rs
index 29fa448..8631b77 100644
--- a/src/custom_punctuation.rs
+++ b/src/custom_punctuation.rs
@@ -82,7 +82,7 @@
}
#[doc(hidden)]
- #[allow(non_snake_case)]
+ #[allow(dead_code, non_snake_case)]
pub fn $ident<__S: $crate::export::IntoSpans<custom_punctuation_repr!($($tt)+)>>(
spans: __S,
) -> $ident {
diff --git a/src/data.rs b/src/data.rs
index 2554506..0d6b1b5 100644
--- a/src/data.rs
+++ b/src/data.rs
@@ -238,12 +238,15 @@
use super::*;
use crate::ext::IdentExt;
+ use crate::parse::discouraged::Speculative;
use crate::parse::{Parse, ParseStream, Result};
impl Parse for Variant {
fn parse(input: ParseStream) -> Result<Self> {
+ let attrs = input.call(Attribute::parse_outer)?;
+ let _visibility: Visibility = input.parse()?;
Ok(Variant {
- attrs: input.call(Attribute::parse_outer)?,
+ attrs,
ident: input.parse()?,
fields: {
if input.peek(token::Brace) {
@@ -328,27 +331,39 @@
let pub_token = input.parse::<Token![pub]>()?;
if input.peek(token::Paren) {
- // TODO: optimize using advance_to
let ahead = input.fork();
- let mut content;
- parenthesized!(content in ahead);
+ let content;
+ let paren_token = parenthesized!(content in ahead);
if content.peek(Token![crate])
|| content.peek(Token![self])
|| content.peek(Token![super])
{
- return Ok(Visibility::Restricted(VisRestricted {
- pub_token,
- paren_token: parenthesized!(content in input),
- in_token: None,
- path: Box::new(Path::from(content.call(Ident::parse_any)?)),
- }));
+ let path = content.call(Ident::parse_any)?;
+
+ // Ensure there are no additional tokens within `content`.
+ // Without explicitly checking, we may misinterpret a tuple
+ // field as a restricted visibility, causing a parse error.
+ // e.g. `pub (crate::A, crate::B)` (Issue #720).
+ if content.is_empty() {
+ input.advance_to(&ahead);
+ return Ok(Visibility::Restricted(VisRestricted {
+ pub_token,
+ paren_token,
+ in_token: None,
+ path: Box::new(Path::from(path)),
+ }));
+ }
} else if content.peek(Token![in]) {
+ let in_token: Token![in] = content.parse()?;
+ let path = content.call(Path::parse_mod_style)?;
+
+ input.advance_to(&ahead);
return Ok(Visibility::Restricted(VisRestricted {
pub_token,
- paren_token: parenthesized!(content in input),
- in_token: Some(content.parse()?),
- path: Box::new(content.call(Path::parse_mod_style)?),
+ paren_token,
+ in_token: Some(in_token),
+ path: Box::new(path),
}));
}
}
diff --git a/src/discouraged.rs b/src/discouraged.rs
index 4d9ff93..29d1006 100644
--- a/src/discouraged.rs
+++ b/src/discouraged.rs
@@ -164,6 +164,30 @@
panic!("Fork was not derived from the advancing parse stream");
}
+ let (self_unexp, self_sp) = inner_unexpected(self);
+ let (fork_unexp, fork_sp) = inner_unexpected(fork);
+ if !Rc::ptr_eq(&self_unexp, &fork_unexp) {
+ match (fork_sp, self_sp) {
+ // Unexpected set on the fork, but not on `self`, copy it over.
+ (Some(span), None) => {
+ self_unexp.set(Unexpected::Some(span));
+ }
+ // Unexpected unset. Use chain to propagate errors from fork.
+ (None, None) => {
+ fork_unexp.set(Unexpected::Chain(self_unexp));
+
+ // Ensure toplevel 'unexpected' tokens from the fork don't
+ // bubble up the chain by replacing the root `unexpected`
+ // pointer, only 'unexpected' tokens from existing group
+ // parsers should bubble.
+ fork.unexpected
+ .set(Some(Rc::new(Cell::new(Unexpected::None))));
+ }
+ // Unexpected has been set on `self`. No changes needed.
+ (_, Some(_)) => {}
+ }
+ }
+
// See comment on `cell` in the struct definition.
self.cell
.set(unsafe { mem::transmute::<Cursor, Cursor<'static>>(fork.cursor()) })
diff --git a/src/error.rs b/src/error.rs
index 146d652..dbcbc07 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -1,4 +1,3 @@
-use std;
use std::fmt::{self, Debug, Display};
use std::iter::FromIterator;
use std::slice;
@@ -250,6 +249,17 @@
}
}
+#[cfg(all(feature = "parsing", any(feature = "full", feature = "derive")))]
+pub fn new2<T: Display>(start: Span, end: Span, message: T) -> Error {
+ Error {
+ messages: vec![ErrorMessage {
+ start_span: ThreadBound::new(start),
+ end_span: ThreadBound::new(end),
+ message: message.to_string(),
+ }],
+ }
+}
+
impl Debug for Error {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
if self.messages.len() == 1 {
diff --git a/src/expr.rs b/src/expr.rs
index 2874a46..b40f1ca 100644
--- a/src/expr.rs
+++ b/src/expr.rs
@@ -3,7 +3,10 @@
#[cfg(feature = "extra-traits")]
use crate::tt::TokenStreamHelper;
use proc_macro2::{Span, TokenStream};
-#[cfg(feature = "extra-traits")]
+#[cfg(feature = "printing")]
+use quote::IdentFragment;
+#[cfg(feature = "printing")]
+use std::fmt::{self, Display};
use std::hash::{Hash, Hasher};
#[cfg(all(feature = "parsing", feature = "full"))]
use std::mem;
@@ -998,7 +1001,8 @@
///
/// *This type is available if Syn is built with the `"derive"` or `"full"`
/// feature.*
- pub enum Member {
+ #[derive(Eq, PartialEq, Hash)]
+ pub enum Member #manual_extra_traits {
/// A named field like `self.x`.
Named(Ident),
/// An unnamed field like `self.0`.
@@ -1006,6 +1010,23 @@
}
}
+#[cfg(feature = "printing")]
+impl IdentFragment for Member {
+ fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ Member::Named(m) => Display::fmt(m, formatter),
+ Member::Unnamed(m) => Display::fmt(&m.index, formatter),
+ }
+ }
+
+ fn span(&self) -> Option<Span> {
+ match self {
+ Member::Named(m) => Some(m.span()),
+ Member::Unnamed(m) => Some(m.span),
+ }
+ }
+}
+
ast_struct! {
/// The index of an unnamed tuple struct field.
///
@@ -1027,28 +1048,36 @@
}
}
-#[cfg(feature = "extra-traits")]
impl Eq for Index {}
-#[cfg(feature = "extra-traits")]
impl PartialEq for Index {
fn eq(&self, other: &Self) -> bool {
self.index == other.index
}
}
-#[cfg(feature = "extra-traits")]
impl Hash for Index {
fn hash<H: Hasher>(&self, state: &mut H) {
self.index.hash(state);
}
}
+#[cfg(feature = "printing")]
+impl IdentFragment for Index {
+ fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ Display::fmt(&self.index, formatter)
+ }
+
+ fn span(&self) -> Option<Span> {
+ Some(self.span)
+ }
+}
+
#[cfg(feature = "full")]
ast_struct! {
#[derive(Default)]
pub struct Reserved {
- private: (),
+ _private: (),
}
}
@@ -1181,9 +1210,12 @@
pub(crate) mod parsing {
use super::*;
+ use crate::parse::discouraged::Speculative;
use crate::parse::{Parse, ParseStream, Result};
use crate::path;
+ crate::custom_keyword!(raw);
+
// When we're parsing expressions which occur before blocks, like in an if
// statement's condition, we cannot parse a struct literal.
//
@@ -1436,24 +1468,41 @@
// box <trailer>
#[cfg(feature = "full")]
fn unary_expr(input: ParseStream, allow_struct: AllowStruct) -> Result<Expr> {
- // TODO: optimize using advance_to
+ let begin = input.fork();
let ahead = input.fork();
- ahead.call(Attribute::parse_outer)?;
+ let attrs = ahead.call(Attribute::parse_outer)?;
if ahead.peek(Token![&])
|| ahead.peek(Token![box])
|| ahead.peek(Token![*])
|| ahead.peek(Token![!])
|| ahead.peek(Token![-])
{
- let attrs = input.call(Attribute::parse_outer)?;
+ input.advance_to(&ahead);
if input.peek(Token![&]) {
- Ok(Expr::Reference(ExprReference {
- attrs,
- and_token: input.parse()?,
- raw: Reserved::default(),
- mutability: input.parse()?,
- expr: Box::new(unary_expr(input, allow_struct)?),
- }))
+ 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,
@@ -1474,12 +1523,12 @@
#[cfg(not(feature = "full"))]
fn unary_expr(input: ParseStream, allow_struct: AllowStruct) -> Result<Expr> {
- // TODO: optimize using advance_to
let ahead = input.fork();
- ahead.call(Attribute::parse_outer)?;
+ let attrs = ahead.call(Attribute::parse_outer)?;
if ahead.peek(Token![*]) || ahead.peek(Token![!]) || ahead.peek(Token![-]) {
+ input.advance_to(&ahead);
Ok(Expr::Unary(ExprUnary {
- attrs: input.call(Attribute::parse_outer)?,
+ attrs,
op: input.parse()?,
expr: Box::new(unary_expr(input, allow_struct)?),
}))
@@ -1905,7 +1954,7 @@
return parse_expr(input, expr, allow_struct, Precedence::Any);
};
- if input.peek(Token![.]) || input.peek(Token![?]) {
+ if input.peek(Token![.]) && !input.peek(Token![..]) || input.peek(Token![?]) {
expr = trailer_helper(input, expr)?;
attrs.extend(expr.replace_attrs(Vec::new()));
@@ -2399,6 +2448,7 @@
#[cfg(feature = "full")]
impl Parse for FieldValue {
fn parse(input: ParseStream) -> Result<Self> {
+ let attrs = input.call(Attribute::parse_outer)?;
let member: Member = input.parse()?;
let (colon_token, value) = if input.peek(Token![:]) || !member.is_named() {
let colon_token: Token![:] = input.parse()?;
@@ -2416,7 +2466,7 @@
};
Ok(FieldValue {
- attrs: Vec::new(),
+ attrs,
member,
colon_token,
expr: value,
@@ -2433,24 +2483,22 @@
let content;
let brace_token = braced!(content in input);
let inner_attrs = content.call(Attribute::parse_inner)?;
+ let attrs = private::attrs(outer_attrs, inner_attrs);
let mut fields = Punctuated::new();
- loop {
- let attrs = content.call(Attribute::parse_outer)?;
- // TODO: optimize using advance_to
- if content.fork().parse::<Member>().is_err() {
- if attrs.is_empty() {
- break;
- } else {
- return Err(content.error("expected struct field"));
- }
+ while !content.is_empty() {
+ if content.peek(Token![..]) {
+ return Ok(ExprStruct {
+ attrs,
+ brace_token,
+ path,
+ fields,
+ dot2_token: Some(content.parse()?),
+ rest: Some(Box::new(content.parse()?)),
+ });
}
- fields.push(FieldValue {
- attrs,
- ..content.parse()?
- });
-
+ fields.push(content.parse()?);
if !content.peek(Token![,]) {
break;
}
@@ -2458,21 +2506,13 @@
fields.push_punct(punct);
}
- let (dot2_token, rest) = if fields.empty_or_trailing() && content.peek(Token![..]) {
- let dot2_token: Token![..] = content.parse()?;
- let rest: Expr = content.parse()?;
- (Some(dot2_token), Some(Box::new(rest)))
- } else {
- (None, None)
- };
-
Ok(ExprStruct {
- attrs: private::attrs(outer_attrs, inner_attrs),
+ attrs,
brace_token,
path,
fields,
- dot2_token,
- rest,
+ dot2_token: None,
+ rest: None,
})
}
diff --git a/src/generics.rs b/src/generics.rs
index d357cbb..9feeab9 100644
--- a/src/generics.rs
+++ b/src/generics.rs
@@ -28,9 +28,6 @@
/// This type is a [syntax tree enum].
///
/// [syntax tree enum]: enum.Expr.html#syntax-tree-enums
- //
- // TODO: change syntax-tree-enum link to an intra rustdoc link, currently
- // blocked on https://github.com/rust-lang/rust/issues/62833
pub enum GenericParam {
/// A generic type parameter: `T: Into<String>`.
Type(TypeParam),
@@ -448,9 +445,6 @@
/// This type is a [syntax tree enum].
///
/// [syntax tree enum]: enum.Expr.html#syntax-tree-enums
- //
- // TODO: change syntax-tree-enum link to an intra rustdoc link, currently
- // blocked on https://github.com/rust-lang/rust/issues/62833
pub enum WherePredicate {
/// A type predicate in a `where` clause: `for<'c> Foo<'c>: Trait<'c>`.
Type(PredicateType),
diff --git a/src/item.rs b/src/item.rs
index a1ce7bf..83b82fd 100644
--- a/src/item.rs
+++ b/src/item.rs
@@ -7,6 +7,8 @@
use crate::tt::TokenStreamHelper;
#[cfg(feature = "extra-traits")]
use std::hash::{Hash, Hasher};
+#[cfg(feature = "parsing")]
+use std::mem;
ast_enum_of_structs! {
/// Things that can appear directly inside of a module or scope.
@@ -435,6 +437,32 @@
}
}
+impl Item {
+ #[cfg(feature = "parsing")]
+ pub(crate) fn replace_attrs(&mut self, new: Vec<Attribute>) -> Vec<Attribute> {
+ match self {
+ Item::ExternCrate(ItemExternCrate { attrs, .. })
+ | Item::Use(ItemUse { attrs, .. })
+ | Item::Static(ItemStatic { attrs, .. })
+ | Item::Const(ItemConst { attrs, .. })
+ | Item::Fn(ItemFn { attrs, .. })
+ | Item::Mod(ItemMod { attrs, .. })
+ | Item::ForeignMod(ItemForeignMod { attrs, .. })
+ | Item::Type(ItemType { attrs, .. })
+ | Item::Struct(ItemStruct { attrs, .. })
+ | Item::Enum(ItemEnum { attrs, .. })
+ | Item::Union(ItemUnion { attrs, .. })
+ | Item::Trait(ItemTrait { attrs, .. })
+ | Item::TraitAlias(ItemTraitAlias { attrs, .. })
+ | Item::Impl(ItemImpl { attrs, .. })
+ | Item::Macro(ItemMacro { attrs, .. })
+ | Item::Macro2(ItemMacro2 { attrs, .. }) => mem::replace(attrs, new),
+ Item::Verbatim(_) => Vec::new(),
+ Item::__Nonexhaustive => unreachable!(),
+ }
+ }
+}
+
#[cfg(feature = "extra-traits")]
impl Eq for ItemMacro2 {}
@@ -1128,6 +1156,7 @@
use crate::ext::IdentExt;
use crate::parse::discouraged::Speculative;
use crate::parse::{Parse, ParseStream, Result};
+ use crate::token::Brace;
use proc_macro2::{Delimiter, Group, Punct, Spacing, TokenTree};
use std::iter::{self, FromIterator};
@@ -1234,31 +1263,8 @@
Err(lookahead.error())
}?;
- {
- let item_attrs = match &mut item {
- Item::ExternCrate(item) => &mut item.attrs,
- Item::Use(item) => &mut item.attrs,
- Item::Static(item) => &mut item.attrs,
- Item::Const(item) => &mut item.attrs,
- Item::Fn(item) => &mut item.attrs,
- Item::Mod(item) => &mut item.attrs,
- Item::ForeignMod(item) => &mut item.attrs,
- Item::Type(item) => &mut item.attrs,
- Item::Struct(item) => &mut item.attrs,
- Item::Enum(item) => &mut item.attrs,
- Item::Union(item) => &mut item.attrs,
- Item::Trait(item) => &mut item.attrs,
- Item::TraitAlias(item) => &mut item.attrs,
- Item::Impl(item) => &mut item.attrs,
- Item::Macro(item) => &mut item.attrs,
- Item::Macro2(item) => &mut item.attrs,
- Item::Verbatim(_) => return Ok(item),
- Item::__Nonexhaustive => unreachable!(),
- };
- attrs.extend(item_attrs.drain(..));
- *item_attrs = attrs;
- }
-
+ attrs.extend(item.replace_attrs(Vec::new()));
+ item.replace_attrs(attrs);
Ok(item)
}
}
@@ -1463,6 +1469,54 @@
}
}
+ fn pop_variadic(args: &mut Punctuated<FnArg, Token![,]>) -> Option<Variadic> {
+ let trailing_punct = args.trailing_punct();
+
+ let last = match args.last_mut()? {
+ FnArg::Typed(last) => last,
+ _ => return None,
+ };
+
+ let ty = match last.ty.as_ref() {
+ Type::Verbatim(ty) => ty,
+ _ => return None,
+ };
+
+ let mut variadic = Variadic {
+ attrs: Vec::new(),
+ dots: parse2(ty.clone()).ok()?,
+ };
+
+ if let Pat::Verbatim(pat) = last.pat.as_ref() {
+ if pat.to_string() == "..." && !trailing_punct {
+ variadic.attrs = mem::replace(&mut last.attrs, Vec::new());
+ args.pop();
+ }
+ }
+
+ Some(variadic)
+ }
+
+ fn variadic_to_tokens(dots: &Token![...]) -> TokenStream {
+ TokenStream::from_iter(vec![
+ TokenTree::Punct({
+ let mut dot = Punct::new('.', Spacing::Joint);
+ dot.set_span(dots.spans[0]);
+ dot
+ }),
+ TokenTree::Punct({
+ let mut dot = Punct::new('.', Spacing::Joint);
+ dot.set_span(dots.spans[1]);
+ dot
+ }),
+ TokenTree::Punct({
+ let mut dot = Punct::new('.', Spacing::Alone);
+ dot.set_span(dots.spans[2]);
+ dot
+ }),
+ ])
+ }
+
impl Parse for ItemFn {
fn parse(input: ParseStream) -> Result<Self> {
let outer_attrs = input.call(Attribute::parse_outer)?;
@@ -1477,22 +1531,8 @@
let content;
let paren_token = parenthesized!(content in input);
- let inputs = content.parse_terminated(FnArg::parse)?;
- let variadic = inputs.last().as_ref().and_then(get_variadic);
-
- fn get_variadic(input: &&FnArg) -> Option<Variadic> {
- if let FnArg::Typed(PatType { ty, .. }) = input {
- if let Type::Verbatim(tokens) = &**ty {
- if let Ok(dots) = parse2(tokens.clone()) {
- return Some(Variadic {
- attrs: Vec::new(),
- dots,
- });
- }
- }
- }
- None
- }
+ let mut inputs = parse_fn_args(&content)?;
+ let variadic = pop_variadic(&mut inputs);
let output: ReturnType = input.parse()?;
let where_clause: Option<WhereClause> = input.parse()?;
@@ -1562,6 +1602,56 @@
}
}
+ fn parse_fn_args(input: ParseStream) -> Result<Punctuated<FnArg, Token![,]>> {
+ let mut args = Punctuated::new();
+ let mut has_receiver = false;
+
+ while !input.is_empty() {
+ let attrs = input.call(Attribute::parse_outer)?;
+
+ let arg = if let Some(dots) = input.parse::<Option<Token![...]>>()? {
+ FnArg::Typed(PatType {
+ attrs,
+ pat: Box::new(Pat::Verbatim(variadic_to_tokens(&dots))),
+ colon_token: Token,
+ ty: Box::new(Type::Verbatim(variadic_to_tokens(&dots))),
+ })
+ } else {
+ let mut arg: FnArg = input.parse()?;
+ match &mut arg {
+ FnArg::Receiver(receiver) if has_receiver => {
+ return Err(Error::new(
+ receiver.self_token.span,
+ "unexpected second method receiver",
+ ));
+ }
+ FnArg::Receiver(receiver) if !args.is_empty() => {
+ return Err(Error::new(
+ receiver.self_token.span,
+ "unexpected method receiver",
+ ));
+ }
+ FnArg::Receiver(receiver) => {
+ has_receiver = true;
+ receiver.attrs = attrs;
+ }
+ FnArg::Typed(arg) => arg.attrs = attrs,
+ }
+ arg
+ };
+ args.push_value(arg);
+
+ if input.is_empty() {
+ break;
+ }
+
+ let comma: Token![,] = input.parse()?;
+ args.push_punct(comma);
+ }
+
+ Ok(args)
+ }
+
fn fn_arg_typed(input: ParseStream) -> Result<PatType> {
// Hack to parse pre-2018 syntax in
// test/ui/rfc-2565-param-attrs/param-attrs-pretty.rs
@@ -1584,20 +1674,7 @@
pat: input.parse()?,
colon_token: input.parse()?,
ty: Box::new(match input.parse::<Option<Token![...]>>()? {
- Some(dot3) => {
- let args = vec![
- TokenTree::Punct(Punct::new('.', Spacing::Joint)),
- TokenTree::Punct(Punct::new('.', Spacing::Joint)),
- TokenTree::Punct(Punct::new('.', Spacing::Alone)),
- ];
- let tokens = TokenStream::from_iter(args.into_iter().zip(&dot3.spans).map(
- |(mut arg, span)| {
- arg.set_span(*span);
- arg
- },
- ));
- Type::Verbatim(tokens)
- }
+ Some(dot3) => Type::Verbatim(variadic_to_tokens(&dot3)),
None => input.parse()?,
}),
})
@@ -1718,25 +1795,8 @@
let content;
let paren_token = parenthesized!(content in input);
- let mut inputs = Punctuated::new();
- let mut variadic = None;
- while !content.is_empty() {
- let attrs = content.call(Attribute::parse_outer)?;
-
- if let Some(dots) = content.parse()? {
- variadic = Some(Variadic { attrs, dots });
- break;
- }
-
- let mut arg = content.call(fn_arg_typed)?;
- arg.attrs = attrs;
- inputs.push_value(FnArg::Typed(arg));
- if content.is_empty() {
- break;
- }
-
- inputs.push_punct(content.parse()?);
- }
+ let mut inputs = parse_fn_args(&content)?;
+ let variadic = pop_variadic(&mut inputs);
let output: ReturnType = input.parse()?;
let where_clause: Option<WhereClause> = input.parse()?;
@@ -2101,7 +2161,10 @@
impl Parse for TraitItem {
fn parse(input: ParseStream) -> Result<Self> {
+ let begin = input.fork();
let mut attrs = input.call(Attribute::parse_outer)?;
+ let vis: Visibility = input.parse()?;
+ let defaultness: Option<Token![default]> = input.parse()?;
let ahead = input.fork();
let lookahead = ahead.lookahead1();
@@ -2139,18 +2202,20 @@
Err(lookahead.error())
}?;
- {
- let item_attrs = match &mut item {
- TraitItem::Const(item) => &mut item.attrs,
- TraitItem::Method(item) => &mut item.attrs,
- TraitItem::Type(item) => &mut item.attrs,
- TraitItem::Macro(item) => &mut item.attrs,
- TraitItem::Verbatim(_) | TraitItem::__Nonexhaustive => unreachable!(),
- };
- attrs.extend(item_attrs.drain(..));
- *item_attrs = attrs;
+ match (vis, defaultness) {
+ (Visibility::Inherited, None) => {}
+ _ => return Ok(TraitItem::Verbatim(verbatim::between(begin, input))),
}
+ let item_attrs = match &mut item {
+ TraitItem::Const(item) => &mut item.attrs,
+ TraitItem::Method(item) => &mut item.attrs,
+ TraitItem::Type(item) => &mut item.attrs,
+ TraitItem::Macro(item) => &mut item.attrs,
+ TraitItem::Verbatim(_) | TraitItem::__Nonexhaustive => unreachable!(),
+ };
+ attrs.extend(item_attrs.drain(..));
+ *item_attrs = attrs;
Ok(item)
}
}
@@ -2190,7 +2255,8 @@
let content;
let paren_token = parenthesized!(content in input);
- let inputs = content.parse_terminated(FnArg::parse)?;
+ let mut inputs = parse_fn_args(&content)?;
+ let variadic = pop_variadic(&mut inputs);
let output: ReturnType = input.parse()?;
let where_clause: Option<WhereClause> = input.parse()?;
@@ -2221,7 +2287,7 @@
paren_token,
inputs,
output,
- variadic: None,
+ variadic,
generics: Generics {
where_clause,
..generics
@@ -2312,21 +2378,14 @@
Generics::default()
};
- let trait_ = {
- // TODO: optimize using advance_to
+ let trait_ = (|| -> Option<_> {
let ahead = input.fork();
- if ahead.parse::<Option<Token![!]>>().is_ok()
- && ahead.parse::<Path>().is_ok()
- && ahead.parse::<Token![for]>().is_ok()
- {
- let polarity: Option<Token![!]> = input.parse()?;
- let path: Path = input.parse()?;
- let for_token: Token![for] = input.parse()?;
- Some((polarity, path, for_token))
- } else {
- None
- }
- };
+ let polarity: Option<Token![!]> = ahead.parse().ok()?;
+ let path: Path = ahead.parse().ok()?;
+ let for_token: Token![for] = ahead.parse().ok()?;
+ input.advance_to(&ahead);
+ Some((polarity, path, for_token))
+ })();
let self_ty: Type = input.parse()?;
let where_clause: Option<WhereClause> = input.parse()?;
@@ -2358,6 +2417,7 @@
impl Parse for ImplItem {
fn parse(input: ParseStream) -> Result<Self> {
+ let begin = input.fork();
let mut attrs = input.call(Attribute::parse_outer)?;
let ahead = input.fork();
let vis: Visibility = ahead.parse()?;
@@ -2372,10 +2432,30 @@
};
let mut item = if lookahead.peek(Token![const]) {
- ahead.parse::<Token![const]>()?;
+ let const_token: Token![const] = ahead.parse()?;
let lookahead = ahead.lookahead1();
if lookahead.peek(Ident) {
- input.parse().map(ImplItem::Const)
+ input.advance_to(&ahead);
+ let ident: Ident = input.parse()?;
+ let colon_token: Token![:] = input.parse()?;
+ let ty: Type = input.parse()?;
+ if let Some(eq_token) = input.parse()? {
+ return Ok(ImplItem::Const(ImplItemConst {
+ attrs,
+ vis,
+ defaultness,
+ const_token,
+ ident,
+ colon_token,
+ ty,
+ eq_token,
+ expr: input.parse()?,
+ semi_token: input.parse()?,
+ }));
+ } else {
+ input.parse::<Token![;]>()?;
+ return Ok(ImplItem::Verbatim(verbatim::between(begin, input)));
+ }
} else if lookahead.peek(Token![unsafe])
|| lookahead.peek(Token![async])
|| lookahead.peek(Token![extern])
@@ -2392,7 +2472,41 @@
{
input.parse().map(ImplItem::Method)
} else if lookahead.peek(Token![type]) {
- input.parse().map(ImplItem::Type)
+ input.advance_to(&ahead);
+ let type_token: Token![type] = input.parse()?;
+ let ident: Ident = input.parse()?;
+ let mut generics: Generics = input.parse()?;
+ let colon_token: Option<Token![:]> = input.parse()?;
+ if colon_token.is_some() {
+ let mut first = true;
+ while !input.peek(Token![where])
+ && !input.peek(Token![=])
+ && !input.peek(Token![;])
+ {
+ if !first {
+ input.parse::<Token![+]>()?;
+ }
+ input.parse::<TypeParamBound>()?;
+ first = false;
+ }
+ }
+ generics.where_clause = input.parse()?;
+ if let Some(eq_token) = input.parse()? {
+ return Ok(ImplItem::Type(ImplItemType {
+ attrs,
+ vis,
+ defaultness,
+ type_token,
+ ident,
+ generics,
+ eq_token,
+ ty: input.parse()?,
+ semi_token: input.parse()?,
+ }));
+ } else {
+ input.parse::<Token![;]>()?;
+ return Ok(ImplItem::Verbatim(verbatim::between(begin, input)));
+ }
} else if vis.is_inherited() && defaultness.is_none() && lookahead.peek(existential) {
input.call(item_existential).map(ImplItem::Verbatim)
} else if vis.is_inherited()
@@ -2445,7 +2559,7 @@
impl Parse for ImplItemMethod {
fn parse(input: ParseStream) -> Result<Self> {
- let outer_attrs = input.call(Attribute::parse_outer)?;
+ let mut attrs = input.call(Attribute::parse_outer)?;
let vis: Visibility = input.parse()?;
let defaultness: Option<Token![default]> = input.parse()?;
let constness: Option<Token![const]> = input.parse()?;
@@ -2458,18 +2572,36 @@
let content;
let paren_token = parenthesized!(content in input);
- let inputs = content.parse_terminated(FnArg::parse)?;
+ let mut inputs = parse_fn_args(&content)?;
+ let variadic = pop_variadic(&mut inputs);
let output: ReturnType = input.parse()?;
let where_clause: Option<WhereClause> = input.parse()?;
- let content;
- let brace_token = braced!(content in input);
- let inner_attrs = content.call(Attribute::parse_inner)?;
- let stmts = content.call(Block::parse_within)?;
+ let block = if let Some(semi) = input.parse::<Option<Token![;]>>()? {
+ // Accept methods without a body in an impl block because
+ // rustc's *parser* does not reject them (the compilation error
+ // is emitted later than parsing) and it can be useful for macro
+ // DSLs.
+ let mut punct = Punct::new(';', Spacing::Alone);
+ punct.set_span(semi.span);
+ let tokens = TokenStream::from_iter(vec![TokenTree::Punct(punct)]);
+ Block {
+ brace_token: Brace::default(),
+ stmts: vec![Stmt::Item(Item::Verbatim(tokens))],
+ }
+ } else {
+ let content;
+ let brace_token = braced!(content in input);
+ attrs.extend(content.call(Attribute::parse_inner)?);
+ Block {
+ brace_token,
+ stmts: content.call(Block::parse_within)?,
+ }
+ };
Ok(ImplItemMethod {
- attrs: private::attrs(outer_attrs, inner_attrs),
+ attrs,
vis,
defaultness,
sig: Signature {
@@ -2482,13 +2614,13 @@
paren_token,
inputs,
output,
- variadic: None,
+ variadic,
generics: Generics {
where_clause,
..generics
},
},
- block: Block { brace_token, stmts },
+ block,
})
}
}
@@ -2558,6 +2690,7 @@
use crate::attr::FilterAttrs;
use crate::print::TokensOrDefault;
+ use crate::punctuated::Pair;
impl ToTokens for ItemExternCrate {
fn to_tokens(&self, tokens: &mut TokenStream) {
@@ -2922,6 +3055,14 @@
self.vis.to_tokens(tokens);
self.defaultness.to_tokens(tokens);
self.sig.to_tokens(tokens);
+ if self.block.stmts.len() == 1 {
+ if let Stmt::Item(Item::Verbatim(verbatim)) = &self.block.stmts[0] {
+ if verbatim.to_string() == ";" {
+ verbatim.to_tokens(tokens);
+ return;
+ }
+ }
+ }
self.block.brace_token.surround(tokens, |tokens| {
tokens.append_all(self.attrs.inner());
tokens.append_all(&self.block.stmts);
@@ -2992,23 +3133,31 @@
}
}
- fn has_variadic(inputs: &Punctuated<FnArg, Token![,]>) -> bool {
- let last = match inputs.last() {
- Some(last) => last,
- None => return false,
+ fn maybe_variadic_to_tokens(arg: &FnArg, tokens: &mut TokenStream) -> bool {
+ let arg = match arg {
+ FnArg::Typed(arg) => arg,
+ FnArg::Receiver(receiver) => {
+ receiver.to_tokens(tokens);
+ return false;
+ }
};
- let pat = match last {
- FnArg::Typed(pat) => pat,
- FnArg::Receiver(_) => return false,
- };
-
- let tokens = match pat.ty.as_ref() {
- Type::Verbatim(tokens) => tokens,
- _ => return false,
- };
-
- tokens.to_string() == "..."
+ match arg.ty.as_ref() {
+ Type::Verbatim(ty) if ty.to_string() == "..." => {
+ match arg.pat.as_ref() {
+ Pat::Verbatim(pat) if pat.to_string() == "..." => {
+ tokens.append_all(arg.attrs.outer());
+ pat.to_tokens(tokens);
+ }
+ _ => arg.to_tokens(tokens),
+ }
+ true
+ }
+ _ => {
+ arg.to_tokens(tokens);
+ false
+ }
+ }
}
impl ToTokens for Signature {
@@ -3021,8 +3170,19 @@
self.ident.to_tokens(tokens);
self.generics.to_tokens(tokens);
self.paren_token.surround(tokens, |tokens| {
- self.inputs.to_tokens(tokens);
- if self.variadic.is_some() && !has_variadic(&self.inputs) {
+ let mut last_is_variadic = false;
+ for input in self.inputs.pairs() {
+ match input {
+ Pair::Punctuated(input, comma) => {
+ maybe_variadic_to_tokens(input, tokens);
+ comma.to_tokens(tokens);
+ }
+ Pair::End(input) => {
+ last_is_variadic = maybe_variadic_to_tokens(input, tokens);
+ }
+ }
+ }
+ if self.variadic.is_some() && !last_is_variadic {
if !self.inputs.empty_or_trailing() {
<Token![,]>::default().to_tokens(tokens);
}
diff --git a/src/keyword.rs b/src/keyword.rs
deleted file mode 100644
index e69de29..0000000
--- a/src/keyword.rs
+++ /dev/null
diff --git a/src/lib.rs b/src/lib.rs
index 2a2ff61..c88a813 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -242,7 +242,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.7")]
+#![doc(html_root_url = "https://docs.rs/syn/1.0.16")]
#![deny(clippy::all, clippy::pedantic)]
// Ignored clippy lints.
#![allow(
@@ -265,6 +265,8 @@
clippy::empty_enum,
clippy::if_not_else,
clippy::items_after_statements,
+ clippy::match_same_arms,
+ clippy::missing_errors_doc,
clippy::module_name_repetitions,
clippy::must_use_candidate,
clippy::shadow_unrelated,
@@ -760,6 +762,8 @@
mod custom_keyword;
mod custom_punctuation;
mod sealed;
+mod span;
+mod thread;
#[cfg(feature = "parsing")]
mod lookahead;
@@ -767,13 +771,12 @@
#[cfg(feature = "parsing")]
pub mod parse;
-mod span;
+#[cfg(all(feature = "parsing", feature = "full"))]
+mod verbatim;
#[cfg(all(any(feature = "full", feature = "derive"), feature = "printing"))]
mod print;
-mod thread;
-
////////////////////////////////////////////////////////////////////////////////
#[allow(dead_code, non_camel_case_types)]
diff --git a/src/lit.rs b/src/lit.rs
index abc4ec2..0f271e4 100644
--- a/src/lit.rs
+++ b/src/lit.rs
@@ -403,18 +403,23 @@
impl LitInt {
pub fn new(repr: &str, span: Span) -> Self {
- if let Some((digits, suffix)) = value::parse_lit_int(repr) {
- let mut token = value::to_literal(repr);
- token.set_span(span);
- LitInt {
- repr: Box::new(LitIntRepr {
- token,
- digits,
- suffix,
- }),
- }
- } else {
- panic!("Not an integer literal: `{}`", repr);
+ let (digits, suffix) = match value::parse_lit_int(repr) {
+ Some(parse) => parse,
+ None => panic!("Not an integer literal: `{}`", repr),
+ };
+
+ let mut token = match value::to_literal(repr, &digits, &suffix) {
+ Some(token) => token,
+ None => panic!("Unsupported integer literal: `{}`", repr),
+ };
+
+ token.set_span(span);
+ LitInt {
+ repr: Box::new(LitIntRepr {
+ token,
+ digits,
+ suffix,
+ }),
}
}
@@ -492,18 +497,23 @@
impl LitFloat {
pub fn new(repr: &str, span: Span) -> Self {
- if let Some((digits, suffix)) = value::parse_lit_float(repr) {
- let mut token = value::to_literal(repr);
- token.set_span(span);
- LitFloat {
- repr: Box::new(LitFloatRepr {
- token,
- digits,
- suffix,
- }),
- }
- } else {
- panic!("Not a float literal: `{}`", repr);
+ let (digits, suffix) = match value::parse_lit_float(repr) {
+ Some(parse) => parse,
+ None => panic!("Not a float literal: `{}`", repr),
+ };
+
+ let mut token = match value::to_literal(repr, &digits, &suffix) {
+ Some(token) => token,
+ None => panic!("Unsupported float literal: `{}`", repr),
+ };
+
+ token.set_span(span);
+ LitFloat {
+ repr: Box::new(LitFloatRepr {
+ token,
+ digits,
+ suffix,
+ }),
}
}
@@ -691,7 +701,9 @@
#[cfg(feature = "parsing")]
pub mod parsing {
use super::*;
+ use crate::buffer::Cursor;
use crate::parse::{Parse, ParseStream, Result};
+ use proc_macro2::Punct;
impl Parse for Lit {
fn parse(input: ParseStream) -> Result<Self> {
@@ -699,25 +711,73 @@
if let Some((lit, rest)) = cursor.literal() {
return Ok((Lit::new(lit), rest));
}
- while let Some((ident, rest)) = cursor.ident() {
- let value = if ident == "true" {
- true
- } else if ident == "false" {
- false
- } else {
- break;
- };
- let lit_bool = LitBool {
- value,
- span: ident.span(),
- };
- return Ok((Lit::Bool(lit_bool), rest));
+
+ if let Some((ident, rest)) = cursor.ident() {
+ let value = ident == "true";
+ if value || ident == "false" {
+ let lit_bool = LitBool {
+ value,
+ span: ident.span(),
+ };
+ return Ok((Lit::Bool(lit_bool), rest));
+ }
}
+
+ if let Some((punct, rest)) = cursor.punct() {
+ if punct.as_char() == '-' {
+ if let Some((lit, rest)) = parse_negative_lit(punct, rest) {
+ return Ok((lit, rest));
+ }
+ }
+ }
+
Err(cursor.error("expected literal"))
})
}
}
+ fn parse_negative_lit(neg: Punct, cursor: Cursor) -> Option<(Lit, Cursor)> {
+ let (lit, rest) = cursor.literal()?;
+
+ let mut span = neg.span();
+ span = span.join(lit.span()).unwrap_or(span);
+
+ let mut repr = lit.to_string();
+ repr.insert(0, '-');
+
+ if !(repr.ends_with("f32") || repr.ends_with("f64")) {
+ if let Some((digits, suffix)) = value::parse_lit_int(&repr) {
+ if let Some(mut token) = value::to_literal(&repr, &digits, &suffix) {
+ token.set_span(span);
+ return Some((
+ Lit::Int(LitInt {
+ repr: Box::new(LitIntRepr {
+ token,
+ digits,
+ suffix,
+ }),
+ }),
+ rest,
+ ));
+ }
+ }
+ }
+
+ let (digits, suffix) = value::parse_lit_float(&repr)?;
+ let mut token = value::to_literal(&repr, &digits, &suffix)?;
+ token.set_span(span);
+ Some((
+ Lit::Float(LitFloat {
+ repr: Box::new(LitFloatRepr {
+ token,
+ digits,
+ suffix,
+ }),
+ }),
+ rest,
+ ))
+ }
+
impl Parse for LitStr {
fn parse(input: ParseStream) -> Result<Self> {
let head = input.fork();
@@ -1372,11 +1432,33 @@
}
}
- pub fn to_literal(s: &str) -> Literal {
- let stream = s.parse::<TokenStream>().unwrap();
- match stream.into_iter().next().unwrap() {
- TokenTree::Literal(l) => l,
- _ => unreachable!(),
+ pub fn to_literal(repr: &str, digits: &str, suffix: &str) -> Option<Literal> {
+ if repr.starts_with('-') {
+ if suffix == "f64" {
+ digits.parse().ok().map(Literal::f64_suffixed)
+ } else if suffix == "f32" {
+ digits.parse().ok().map(Literal::f32_suffixed)
+ } else if suffix == "i64" {
+ digits.parse().ok().map(Literal::i64_suffixed)
+ } else if suffix == "i32" {
+ digits.parse().ok().map(Literal::i32_suffixed)
+ } else if suffix == "i16" {
+ digits.parse().ok().map(Literal::i16_suffixed)
+ } else if suffix == "i8" {
+ digits.parse().ok().map(Literal::i8_suffixed)
+ } else if !suffix.is_empty() {
+ None
+ } else if digits.contains('.') {
+ digits.parse().ok().map(Literal::f64_unsuffixed)
+ } else {
+ digits.parse().ok().map(Literal::i64_unsuffixed)
+ }
+ } else {
+ let stream = repr.parse::<TokenStream>().unwrap();
+ match stream.into_iter().next().unwrap() {
+ TokenTree::Literal(l) => Some(l),
+ _ => unreachable!(),
+ }
}
}
}
diff --git a/src/parse.rs b/src/parse.rs
index 0dd698b..96eecce 100644
--- a/src/parse.rs
+++ b/src/parse.rs
@@ -261,13 +261,16 @@
// the cell.
cell: Cell<Cursor<'static>>,
marker: PhantomData<Cursor<'a>>,
- unexpected: Rc<Cell<Option<Span>>>,
+ unexpected: Cell<Option<Rc<Cell<Unexpected>>>>,
}
impl<'a> Drop for ParseBuffer<'a> {
fn drop(&mut self) {
- if !self.is_empty() && self.unexpected.get().is_none() {
- self.unexpected.set(Some(self.cursor().span()));
+ if !self.is_empty() {
+ let (inner, old_span) = inner_unexpected(self);
+ if old_span.is_none() {
+ inner.set(Unexpected::Some(self.cursor().span()));
+ }
}
}
}
@@ -371,36 +374,55 @@
unsafe { mem::transmute::<Cursor<'c>, Cursor<'a>>(to) }
}
-fn skip(input: ParseStream) -> bool {
- input
- .step(|cursor| {
- if let Some((_lifetime, rest)) = cursor.lifetime() {
- Ok((true, rest))
- } else if let Some((_token, rest)) = cursor.token_tree() {
- Ok((true, rest))
- } else {
- Ok((false, *cursor))
- }
- })
- .unwrap()
-}
-
pub(crate) fn new_parse_buffer(
scope: Span,
cursor: Cursor,
- unexpected: Rc<Cell<Option<Span>>>,
+ unexpected: Rc<Cell<Unexpected>>,
) -> ParseBuffer {
ParseBuffer {
scope,
// See comment on `cell` in the struct definition.
cell: Cell::new(unsafe { mem::transmute::<Cursor, Cursor<'static>>(cursor) }),
marker: PhantomData,
- unexpected,
+ unexpected: Cell::new(Some(unexpected)),
}
}
-pub(crate) fn get_unexpected(buffer: &ParseBuffer) -> Rc<Cell<Option<Span>>> {
- buffer.unexpected.clone()
+#[derive(Clone)]
+pub(crate) enum Unexpected {
+ None,
+ Some(Span),
+ Chain(Rc<Cell<Unexpected>>),
+}
+
+impl Default for Unexpected {
+ fn default() -> Self {
+ Unexpected::None
+ }
+}
+
+// We call this on Cell<Unexpected> and Cell<Option<T>> where temporarily
+// swapping in a None is cheap.
+fn cell_clone<T: Default + Clone>(cell: &Cell<T>) -> T {
+ let prev = cell.take();
+ let ret = prev.clone();
+ cell.set(prev);
+ ret
+}
+
+fn inner_unexpected(buffer: &ParseBuffer) -> (Rc<Cell<Unexpected>>, Option<Span>) {
+ let mut unexpected = get_unexpected(buffer);
+ loop {
+ match cell_clone(&unexpected) {
+ Unexpected::None => return (unexpected, None),
+ Unexpected::Some(span) => return (unexpected, Some(span)),
+ Unexpected::Chain(next) => unexpected = next,
+ }
+ }
+}
+
+pub(crate) fn get_unexpected(buffer: &ParseBuffer) -> Rc<Cell<Unexpected>> {
+ cell_clone(&buffer.unexpected).unwrap()
}
impl<'a> ParseBuffer<'a> {
@@ -562,14 +584,17 @@
/// }
/// ```
pub fn peek2<T: Peek>(&self, token: T) -> bool {
- let ahead = self.fork();
- skip(&ahead) && ahead.peek(token)
+ let _ = token;
+ self.cursor().skip().map_or(false, T::Token::peek)
}
/// Looks at the third-next token in the parse stream.
pub fn peek3<T: Peek>(&self, token: T) -> bool {
- let ahead = self.fork();
- skip(&ahead) && skip(&ahead) && ahead.peek(token)
+ let _ = token;
+ self.cursor()
+ .skip()
+ .and_then(Cursor::skip)
+ .map_or(false, T::Token::peek)
}
/// Parses zero or more occurrences of `T` separated by punctuation of type
@@ -841,8 +866,8 @@
cell: self.cell.clone(),
marker: PhantomData,
// Not the parent's unexpected. Nothing cares whether the clone
- // parses all the way.
- unexpected: Rc::new(Cell::new(None)),
+ // parses all the way unless we `advance_to`.
+ unexpected: Cell::new(Some(Rc::new(Cell::new(Unexpected::None)))),
}
}
@@ -953,6 +978,18 @@
Ok(node)
}
+ /// Returns the `Span` of the next token in the parse stream, or
+ /// `Span::call_site()` if this parse stream has completely exhausted its
+ /// input `TokenStream`.
+ pub fn span(&self) -> Span {
+ let cursor = self.cursor();
+ if cursor.eof() {
+ self.scope
+ } else {
+ crate::buffer::open_span_of_group(cursor)
+ }
+ }
+
/// Provides low-level access to the token representation underlying this
/// parse stream.
///
@@ -963,7 +1000,7 @@
}
fn check_unexpected(&self) -> Result<()> {
- match self.unexpected.get() {
+ match inner_unexpected(self).1 {
Some(span) => Err(Error::new(span, "unexpected token")),
None => Ok(()),
}
@@ -1095,7 +1132,7 @@
fn tokens_to_parse_buffer(tokens: &TokenBuffer) -> ParseBuffer {
let scope = Span::call_site();
let cursor = tokens.begin();
- let unexpected = Rc::new(Cell::new(None));
+ let unexpected = Rc::new(Cell::new(Unexpected::None));
new_parse_buffer(scope, cursor, unexpected)
}
@@ -1121,7 +1158,7 @@
fn __parse_scoped(self, scope: Span, tokens: TokenStream) -> Result<Self::Output> {
let buf = TokenBuffer::new2(tokens);
let cursor = buf.begin();
- let unexpected = Rc::new(Cell::new(None));
+ let unexpected = Rc::new(Cell::new(Unexpected::None));
let state = new_parse_buffer(scope, cursor, unexpected);
let node = self(&state)?;
state.check_unexpected()?;
diff --git a/src/parse_quote.rs b/src/parse_quote.rs
index 18a47b9..96e4765 100644
--- a/src/parse_quote.rs
+++ b/src/parse_quote.rs
@@ -56,8 +56,10 @@
/// or inner like `#![...]`
/// - [`Punctuated<T, P>`] — parses zero or more `T` separated by punctuation
/// `P` with optional trailing punctuation
+/// - [`Vec<Stmt>`] — parses the same as `Block::parse_within`
///
/// [`Punctuated<T, P>`]: punctuated::Punctuated
+/// [`Vec<Stmt>`]: Block::parse_within
///
/// # Panics
///
@@ -112,6 +114,8 @@
use crate::punctuated::Punctuated;
#[cfg(any(feature = "full", feature = "derive"))]
use crate::{attr, Attribute};
+#[cfg(feature = "full")]
+use crate::{Block, Stmt};
#[cfg(any(feature = "full", feature = "derive"))]
impl ParseQuote for Attribute {
@@ -129,3 +133,10 @@
Self::parse_terminated(input)
}
}
+
+#[cfg(feature = "full")]
+impl ParseQuote for Vec<Stmt> {
+ fn parse(input: ParseStream) -> Result<Self> {
+ Block::parse_within(input)
+ }
+}
diff --git a/src/pat.rs b/src/pat.rs
index 262129b..fac651e 100644
--- a/src/pat.rs
+++ b/src/pat.rs
@@ -386,11 +386,12 @@
use super::*;
use crate::ext::IdentExt;
- use crate::parse::{Parse, ParseStream, Result};
+ use crate::parse::{Parse, ParseBuffer, ParseStream, Result};
use crate::path;
impl Parse for Pat {
fn parse(input: ParseStream) -> Result<Self> {
+ let begin = input.fork();
let lookahead = input.lookahead1();
if lookahead.peek(Ident)
&& ({
@@ -434,7 +435,7 @@
} else if lookahead.peek(token::Bracket) {
input.call(pat_slice).map(Pat::Slice)
} else if lookahead.peek(Token![..]) && !input.peek(Token![...]) {
- input.call(pat_rest).map(Pat::Rest)
+ pat_range_half_open(input, begin)
} else {
Err(lookahead.error())
}
@@ -442,10 +443,11 @@
}
fn pat_path_or_macro_or_struct_or_range(input: ParseStream) -> Result<Pat> {
+ let begin = input.fork();
let (qself, path) = path::parsing::qpath(input, true)?;
if input.peek(Token![..]) {
- return pat_range(input, qself, path).map(Pat::Range);
+ return pat_range(input, begin, qself, path);
}
if qself.is_some() {
@@ -487,7 +489,7 @@
} else if input.peek(token::Paren) {
pat_tuple_struct(input, path).map(Pat::TupleStruct)
} else if input.peek(Token![..]) {
- pat_range(input, qself, path).map(Pat::Range)
+ pat_range(input, begin, qself, path)
} else {
Ok(Pat::Path(PatPath {
attrs: Vec::new(),
@@ -624,17 +626,44 @@
})
}
- fn pat_range(input: ParseStream, qself: Option<QSelf>, path: Path) -> Result<PatRange> {
- Ok(PatRange {
- attrs: Vec::new(),
- lo: Box::new(Expr::Path(ExprPath {
+ fn pat_range(
+ input: ParseStream,
+ begin: ParseBuffer,
+ qself: Option<QSelf>,
+ path: Path,
+ ) -> Result<Pat> {
+ let limits: RangeLimits = input.parse()?;
+ let hi = input.call(pat_lit_expr)?;
+ if let Some(hi) = hi {
+ Ok(Pat::Range(PatRange {
attrs: Vec::new(),
- qself,
- path,
- })),
- limits: input.parse()?,
- hi: input.call(pat_lit_expr)?,
- })
+ lo: Box::new(Expr::Path(ExprPath {
+ attrs: Vec::new(),
+ qself,
+ path,
+ })),
+ limits,
+ hi,
+ }))
+ } else {
+ Ok(Pat::Verbatim(verbatim::between(begin, input)))
+ }
+ }
+
+ fn pat_range_half_open(input: ParseStream, begin: ParseBuffer) -> Result<Pat> {
+ let limits: RangeLimits = input.parse()?;
+ let hi = input.call(pat_lit_expr)?;
+ if hi.is_some() {
+ Ok(Pat::Verbatim(verbatim::between(begin, input)))
+ } else {
+ match limits {
+ RangeLimits::HalfOpen(dot2_token) => Ok(Pat::Rest(PatRest {
+ attrs: Vec::new(),
+ dot2_token,
+ })),
+ RangeLimits::Closed(_) => Err(input.error("expected range upper bound")),
+ }
+ }
}
fn pat_tuple(input: ParseStream) -> Result<PatTuple> {
@@ -669,14 +698,21 @@
}
fn pat_lit_or_range(input: ParseStream) -> Result<Pat> {
- let lo = input.call(pat_lit_expr)?;
+ let begin = input.fork();
+ let lo = input.call(pat_lit_expr)?.unwrap();
if input.peek(Token![..]) {
- Ok(Pat::Range(PatRange {
- attrs: Vec::new(),
- lo,
- limits: input.parse()?,
- hi: input.call(pat_lit_expr)?,
- }))
+ let limits: RangeLimits = input.parse()?;
+ let hi = input.call(pat_lit_expr)?;
+ if let Some(hi) = hi {
+ Ok(Pat::Range(PatRange {
+ attrs: Vec::new(),
+ lo,
+ limits,
+ hi,
+ }))
+ } else {
+ Ok(Pat::Verbatim(verbatim::between(begin, input)))
+ }
} else {
Ok(Pat::Lit(PatLit {
attrs: Vec::new(),
@@ -685,7 +721,17 @@
}
}
- fn pat_lit_expr(input: ParseStream) -> Result<Box<Expr>> {
+ fn pat_lit_expr(input: ParseStream) -> Result<Option<Box<Expr>>> {
+ if input.is_empty()
+ || input.peek(Token![|])
+ || input.peek(Token![=>])
+ || input.peek(Token![:]) && !input.peek(Token![::])
+ || input.peek(Token![,])
+ || input.peek(Token![;])
+ {
+ return Ok(None);
+ }
+
let neg: Option<Token![-]> = input.parse()?;
let lookahead = input.lookahead1();
@@ -705,7 +751,7 @@
return Err(lookahead.error());
};
- Ok(Box::new(if let Some(neg) = neg {
+ Ok(Some(Box::new(if let Some(neg) = neg {
Expr::Unary(ExprUnary {
attrs: Vec::new(),
op: UnOp::Neg(neg),
@@ -713,7 +759,7 @@
})
} else {
expr
- }))
+ })))
}
fn pat_slice(input: ParseStream) -> Result<PatSlice> {
@@ -737,13 +783,6 @@
elems,
})
}
-
- fn pat_rest(input: ParseStream) -> Result<PatRest> {
- Ok(PatRest {
- attrs: Vec::new(),
- dot2_token: input.parse()?,
- })
- }
}
#[cfg(feature = "printing")]
diff --git a/src/punctuated.rs b/src/punctuated.rs
index 38c7bf4..e02ac44 100644
--- a/src/punctuated.rs
+++ b/src/punctuated.rs
@@ -76,22 +76,19 @@
self.iter().next()
}
+ /// Mutably borrows the first element in this sequence.
+ pub fn first_mut(&mut self) -> Option<&mut T> {
+ self.iter_mut().next()
+ }
+
/// Borrows the last element in this sequence.
pub fn last(&self) -> Option<&T> {
- if self.last.is_some() {
- self.last.as_ref().map(Box::as_ref)
- } else {
- self.inner.last().map(|pair| &pair.0)
- }
+ self.iter().next_back()
}
/// Mutably borrows the last element in this sequence.
pub fn last_mut(&mut self) -> Option<&mut T> {
- if self.last.is_some() {
- self.last.as_mut().map(Box::as_mut)
- } else {
- self.inner.last_mut().map(|pair| &mut pair.0)
- }
+ self.iter_mut().next_back()
}
/// Returns an iterator over borrowed syntax tree nodes of type `&T`.
diff --git a/src/stmt.rs b/src/stmt.rs
index e4277fd..acee5a3 100644
--- a/src/stmt.rs
+++ b/src/stmt.rs
@@ -47,8 +47,10 @@
pub mod parsing {
use super::*;
+ use crate::parse::discouraged::Speculative;
use crate::parse::{Parse, ParseStream, Result};
use crate::punctuated::Punctuated;
+ use proc_macro2::TokenStream;
impl Block {
/// Parse the body of a block as zero or more statements, possibly
@@ -106,8 +108,8 @@
pub fn parse_within(input: ParseStream) -> Result<Vec<Stmt>> {
let mut stmts = Vec::new();
loop {
- while input.peek(Token![;]) {
- input.parse::<Token![;]>()?;
+ while let Some(semi) = input.parse::<Option<Token![;]>>()? {
+ stmts.push(Stmt::Semi(Expr::Verbatim(TokenStream::new()), semi));
}
if input.is_empty() {
break;
@@ -146,55 +148,55 @@
}
fn parse_stmt(input: ParseStream, allow_nosemi: bool) -> Result<Stmt> {
- // TODO: optimize using advance_to
- let ahead = input.fork();
- ahead.call(Attribute::parse_outer)?;
+ let mut attrs = input.call(Attribute::parse_outer)?;
- if {
- let ahead = ahead.fork();
- // Only parse braces here; paren and bracket will get parsed as
- // expression statements
- ahead.call(Path::parse_mod_style).is_ok()
- && ahead.parse::<Token![!]>().is_ok()
- && (ahead.peek(token::Brace) || ahead.peek(Ident))
- } {
- stmt_mac(input)
- } else if ahead.peek(Token![let]) {
- stmt_local(input).map(Stmt::Local)
- } else if ahead.peek(Token![pub])
- || ahead.peek(Token![crate]) && !ahead.peek2(Token![::])
- || ahead.peek(Token![extern]) && !ahead.peek2(Token![::])
- || ahead.peek(Token![use])
- || ahead.peek(Token![static]) && (ahead.peek2(Token![mut]) || ahead.peek2(Ident))
- || ahead.peek(Token![const])
- || ahead.peek(Token![unsafe]) && !ahead.peek2(token::Brace)
- || ahead.peek(Token![async])
- && (ahead.peek2(Token![unsafe])
- || ahead.peek2(Token![extern])
- || ahead.peek2(Token![fn]))
- || ahead.peek(Token![fn])
- || ahead.peek(Token![mod])
- || ahead.peek(Token![type])
- || ahead.peek(item::parsing::existential) && ahead.peek2(Token![type])
- || ahead.peek(Token![struct])
- || ahead.peek(Token![enum])
- || ahead.peek(Token![union]) && ahead.peek2(Ident)
- || ahead.peek(Token![auto]) && ahead.peek2(Token![trait])
- || ahead.peek(Token![trait])
- || ahead.peek(Token![default])
- && (ahead.peek2(Token![unsafe]) || ahead.peek2(Token![impl]))
- || ahead.peek(Token![impl])
- || ahead.peek(Token![macro])
+ // brace-style macros; paren and bracket macros get parsed as
+ // expression statements.
+ let ahead = input.fork();
+ if let Ok(path) = ahead.call(Path::parse_mod_style) {
+ if ahead.peek(Token![!]) && (ahead.peek2(token::Brace) || ahead.peek2(Ident)) {
+ input.advance_to(&ahead);
+ return stmt_mac(input, attrs, path);
+ }
+ }
+
+ if input.peek(Token![let]) {
+ stmt_local(input, attrs).map(Stmt::Local)
+ } else if input.peek(Token![pub])
+ || input.peek(Token![crate]) && !input.peek2(Token![::])
+ || input.peek(Token![extern]) && !input.peek2(Token![::])
+ || input.peek(Token![use])
+ || input.peek(Token![static]) && (input.peek2(Token![mut]) || input.peek2(Ident))
+ || input.peek(Token![const])
+ || input.peek(Token![unsafe]) && !input.peek2(token::Brace)
+ || input.peek(Token![async])
+ && (input.peek2(Token![unsafe])
+ || input.peek2(Token![extern])
+ || input.peek2(Token![fn]))
+ || input.peek(Token![fn])
+ || input.peek(Token![mod])
+ || input.peek(Token![type])
+ || input.peek(item::parsing::existential) && input.peek2(Token![type])
+ || input.peek(Token![struct])
+ || input.peek(Token![enum])
+ || input.peek(Token![union]) && input.peek2(Ident)
+ || input.peek(Token![auto]) && input.peek2(Token![trait])
+ || input.peek(Token![trait])
+ || input.peek(Token![default])
+ && (input.peek2(Token![unsafe]) || input.peek2(Token![impl]))
+ || input.peek(Token![impl])
+ || input.peek(Token![macro])
{
- input.parse().map(Stmt::Item)
+ let mut item: Item = input.parse()?;
+ attrs.extend(item.replace_attrs(Vec::new()));
+ item.replace_attrs(attrs);
+ Ok(Stmt::Item(item))
} else {
- stmt_expr(input, allow_nosemi)
+ stmt_expr(input, allow_nosemi, attrs)
}
}
- fn stmt_mac(input: ParseStream) -> Result<Stmt> {
- let attrs = input.call(Attribute::parse_outer)?;
- let path = input.call(Path::parse_mod_style)?;
+ fn stmt_mac(input: ParseStream, attrs: Vec<Attribute>, path: Path) -> Result<Stmt> {
let bang_token: Token![!] = input.parse()?;
let ident: Option<Ident> = input.parse()?;
let (delimiter, tokens) = mac::parse_delimiter(input)?;
@@ -213,9 +215,9 @@
})))
}
- fn stmt_local(input: ParseStream) -> Result<Local> {
+ fn stmt_local(input: ParseStream, attrs: Vec<Attribute>) -> Result<Local> {
Ok(Local {
- attrs: input.call(Attribute::parse_outer)?,
+ attrs,
let_token: input.parse()?,
pat: {
let leading_vert: Option<Token![|]> = input.parse()?;
@@ -265,8 +267,11 @@
})
}
- fn stmt_expr(input: ParseStream, allow_nosemi: bool) -> Result<Stmt> {
- let mut attrs = input.call(Attribute::parse_outer)?;
+ fn stmt_expr(
+ input: ParseStream,
+ allow_nosemi: bool,
+ mut attrs: Vec<Attribute>,
+ ) -> Result<Stmt> {
let mut e = expr::parsing::expr_early(input)?;
attrs.extend(e.replace_attrs(Vec::new()));
diff --git a/src/token.rs b/src/token.rs
index 0b8c181..0b4ad88 100644
--- a/src/token.rs
+++ b/src/token.rs
@@ -88,7 +88,6 @@
//! [Printing]: https://docs.rs/quote/1.0/quote/trait.ToTokens.html
//! [`Span`]: https://docs.rs/proc-macro2/1.0/proc_macro2/struct.Span.html
-use std;
#[cfg(feature = "extra-traits")]
use std::cmp;
#[cfg(feature = "extra-traits")]
@@ -158,11 +157,12 @@
#[cfg(any(feature = "full", feature = "derive"))]
#[cfg(feature = "parsing")]
fn peek_impl(cursor: Cursor, peek: fn(ParseStream) -> bool) -> bool {
+ use crate::parse::Unexpected;
use std::cell::Cell;
use std::rc::Rc;
let scope = Span::call_site();
- let unexpected = Rc::new(Cell::new(None));
+ let unexpected = Rc::new(Cell::new(Unexpected::None));
let buffer = crate::parse::new_parse_buffer(scope, cursor, unexpected);
peek(&buffer)
}
@@ -855,7 +855,7 @@
}
pub fn punct<S: FromSpans>(input: ParseStream, token: &str) -> Result<S> {
- let mut spans = [input.cursor().span(); 3];
+ let mut spans = [input.span(); 3];
punct_helper(input, token, &mut spans)?;
Ok(S::from_spans(&spans))
}
diff --git a/src/ty.rs b/src/ty.rs
index 1e5b51f..b2e086b 100644
--- a/src/ty.rs
+++ b/src/ty.rs
@@ -784,9 +784,27 @@
impl Parse for TypeTuple {
fn parse(input: ParseStream) -> Result<Self> {
let content;
+ let paren_token = parenthesized!(content in input);
+
+ if content.is_empty() {
+ return Ok(TypeTuple {
+ paren_token,
+ elems: Punctuated::new(),
+ });
+ }
+
+ let first: Type = content.parse()?;
Ok(TypeTuple {
- paren_token: parenthesized!(content in input),
- elems: content.parse_terminated(Type::parse)?,
+ paren_token,
+ elems: {
+ let mut elems = Punctuated::new();
+ elems.push_value(first);
+ elems.push_punct(content.parse()?);
+ let rest: Punctuated<Type, Token![,]> =
+ content.parse_terminated(Parse::parse)?;
+ elems.extend(rest);
+ elems
+ },
})
}
}
diff --git a/src/verbatim.rs b/src/verbatim.rs
new file mode 100644
index 0000000..0686352
--- /dev/null
+++ b/src/verbatim.rs
@@ -0,0 +1,15 @@
+use crate::parse::{ParseBuffer, ParseStream};
+use proc_macro2::TokenStream;
+use std::iter;
+
+pub fn between<'a>(begin: ParseBuffer<'a>, end: ParseStream<'a>) -> TokenStream {
+ let end = end.cursor();
+ let mut cursor = begin.cursor();
+ let mut tokens = TokenStream::new();
+ while cursor != end {
+ let (tt, next) = cursor.token_tree().unwrap();
+ tokens.extend(iter::once(tt));
+ cursor = next;
+ }
+ tokens
+}
diff --git a/tests/clone.sh b/tests/clone.sh
deleted file mode 100755
index 03d0c22..0000000
--- a/tests/clone.sh
+++ /dev/null
@@ -1,16 +0,0 @@
-#!/bin/bash
-
-REV=7979016aff545f7b41cc517031026020b340989d
-
-set -euo pipefail
-cd "$(dirname "${BASH_SOURCE[0]}")"
-mkdir -p rust
-touch rust/COMMIT
-
-if [ "$(cat rust/COMMIT)" != "$REV" ]; then
- rm -rf rust
- mkdir rust
- curl -L "https://github.com/rust-lang/rust/archive/${REV}.tar.gz" \
- | tar xz --directory rust --strip-components 1
- echo "$REV" > rust/COMMIT
-fi
diff --git a/tests/common/eq.rs b/tests/common/eq.rs
index 199e0fe..e03b25b 100644
--- a/tests/common/eq.rs
+++ b/tests/common/eq.rs
@@ -1,36 +1,33 @@
extern crate rustc_data_structures;
+extern crate rustc_span;
extern crate rustc_target;
extern crate syntax;
-extern crate syntax_pos;
use std::mem;
use rustc_data_structures::sync::Lrc;
use rustc_data_structures::thin_vec::ThinVec;
-use rustc_target::abi::FloatTy;
-use rustc_target::spec::abi::Abi;
+use rustc_span::source_map::Spanned;
+use rustc_span::{sym, Span, Symbol, SyntaxContext, DUMMY_SP};
use syntax::ast::{
- AngleBracketedArgs, AnonConst, Arm, AsmDialect, AssocTyConstraint, AssocTyConstraintKind,
- AttrId, AttrItem, AttrStyle, Attribute, BareFnTy, BinOpKind, BindingMode, Block,
- BlockCheckMode, CaptureBy, Constness, Crate, CrateSugar, Defaultness, EnumDef, Expr, ExprKind,
- Field, FieldPat, FnDecl, FnHeader, ForeignItem, ForeignItemKind, ForeignMod, FunctionRetTy,
- GenericArg, GenericArgs, GenericBound, GenericParam, GenericParamKind, Generics, GlobalAsm,
- Ident, ImplItem, ImplItemKind, ImplPolarity, InlineAsm, InlineAsmOutput, IntTy, IsAsync,
- IsAuto, Item, ItemKind, Label, Lifetime, Lit, LitIntType, LitKind, Local, Mac, MacDelimiter,
- MacStmtStyle, MacroDef, MethodSig, Mod, Movability, MutTy, Mutability, NodeId, Param,
- ParenthesizedArgs, Pat, PatKind, Path, PathSegment, PolyTraitRef, QSelf, RangeEnd, RangeLimits,
- RangeSyntax, Stmt, StmtKind, StrStyle, StructField, TraitBoundModifier, TraitItem,
- TraitItemKind, TraitObjectSyntax, TraitRef, Ty, TyKind, UintTy, UnOp, UnsafeSource, Unsafety,
- UseTree, UseTreeKind, Variant, VariantData, VisibilityKind, WhereBoundPredicate, WhereClause,
+ AngleBracketedArgs, AnonConst, Arm, AsmDialect, AssocItem, AssocItemKind, AssocTyConstraint,
+ AssocTyConstraintKind, Async, AttrId, AttrItem, AttrKind, AttrStyle, Attribute, BareFnTy,
+ BinOpKind, BindingMode, Block, BlockCheckMode, BorrowKind, CaptureBy, Const, Crate, CrateSugar,
+ Defaultness, EnumDef, Expr, ExprKind, Extern, Field, FieldPat, FloatTy, FnDecl, FnHeader,
+ FnRetTy, FnSig, ForeignMod, GenericArg, GenericArgs, GenericBound, GenericParam,
+ GenericParamKind, Generics, GlobalAsm, Ident, ImplPolarity, InlineAsm, InlineAsmOutput, IntTy,
+ IsAuto, Item, ItemKind, Label, Lifetime, Lit, LitFloatType, LitIntType, LitKind, Local, Mac,
+ MacArgs, MacDelimiter, MacStmtStyle, MacroDef, Mod, Movability, MutTy, Mutability, NodeId,
+ Param, ParenthesizedArgs, Pat, PatKind, Path, PathSegment, PolyTraitRef, QSelf, RangeEnd,
+ RangeLimits, RangeSyntax, Stmt, StmtKind, StrLit, StrStyle, StructField, TraitBoundModifier,
+ TraitObjectSyntax, TraitRef, Ty, TyKind, UintTy, UnOp, Unsafe, UnsafeSource, UseTree,
+ UseTreeKind, Variant, VariantData, VisibilityKind, WhereBoundPredicate, WhereClause,
WhereEqPredicate, WherePredicate, WhereRegionPredicate,
};
-use syntax::parse::lexer::comments;
-use syntax::parse::token::{self, DelimToken, Token, TokenKind};
use syntax::ptr::P;
-use syntax::source_map::Spanned;
-use syntax::symbol::{sym, Symbol};
+use syntax::token::{self, DelimToken, Token, TokenKind};
use syntax::tokenstream::{DelimSpan, TokenStream, TokenTree};
-use syntax_pos::{Span, SyntaxContext, DUMMY_SP};
+use syntax::util::comments;
pub trait SpanlessEq {
fn eq(&self, other: &Self) -> bool;
@@ -127,16 +124,15 @@
spanless_eq_partial_eq!(usize);
spanless_eq_partial_eq!(char);
spanless_eq_partial_eq!(Symbol);
-spanless_eq_partial_eq!(Abi);
spanless_eq_partial_eq!(DelimToken);
macro_rules! spanless_eq_struct {
{
- $name:ident;
+ $name:ident $(<$param:ident>)?;
$([$field:ident $other:ident])*
$(![$ignore:ident])*
} => {
- impl SpanlessEq for $name {
+ impl $(<$param: SpanlessEq>)* SpanlessEq for $name $(<$param>)* {
fn eq(&self, other: &Self) -> bool {
let $name { $($field,)* $($ignore: _,)* } = self;
let $name { $($field: $other,)* $($ignore: _,)* } = other;
@@ -146,14 +142,14 @@
};
{
- $name:ident;
+ $name:ident $(<$param:ident>)?;
$([$field:ident $other:ident])*
$next:ident
$($rest:ident)*
$(!$ignore:ident)*
} => {
spanless_eq_struct! {
- $name;
+ $name $(<$param>)*;
$([$field $other])*
[$next other]
$($rest)*
@@ -162,14 +158,14 @@
};
{
- $name:ident;
+ $name:ident $(<$param:ident>)?;
$([$field:ident $other:ident])*
$(![$ignore:ident])*
!$next:ident
$(!$rest:ident)*
} => {
spanless_eq_struct! {
- $name;
+ $name $(<$param>)*;
$([$field $other])*
$(![$ignore])*
![$next]
@@ -266,34 +262,33 @@
spanless_eq_struct!(AngleBracketedArgs; span args constraints);
spanless_eq_struct!(AnonConst; id value);
spanless_eq_struct!(Arm; attrs pat guard body span id is_placeholder);
+spanless_eq_struct!(AssocItem; attrs id span vis ident defaultness kind !tokens);
spanless_eq_struct!(AssocTyConstraint; id ident kind span);
-spanless_eq_struct!(AttrItem; path tokens);
-spanless_eq_struct!(Attribute; item id style span !is_sugared_doc);
-spanless_eq_struct!(BareFnTy; unsafety abi generic_params decl);
+spanless_eq_struct!(AttrItem; path args);
+spanless_eq_struct!(Attribute; kind id style span);
+spanless_eq_struct!(BareFnTy; unsafety ext generic_params decl);
spanless_eq_struct!(Block; stmts id rules span);
-spanless_eq_struct!(Crate; module attrs span);
+spanless_eq_struct!(Crate; module attrs span proc_macros);
spanless_eq_struct!(EnumDef; variants);
spanless_eq_struct!(Expr; id kind span attrs);
-spanless_eq_struct!(Field; ident expr span is_shorthand attrs id is_placeholder);
+spanless_eq_struct!(Field; attrs id span ident expr is_shorthand is_placeholder);
spanless_eq_struct!(FieldPat; ident pat is_shorthand attrs id span is_placeholder);
spanless_eq_struct!(FnDecl; inputs output);
-spanless_eq_struct!(FnHeader; constness asyncness unsafety abi);
-spanless_eq_struct!(ForeignItem; ident attrs kind id span vis);
+spanless_eq_struct!(FnHeader; constness asyncness unsafety ext);
+spanless_eq_struct!(FnSig; header decl);
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!(ImplItem; id ident vis defaultness attrs generics kind span !tokens);
spanless_eq_struct!(InlineAsm; asm asm_str_style outputs inputs clobbers volatile alignstack dialect);
spanless_eq_struct!(InlineAsmOutput; constraint expr is_rw is_indirect);
-spanless_eq_struct!(Item; ident attrs id kind vis span !tokens);
+spanless_eq_struct!(Item<K>; attrs id span vis ident kind !tokens);
spanless_eq_struct!(Label; ident);
spanless_eq_struct!(Lifetime; id ident);
spanless_eq_struct!(Lit; token kind span);
spanless_eq_struct!(Local; pat ty init id span attrs);
-spanless_eq_struct!(Mac; path delim tts span prior_type_ascription);
-spanless_eq_struct!(MacroDef; tokens legacy);
-spanless_eq_struct!(MethodSig; header decl);
+spanless_eq_struct!(Mac; path args prior_type_ascription);
+spanless_eq_struct!(MacroDef; body legacy);
spanless_eq_struct!(Mod; inner items inline);
spanless_eq_struct!(MutTy; ty mutbl);
spanless_eq_struct!(Param; attrs ty pat id span is_placeholder);
@@ -304,56 +299,59 @@
spanless_eq_struct!(PolyTraitRef; bound_generic_params trait_ref span);
spanless_eq_struct!(QSelf; ty path_span position);
spanless_eq_struct!(Stmt; id kind span);
-spanless_eq_struct!(StructField; span ident vis id ty attrs is_placeholder);
+spanless_eq_struct!(StrLit; style symbol suffix span symbol_unescaped);
+spanless_eq_struct!(StructField; attrs id span vis ident ty is_placeholder);
spanless_eq_struct!(Token; kind span);
-spanless_eq_struct!(TraitItem; id ident attrs generics kind span !tokens);
spanless_eq_struct!(TraitRef; path ref_id);
spanless_eq_struct!(Ty; id kind span);
spanless_eq_struct!(UseTree; prefix kind span);
-spanless_eq_struct!(Variant; ident attrs id data disr_expr span is_placeholder);
+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!(WhereEqPredicate; id span lhs_ty rhs_ty);
spanless_eq_struct!(WhereRegionPredicate; span lifetime bounds);
spanless_eq_enum!(AsmDialect; Att Intel);
+spanless_eq_enum!(AssocItemKind; Const(0 1) Static(0 1 2) Fn(0 1 2) TyAlias(0 1 2) Macro(0));
spanless_eq_enum!(AssocTyConstraintKind; Equality(ty) Bound(bounds));
+spanless_eq_enum!(Async; Yes(span closure_id return_impl_trait_id) No);
+spanless_eq_enum!(AttrKind; Normal(0) DocComment(0));
spanless_eq_enum!(AttrStyle; Outer Inner);
spanless_eq_enum!(BinOpKind; Add Sub Mul Div Rem And Or BitXor BitAnd BitOr Shl Shr Eq Lt Le Ne Ge Gt);
spanless_eq_enum!(BindingMode; ByRef(0) ByValue(0));
spanless_eq_enum!(BlockCheckMode; Default Unsafe(0));
+spanless_eq_enum!(BorrowKind; Ref Raw);
spanless_eq_enum!(CaptureBy; Value Ref);
-spanless_eq_enum!(Constness; Const NotConst);
+spanless_eq_enum!(Const; Yes(0) No);
spanless_eq_enum!(CrateSugar; PubCrate JustCrate);
spanless_eq_enum!(Defaultness; Default Final);
+spanless_eq_enum!(Extern; None Implicit Explicit(0));
spanless_eq_enum!(FloatTy; F32 F64);
-spanless_eq_enum!(ForeignItemKind; Fn(0 1) Static(0 1) Ty Macro(0));
-spanless_eq_enum!(FunctionRetTy; Default(0) Ty(0));
+spanless_eq_enum!(FnRetTy; Default(0) Ty(0));
spanless_eq_enum!(GenericArg; Lifetime(0) Type(0) Const(0));
spanless_eq_enum!(GenericArgs; AngleBracketed(0) Parenthesized(0));
spanless_eq_enum!(GenericBound; Trait(0 1) Outlives(0));
spanless_eq_enum!(GenericParamKind; Lifetime Type(default) Const(ty));
-spanless_eq_enum!(ImplItemKind; Const(0 1) Method(0 1) TyAlias(0) OpaqueTy(0) Macro(0));
spanless_eq_enum!(ImplPolarity; Positive Negative);
spanless_eq_enum!(IntTy; Isize I8 I16 I32 I64 I128);
-spanless_eq_enum!(IsAsync; Async(closure_id return_impl_trait_id) NotAsync);
spanless_eq_enum!(IsAuto; Yes No);
+spanless_eq_enum!(LitFloatType; Suffixed(0) Unsuffixed);
spanless_eq_enum!(LitIntType; Signed(0) Unsigned(0) Unsuffixed);
+spanless_eq_enum!(MacArgs; Empty Delimited(0 1 2) Eq(0 1));
spanless_eq_enum!(MacDelimiter; Parenthesis Bracket Brace);
spanless_eq_enum!(MacStmtStyle; Semicolon Braces NoBraces);
spanless_eq_enum!(Movability; Static Movable);
-spanless_eq_enum!(Mutability; Mutable Immutable);
+spanless_eq_enum!(Mutability; Mut Not);
spanless_eq_enum!(RangeEnd; Included(0) Excluded);
spanless_eq_enum!(RangeLimits; HalfOpen Closed);
spanless_eq_enum!(StmtKind; Local(0) Item(0) Expr(0) Semi(0) Mac(0));
spanless_eq_enum!(StrStyle; Cooked Raw(0));
spanless_eq_enum!(TokenTree; Token(0) Delimited(0 1 2));
-spanless_eq_enum!(TraitBoundModifier; None Maybe);
-spanless_eq_enum!(TraitItemKind; Const(0 1) Method(0 1) Type(0 1) Macro(0));
+spanless_eq_enum!(TraitBoundModifier; None Maybe MaybeConst MaybeConstMaybe);
spanless_eq_enum!(TraitObjectSyntax; Dyn None);
spanless_eq_enum!(UintTy; Usize U8 U16 U32 U64 U128);
spanless_eq_enum!(UnOp; Deref Not Neg);
+spanless_eq_enum!(Unsafe; Yes(0) No);
spanless_eq_enum!(UnsafeSource; CompilerGenerated UserProvided);
-spanless_eq_enum!(Unsafety; Unsafe Normal);
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);
@@ -361,16 +359,17 @@
spanless_eq_enum!(ExprKind; Box(0) Array(0) Call(0 1) MethodCall(0 1) 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) AssignOp(0 1 2)
- Field(0 1) Index(0 1) Range(0 1 2) Path(0 1) AddrOf(0 1) Break(0 1)
+ Block(0 1) Async(0 1 2) Await(0) TryBlock(0) Assign(0 1 2) AssignOp(0 1 2)
+ Field(0 1) Index(0 1) Range(0 1 2) Path(0 1) AddrOf(0 1 2) Break(0 1)
Continue(0) Ret(0) InlineAsm(0) Mac(0) Struct(0 1 2) Repeat(0 1) Paren(0)
Try(0) Yield(0) Err);
spanless_eq_enum!(ItemKind; ExternCrate(0) Use(0) Static(0 1 2) Const(0 1)
- Fn(0 1 2 3) Mod(0) ForeignMod(0) GlobalAsm(0) TyAlias(0 1) OpaqueTy(0 1)
- Enum(0 1) Struct(0 1) Union(0 1) Trait(0 1 2 3 4) TraitAlias(0 1)
- Impl(0 1 2 3 4 5 6) Mac(0) MacroDef(0));
+ Fn(0 1 2) Mod(0) ForeignMod(0) GlobalAsm(0) TyAlias(0 1 2) Enum(0 1)
+ Struct(0 1) Union(0 1) Trait(0 1 2 3 4) TraitAlias(0 1)
+ Impl(unsafety polarity defaultness constness generics of_trait self_ty items)
+ Mac(0) MacroDef(0));
spanless_eq_enum!(LitKind; Str(0 1) ByteStr(0) Byte(0) Char(0) Int(0 1)
- Float(0 1) FloatUnsuffixed(0) Bool(0) Err(0));
+ Float(0 1) Bool(0) Err(0));
spanless_eq_enum!(PatKind; Wild Ident(0 1 2) Struct(0 1 2) TupleStruct(0 1)
Or(0) Path(0 1) Tuple(0) Box(0) Ref(0 1) Lit(0) Range(0 1 2) Slice(0) Rest
Paren(0) Mac(0));
diff --git a/tests/common/mod.rs b/tests/common/mod.rs
index 8b784be..3dd2552 100644
--- a/tests/common/mod.rs
+++ b/tests/common/mod.rs
@@ -12,3 +12,8 @@
Err(_) => usize::max_value(),
}
}
+
+/// Are we running in travis-ci.org.
+pub fn travis_ci() -> bool {
+ env::var_os("TRAVIS").is_some()
+}
diff --git a/tests/common/parse.rs b/tests/common/parse.rs
index e6425b7..acdd172 100644
--- a/tests/common/parse.rs
+++ b/tests/common/parse.rs
@@ -1,19 +1,20 @@
+extern crate rustc_expand;
+extern crate rustc_parse as parse;
+extern crate rustc_session;
+extern crate rustc_span;
extern crate syntax;
-extern crate syntax_pos;
+use rustc_session::parse::ParseSess;
+use rustc_span::source_map::FilePathMapping;
+use rustc_span::FileName;
use syntax::ast;
-use syntax::parse;
use syntax::ptr::P;
-use syntax::sess::ParseSess;
-use syntax::source_map::FilePathMapping;
-use syntax_pos::FileName;
use std::panic;
pub fn libsyntax_expr(input: &str) -> Option<P<ast::Expr>> {
match panic::catch_unwind(|| {
let sess = ParseSess::new(FilePathMapping::empty());
- sess.span_diagnostic.set_continue_after_error(false);
let e = parse::new_parser_from_source_str(
&sess,
FileName::Custom("test_precedence".to_string()),
diff --git a/tests/repo/mod.rs b/tests/repo/mod.rs
index 02b4e32..6ab8d2e 100644
--- a/tests/repo/mod.rs
+++ b/tests/repo/mod.rs
@@ -1,7 +1,16 @@
-use std::process::Command;
+mod progress;
+use self::progress::Progress;
+use crate::common;
+use anyhow::Result;
+use flate2::read::GzDecoder;
+use std::fs;
+use std::path::Path;
+use tar::Archive;
use walkdir::DirEntry;
+const REVISION: &str = "2c462a2f776b899d46743b1b44eda976e846e61d";
+
pub fn base_dir_filter(entry: &DirEntry) -> bool {
let path = entry.path();
if path.is_dir() {
@@ -10,51 +19,159 @@
if path.extension().map(|e| e != "rs").unwrap_or(true) {
return false;
}
- let path_string = path.to_string_lossy();
- let path_string = if cfg!(windows) {
- path_string.replace('\\', "/").into()
- } else {
- path_string
- };
+
+ let mut path_string = path.to_string_lossy();
+ if cfg!(windows) {
+ path_string = path_string.replace('\\', "/").into();
+ }
+ assert!(path_string.starts_with("tests/rust/src/"));
+ let path = &path_string["tests/rust/src/".len()..];
+
// TODO assert that parsing fails on the parse-fail cases
- if path_string.starts_with("tests/rust/src/test/parse-fail")
- || path_string.starts_with("tests/rust/src/test/compile-fail")
- || path_string.starts_with("tests/rust/src/test/rustfix")
+ if path.starts_with("test/parse-fail")
+ || path.starts_with("test/compile-fail")
+ || path.starts_with("test/rustfix")
{
return false;
}
- if path_string.starts_with("tests/rust/src/test/ui") {
- let stderr_path = path.with_extension("stderr");
+ if path.starts_with("test/ui") {
+ let stderr_path = entry.path().with_extension("stderr");
if stderr_path.exists() {
// Expected to fail in some way
return false;
}
}
- match path_string.as_ref() {
+ match path {
+ // TODO: or-patterns patterns: `Some(1 | 8)`
+ // https://github.com/dtolnay/syn/issues/758
+ "test/mir-opt/exponential-or.rs" |
+ "test/ui/or-patterns/basic-switch.rs" |
+ "test/ui/or-patterns/basic-switchint.rs" |
+ "test/ui/or-patterns/bindings-runpass-1.rs" |
+ "test/ui/or-patterns/bindings-runpass-2.rs" |
+ "test/ui/or-patterns/consistent-bindings.rs" |
+ "test/ui/or-patterns/exhaustiveness-pass.rs" |
+ "test/ui/or-patterns/for-loop.rs" |
+ "test/ui/or-patterns/if-let-while-let.rs" |
+ "test/ui/or-patterns/issue-67514-irrefutable-param.rs" |
+ "test/ui/or-patterns/issue-68785-irrefutable-param-with-at.rs" |
+ "test/ui/or-patterns/let-pattern.rs" |
+ "test/ui/or-patterns/mix-with-wild.rs" |
+ "test/ui/or-patterns/or-patterns-default-binding-modes.rs" |
+ "test/ui/or-patterns/or-patterns-syntactic-pass.rs" |
+ "test/ui/or-patterns/search-via-bindings.rs" |
+ "test/ui/or-patterns/struct-like.rs" |
+
+ // TODO: inner attr in traits: `trait Foo { #![...] }`
+ // https://github.com/dtolnay/syn/issues/759
+ "test/pretty/trait-inner-attr.rs" |
+ "test/ui/parser/inner-attr-in-trait-def.rs" |
+
+ // TODO: const underscore in traits: `trait A { const _: (); }`
+ // https://github.com/dtolnay/syn/issues/760
+ "test/ui/parser/assoc-const-underscore-syntactic-pass.rs" |
+
+ // TODO: top level fn without body: `fn f();`
+ // https://github.com/dtolnay/syn/issues/761
+ "test/ui/parser/fn-body-optional-syntactic-pass.rs" |
+ "test/ui/parser/fn-header-syntactic-pass.rs" |
+
+ // TODO: extern static with value: `extern { static X: u8 = 0; }`
+ // https://github.com/dtolnay/syn/issues/762
+ "test/ui/parser/foreign-static-syntactic-pass.rs" |
+
+ // TODO: extern type with bound: `extern { type A: Ord; }`
+ // https://github.com/dtolnay/syn/issues/763
+ "test/ui/parser/foreign-ty-syntactic-pass.rs" |
+
+ // TODO: top level const/static without value: `const X: u8;`
+ // https://github.com/dtolnay/syn/issues/764
+ "test/ui/parser/item-free-const-no-body-syntactic-pass.rs" |
+ "test/ui/parser/item-free-static-no-body-syntactic-pass.rs" |
+
+ // TODO: mut receiver in fn pointer type: `fn(mut self)`
+ // https://github.com/dtolnay/syn/issues/765
+ "test/ui/parser/self-param-syntactic-pass.rs" |
+
+ // TODO: const trait impls and bounds
+ // https://github.com/dtolnay/syn/issues/766
+ // https://github.com/dtolnay/syn/issues/767
+ "test/ui/rfc-2632-const-trait-impl/assoc-type.rs" |
+ "test/ui/rfc-2632-const-trait-impl/call-const-trait-method-pass.rs" |
+ "test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/feature-gate.rs" |
+ "test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/syntax.rs" |
+ "test/ui/rfc-2632-const-trait-impl/feature-gate.rs" |
+ "test/ui/rfc-2632-const-trait-impl/generic-bound.rs" |
+ "test/ui/rfc-2632-const-trait-impl/syntax.rs" |
+
// Deprecated placement syntax
- "tests/rust/src/test/ui/obsolete-in-place/bad.rs" |
+ "test/ui/obsolete-in-place/bad.rs" |
+
// Deprecated anonymous parameter syntax in traits
- "tests/rust/src/test/ui/error-codes/e0119/auxiliary/issue-23563-a.rs" |
- "tests/rust/src/test/ui/issues/issue-13105.rs" |
- "tests/rust/src/test/ui/issues/issue-13775.rs" |
- "tests/rust/src/test/ui/issues/issue-34074.rs" |
- // Deprecated await macro syntax
- "tests/rust/src/test/ui/async-await/await-macro.rs" |
+ "test/ui/error-codes/e0119/auxiliary/issue-23563-a.rs" |
+ "test/ui/issues/issue-13105.rs" |
+ "test/ui/issues/issue-13775.rs" |
+ "test/ui/issues/issue-34074.rs" |
+
// 2015-style dyn that libsyntax rejects
- "tests/rust/src/test/ui/dyn-keyword/dyn-2015-no-warnings-without-lints.rs" |
+ "test/ui/dyn-keyword/dyn-2015-no-warnings-without-lints.rs" |
+
// not actually test cases
- "tests/rust/src/test/ui/include-single-expr-helper.rs" |
- "tests/rust/src/test/ui/include-single-expr-helper-1.rs" |
- "tests/rust/src/test/ui/issues/auxiliary/issue-21146-inc.rs" |
- "tests/rust/src/test/ui/macros/auxiliary/macro-comma-support.rs" |
- "tests/rust/src/test/ui/macros/auxiliary/macro-include-items-expr.rs" => false,
+ "test/rustdoc-ui/test-compile-fail2.rs" |
+ "test/rustdoc-ui/test-compile-fail3.rs" |
+ "test/ui/include-single-expr-helper.rs" |
+ "test/ui/include-single-expr-helper-1.rs" |
+ "test/ui/issues/auxiliary/issue-21146-inc.rs" |
+ "test/ui/macros/auxiliary/macro-comma-support.rs" |
+ "test/ui/macros/auxiliary/macro-include-items-expr.rs" => false,
+
_ => true,
}
}
pub fn clone_rust() {
- let result = Command::new("tests/clone.sh").status().unwrap();
- assert!(result.success());
+ let needs_clone = match fs::read_to_string("tests/rust/COMMIT") {
+ Err(_) => true,
+ Ok(contents) => contents.trim() != REVISION,
+ };
+ if needs_clone {
+ download_and_unpack().unwrap();
+ }
+}
+
+fn download_and_unpack() -> Result<()> {
+ let url = format!(
+ "https://github.com/rust-lang/rust/archive/{}.tar.gz",
+ REVISION
+ );
+ let response = reqwest::blocking::get(&url)?.error_for_status()?;
+ let progress = Progress::new(response);
+ let decoder = GzDecoder::new(progress);
+ let mut archive = Archive::new(decoder);
+ let prefix = format!("rust-{}", REVISION);
+
+ let tests_rust = Path::new("tests/rust");
+ if tests_rust.exists() {
+ fs::remove_dir_all(tests_rust)?;
+ }
+
+ for entry in archive.entries()? {
+ let mut entry = entry?;
+ let path = entry.path()?;
+ if path == Path::new("pax_global_header") {
+ continue;
+ }
+ let relative = path.strip_prefix(&prefix)?;
+ let out = tests_rust.join(relative);
+ entry.unpack(&out)?;
+ if common::travis_ci() {
+ // Something about this makes the travis build not deadlock...
+ errorf!(".");
+ }
+ }
+
+ fs::write("tests/rust/COMMIT", REVISION)?;
+ Ok(())
}
diff --git a/tests/repo/progress.rs b/tests/repo/progress.rs
new file mode 100644
index 0000000..28c8a44
--- /dev/null
+++ b/tests/repo/progress.rs
@@ -0,0 +1,37 @@
+use std::io::{Read, Result};
+use std::time::{Duration, Instant};
+
+pub struct Progress<R> {
+ bytes: usize,
+ tick: Instant,
+ stream: R,
+}
+
+impl<R> Progress<R> {
+ pub fn new(stream: R) -> Self {
+ Progress {
+ bytes: 0,
+ tick: Instant::now() + Duration::from_millis(2000),
+ stream,
+ }
+ }
+}
+
+impl<R: Read> Read for Progress<R> {
+ fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
+ let num = self.stream.read(buf)?;
+ self.bytes += num;
+ let now = Instant::now();
+ if now > self.tick {
+ self.tick = now + Duration::from_millis(500);
+ errorf!("downloading... {} bytes\n", self.bytes);
+ }
+ Ok(num)
+ }
+}
+
+impl<R> Drop for Progress<R> {
+ fn drop(&mut self) {
+ errorf!("done ({} bytes)\n", self.bytes);
+ }
+}
diff --git a/tests/test_attribute.rs b/tests/test_attribute.rs
index d77c0c0..e1b0142 100644
--- a/tests/test_attribute.rs
+++ b/tests/test_attribute.rs
@@ -286,6 +286,48 @@
"###);
}
+#[test]
+fn test_negative_lit() {
+ let meta = test("#[form(min = -1, max = 200)]");
+
+ snapshot!(meta, @r###"
+ Meta::List {
+ path: Path {
+ segments: [
+ PathSegment {
+ ident: "form",
+ arguments: None,
+ },
+ ],
+ },
+ nested: [
+ Meta(Meta::NameValue {
+ path: Path {
+ segments: [
+ PathSegment {
+ ident: "min",
+ arguments: None,
+ },
+ ],
+ },
+ lit: -1,
+ }),
+ Meta(Meta::NameValue {
+ path: Path {
+ segments: [
+ PathSegment {
+ ident: "max",
+ arguments: None,
+ },
+ ],
+ },
+ lit: 200,
+ }),
+ ],
+ }
+ "###);
+}
+
fn test(input: &str) -> Meta {
let attrs = Attribute::parse_outer.parse_str(input).unwrap();
diff --git a/tests/test_lit.rs b/tests/test_lit.rs
index 0b300c4..d4495ce 100644
--- a/tests/test_lit.rs
+++ b/tests/test_lit.rs
@@ -1,9 +1,9 @@
mod features;
-use proc_macro2::{TokenStream, TokenTree};
+use proc_macro2::{Span, TokenStream, TokenTree};
use quote::ToTokens;
use std::str::FromStr;
-use syn::Lit;
+use syn::{Lit, LitFloat, LitInt};
fn lit(s: &str) -> Lit {
match TokenStream::from_str(s)
@@ -182,3 +182,16 @@
test_float("1.0__3e-12", 1.03e-12, "");
test_float("1.03e+12", 1.03e12, "");
}
+
+#[test]
+fn negative() {
+ let span = Span::call_site();
+ assert_eq!("-1", LitInt::new("-1", span).to_string());
+ assert_eq!("-1i8", LitInt::new("-1i8", span).to_string());
+ assert_eq!("-1i16", LitInt::new("-1i16", span).to_string());
+ assert_eq!("-1i32", LitInt::new("-1i32", span).to_string());
+ assert_eq!("-1i64", LitInt::new("-1i64", span).to_string());
+ assert_eq!("-1.5", LitFloat::new("-1.5", span).to_string());
+ assert_eq!("-1.5f32", LitFloat::new("-1.5f32", span).to_string());
+ assert_eq!("-1.5f64", LitFloat::new("-1.5f64", span).to_string());
+}
diff --git a/tests/test_precedence.rs b/tests/test_precedence.rs
index 582c0fd..80f5130 100644
--- a/tests/test_precedence.rs
+++ b/tests/test_precedence.rs
@@ -15,19 +15,17 @@
//! 5. Compare the expressions with one another, if they are not equal fail.
extern crate rustc_data_structures;
-extern crate smallvec;
+extern crate rustc_span;
extern crate syntax;
-extern crate syntax_pos;
mod features;
use quote::quote;
use rayon::iter::{IntoParallelIterator, ParallelIterator};
use regex::Regex;
-use smallvec::smallvec;
+use rustc_span::edition::Edition;
use syntax::ast;
use syntax::ptr::P;
-use syntax_pos::edition::Edition;
use walkdir::{DirEntry, WalkDir};
use std::fs::File;
@@ -86,7 +84,6 @@
/// Test expressions from rustc, like in `test_round_trip`.
#[test]
-#[cfg_attr(target_os = "windows", ignore = "requires nix .sh")]
fn test_rustc_precedence() {
repo::clone_rust();
let abort_after = common::abort_after();
@@ -212,16 +209,58 @@
/// This method operates on libsyntax objects.
fn libsyntax_brackets(mut libsyntax_expr: P<ast::Expr>) -> Option<P<ast::Expr>> {
use rustc_data_structures::thin_vec::ThinVec;
- use smallvec::SmallVec;
+ use rustc_span::DUMMY_SP;
use std::mem;
- use syntax::ast::{Expr, ExprKind, Field, Mac, Pat, Stmt, StmtKind, Ty};
- use syntax::mut_visit::{noop_visit_expr, MutVisitor};
- use syntax_pos::DUMMY_SP;
+ use syntax::ast::{Block, BorrowKind, Expr, ExprKind, Field, Mac, Pat, Stmt, StmtKind, Ty};
+ use syntax::mut_visit::MutVisitor;
+ use syntax::util::map_in_place::MapInPlace;
struct BracketsVisitor {
failed: bool,
};
+ fn flat_map_field<T: MutVisitor>(mut f: Field, vis: &mut T) -> Vec<Field> {
+ if f.is_shorthand {
+ noop_visit_expr(&mut f.expr, vis);
+ } else {
+ vis.visit_expr(&mut f.expr);
+ }
+ vec![f]
+ }
+
+ fn flat_map_stmt<T: MutVisitor>(stmt: Stmt, vis: &mut T) -> Vec<Stmt> {
+ let kind = match stmt.kind {
+ // Don't wrap toplevel expressions in statements.
+ StmtKind::Expr(mut e) => {
+ noop_visit_expr(&mut e, vis);
+ StmtKind::Expr(e)
+ }
+ StmtKind::Semi(mut e) => {
+ noop_visit_expr(&mut e, vis);
+ StmtKind::Semi(e)
+ }
+ s => s,
+ };
+
+ vec![Stmt { kind, ..stmt }]
+ }
+
+ fn noop_visit_expr<T: MutVisitor>(e: &mut Expr, vis: &mut T) {
+ use syntax::mut_visit::{noop_visit_expr, visit_opt, visit_thin_attrs};
+ match &mut e.kind {
+ ExprKind::AddrOf(BorrowKind::Raw, ..) => {}
+ ExprKind::Struct(path, fields, expr) => {
+ vis.visit_path(path);
+ fields.flat_map_in_place(|field| flat_map_field(field, vis));
+ visit_opt(expr, |expr| vis.visit_expr(expr));
+ vis.visit_id(&mut e.id);
+ vis.visit_span(&mut e.span);
+ visit_thin_attrs(&mut e.attrs, vis);
+ }
+ _ => noop_visit_expr(e, vis),
+ }
+ }
+
impl MutVisitor for BracketsVisitor {
fn visit_expr(&mut self, e: &mut P<Expr>) {
noop_visit_expr(e, self);
@@ -242,13 +281,12 @@
}
}
- fn flat_map_field(&mut self, mut f: Field) -> SmallVec<[Field; 1]> {
- if f.is_shorthand {
- noop_visit_expr(&mut f.expr, self);
- } else {
- self.visit_expr(&mut f.expr);
- }
- SmallVec::from([f])
+ fn visit_block(&mut self, block: &mut P<Block>) {
+ self.visit_id(&mut block.id);
+ block
+ .stmts
+ .flat_map_in_place(|stmt| flat_map_stmt(stmt, self));
+ self.visit_span(&mut block.span);
}
// We don't want to look at expressions that might appear in patterns or
@@ -262,23 +300,6 @@
let _ = ty;
}
- fn flat_map_stmt(&mut self, stmt: Stmt) -> SmallVec<[Stmt; 1]> {
- let kind = match stmt.kind {
- // Don't wrap toplevel expressions in statements.
- StmtKind::Expr(mut e) => {
- noop_visit_expr(&mut e, self);
- StmtKind::Expr(e)
- }
- StmtKind::Semi(mut e) => {
- noop_visit_expr(&mut e, self);
- StmtKind::Semi(e)
- }
- s => s,
- };
-
- smallvec![Stmt { kind, ..stmt }]
- }
-
fn visit_mac(&mut self, mac: &mut Mac) {
// By default when folding over macros, libsyntax panics. This is
// because it's usually not what you want, you want to run after
@@ -355,7 +376,10 @@
struct CollectExprs(Vec<Expr>);
impl Fold for CollectExprs {
fn fold_expr(&mut self, expr: Expr) -> Expr {
- self.0.push(expr);
+ match expr {
+ Expr::Verbatim(tokens) if tokens.is_empty() => {}
+ _ => self.0.push(expr),
+ }
Expr::Tuple(ExprTuple {
attrs: vec![],
diff --git a/tests/test_round_trip.rs b/tests/test_round_trip.rs
index b6e8ee1..a7e48c8 100644
--- a/tests/test_round_trip.rs
+++ b/tests/test_round_trip.rs
@@ -2,19 +2,23 @@
#![recursion_limit = "1024"]
#![feature(rustc_private)]
+extern crate rustc_errors;
+extern crate rustc_expand;
+extern crate rustc_parse as parse;
+extern crate rustc_session;
+extern crate rustc_span;
extern crate syntax;
-extern crate syntax_pos;
mod features;
use quote::quote;
use rayon::iter::{IntoParallelIterator, ParallelIterator};
+use rustc_errors::PResult;
+use rustc_session::parse::ParseSess;
+use rustc_span::edition::Edition;
+use rustc_span::source_map::FilePathMapping;
+use rustc_span::FileName;
use syntax::ast;
-use syntax::parse::{self, PResult};
-use syntax::sess::ParseSess;
-use syntax::source_map::FilePathMapping;
-use syntax_pos::edition::Edition;
-use syntax_pos::FileName;
use walkdir::{DirEntry, WalkDir};
use std::fs::File;
@@ -35,7 +39,6 @@
use common::eq::SpanlessEq;
#[test]
-#[cfg_attr(target_os = "windows", ignore = "requires nix .sh")]
fn test_round_trip() {
repo::clone_rust();
let abort_after = common::abort_after();
diff --git a/tests/test_stmt.rs b/tests/test_stmt.rs
new file mode 100644
index 0000000..d68b47f
--- /dev/null
+++ b/tests/test_stmt.rs
@@ -0,0 +1,44 @@
+#[macro_use]
+mod macros;
+
+use syn::Stmt;
+
+#[test]
+fn test_raw_operator() {
+ let stmt = syn::parse_str::<Stmt>("let _ = &raw const x;").unwrap();
+
+ snapshot!(stmt, @r###"
+ Local(Local {
+ pat: Pat::Wild,
+ init: Some(Verbatim(`& raw const x`)),
+ })
+ "###);
+}
+
+#[test]
+fn test_raw_variable() {
+ let stmt = syn::parse_str::<Stmt>("let _ = &raw;").unwrap();
+
+ snapshot!(stmt, @r###"
+ Local(Local {
+ pat: Pat::Wild,
+ init: Some(Expr::Reference {
+ expr: Expr::Path {
+ path: Path {
+ segments: [
+ PathSegment {
+ ident: "raw",
+ arguments: None,
+ },
+ ],
+ },
+ },
+ }),
+ })
+ "###);
+}
+
+#[test]
+fn test_raw_invalid() {
+ assert!(syn::parse_str::<Stmt>("let _ = &raw x;").is_err());
+}
diff --git a/tests/test_visibility.rs b/tests/test_visibility.rs
new file mode 100644
index 0000000..21c49c9
--- /dev/null
+++ b/tests/test_visibility.rs
@@ -0,0 +1,99 @@
+mod features;
+
+use proc_macro2::TokenStream;
+use syn::parse::{Parse, ParseStream};
+use syn::{Result, Visibility};
+
+#[derive(Debug)]
+struct VisRest {
+ vis: Visibility,
+ rest: TokenStream,
+}
+
+impl Parse for VisRest {
+ fn parse(input: ParseStream) -> Result<Self> {
+ Ok(VisRest {
+ vis: input.parse()?,
+ rest: input.parse()?,
+ })
+ }
+}
+
+macro_rules! assert_vis_parse {
+ ($input:expr, Ok($p:pat)) => {
+ assert_vis_parse!($input, Ok($p) + "");
+ };
+
+ ($input:expr, Ok($p:pat) + $rest:expr) => {
+ let expected = $rest.parse::<TokenStream>().unwrap();
+ let parse: VisRest = syn::parse_str($input).unwrap();
+
+ match parse.vis {
+ $p => {}
+ _ => panic!("Expected {}, got {:?}", stringify!($p), parse.vis),
+ }
+
+ // NOTE: Round-trips through `to_string` to avoid potential whitespace
+ // diffs.
+ assert_eq!(parse.rest.to_string(), expected.to_string());
+ };
+
+ ($input:expr, Err) => {
+ syn::parse2::<VisRest>($input.parse().unwrap()).unwrap_err();
+ };
+}
+
+#[test]
+fn test_pub() {
+ assert_vis_parse!("pub", Ok(Visibility::Public(_)));
+}
+
+#[test]
+fn test_crate() {
+ assert_vis_parse!("crate", Ok(Visibility::Crate(_)));
+}
+
+#[test]
+fn test_inherited() {
+ assert_vis_parse!("", Ok(Visibility::Inherited));
+}
+
+#[test]
+fn test_in() {
+ assert_vis_parse!("pub(in foo::bar)", Ok(Visibility::Restricted(_)));
+}
+
+#[test]
+fn test_pub_crate() {
+ assert_vis_parse!("pub(crate)", Ok(Visibility::Restricted(_)));
+}
+
+#[test]
+fn test_pub_self() {
+ assert_vis_parse!("pub(self)", Ok(Visibility::Restricted(_)));
+}
+
+#[test]
+fn test_pub_super() {
+ assert_vis_parse!("pub(super)", Ok(Visibility::Restricted(_)));
+}
+
+#[test]
+fn test_missing_in() {
+ assert_vis_parse!("pub(foo::bar)", Ok(Visibility::Public(_)) + "(foo::bar)");
+}
+
+#[test]
+fn test_missing_in_path() {
+ assert_vis_parse!("pub(in)", Err);
+}
+
+#[test]
+fn test_crate_path() {
+ assert_vis_parse!("pub(crate::A, crate::B)", Ok(Visibility::Public(_)) + "(crate::A, crate::B)");
+}
+
+#[test]
+fn test_junk_after_in() {
+ assert_vis_parse!("pub(in some::path @@garbage)", Err);
+}