#![allow(clippy::non_ascii_literal)] use proc_macro2::{Delimiter, Group, Ident, Punct, Spacing, TokenStream, TokenTree}; use std::panic; use syn::parse::discouraged::Speculative as _; use syn::parse::{Parse, ParseStream, Parser, Result}; use syn::{parenthesized, Token}; #[test] #[should_panic(expected = "fork was not derived from the advancing parse stream")] fn smuggled_speculative_cursor_between_sources() { struct BreakRules; impl Parse for BreakRules { fn parse(input1: ParseStream) -> Result { let nested = |input2: ParseStream| { input1.advance_to(input2); Ok(Self) }; nested.parse_str("") } } syn::parse_str::("").unwrap(); } #[test] #[should_panic(expected = "fork was not derived from the advancing parse stream")] fn smuggled_speculative_cursor_between_brackets() { struct BreakRules; impl Parse for BreakRules { fn parse(input: ParseStream) -> Result { let a; let b; parenthesized!(a in input); parenthesized!(b in input); a.advance_to(&b); Ok(Self) } } syn::parse_str::("()()").unwrap(); } #[test] #[should_panic(expected = "fork was not derived from the advancing parse stream")] fn smuggled_speculative_cursor_into_brackets() { struct BreakRules; impl Parse for BreakRules { fn parse(input: ParseStream) -> Result { let a; parenthesized!(a in input); input.advance_to(&a); Ok(Self) } } syn::parse_str::("()").unwrap(); } #[test] fn trailing_empty_none_group() { fn parse(input: ParseStream) -> Result<()> { input.parse::()?; let content; parenthesized!(content in input); content.parse::()?; Ok(()) } // `+ ( + «∅ ∅» ) «∅ «∅ ∅» ∅»` let tokens = TokenStream::from_iter([ TokenTree::Punct(Punct::new('+', Spacing::Alone)), TokenTree::Group(Group::new( Delimiter::Parenthesis, TokenStream::from_iter([ TokenTree::Punct(Punct::new('+', Spacing::Alone)), TokenTree::Group(Group::new(Delimiter::None, TokenStream::new())), ]), )), TokenTree::Group(Group::new(Delimiter::None, TokenStream::new())), TokenTree::Group(Group::new( Delimiter::None, TokenStream::from_iter([TokenTree::Group(Group::new( Delimiter::None, TokenStream::new(), ))]), )), ]); parse.parse2(tokens).unwrap(); } #[test] fn test_unwind_safe() { fn parse(input: ParseStream) -> Result { let thread_result = panic::catch_unwind(|| input.parse()); thread_result.unwrap() } parse.parse_str("throw").unwrap(); }