Detect unparsed tokens
diff --git a/src/group.rs b/src/group.rs
index 9e53757..1490d89 100644
--- a/src/group.rs
+++ b/src/group.rs
@@ -23,7 +23,8 @@
fn parse_group(&self, delimiter: Delimiter) -> Result<(Span, ParseBuffer<'a>)> {
self.step_cursor(|cursor| {
if let Some((content, span, rest)) = cursor.group(delimiter) {
- let content = ParseBuffer::new(span, cursor.advance(content));
+ let content =
+ ParseBuffer::new(span, cursor.advance(content), self.get_unexpected());
Ok(((span, content), rest))
} else {
let message = match delimiter {
diff --git a/src/next.rs b/src/next.rs
index c8022bf..e5b902c 100644
--- a/src/next.rs
+++ b/src/next.rs
@@ -1,3 +1,5 @@
+use std::cell::Cell;
+use std::rc::Rc;
use std::str::FromStr;
use buffer::TokenBuffer;
@@ -17,8 +19,11 @@
/// Parse a proc-macro2 token stream into the chosen syntax tree node.
pub fn parse2<T: Parse>(input: proc_macro2::TokenStream) -> Result<T> {
let buf = TokenBuffer::new2(input);
- let state = ParseBuffer::new(Span::call_site(), buf.begin());
- T::parse(&state)
+ let unexpected = Rc::new(Cell::new(None));
+ let state = ParseBuffer::new(Span::call_site(), buf.begin(), unexpected);
+ let node = T::parse(&state)?;
+ state.check_unexpected()?;
+ Ok(node)
}
/// Parse a string of Rust code into the chosen syntax tree node.
diff --git a/src/parse.rs b/src/parse.rs
index cb4eec0..efd7726 100644
--- a/src/parse.rs
+++ b/src/parse.rs
@@ -5,10 +5,12 @@
use std::marker::PhantomData;
use std::mem;
use std::ops::Deref;
+use std::rc::Rc;
+
+use proc_macro2::{Ident, Span};
use buffer::Cursor;
use error;
-use proc_macro2::{Ident, Span};
pub use error::{Error, Result};
pub use lookahead::{Lookahead1, Peek};
@@ -28,6 +30,15 @@
scope: Span,
cell: Cell<Cursor<'static>>,
marker: PhantomData<Cursor<'a>>,
+ unexpected: Rc<Cell<Option<Span>>>,
+}
+
+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()));
+ }
+ }
}
// Not public API.
@@ -64,12 +75,13 @@
impl<'a> ParseBuffer<'a> {
// Not public API.
#[doc(hidden)]
- pub fn new(scope: Span, cursor: Cursor<'a>) -> Self {
+ pub fn new(scope: Span, cursor: Cursor<'a>, unexpected: Rc<Cell<Option<Span>>>) -> Self {
let extend = unsafe { mem::transmute::<Cursor<'a>, Cursor<'static>>(cursor) };
ParseBuffer {
scope: scope,
cell: Cell::new(extend),
marker: PhantomData,
+ unexpected: unexpected,
}
}
@@ -86,6 +98,7 @@
}
pub fn parse<T: Parse>(&self) -> Result<T> {
+ self.check_unexpected()?;
T::parse(self)
}
@@ -95,6 +108,7 @@
where
F: for<'c> FnOnce(StepCursor<'c, 'a>) -> Result<(R, Cursor<'c>)>,
{
+ self.check_unexpected()?;
match function(StepCursor {
scope: self.scope,
cursor: self.cell.get(),
@@ -107,6 +121,21 @@
Err(err) => Err(err),
}
}
+
+ // Not public API.
+ #[doc(hidden)]
+ pub fn get_unexpected(&self) -> Rc<Cell<Option<Span>>> {
+ self.unexpected.clone()
+ }
+
+ // Not public API.
+ #[doc(hidden)]
+ pub fn check_unexpected(&self) -> Result<()> {
+ match self.unexpected.get() {
+ Some(span) => Err(Error::new(span, "unexpected token")),
+ None => Ok(()),
+ }
+ }
}
impl Parse for Ident {
diff --git a/src/synom.rs b/src/synom.rs
index dbe8444..9c2eb28 100644
--- a/src/synom.rs
+++ b/src/synom.rs
@@ -150,6 +150,9 @@
//!
//! *This module is available if Syn is built with the `"parsing"` feature.*
+use std::cell::Cell;
+use std::rc::Rc;
+
#[cfg(all(
not(all(target_arch = "wasm32", target_os = "unknown")),
feature = "proc-macro"
@@ -210,11 +213,11 @@
impl<T: Parse> Synom for T {
fn parse(input: Cursor) -> PResult<Self> {
- let state = ParseBuffer::new(Span::call_site(), input);
- match <T as Parse>::parse(&state) {
- Ok(node) => Ok((node, state.cursor())),
- Err(err) => Err(err),
- }
+ let unexpected = Rc::new(Cell::new(None));
+ let state = ParseBuffer::new(Span::call_site(), input, unexpected);
+ let node = <T as Parse>::parse(&state)?;
+ state.check_unexpected()?;
+ Ok((node, state.cursor()))
}
}