From e52425b6d52a2b22bcb0329e30bfccfa7fa90a0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Mon, 24 Aug 2026 15:07:06 +0200 Subject: [PATCH] Reduce size of errors. This improves performance significantly without costing too much in error reporting ability. Reviewed upstream in https://phabricator.services.mozilla.com/D320989 This is of course a breaking change, but it also allows most parsing routines to have simpler signatures. --- color/lib.rs | 105 ++++++------ fuzz/fuzz_targets/cssparser.rs | 12 +- src/color.rs | 8 +- src/nth.rs | 36 ++-- src/parser.rs | 300 ++++++++++++++------------------- src/rules_and_declarations.rs | 156 ++++++++++------- src/size_of_tests.rs | 4 +- src/tests.rs | 51 +++--- src/unicode_range.rs | 20 +-- 9 files changed, 322 insertions(+), 370 deletions(-) diff --git a/color/lib.rs b/color/lib.rs index c7795823..bc59daff 100644 --- a/color/lib.rs +++ b/color/lib.rs @@ -42,14 +42,13 @@ where /// Parse a CSS color using the specified [`ColorParser`] and return a new color /// value on success. -pub fn parse_color_with<'i, 't, P>( +pub fn parse_color_with<'i, P>( color_parser: &P, - input: &mut Parser<'i, 't>, -) -> Result> + input: &mut Parser<'i, '_>, +) -> Result> where P: ColorParser<'i>, { - let location = input.current_source_location(); let token = input.next()?; match *token { Token::Hash(ref value) | Token::IDHash(ref value) => { @@ -64,16 +63,16 @@ where } _ => Err(()), } - .map_err(|()| location.new_unexpected_token_error(token.clone())) + .map_err(|()| ParseError::unexpected_token()) } /// Parse one of the color functions: rgba(), lab(), color(), etc. #[inline] -fn parse_color_function<'i, 't, P>( +fn parse_color_function<'i, P>( color_parser: &P, name: CowRcStr<'i>, - arguments: &mut Parser<'i, 't>, -) -> Result> + arguments: &mut Parser, +) -> Result> where P: ColorParser<'i>, { @@ -102,7 +101,7 @@ where "color" => parse_color_with_color_space(color_parser, arguments), - _ => return Err(arguments.new_unexpected_token_error(Token::Ident(name))), + _ => return Err(ParseError::unexpected_token()), }?; arguments.expect_exhausted()?; @@ -115,8 +114,8 @@ where #[inline] fn parse_alpha_component<'i, 't, P>( color_parser: &P, - arguments: &mut Parser<'i, 't>, -) -> Result> + arguments: &mut Parser, +) -> Result> where P: ColorParser<'i>, { @@ -128,8 +127,8 @@ where fn parse_legacy_alpha<'i, 't, P>( color_parser: &P, - arguments: &mut Parser<'i, 't>, -) -> Result> + arguments: &mut Parser, +) -> Result> where P: ColorParser<'i>, { @@ -143,8 +142,8 @@ where fn parse_modern_alpha<'i, 't, P>( color_parser: &P, - arguments: &mut Parser<'i, 't>, -) -> Result, ParseError<'i, P::Error>> + arguments: &mut Parser, +) -> Result, ParseError> where P: ColorParser<'i>, { @@ -159,8 +158,8 @@ where #[inline] fn parse_rgb<'i, 't, P>( color_parser: &P, - arguments: &mut Parser<'i, 't>, -) -> Result> + arguments: &mut Parser, +) -> Result> where P: ColorParser<'i>, { @@ -225,8 +224,8 @@ where #[inline] fn parse_hsl<'i, 't, P>( color_parser: &P, - arguments: &mut Parser<'i, 't>, -) -> Result> + arguments: &mut Parser, +) -> Result> where P: ColorParser<'i>, { @@ -264,8 +263,8 @@ where #[inline] fn parse_hwb<'i, 't, P>( color_parser: &P, - arguments: &mut Parser<'i, 't>, -) -> Result> + arguments: &mut Parser, +) -> Result> where P: ColorParser<'i>, { @@ -343,11 +342,11 @@ type IntoColorFn = #[inline] fn parse_lab_like<'i, 't, P>( color_parser: &P, - arguments: &mut Parser<'i, 't>, + arguments: &mut Parser, lightness_range: f32, a_b_range: f32, into_color: IntoColorFn, -) -> Result> +) -> Result> where P: ColorParser<'i>, { @@ -369,11 +368,11 @@ where #[inline] fn parse_lch_like<'i, 't, P>( color_parser: &P, - arguments: &mut Parser<'i, 't>, + arguments: &mut Parser, lightness_range: f32, chroma_range: f32, into_color: IntoColorFn, -) -> Result> +) -> Result> where P: ColorParser<'i>, { @@ -396,8 +395,8 @@ where #[inline] fn parse_color_with_color_space<'i, 't, P>( color_parser: &P, - arguments: &mut Parser<'i, 't>, -) -> Result> + arguments: &mut Parser, +) -> Result> where P: ColorParser<'i>, { @@ -424,22 +423,22 @@ where )) } -type ComponentParseResult<'i, R1, R2, R3, Error> = - Result<(Option, Option, Option, Option), ParseError<'i, Error>>; +type ComponentParseResult = + Result<(Option, Option, Option, Option), ParseError>; /// Parse the color components and alpha with the modern [color-4] syntax. pub fn parse_components<'i, 't, P, F1, F2, F3, R1, R2, R3>( color_parser: &P, - input: &mut Parser<'i, 't>, + input: &mut Parser, f1: F1, f2: F2, f3: F3, -) -> ComponentParseResult<'i, R1, R2, R3, P::Error> +) -> ComponentParseResult where P: ColorParser<'i>, - F1: FnOnce(&P, &mut Parser<'i, 't>) -> Result>, - F2: FnOnce(&P, &mut Parser<'i, 't>) -> Result>, - F3: FnOnce(&P, &mut Parser<'i, 't>) -> Result>, + F1: FnOnce(&P, &mut Parser) -> Result>, + F2: FnOnce(&P, &mut Parser) -> Result>, + F3: FnOnce(&P, &mut Parser) -> Result>, { let r1 = parse_none_or(input, |p| f1(color_parser, p))?; let r2 = parse_none_or(input, |p| f2(color_parser, p))?; @@ -450,9 +449,9 @@ where Ok((r1, r2, r3, alpha)) } -fn parse_none_or<'i, 't, F, T, E>(input: &mut Parser<'i, 't>, thing: F) -> Result, E> +fn parse_none_or(input: &mut Parser, thing: F) -> Result, E> where - F: FnOnce(&mut Parser<'i, 't>) -> Result, + F: FnOnce(&mut Parser) -> Result, { match input.try_parse(|p| p.expect_ident_matching("none")) { Ok(_) => Ok(None), @@ -980,11 +979,10 @@ pub trait ColorParser<'i> { /// Parse an `` or ``. /// /// Returns the result in degrees. - fn parse_angle_or_number<'t>( + fn parse_angle_or_number( &self, - input: &mut Parser<'i, 't>, - ) -> Result> { - let location = input.current_source_location(); + input: &mut Parser, + ) -> Result> { Ok(match *input.next()? { Token::Number { value, .. } => AngleOrNumber::Number { value }, Token::Dimension { @@ -995,45 +993,36 @@ pub trait ColorParser<'i> { "grad" => v * 360. / 400., "rad" => v * 360. / (2. * PI), "turn" => v * 360., - _ => { - return Err(location.new_unexpected_token_error(Token::Ident(unit.clone()))) - } + _ => return Err(ParseError::unexpected_token()), }; AngleOrNumber::Angle { degrees } } - ref t => return Err(location.new_unexpected_token_error(t.clone())), + _ => return Err(ParseError::unexpected_token()), }) } /// Parse a `` value. /// /// Returns the result in a number from 0.0 to 1.0. - fn parse_percentage<'t>( - &self, - input: &mut Parser<'i, 't>, - ) -> Result> { + fn parse_percentage(&self, input: &mut Parser) -> Result> { input.expect_percentage().map_err(From::from) } /// Parse a `` value. - fn parse_number<'t>( - &self, - input: &mut Parser<'i, 't>, - ) -> Result> { + fn parse_number(&self, input: &mut Parser) -> Result> { input.expect_number().map_err(From::from) } /// Parse a `` value or a `` value. - fn parse_number_or_percentage<'t>( + fn parse_number_or_percentage( &self, - input: &mut Parser<'i, 't>, - ) -> Result> { - let location = input.current_source_location(); + input: &mut Parser, + ) -> Result> { Ok(match *input.next()? { Token::Number { value, .. } => NumberOrPercentage::Number { value }, Token::Percentage { unit_value, .. } => NumberOrPercentage::Percentage { unit_value }, - ref t => return Err(location.new_unexpected_token_error(t.clone())), + _ => return Err(ParseError::unexpected_token()), }) } } @@ -1050,7 +1039,7 @@ impl Color { /// Parse a value, per CSS Color Module Level 3. /// /// FIXME(#2) Deprecated CSS2 System Colors are not supported yet. - pub fn parse<'i>(input: &mut Parser<'i, '_>) -> Result> { + pub fn parse(input: &mut Parser) -> Result> { parse_color_with(&DefaultColorParser, input) } } diff --git a/fuzz/fuzz_targets/cssparser.rs b/fuzz/fuzz_targets/cssparser.rs index 6299a14d..a9d89388 100644 --- a/fuzz/fuzz_targets/cssparser.rs +++ b/fuzz/fuzz_targets/cssparser.rs @@ -22,7 +22,7 @@ fn parse_and_serialize(input: &str, preserving_comments: bool) -> String { } fn do_parse_and_serialize<'i>( - input: &mut Parser<'i, '_>, + input: &mut Parser, preserving_comments: bool, mut previous_token_type: TokenSerializationType, serialization: &mut String, @@ -46,7 +46,7 @@ fn do_parse_and_serialize<'i>( } if token.is_parse_error() { let token = token.clone(); - return Err(input.new_unexpected_token_error(token)) + return Err(input.new_unexpected_token_error(token)); } let token_type = token.serialization_type(); if previous_token_type.needs_separator_when_before(token_type) { @@ -62,7 +62,13 @@ fn do_parse_and_serialize<'i>( }; input.parse_nested_block(|input| -> Result<_, ParseError<()>> { - do_parse_and_serialize(input, preserving_comments, previous_token_type, serialization, indent_level + 1) + do_parse_and_serialize( + input, + preserving_comments, + previous_token_type, + serialization, + indent_level + 1, + ) })?; closing_token.to_css(serialization).unwrap(); diff --git a/src/color.rs b/src/color.rs index bc52ed95..448b142f 100644 --- a/src/color.rs +++ b/src/color.rs @@ -14,7 +14,7 @@ /// The opaque alpha value of 1.0. pub const OPAQUE: f32 = 1.0; -use crate::{BasicParseError, Parser, ToCss, Token}; +use crate::{BasicParseError, Parser, ToCss}; use std::fmt; /// Clamp a 0..1 number to a 0..255 range to u8. @@ -101,9 +101,7 @@ pub enum PredefinedColorSpace { impl PredefinedColorSpace { /// Parse a PredefinedColorSpace from the given input. - pub fn parse<'i>(input: &mut Parser<'i, '_>) -> Result> { - let location = input.current_source_location(); - + pub fn parse(input: &mut Parser) -> Result { let ident = input.expect_ident()?; Ok(match_ignore_ascii_case! { ident, "srgb" => Self::Srgb, @@ -115,7 +113,7 @@ impl PredefinedColorSpace { "rec2020" => Self::Rec2020, "xyz-d50" => Self::XyzD50, "xyz" | "xyz-d65" => Self::XyzD65, - _ => return Err(location.new_basic_unexpected_token_error(Token::Ident(ident.clone()))), + _ => return Err(BasicParseError::unexpected_token()), }) } } diff --git a/src/nth.rs b/src/nth.rs index 76c13f35..69f06f77 100644 --- a/src/nth.rs +++ b/src/nth.rs @@ -8,7 +8,7 @@ use super::{BasicParseError, Parser, ParserInput, Token}; /// The input is typically the arguments of a function, /// in which case the caller needs to check if the arguments’ parser is exhausted. /// Return `Ok((A, B))`, or an `Err(..)` for a syntax error. -pub fn parse_nth<'i>(input: &mut Parser<'i, '_>) -> Result<(i32, i32), BasicParseError<'i>> { +pub fn parse_nth(input: &mut Parser) -> Result<(i32, i32), BasicParseError> { match *input.next()? { Token::Number { int_value: Some(b), .. @@ -25,8 +25,7 @@ pub fn parse_nth<'i>(input: &mut Parser<'i, '_>) -> Result<(i32, i32), BasicPars _ => match parse_n_dash_digits(unit) { Ok(b) => Ok((a, b)), Err(()) => { - let unit = unit.clone(); - Err(input.new_basic_unexpected_token_error(Token::Ident(unit))) + Err(BasicParseError::unexpected_token()) } } } @@ -48,8 +47,7 @@ pub fn parse_nth<'i>(input: &mut Parser<'i, '_>) -> Result<(i32, i32), BasicPars match parse_n_dash_digits(slice) { Ok(b) => Ok((a, b)), Err(()) => { - let value = value.clone(); - Err(input.new_basic_unexpected_token_error(Token::Ident(value))) + Err(BasicParseError::unexpected_token()) } } } @@ -63,25 +61,18 @@ pub fn parse_nth<'i>(input: &mut Parser<'i, '_>) -> Result<(i32, i32), BasicPars _ => match parse_n_dash_digits(value) { Ok(b) => Ok((1, b)), Err(()) => { - let value = value.clone(); - Err(input.new_basic_unexpected_token_error(Token::Ident(value))) + Err(BasicParseError::unexpected_token()) } } } } - ref token => { - let token = token.clone(); - Err(input.new_basic_unexpected_token_error(token)) - } + _ => Err(BasicParseError::unexpected_token()), }, - ref token => { - let token = token.clone(); - Err(input.new_basic_unexpected_token_error(token)) - } + _ => Err(BasicParseError::unexpected_token()), } } -fn parse_b<'i>(input: &mut Parser<'i, '_>, a: i32) -> Result<(i32, i32), BasicParseError<'i>> { +fn parse_b(input: &mut Parser, a: i32) -> Result<(i32, i32), BasicParseError> { let start = input.state(); match input.next() { Ok(&Token::Delim('+')) => parse_signless_b(input, a, 1), @@ -98,19 +89,18 @@ fn parse_b<'i>(input: &mut Parser<'i, '_>, a: i32) -> Result<(i32, i32), BasicPa } } -fn parse_signless_b<'i>( - input: &mut Parser<'i, '_>, +fn parse_signless_b( + input: &mut Parser, a: i32, b_sign: i32, -) -> Result<(i32, i32), BasicParseError<'i>> { - // FIXME: remove .clone() when lifetimes are non-lexical. - match input.next()?.clone() { - Token::Number { +) -> Result<(i32, i32), BasicParseError> { + match input.next()? { + &Token::Number { has_sign: false, int_value: Some(b), .. } => Ok((a, b_sign * b)), - token => Err(input.new_basic_unexpected_token_error(token)), + _ => Err(BasicParseError::unexpected_token()), } } diff --git a/src/parser.rs b/src/parser.rs index 29b01a1f..1466ac7b 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -63,13 +63,20 @@ pub enum ParseUntilErrorBehavior { /// Details about a `BasicParseError` #[derive(Clone, Debug, PartialEq)] -pub enum BasicParseErrorKind<'i> { +pub enum BasicParseErrorKind { /// An unexpected token was encountered. - UnexpectedToken(Token<'i>), + /// + /// The token itself is deliberately not stored: it made this enum 32 bytes, + /// which pushed `Result<&Token, BasicParseError>` (returned from every token + /// fetch) to 40 bytes and therefore out of registers and into memory. + /// Callers that want to name the token can recover it from the source text + /// they already carry for the error message. + UnexpectedToken, /// The end of the input was encountered unexpectedly. EndOfInput, - /// An `@` rule was encountered that was invalid. - AtRuleInvalid(CowRcStr<'i>), + /// An `@` rule was encountered that was invalid. See `UnexpectedToken` for + /// why the rule name is not stored. + AtRuleInvalid, /// The body of an '@' rule was invalid. AtRuleBodyInvalid, /// A qualified rule was encountered that was invalid. @@ -78,19 +85,15 @@ pub enum BasicParseErrorKind<'i> { TooManyNestedBlocks, } -impl fmt::Display for BasicParseErrorKind<'_> { +impl fmt::Display for BasicParseErrorKind { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { BasicParseErrorKind::TooManyNestedBlocks => { write!(f, "nesting block limit reached") } - BasicParseErrorKind::UnexpectedToken(token) => { - write!(f, "unexpected token: {token:?}") - } + BasicParseErrorKind::UnexpectedToken => write!(f, "unexpected token"), BasicParseErrorKind::EndOfInput => write!(f, "unexpected end of input"), - BasicParseErrorKind::AtRuleInvalid(rule) => { - write!(f, "invalid @ rule encountered: '@{rule}'") - } + BasicParseErrorKind::AtRuleInvalid => write!(f, "invalid @ rule encountered"), BasicParseErrorKind::AtRuleBodyInvalid => write!(f, "invalid @ rule body encountered"), BasicParseErrorKind::QualifiedRuleInvalid => { write!(f, "invalid qualified rule encountered") @@ -101,76 +104,46 @@ impl fmt::Display for BasicParseErrorKind<'_> { /// The fundamental parsing errors that can be triggered by built-in parsing routines. #[derive(Clone, Debug, PartialEq)] -pub struct BasicParseError<'i> { +pub struct BasicParseError { /// Details of this error - pub kind: BasicParseErrorKind<'i>, - /// Location where this error occurred - pub location: SourceLocation, -} - -impl<'i, T> From> for ParseError<'i, T> { - #[inline] - fn from(this: BasicParseError<'i>) -> ParseError<'i, T> { - ParseError { - kind: ParseErrorKind::Basic(this.kind), - location: this.location, - } - } + pub kind: BasicParseErrorKind, } -impl SourceLocation { - /// Create a new BasicParseError at this location for an unexpected token - #[inline] - pub fn new_basic_unexpected_token_error(self, token: Token<'_>) -> BasicParseError<'_> { - self.new_basic_error(BasicParseErrorKind::UnexpectedToken(token)) - } - - /// Create a new BasicParseError at this location - #[inline] - pub fn new_basic_error(self, kind: BasicParseErrorKind<'_>) -> BasicParseError<'_> { - BasicParseError { - kind, - location: self, - } - } - - /// Create a new ParseError at this location for an unexpected token +impl BasicParseError { + /// Create a new BasicParseError of the given kind. #[inline] - pub fn new_unexpected_token_error(self, token: Token<'_>) -> ParseError<'_, E> { - self.new_error(BasicParseErrorKind::UnexpectedToken(token)) + pub fn new(kind: BasicParseErrorKind) -> Self { + Self { kind } } - /// Create a new basic ParseError at the current location + /// Create a new BasicParseError for an unexpected token. #[inline] - pub fn new_error(self, kind: BasicParseErrorKind<'_>) -> ParseError<'_, E> { - ParseError { - kind: ParseErrorKind::Basic(kind), - location: self, - } + pub fn unexpected_token() -> Self { + Self::new(BasicParseErrorKind::UnexpectedToken) } +} - /// Create a new custom ParseError at this location +impl From for ParseError { #[inline] - pub fn new_custom_error<'i, E1: Into, E2>(self, error: E1) -> ParseError<'i, E2> { + fn from(this: BasicParseError) -> ParseError { ParseError { - kind: ParseErrorKind::Custom(error.into()), - location: self, + kind: ParseErrorKind::Basic(this.kind), } } } /// Details of a `ParseError` #[derive(Clone, Debug, PartialEq)] -pub enum ParseErrorKind<'i, T: 'i> { +pub enum ParseErrorKind { /// A fundamental parse error from a built-in parsing routine. - Basic(BasicParseErrorKind<'i>), + Basic(BasicParseErrorKind), /// A parse error reported by downstream consumer code. Custom(T), } -impl<'i, T> ParseErrorKind<'i, T> { +impl ParseErrorKind { /// Like `std::convert::Into::into` - pub fn into(self) -> ParseErrorKind<'i, U> + pub fn into(self) -> ParseErrorKind where T: Into, { @@ -181,7 +154,7 @@ impl<'i, T> ParseErrorKind<'i, T> { } } -impl fmt::Display for ParseErrorKind<'_, E> { +impl fmt::Display for ParseErrorKind { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { ParseErrorKind::Basic(ref basic) => basic.fmt(f), @@ -192,44 +165,60 @@ impl fmt::Display for ParseErrorKind<'_, E> { /// Extensible parse errors that can be encountered by client parsing implementations. #[derive(Clone, Debug, PartialEq)] -pub struct ParseError<'i, E> { +pub struct ParseError { /// Details of this error - pub kind: ParseErrorKind<'i, E>, - /// Location where this error occurred - pub location: SourceLocation, + pub kind: ParseErrorKind, } -impl<'i, T> ParseError<'i, T> { +impl ParseError { + /// Create a new ParseError from a basic error kind. + #[inline] + pub fn from_basic_kind(kind: BasicParseErrorKind) -> Self { + Self { + kind: ParseErrorKind::Basic(kind), + } + } + + /// Create a new ParseError for an unexpected token. + #[inline] + pub fn unexpected_token() -> Self { + Self::from_basic_kind(BasicParseErrorKind::UnexpectedToken) + } + + /// Create a new ParseError from a consumer-defined error. + #[inline] + pub fn custom>(error: E) -> Self { + Self { + kind: ParseErrorKind::Custom(error.into()), + } + } + /// Extract the fundamental parse error from an extensible error. - pub fn basic(self) -> BasicParseError<'i> { + pub fn basic(self) -> BasicParseError { match self.kind { - ParseErrorKind::Basic(kind) => BasicParseError { - kind, - location: self.location, - }, + ParseErrorKind::Basic(kind) => BasicParseError { kind }, ParseErrorKind::Custom(_) => panic!("Not a basic parse error"), } } /// Like `std::convert::Into::into` - pub fn into(self) -> ParseError<'i, U> + pub fn into(self) -> ParseError where T: Into, { ParseError { kind: self.kind.into(), - location: self.location, } } } -impl fmt::Display for ParseError<'_, E> { +impl fmt::Display for ParseError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.kind.fmt(f) } } -impl std::error::Error for ParseError<'_, E> {} +impl std::error::Error for ParseError {} /// The owned input for a parser. pub struct ParserInput<'i> { @@ -388,11 +377,10 @@ impl Delimiters { macro_rules! expect { ($parser: ident, $($branches: tt)+) => { { - let start_location = $parser.current_source_location(); match *$parser.next()? { $($branches)+ - ref token => { - return Err(start_location.new_basic_unexpected_token_error(token.clone())) + _ => { + return Err(BasicParseError::unexpected_token()) } } } @@ -432,7 +420,7 @@ impl<'i: 't, 't> Parser<'i, 't> { /// /// This ignores whitespace and comments. #[inline] - pub fn expect_exhausted(&mut self) -> Result<(), BasicParseError<'i>> { + pub fn expect_exhausted(&mut self) -> Result<(), BasicParseError> { let start = self.state(); let result = match self.next() { Err(BasicParseError { @@ -440,9 +428,7 @@ impl<'i: 't, 't> Parser<'i, 't> { .. }) => Ok(()), Err(e) => unreachable!("Unexpected error encountered: {:?}", e), - Ok(t) => Err(start - .source_location() - .new_basic_unexpected_token_error(t.clone())), + Ok(_) => Err(BasicParseError::unexpected_token()), }; self.reset(&start); result @@ -480,44 +466,13 @@ impl<'i: 't, 't> Parser<'i, 't> { self.input.tokenizer.current_source_url() } - /// Create a new BasicParseError at the current location - #[inline] - pub fn new_basic_error(&self, kind: BasicParseErrorKind<'i>) -> BasicParseError<'i> { - self.current_source_location().new_basic_error(kind) - } - - /// Create a new basic ParseError at the current location - #[inline] - pub fn new_error(&self, kind: BasicParseErrorKind<'i>) -> ParseError<'i, E> { - self.current_source_location().new_error(kind) - } - - /// Create a new custom BasicParseError at the current location - #[inline] - pub fn new_custom_error, E2>(&self, error: E1) -> ParseError<'i, E2> { - self.current_source_location().new_custom_error(error) - } - - /// Create a new unexpected token BasicParseError at the current location - #[inline] - pub fn new_basic_unexpected_token_error(&self, token: Token<'i>) -> BasicParseError<'i> { - self.new_basic_error(BasicParseErrorKind::UnexpectedToken(token)) - } - - /// Create a new unexpected token ParseError at the current location - #[inline] - pub fn new_unexpected_token_error(&self, token: Token<'i>) -> ParseError<'i, E> { - self.new_error(BasicParseErrorKind::UnexpectedToken(token)) - } - /// Create a new unexpected token or EOF ParseError at the current location #[inline] - pub fn new_error_for_next_token(&mut self) -> ParseError<'i, E> { - let token = match self.next() { - Ok(token) => token.clone(), - Err(e) => return e.into(), - }; - self.new_error(BasicParseErrorKind::UnexpectedToken(token)) + pub fn new_error_for_next_token(&mut self) -> ParseError { + match self.next() { + Ok(_) => ParseError::unexpected_token(), + Err(e) => e.into(), + } } /// Return the current internal state of the parser (including position within the input). @@ -638,13 +593,13 @@ impl<'i: 't, 't> Parser<'i, 't> { /// /// This only returns a closing token when it is unmatched (and therefore an error). #[allow(clippy::should_implement_trait)] - pub fn next(&mut self) -> Result<&Token<'i>, BasicParseError<'i>> { + pub fn next(&mut self) -> Result<&Token<'i>, BasicParseError> { self.skip_whitespace(); self.next_including_whitespace_and_comments() } /// Same as `Parser::next`, but does not skip whitespace tokens. - pub fn next_including_whitespace(&mut self) -> Result<&Token<'i>, BasicParseError<'i>> { + pub fn next_including_whitespace(&mut self) -> Result<&Token<'i>, BasicParseError> { loop { match self.next_including_whitespace_and_comments() { Err(e) => return Err(e), @@ -663,14 +618,14 @@ impl<'i: 't, 't> Parser<'i, 't> { /// comments should always be ignored between tokens. pub fn next_including_whitespace_and_comments( &mut self, - ) -> Result<&Token<'i>, BasicParseError<'i>> { + ) -> Result<&Token<'i>, BasicParseError> { if let Some(block_type) = self.at_start_of.take() { consume_until_end_of_block(block_type, &mut self.input.tokenizer); } let byte = self.input.tokenizer.next_byte(); if self.stop_before.contains(Delimiters::from_byte(byte)) { - return Err(self.new_basic_error(BasicParseErrorKind::EndOfInput)); + return Err(BasicParseError::new(BasicParseErrorKind::EndOfInput)); } let token_start_position = self.input.tokenizer.position(); @@ -688,7 +643,7 @@ impl<'i: 't, 't> Parser<'i, 't> { &cached_token.token } else { let Ok(new_token) = self.input.tokenizer.next() else { - return Err(self.new_basic_error(BasicParseErrorKind::EndOfInput)); + return Err(BasicParseError::new(BasicParseErrorKind::EndOfInput)); }; self.input.cached_token = Some(CachedToken { token: new_token, @@ -709,9 +664,9 @@ impl<'i: 't, 't> Parser<'i, 't> { /// /// This can help tell e.g. `color: green;` from `color: green 4px;` #[inline] - pub fn parse_entirely(&mut self, parse: F) -> Result> + pub fn parse_entirely(&mut self, parse: F) -> Result> where - F: FnOnce(&mut Parser<'i, 't>) -> Result>, + F: FnOnce(&mut Parser<'i, 't>) -> Result>, { let result = parse(self)?; self.expect_exhausted()?; @@ -730,12 +685,9 @@ impl<'i: 't, 't> Parser<'i, 't> { /// or if a closure call leaves some input before the next comma or the end /// of the input. #[inline] - pub fn parse_comma_separated( - &mut self, - parse_one: F, - ) -> Result, ParseError<'i, E>> + pub fn parse_comma_separated(&mut self, parse_one: F) -> Result, ParseError> where - F: for<'tt> FnMut(&mut Parser<'i, 'tt>) -> Result>, + F: for<'tt> FnMut(&mut Parser<'i, 'tt>) -> Result>, { self.parse_comma_separated_internal(parse_one, /* ignore_errors = */ false) } @@ -746,9 +698,9 @@ impl<'i: 't, 't> Parser<'i, 't> { /// Caller must deal with the fact that the resulting list might be empty, /// if there's no valid component on the list. #[inline] - pub fn parse_comma_separated_ignoring_errors(&mut self, parse_one: F) -> Vec + pub fn parse_comma_separated_ignoring_errors(&mut self, parse_one: F) -> Vec where - F: for<'tt> FnMut(&mut Parser<'i, 'tt>) -> Result>, + F: for<'tt> FnMut(&mut Parser<'i, 'tt>) -> Result>, { match self.parse_comma_separated_internal(parse_one, /* ignore_errors = */ true) { Ok(values) => values, @@ -761,9 +713,9 @@ impl<'i: 't, 't> Parser<'i, 't> { &mut self, mut parse_one: F, ignore_errors: bool, - ) -> Result, ParseError<'i, E>> + ) -> Result, ParseError> where - F: for<'tt> FnMut(&mut Parser<'i, 'tt>) -> Result>, + F: for<'tt> FnMut(&mut Parser<'i, 'tt>) -> Result>, { // Vec grows from 0 to 4 by default on first push(). So allocate with // capacity 1, so in the somewhat common case of only one item we don't @@ -797,9 +749,9 @@ impl<'i: 't, 't> Parser<'i, 't> { /// /// The result is overridden to an `Err(..)` if the closure leaves some input before that point. #[inline] - pub fn parse_nested_block(&mut self, parse: F) -> Result> + pub fn parse_nested_block(&mut self, parse: F) -> Result> where - F: for<'tt> FnOnce(&mut Parser<'i, 'tt>) -> Result>, + F: for<'tt> FnOnce(&mut Parser<'i, 'tt>) -> Result>, { parse_nested_block(self, parse) } @@ -817,9 +769,9 @@ impl<'i: 't, 't> Parser<'i, 't> { &mut self, delimiters: Delimiters, parse: F, - ) -> Result> + ) -> Result> where - F: for<'tt> FnOnce(&mut Parser<'i, 'tt>) -> Result>, + F: for<'tt> FnOnce(&mut Parser<'i, 'tt>) -> Result>, { parse_until_before(self, delimiters, ParseUntilErrorBehavior::Consume, parse) } @@ -834,26 +786,25 @@ impl<'i: 't, 't> Parser<'i, 't> { &mut self, delimiters: Delimiters, parse: F, - ) -> Result> + ) -> Result> where - F: for<'tt> FnOnce(&mut Parser<'i, 'tt>) -> Result>, + F: for<'tt> FnOnce(&mut Parser<'i, 'tt>) -> Result>, { parse_until_after(self, delimiters, ParseUntilErrorBehavior::Consume, parse) } /// Parse a and return its value. #[inline] - pub fn expect_whitespace(&mut self) -> Result<&'i str, BasicParseError<'i>> { - let start_location = self.current_source_location(); + pub fn expect_whitespace(&mut self) -> Result<&'i str, BasicParseError> { match *self.next_including_whitespace()? { Token::WhiteSpace(value) => Ok(value), - ref t => Err(start_location.new_basic_unexpected_token_error(t.clone())), + _ => Err(BasicParseError::unexpected_token()), } } /// Parse a and return the unescaped value. #[inline] - pub fn expect_ident(&mut self) -> Result<&CowRcStr<'i>, BasicParseError<'i>> { + pub fn expect_ident(&mut self) -> Result<&CowRcStr<'i>, BasicParseError> { expect! {self, Token::Ident(ref value) => Ok(value), } @@ -861,16 +812,13 @@ impl<'i: 't, 't> Parser<'i, 't> { /// expect_ident, but clone the CowRcStr #[inline] - pub fn expect_ident_cloned(&mut self) -> Result, BasicParseError<'i>> { + pub fn expect_ident_cloned(&mut self) -> Result, BasicParseError> { self.expect_ident().cloned() } /// Parse a whose unescaped value is an ASCII-insensitive match for the given value. #[inline] - pub fn expect_ident_matching( - &mut self, - expected_value: &str, - ) -> Result<(), BasicParseError<'i>> { + pub fn expect_ident_matching(&mut self, expected_value: &str) -> Result<(), BasicParseError> { expect! {self, Token::Ident(ref value) if value.eq_ignore_ascii_case(expected_value) => Ok(()), } @@ -878,7 +826,7 @@ impl<'i: 't, 't> Parser<'i, 't> { /// Parse a and return the unescaped value. #[inline] - pub fn expect_string(&mut self) -> Result<&CowRcStr<'i>, BasicParseError<'i>> { + pub fn expect_string(&mut self) -> Result<&CowRcStr<'i>, BasicParseError> { expect! {self, Token::QuotedString(ref value) => Ok(value), } @@ -886,13 +834,13 @@ impl<'i: 't, 't> Parser<'i, 't> { /// expect_string, but clone the CowRcStr #[inline] - pub fn expect_string_cloned(&mut self) -> Result, BasicParseError<'i>> { + pub fn expect_string_cloned(&mut self) -> Result, BasicParseError> { self.expect_string().cloned() } /// Parse either a or a , and return the unescaped value. #[inline] - pub fn expect_ident_or_string(&mut self) -> Result<&CowRcStr<'i>, BasicParseError<'i>> { + pub fn expect_ident_or_string(&mut self) -> Result<&CowRcStr<'i>, BasicParseError> { expect! {self, Token::Ident(ref value) => Ok(value), Token::QuotedString(ref value) => Ok(value), @@ -901,7 +849,7 @@ impl<'i: 't, 't> Parser<'i, 't> { /// Parse a and return the unescaped value. #[inline] - pub fn expect_url(&mut self) -> Result, BasicParseError<'i>> { + pub fn expect_url(&mut self) -> Result, BasicParseError> { expect! {self, Token::UnquotedUrl(ref value) => Ok(value.clone()), Token::Function(ref name) if name.eq_ignore_ascii_case("url") => { @@ -915,7 +863,7 @@ impl<'i: 't, 't> Parser<'i, 't> { /// Parse either a or a , and return the unescaped value. #[inline] - pub fn expect_url_or_string(&mut self) -> Result, BasicParseError<'i>> { + pub fn expect_url_or_string(&mut self) -> Result, BasicParseError> { expect! {self, Token::UnquotedUrl(ref value) => Ok(value.clone()), Token::QuotedString(ref value) => Ok(value.clone()), @@ -930,7 +878,7 @@ impl<'i: 't, 't> Parser<'i, 't> { /// Parse a and return the integer value. #[inline] - pub fn expect_number(&mut self) -> Result> { + pub fn expect_number(&mut self) -> Result { expect! {self, Token::Number { value, .. } => Ok(value), } @@ -938,7 +886,7 @@ impl<'i: 't, 't> Parser<'i, 't> { /// Parse a that does not have a fractional part, and return the integer value. #[inline] - pub fn expect_integer(&mut self) -> Result> { + pub fn expect_integer(&mut self) -> Result { expect! {self, Token::Number { int_value: Some(int_value), .. } => Ok(int_value), } @@ -947,7 +895,7 @@ impl<'i: 't, 't> Parser<'i, 't> { /// Parse a and return the value. /// `0%` and `100%` map to `0.0` and `1.0` (not `100.0`), respectively. #[inline] - pub fn expect_percentage(&mut self) -> Result> { + pub fn expect_percentage(&mut self) -> Result { expect! {self, Token::Percentage { unit_value, .. } => Ok(unit_value), } @@ -955,7 +903,7 @@ impl<'i: 't, 't> Parser<'i, 't> { /// Parse a `:` . #[inline] - pub fn expect_colon(&mut self) -> Result<(), BasicParseError<'i>> { + pub fn expect_colon(&mut self) -> Result<(), BasicParseError> { expect! {self, Token::Colon => Ok(()), } @@ -963,7 +911,7 @@ impl<'i: 't, 't> Parser<'i, 't> { /// Parse a `;` . #[inline] - pub fn expect_semicolon(&mut self) -> Result<(), BasicParseError<'i>> { + pub fn expect_semicolon(&mut self) -> Result<(), BasicParseError> { expect! {self, Token::Semicolon => Ok(()), } @@ -971,7 +919,7 @@ impl<'i: 't, 't> Parser<'i, 't> { /// Parse a `,` . #[inline] - pub fn expect_comma(&mut self) -> Result<(), BasicParseError<'i>> { + pub fn expect_comma(&mut self) -> Result<(), BasicParseError> { expect! {self, Token::Comma => Ok(()), } @@ -979,7 +927,7 @@ impl<'i: 't, 't> Parser<'i, 't> { /// Parse a with the given value. #[inline] - pub fn expect_delim(&mut self, expected_value: char) -> Result<(), BasicParseError<'i>> { + pub fn expect_delim(&mut self, expected_value: char) -> Result<(), BasicParseError> { expect! {self, Token::Delim(value) if value == expected_value => Ok(()), } @@ -989,7 +937,7 @@ impl<'i: 't, 't> Parser<'i, 't> { /// /// If the result is `Ok`, you can then call the `Parser::parse_nested_block` method. #[inline] - pub fn expect_curly_bracket_block(&mut self) -> Result<(), BasicParseError<'i>> { + pub fn expect_curly_bracket_block(&mut self) -> Result<(), BasicParseError> { expect! {self, Token::CurlyBracketBlock => Ok(()), } @@ -999,7 +947,7 @@ impl<'i: 't, 't> Parser<'i, 't> { /// /// If the result is `Ok`, you can then call the `Parser::parse_nested_block` method. #[inline] - pub fn expect_square_bracket_block(&mut self) -> Result<(), BasicParseError<'i>> { + pub fn expect_square_bracket_block(&mut self) -> Result<(), BasicParseError> { expect! {self, Token::SquareBracketBlock => Ok(()), } @@ -1009,7 +957,7 @@ impl<'i: 't, 't> Parser<'i, 't> { /// /// If the result is `Ok`, you can then call the `Parser::parse_nested_block` method. #[inline] - pub fn expect_parenthesis_block(&mut self) -> Result<(), BasicParseError<'i>> { + pub fn expect_parenthesis_block(&mut self) -> Result<(), BasicParseError> { expect! {self, Token::ParenthesisBlock => Ok(()), } @@ -1019,7 +967,7 @@ impl<'i: 't, 't> Parser<'i, 't> { /// /// If the result is `Ok`, you can then call the `Parser::parse_nested_block` method. #[inline] - pub fn expect_function(&mut self) -> Result<&CowRcStr<'i>, BasicParseError<'i>> { + pub fn expect_function(&mut self) -> Result<&CowRcStr<'i>, BasicParseError> { expect! {self, Token::Function(ref name) => Ok(name), } @@ -1029,10 +977,7 @@ impl<'i: 't, 't> Parser<'i, 't> { /// /// If the result is `Ok`, you can then call the `Parser::parse_nested_block` method. #[inline] - pub fn expect_function_matching( - &mut self, - expected_name: &str, - ) -> Result<(), BasicParseError<'i>> { + pub fn expect_function_matching(&mut self, expected_name: &str) -> Result<(), BasicParseError> { expect! {self, Token::Function(ref name) if name.eq_ignore_ascii_case(expected_name) => Ok(()), } @@ -1042,7 +987,7 @@ impl<'i: 't, 't> Parser<'i, 't> { /// /// See `Token::is_parse_error`. This also checks nested blocks and functions recursively. #[inline] - pub fn expect_no_error_token(&mut self) -> Result<(), BasicParseError<'i>> { + pub fn expect_no_error_token(&mut self) -> Result<(), BasicParseError> { loop { match self.next_including_whitespace_and_comments() { Ok(&Token::Function(_)) @@ -1055,8 +1000,7 @@ impl<'i: 't, 't> Parser<'i, 't> { // FIXME: maybe these should be separate variants of // BasicParseError instead? if t.is_parse_error() { - let token = t.clone(); - return Err(self.new_basic_unexpected_token_error(token)); + return Err(BasicParseError::unexpected_token()); } } Err(_) => return Ok(()), @@ -1070,9 +1014,9 @@ pub fn parse_until_before<'i: 't, 't, F, T, E>( delimiters: Delimiters, error_behavior: ParseUntilErrorBehavior, parse: F, -) -> Result> +) -> Result> where - F: for<'tt> FnOnce(&mut Parser<'i, 'tt>) -> Result>, + F: for<'tt> FnOnce(&mut Parser<'i, 'tt>) -> Result>, { let delimiters = parser.stop_before | delimiters; let result; @@ -1112,9 +1056,9 @@ pub fn parse_until_after<'i: 't, 't, F, T, E>( delimiters: Delimiters, error_behavior: ParseUntilErrorBehavior, parse: F, -) -> Result> +) -> Result> where - F: for<'tt> FnOnce(&mut Parser<'i, 'tt>) -> Result>, + F: for<'tt> FnOnce(&mut Parser<'i, 'tt>) -> Result>, { let result = parse_until_before(parser, delimiters, error_behavior, parse); if error_behavior == ParseUntilErrorBehavior::Stop && result.is_err() { @@ -1139,9 +1083,9 @@ where pub fn parse_nested_block<'i: 't, 't, F, T, E>( parser: &mut Parser<'i, 't>, parse: F, -) -> Result> +) -> Result> where - F: for<'tt> FnOnce(&mut Parser<'i, 'tt>) -> Result>, + F: for<'tt> FnOnce(&mut Parser<'i, 'tt>) -> Result>, { let block_type = parser.at_start_of.take().expect( "\ @@ -1153,7 +1097,9 @@ where if parser.input.current_block_depth >= parser.input.nested_block_limit && parser.input.nested_block_limit != 0 { - return Err(parser.new_error(BasicParseErrorKind::TooManyNestedBlocks)); + return Err(ParseError::from_basic_kind( + BasicParseErrorKind::TooManyNestedBlocks, + )); } // Fine to use wrapping addition, overflow can only occur without a limit. parser.input.current_block_depth = parser.input.current_block_depth.wrapping_add(1); diff --git a/src/rules_and_declarations.rs b/src/rules_and_declarations.rs index 7f268c59..e28dc1b6 100644 --- a/src/rules_and_declarations.rs +++ b/src/rules_and_declarations.rs @@ -7,12 +7,13 @@ use super::{BasicParseError, BasicParseErrorKind, Delimiter, ParseError, Parser, Token}; use crate::cow_rc_str::CowRcStr; use crate::parser::{parse_nested_block, parse_until_after, ParseUntilErrorBehavior, ParserState}; +use crate::tokenizer::SourceLocation; /// Parse `!important`. /// /// Typical usage is `input.try_parse(parse_important).is_ok()` /// at the end of a `DeclarationParser::parse_value` implementation. -pub fn parse_important<'i>(input: &mut Parser<'i, '_>) -> Result<(), BasicParseError<'i>> { +pub fn parse_important(input: &mut Parser) -> Result<(), BasicParseError> { input.expect_delim('!')?; input.expect_ident_matching("important") } @@ -26,7 +27,7 @@ pub trait DeclarationParser<'i> { type Declaration; /// The error type that is included in the ParseError value that can be returned. - type Error: 'i; + type Error; /// Parse the value of a declaration with the given `name`. /// @@ -45,13 +46,13 @@ pub trait DeclarationParser<'i> { /// If `!important` can be used in a given context, /// `input.try_parse(parse_important).is_ok()` should be used at the end /// of the implementation of this method and the result should be part of the return value. - fn parse_value<'t>( + fn parse_value( &mut self, - name: CowRcStr<'i>, - input: &mut Parser<'i, 't>, + _name: CowRcStr<'i>, + _input: &mut Parser<'i, '_>, _declaration_start: &ParserState, - ) -> Result> { - Err(input.new_error(BasicParseErrorKind::UnexpectedToken(Token::Ident(name)))) + ) -> Result> { + Err(ParseError::unexpected_token()) } } @@ -72,7 +73,7 @@ pub trait AtRuleParser<'i> { type AtRule; /// The error type that is included in the ParseError value that can be returned. - type Error: 'i; + type Error; /// Parse the prelude of an at-rule with the given `name`. /// @@ -89,18 +90,20 @@ pub trait AtRuleParser<'i> { /// The given `input` is a "delimited" parser /// that ends wherever the prelude should end. /// (Before the next semicolon, the next `{`, or the end of the current block.) - fn parse_prelude<'t>( + fn parse_prelude( &mut self, - name: CowRcStr<'i>, - input: &mut Parser<'i, 't>, - ) -> Result> { - Err(input.new_error(BasicParseErrorKind::AtRuleInvalid(name))) + _name: CowRcStr<'i>, + _input: &mut Parser<'i, '_>, + ) -> Result> { + Err(ParseError::from_basic_kind( + BasicParseErrorKind::AtRuleInvalid, + )) } /// End an at-rule which doesn't have block. Return the finished /// representation of the at-rule. /// - /// The location passed in is source location of the start of the prelude. + /// The state passed in is the parser state at the start of the prelude. /// /// This is only called when `parse_prelude` returned `WithoutBlock`, and /// either the `;` semicolon indeed follows the prelude, or parser is at @@ -118,7 +121,7 @@ pub trait AtRuleParser<'i> { /// Parse the content of a `{ /* ... */ }` block for the body of the at-rule. /// - /// The location passed in is source location of the start of the prelude. + /// The state passed in is the parser state at the start of the prelude. /// /// Return the finished representation of the at-rule /// as returned by `StyleSheetParser::next` or `RuleBodyParser::next`, @@ -126,15 +129,17 @@ pub trait AtRuleParser<'i> { /// /// This is only called when `parse_prelude` returned `WithBlock`, and a block /// was indeed found following the prelude. - fn parse_block<'t>( + fn parse_block( &mut self, prelude: Self::Prelude, start: &ParserState, - input: &mut Parser<'i, 't>, - ) -> Result> { + _input: &mut Parser<'i, '_>, + ) -> Result> { let _ = prelude; let _ = start; - Err(input.new_error(BasicParseErrorKind::AtRuleBodyInvalid)) + Err(ParseError::from_basic_kind( + BasicParseErrorKind::AtRuleBodyInvalid, + )) } } @@ -156,7 +161,7 @@ pub trait QualifiedRuleParser<'i> { type QualifiedRule; /// The error type that is included in the ParseError value that can be returned. - type Error: 'i; + type Error; /// Parse the prelude of a qualified rule. For style rules, this is as Selector list. /// @@ -167,29 +172,33 @@ pub trait QualifiedRuleParser<'i> { /// /// The given `input` is a "delimited" parser /// that ends where the prelude should end (before the next `{`). - fn parse_prelude<'t>( + fn parse_prelude( &mut self, - input: &mut Parser<'i, 't>, - ) -> Result> { - Err(input.new_error(BasicParseErrorKind::QualifiedRuleInvalid)) + _input: &mut Parser<'i, '_>, + ) -> Result> { + Err(ParseError::from_basic_kind( + BasicParseErrorKind::QualifiedRuleInvalid, + )) } /// Parse the content of a `{ /* ... */ }` block for the body of the qualified rule. /// - /// The location passed in is source location of the start of the prelude. + /// The state passed in is the parser state at the start of the prelude. /// /// Return the finished representation of the qualified rule /// as returned by `StyleSheetParser::next`, /// or an `Err(..)` to ignore the entire at-rule as invalid. - fn parse_block<'t>( + fn parse_block( &mut self, prelude: Self::Prelude, start: &ParserState, - input: &mut Parser<'i, 't>, - ) -> Result> { + _input: &mut Parser<'i, '_>, + ) -> Result> { let _ = prelude; let _ = start; - Err(input.new_error(BasicParseErrorKind::QualifiedRuleInvalid)) + Err(ParseError::from_basic_kind( + BasicParseErrorKind::QualifiedRuleInvalid, + )) } } @@ -204,7 +213,7 @@ pub struct RuleBodyParser<'i, 't, 'a, P, I, E> { } /// A parser for a rule body item. -pub trait RuleBodyItemParser<'i, DeclOrRule, Error: 'i>: +pub trait RuleBodyItemParser<'i, DeclOrRule, Error>: DeclarationParser<'i, Declaration = DeclOrRule, Error = Error> + QualifiedRuleParser<'i, QualifiedRule = DeclOrRule, Error = Error> + AtRuleParser<'i, AtRule = DeclOrRule, Error = Error> @@ -242,11 +251,11 @@ impl<'i, 't, 'a, P, I, E> RuleBodyParser<'i, 't, 'a, P, I, E> { } /// https://drafts.csswg.org/css-syntax/#consume-a-blocks-contents -impl<'i, I, P, E: 'i> Iterator for RuleBodyParser<'i, '_, '_, P, I, E> +impl<'i, I, P, E> Iterator for RuleBodyParser<'i, '_, '_, P, I, E> where P: RuleBodyItemParser<'i, I, E>, { - type Item = Result, &'i str)>; + type Item = Result, &'i str, SourceLocation)>; fn next(&mut self) -> Option { loop { @@ -298,20 +307,31 @@ where } } - return Some(result.map_err(|e| (e, self.input.slice_from(start.position())))); + return Some(result.map_err(|e| { + ( + e, + self.input.slice_from(start.position()), + start.source_location(), + ) + })); } - token => { + _ => { let result = if self.parser.parse_qualified() { self.input.reset(&start); let nested = self.parser.parse_declarations(); parse_qualified_rule(&start, self.input, &mut *self.parser, nested) } else { - let token = token.clone(); self.input.parse_until_after(Delimiter::Semicolon, |_| { - Err(start.source_location().new_unexpected_token_error(token)) + Err(ParseError::unexpected_token()) }) }; - return Some(result.map_err(|e| (e, self.input.slice_from(start.position())))); + return Some(result.map_err(|e| { + ( + e, + self.input.slice_from(start.position()), + start.source_location(), + ) + })); } } } @@ -329,7 +349,7 @@ pub struct StyleSheetParser<'i, 't, 'a, P> { any_rule_so_far: bool, } -impl<'i, 't, 'a, R, P, E: 'i> StyleSheetParser<'i, 't, 'a, P> +impl<'i, 't, 'a, R, P, E> StyleSheetParser<'i, 't, 'a, P> where P: QualifiedRuleParser<'i, QualifiedRule = R, Error = E> + AtRuleParser<'i, AtRule = R, Error = E>, @@ -350,12 +370,12 @@ where } /// `StyleSheetParser` is an iterator that yields `Ok(_)` for a rule or an `Err(..)` for an invalid one. -impl<'i, R, P, E: 'i> Iterator for StyleSheetParser<'i, '_, '_, P> +impl<'i, R, P, E> Iterator for StyleSheetParser<'i, '_, '_, P> where P: QualifiedRuleParser<'i, QualifiedRule = R, Error = E> + AtRuleParser<'i, AtRule = R, Error = E>, { - type Item = Result, &'i str)>; + type Item = Result, &'i str, SourceLocation)>; fn next(&mut self) -> Option { loop { @@ -395,17 +415,23 @@ where &mut *self.parser, /* nested = */ false, ); - return Some(result.map_err(|e| (e, self.input.slice_from(start.position())))); + return Some(result.map_err(|e| { + ( + e, + self.input.slice_from(start.position()), + start.source_location(), + ) + })); } } } } /// Parse a single declaration, such as an `( /* ... */ )` parenthesis in an `@supports` prelude. -pub fn parse_one_declaration<'i, 't, P, E>( - input: &mut Parser<'i, 't>, +pub fn parse_one_declaration<'i, P, E>( + input: &mut Parser<'i, '_>, parser: &mut P, -) -> Result<

>::Declaration, (ParseError<'i, E>, &'i str)> +) -> Result<

>::Declaration, (ParseError, &'i str, SourceLocation)> where P: DeclarationParser<'i, Error = E>, { @@ -417,14 +443,14 @@ where input.expect_colon()?; parser.parse_value(name, input, &start) }) - .map_err(|e| (e, input.slice_from(start_position))) + .map_err(|e| (e, input.slice_from(start_position), start.source_location())) } /// Parse a single rule, such as for CSSOM’s `CSSStyleSheet.insertRule`. -pub fn parse_one_rule<'i, 't, R, P, E>( - input: &mut Parser<'i, 't>, +pub fn parse_one_rule<'i, R, P, E>( + input: &mut Parser<'i, '_>, parser: &mut P, -) -> Result> +) -> Result> where P: QualifiedRuleParser<'i, QualifiedRule = R, Error = E> + AtRuleParser<'i, AtRule = R, Error = E>, @@ -452,12 +478,12 @@ where }) } -fn parse_at_rule<'i, 't, P, E>( +fn parse_at_rule<'i, P, E>( start: &ParserState, name: CowRcStr<'i>, - input: &mut Parser<'i, 't>, + input: &mut Parser<'i, '_>, parser: &mut P, -) -> Result<

>::AtRule, (ParseError<'i, E>, &'i str)> +) -> Result<

>::AtRule, (ParseError, &'i str, SourceLocation)> where P: AtRuleParser<'i, Error = E>, { @@ -468,13 +494,19 @@ where let result = match input.next() { Ok(&Token::Semicolon) | Err(_) => parser .rule_without_block(prelude, start) - .map_err(|()| input.new_unexpected_token_error(Token::Semicolon)), + .map_err(|()| ParseError::unexpected_token()), Ok(&Token::CurlyBracketBlock) => { parse_nested_block(input, |input| parser.parse_block(prelude, start, input)) } Ok(_) => unreachable!(), }; - result.map_err(|e| (e, input.slice_from(start.position()))) + result.map_err(|e| { + ( + e, + input.slice_from(start.position()), + start.source_location(), + ) + }) } Err(error) => { let end_position = input.position(); @@ -482,7 +514,11 @@ where Ok(&Token::CurlyBracketBlock) | Ok(&Token::Semicolon) | Err(_) => {} _ => unreachable!(), }; - Err((error, input.slice(start.position()..end_position))) + Err(( + error, + input.slice(start.position()..end_position), + start.source_location(), + )) } } } @@ -498,12 +534,12 @@ fn looks_like_a_custom_property(input: &mut Parser) -> bool { } // https://drafts.csswg.org/css-syntax/#consume-a-qualified-rule -fn parse_qualified_rule<'i, 't, P, E>( +fn parse_qualified_rule<'i, P, E>( start: &ParserState, - input: &mut Parser<'i, 't>, + input: &mut Parser<'i, '_>, parser: &mut P, nested: bool, -) -> Result<

>::QualifiedRule, ParseError<'i, E>> +) -> Result<

>::QualifiedRule, ParseError> where P: QualifiedRuleParser<'i, Error = E>, { @@ -520,9 +556,9 @@ where Delimiter::CurlyBracketBlock }; let _: Result<(), ParseError<()>> = input.parse_until_after(delimiters, |_| Ok(())); - return Err(state - .source_location() - .new_error(BasicParseErrorKind::QualifiedRuleInvalid)); + return Err(ParseError::from_basic_kind( + BasicParseErrorKind::QualifiedRuleInvalid, + )); } let delimiters = if nested { Delimiter::Semicolon | Delimiter::CurlyBracketBlock diff --git a/src/size_of_tests.rs b/src/size_of_tests.rs index 70ffc5cf..1a53cc0f 100644 --- a/src/size_of_tests.rs +++ b/src/size_of_tests.rs @@ -48,5 +48,5 @@ size_of_test!(parser, crate::parser::Parser, 16); size_of_test!(source_position, crate::SourcePosition, 8); size_of_test!(parser_state, crate::ParserState, 24); -size_of_test!(basic_parse_error, crate::BasicParseError, 40, 48); -size_of_test!(parse_error_lower_bound, crate::ParseError<()>, 40, 48); +size_of_test!(basic_parse_error, crate::BasicParseError, 1); +size_of_test!(parse_error_lower_bound, crate::ParseError<()>, 1); diff --git a/src/tests.rs b/src/tests.rs index 21e8d856..fd5127d9 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -52,10 +52,11 @@ fn normalize(json: &mut Value) { normalize(item) } } - Value::String(ref mut s) if *s == "extra-input" || *s == "empty" => { - *s = "invalid".to_string() + Value::String(ref mut s) => { + if *s == "extra-input" || *s == "empty" { + *s = "invalid".to_string() + } } - _ => {} } } @@ -330,7 +331,7 @@ fn unquoted_url_escaping() { #[test] fn test_expect_url() { - fn parse<'a>(s: &mut ParserInput<'a>) -> Result, BasicParseError<'a>> { + fn parse<'a>(s: &mut ParserInput<'a>) -> Result, BasicParseError> { Parser::new(s).expect_url() } let mut input = ParserInput::new("url()"); @@ -371,11 +372,8 @@ fn parse_comma_separated_ignoring_errors() { let mut input = ParserInput::new(input); let mut input = Parser::new(&mut input); let result = input.parse_comma_separated_ignoring_errors(|input| { - let loc = input.current_source_location(); let ident = input.expect_ident()?; - crate::color::parse_named_color(ident).map_err(|()| { - loc.new_unexpected_token_error::>(Token::Ident(ident.clone())) - }) + crate::color::parse_named_color(ident).map_err(|()| ParseError::<()>::unexpected_token()) }); assert_eq!(result.len(), 3); assert_eq!(result[0], (255, 0, 0)); @@ -852,7 +850,7 @@ fn no_stack_overflow_multiple_nested_blocks() { fn nested_block_limit() { // Recursively descends into `calc(calc(calc(…1…)))`, which is the shape of expression that // would blow the stack without a nesting limit. - fn parse_calc<'i>(input: &mut Parser<'i, '_>) -> Result<(), ParseError<'i, ()>> { + fn parse_calc(input: &mut Parser) -> Result<(), ParseError<()>> { if input.try_parse(|input| input.expect_number()).is_ok() { return Ok(()); } @@ -896,12 +894,12 @@ impl<'i> DeclarationParser<'i> for JsonParser { type Declaration = Value; type Error = (); - fn parse_value<'t>( + fn parse_value( &mut self, name: CowRcStr<'i>, - input: &mut Parser<'i, 't>, + input: &mut Parser, _declaration_start: &ParserState, - ) -> Result> { + ) -> Result> { let mut value = vec![]; let mut important = false; loop { @@ -934,11 +932,11 @@ impl<'i> AtRuleParser<'i> for JsonParser { type AtRule = Value; type Error = (); - fn parse_prelude<'t>( + fn parse_prelude( &mut self, name: CowRcStr<'i>, - input: &mut Parser<'i, 't>, - ) -> Result, ParseError<'i, ()>> { + input: &mut Parser, + ) -> Result, ParseError<()>> { let prelude = vec![ "at-rule".to_json(), name.to_json(), @@ -946,7 +944,7 @@ impl<'i> AtRuleParser<'i> for JsonParser { ]; match_ignore_ascii_case! { &*name, "charset" => { - Err(input.new_error(BasicParseErrorKind::AtRuleInvalid(name.clone()))) + Err(ParseError::from_basic_kind(BasicParseErrorKind::AtRuleInvalid)) }, _ => Ok(prelude), } @@ -961,12 +959,12 @@ impl<'i> AtRuleParser<'i> for JsonParser { Ok(Value::Array(prelude)) } - fn parse_block<'t>( + fn parse_block( &mut self, mut prelude: Vec, _: &ParserState, - input: &mut Parser<'i, 't>, - ) -> Result> { + input: &mut Parser, + ) -> Result> { prelude.push(Value::Array(component_values_to_json(input))); Ok(Value::Array(prelude)) } @@ -977,19 +975,16 @@ impl<'i> QualifiedRuleParser<'i> for JsonParser { type QualifiedRule = Value; type Error = (); - fn parse_prelude<'t>( - &mut self, - input: &mut Parser<'i, 't>, - ) -> Result, ParseError<'i, ()>> { + fn parse_prelude(&mut self, input: &mut Parser) -> Result, ParseError<()>> { Ok(component_values_to_json(input)) } - fn parse_block<'t>( + fn parse_block( &mut self, prelude: Vec, _: &ParserState, - input: &mut Parser<'i, 't>, - ) -> Result> { + input: &mut Parser, + ) -> Result> { Ok(JArray![ "qualified rule", prelude, @@ -1230,7 +1225,6 @@ fn cdc_regression_test() { parser.next(), Err(BasicParseError { kind: BasicParseErrorKind::EndOfInput, - location: SourceLocation { line: 0, column: 5 } }) ); } @@ -1243,12 +1237,11 @@ fn parse_entirely_reports_first_error() { } let mut input = ParserInput::new("ident"); let mut parser = Parser::new(&mut input); - let result: Result<(), _> = parser.parse_entirely(|p| Err(p.new_custom_error(E::Foo))); + let result: Result<(), _> = parser.parse_entirely(|_| Err(ParseError::custom(E::Foo))); assert_eq!( result, Err(ParseError { kind: ParseErrorKind::Custom(E::Foo), - location: SourceLocation { line: 0, column: 1 }, }) ); } diff --git a/src/unicode_range.rs b/src/unicode_range.rs index a4130ef0..5d69507e 100644 --- a/src/unicode_range.rs +++ b/src/unicode_range.rs @@ -24,7 +24,7 @@ pub struct UnicodeRange { impl UnicodeRange { /// https://drafts.csswg.org/css-syntax/#urange-syntax - pub fn parse<'i>(input: &mut Parser<'i, '_>) -> Result> { + pub fn parse(input: &mut Parser) -> Result { // = // u '+' '?'* | // u '?'* | @@ -44,29 +44,23 @@ impl UnicodeRange { let range = match parse_concatenated(concatenated_tokens.as_bytes()) { Ok(range) => range, - Err(()) => { - return Err(input - .new_basic_unexpected_token_error(Token::Ident(concatenated_tokens.into()))) - } + Err(()) => return Err(BasicParseError::unexpected_token()), }; if range.end > char::MAX as u32 || range.start > range.end { - Err(input.new_basic_unexpected_token_error(Token::Ident(concatenated_tokens.into()))) + Err(BasicParseError::unexpected_token()) } else { Ok(range) } } } -fn parse_tokens<'i>(input: &mut Parser<'i, '_>) -> Result<(), BasicParseError<'i>> { - match input.next_including_whitespace()?.clone() { +fn parse_tokens(input: &mut Parser) -> Result<(), BasicParseError> { + match *input.next_including_whitespace()? { Token::Delim('+') => { match *input.next_including_whitespace()? { Token::Ident(_) => {} Token::Delim('?') => {} - ref t => { - let t = t.clone(); - return Err(input.new_basic_unexpected_token_error(t)); - } + _ => return Err(BasicParseError::unexpected_token()), } parse_question_marks(input) } @@ -80,7 +74,7 @@ fn parse_tokens<'i>(input: &mut Parser<'i, '_>) -> Result<(), BasicParseError<'i _ => input.reset(&after_number), } } - t => return Err(input.new_basic_unexpected_token_error(t)), + _ => return Err(BasicParseError::unexpected_token()), } Ok(()) }