diff --git a/src/ibidem.rs b/src/ibidem.rs index ab403a1..d65623d 100644 --- a/src/ibidem.rs +++ b/src/ibidem.rs @@ -5,6 +5,8 @@ use std::cell::RefCell; use proc_macro2::{TokenStream, TokenTree}; use serde::{Deserialize, de::Error, de::Visitor}; +use crate::serde_tokenstream::spanned_error; + /// A wrapper around [`TokenStream`] that implements [`Deserialize`] in the /// context of [`from_tokenstream`]. /// @@ -74,7 +76,21 @@ impl<'de, P: syn::parse::Parse> Deserialize<'de> for ParseWrapper
{ { let token_stream = deserializer.deserialize_bytes(WrapperVisitor)?; - match syn::parse2::
(token_stream) { + let parser = |input: syn::parse::ParseStream<'_>| -> syn::Result
{ + let parsed = P::parse(input)?; + // The deserializer hands over every token up to the next `,`, `=`, + // or EOF, so anything left after `P` is a stray token. Report such + // tokens as errors. + if let Some((tt, _)) = input.cursor().token_tree() { + return Err(spanned_error( + &tt, + format!("expected `,` or nothing, but found `{tt}`"), + )); + } + Ok(parsed) + }; + + match syn::parse::Parser::parse2(parser, token_stream) { Ok(parsed) => Ok(Self(parsed)), Err(err) => { let msg = err.to_string(); diff --git a/ui-tests/tests/ui/bad_parse_wrapper_stray.rs b/ui-tests/tests/ui/bad_parse_wrapper_stray.rs new file mode 100644 index 0000000..0c4d9e3 --- /dev/null +++ b/ui-tests/tests/ui/bad_parse_wrapper_stray.rs @@ -0,0 +1,17 @@ +// Copyright 2026 Oxide Computer Company + +// Ensure that tokens left over after a ParseWrapper value are reported at the +// first stray token. + +use testlib::annotation; + +#[annotation { + string = "test", + options = OptionA, + unit = (), + tup = (1, 2.0), + bool_expr = true false +}] +fn test() {} + +fn main() {} diff --git a/ui-tests/tests/ui/bad_parse_wrapper_stray.stderr b/ui-tests/tests/ui/bad_parse_wrapper_stray.stderr new file mode 100644 index 0000000..cdd315d --- /dev/null +++ b/ui-tests/tests/ui/bad_parse_wrapper_stray.stderr @@ -0,0 +1,5 @@ +error: expected `,` or nothing, but found `false` + --> tests/ui/bad_parse_wrapper_stray.rs:13:22 + | +13 | bool_expr = true false + | ^^^^^