diff --git a/selectors/parser.rs b/selectors/parser.rs index 7703c40a..5eaecb58 100644 --- a/selectors/parser.rs +++ b/selectors/parser.rs @@ -206,7 +206,7 @@ pub enum SelectorParseErrorKind<'i> { InvalidQualNameInAttr(Token<'i>), ExplicitNamespaceUnexpectedToken(Token<'i>), ClassNeedsIdent(Token<'i>), - UnexpectedSelectorAfterPseudoElements(Token<'i>), + UnexpectedSelectorAfterPseudoElement(Token<'i>), } macro_rules! with_all_bounds { @@ -2142,6 +2142,14 @@ where } if state.intersects(SelectorParsingState::AFTER_PSEUDO) { + // Input should be exhausted here. + let source_location = input.current_source_location(); + if let Ok(next) = input.next() { + let next = next.clone(); + return Err( + source_location.new_custom_error(SelectorParseErrorKind::UnexpectedSelectorAfterPseudoElement(next)), + ); + } break; } @@ -2957,6 +2965,7 @@ where Impl: SelectorImpl<'i>, { let start = input.state(); + let token_location = input.current_source_location(); let token = match input.next_including_whitespace().map(|t| t.clone()) { Ok(t) => t, Err(..) => { @@ -2968,14 +2977,18 @@ where Ok(Some(match token { Token::IDHash(id) => { if state.intersects(SelectorParsingState::AFTER_PSEUDO) { - return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState)); + return Err(token_location.new_custom_error( + SelectorParseErrorKind::UnexpectedSelectorAfterPseudoElement(Token::IDHash(id)), + )); } let id = Component::ID(id.into()); SimpleSelectorParseResult::SimpleSelector(id) } Token::Delim('.') => { if state.intersects(SelectorParsingState::AFTER_PSEUDO) { - return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState)); + return Err(token_location.new_custom_error( + SelectorParseErrorKind::UnexpectedSelectorAfterPseudoElement(Token::Delim('.')), + )); } let location = input.current_source_location(); let class = match *input.next_including_whitespace()? { @@ -2990,7 +3003,9 @@ where } Token::SquareBracketBlock => { if state.intersects(SelectorParsingState::AFTER_PSEUDO) { - return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState)); + return Err(token_location.new_custom_error( + SelectorParseErrorKind::UnexpectedSelectorAfterPseudoElement(Token::SquareBracketBlock), + )); } let attr = input.parse_nested_block(|input| parse_attribute_selector(parser, input))?; SimpleSelectorParseResult::SimpleSelector(attr) @@ -3060,25 +3075,6 @@ where SimpleSelectorParseResult::SimpleSelector(Component::Nesting) } _ => { - if state.intersects(SelectorParsingState::AFTER_PSEUDO) { - let source_location = input.current_source_location(); - // Handle the case where the token is a whitespace token - let token = match token { - Token::WhiteSpace(..) => input.next(), - _ => Ok(&token), - }; - - // Use better error message - if let Ok(token @ Token::Ident(..) | token @ Token::IDHash(..) | token @ Token::Delim('.')) = token { - return Err(source_location.new_custom_error( - SelectorParseErrorKind::UnexpectedSelectorAfterPseudoElements(token.clone()), - )); - } - - input.reset(&start); - return Ok(None); - } - input.reset(&start); return Ok(None); } diff --git a/src/error.rs b/src/error.rs index 64341b76..872de438 100644 --- a/src/error.rs +++ b/src/error.rs @@ -237,7 +237,7 @@ pub enum SelectorError<'i> { AmbiguousCssModuleClass(CowArcStr<'i>), /// An unexpected token was encountered after a pseudo element. - UnexpectedSelectorAfterPseudoElements( + UnexpectedSelectorAfterPseudoElement( #[cfg_attr(any(feature = "serde", feature = "nodejs"), serde(skip))] Token<'i>, ), } @@ -266,7 +266,7 @@ impl<'i> fmt::Display for SelectorError<'i> { UnexpectedTokenInAttributeSelector(token) => write!(f, "Unexpected token in attribute selector: {:?}", token), UnsupportedPseudoClassOrElement(name) => write!(f, "Unsupported pseudo class or element: {}", name), AmbiguousCssModuleClass(_) => write!(f, "Ambiguous CSS module class not supported"), - UnexpectedSelectorAfterPseudoElements(token) => { + UnexpectedSelectorAfterPseudoElement(token) => { write!( f, "Pseudo-elements like '::before' or '::after' can't be followed by selectors like '{token:?}'" @@ -313,8 +313,8 @@ impl<'i> From> for SelectorError<'i> { } SelectorParseErrorKind::ClassNeedsIdent(t) => SelectorError::ClassNeedsIdent(t.into()), SelectorParseErrorKind::AmbiguousCssModuleClass(name) => SelectorError::AmbiguousCssModuleClass(name.into()), - SelectorParseErrorKind::UnexpectedSelectorAfterPseudoElements(t) => { - SelectorError::UnexpectedSelectorAfterPseudoElements(t.into()) + SelectorParseErrorKind::UnexpectedSelectorAfterPseudoElement(t) => { + SelectorError::UnexpectedSelectorAfterPseudoElement(t.into()) } } } diff --git a/src/lib.rs b/src/lib.rs index c98eb4a0..fbe37702 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6903,22 +6903,42 @@ mod tests { error_test( "input.defaultCheckbox::before h1 {width: 20px}", - ParserError::SelectorError(SelectorError::UnexpectedSelectorAfterPseudoElements(Token::Ident( + ParserError::SelectorError(SelectorError::UnexpectedSelectorAfterPseudoElement(Token::Ident( "h1".into(), ))), ); - error_test( "input.defaultCheckbox::before .my-class {width: 20px}", - ParserError::SelectorError(SelectorError::UnexpectedSelectorAfterPseudoElements(Token::Delim('.'))), + ParserError::SelectorError(SelectorError::UnexpectedSelectorAfterPseudoElement(Token::Delim('.'))), + ); + error_test( + "input.defaultCheckbox::before.my-class {width: 20px}", + ParserError::SelectorError(SelectorError::UnexpectedSelectorAfterPseudoElement(Token::Delim('.'))), ); - error_test( "input.defaultCheckbox::before #id {width: 20px}", - ParserError::SelectorError(SelectorError::UnexpectedSelectorAfterPseudoElements(Token::IDHash( + ParserError::SelectorError(SelectorError::UnexpectedSelectorAfterPseudoElement(Token::IDHash( + "id".into(), + ))), + ); + error_test( + "input.defaultCheckbox::before#id {width: 20px}", + ParserError::SelectorError(SelectorError::UnexpectedSelectorAfterPseudoElement(Token::IDHash( "id".into(), ))), ); + error_test( + "input.defaultCheckbox::before [attr] {width: 20px}", + ParserError::SelectorError(SelectorError::UnexpectedSelectorAfterPseudoElement( + Token::SquareBracketBlock, + )), + ); + error_test( + "input.defaultCheckbox::before[attr] {width: 20px}", + ParserError::SelectorError(SelectorError::UnexpectedSelectorAfterPseudoElement( + Token::SquareBracketBlock, + )), + ); } #[test]