Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

parser: Implement recent syntax spec change to avoid parsing custom properties that look like selectors as rules. #387

Merged
merged 1 commit into from
Apr 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 16 additions & 10 deletions src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,17 +117,29 @@ 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: BasicParseErrorKind::UnexpectedToken(token),
kind,
location: self,
}
}

/// Create a new ParseError at this location for an unexpected token
#[inline]
pub fn new_unexpected_token_error<E>(self, token: Token<'_>) -> ParseError<'_, E> {
self.new_error(BasicParseErrorKind::UnexpectedToken(token))
}

/// Create a new basic ParseError at the current location
#[inline]
pub fn new_error<E>(self, kind: BasicParseErrorKind<'_>) -> ParseError<'_, E> {
ParseError {
kind: ParseErrorKind::Basic(BasicParseErrorKind::UnexpectedToken(token)),
kind: ParseErrorKind::Basic(kind),
location: self,
}
}
Expand Down Expand Up @@ -450,19 +462,13 @@ impl<'i: 't, 't> Parser<'i, 't> {
/// Create a new BasicParseError at the current location
#[inline]
pub fn new_basic_error(&self, kind: BasicParseErrorKind<'i>) -> BasicParseError<'i> {
BasicParseError {
kind,
location: self.current_source_location(),
}
self.current_source_location().new_basic_error(kind)
}

/// Create a new basic ParseError at the current location
#[inline]
pub fn new_error<E>(&self, kind: BasicParseErrorKind<'i>) -> ParseError<'i, E> {
ParseError {
kind: ParseErrorKind::Basic(kind),
location: self.current_source_location(),
}
self.current_source_location().new_error(kind)
}

/// Create a new custom BasicParseError at the current location
Expand Down
58 changes: 44 additions & 14 deletions src/rules_and_declarations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@

// https://drafts.csswg.org/css-syntax/#parsing

use super::{
BasicParseError, BasicParseErrorKind, Delimiter, Delimiters, ParseError, Parser, Token,
};
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};

Expand Down Expand Up @@ -293,7 +291,7 @@ where
&start,
self.input,
&mut *self.parser,
Delimiter::Semicolon | Delimiter::CurlyBracketBlock,
/* nested = */ true,
) {
return Some(Ok(qual));
}
Expand All @@ -304,12 +302,8 @@ where
token => {
let result = if self.parser.parse_qualified() {
self.input.reset(&start);
let delimiters = if self.parser.parse_declarations() {
Delimiter::Semicolon | Delimiter::CurlyBracketBlock
} else {
Delimiter::CurlyBracketBlock
};
parse_qualified_rule(&start, self.input, &mut *self.parser, delimiters)
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, |_| {
Expand Down Expand Up @@ -398,7 +392,7 @@ where
&start,
self.input,
&mut *self.parser,
Delimiter::CurlyBracketBlock,
/* nested = */ false,
);
return Some(result.map_err(|e| (e, self.input.slice_from(start.position()))));
}
Expand Down Expand Up @@ -451,7 +445,7 @@ where
if let Some(name) = at_keyword {
parse_at_rule(&start, name, input, parser).map_err(|e| e.0)
} else {
parse_qualified_rule(&start, input, parser, Delimiter::CurlyBracketBlock)
parse_qualified_rule(&start, input, parser, /* nested = */ false)
}
})
}
Expand Down Expand Up @@ -491,16 +485,52 @@ where
}
}

// If the first two non-<whitespace-token> values of rule’s prelude are an <ident-token> whose
// value starts with "--" followed by a <colon-token>, then...
fn looks_like_a_custom_property(input: &mut Parser) -> bool {
let ident = match input.expect_ident() {
Ok(i) => i,
Err(..) => return false,
};
ident.starts_with("--") && input.expect_colon().is_ok()
}

// https://drafts.csswg.org/css-syntax/#consume-a-qualified-rule
fn parse_qualified_rule<'i, 't, P, E>(
start: &ParserState,
input: &mut Parser<'i, 't>,
parser: &mut P,
delimiters: Delimiters,
nested: bool,
) -> Result<<P as QualifiedRuleParser<'i>>::QualifiedRule, ParseError<'i, E>>
where
P: QualifiedRuleParser<'i, Error = E>,
{
let prelude = input.parse_until_before(delimiters, |input| parser.parse_prelude(input));
input.skip_whitespace();
let prelude = {
let state = input.state();
if looks_like_a_custom_property(input) {
// If nested is true, consume the remnants of a bad declaration from input, with
// nested set to true, and return nothing.
// If nested is false, consume a block from input, and return nothing.
let delimiters = if nested {
Delimiter::Semicolon
} else {
Delimiter::CurlyBracketBlock
};
let _: Result<(), ParseError<()>> = input.parse_until_after(delimiters, |_| Ok(()));
return Err(state
.source_location()
.new_error(BasicParseErrorKind::QualifiedRuleInvalid));
}
let delimiters = if nested {
Delimiter::Semicolon | Delimiter::CurlyBracketBlock
} else {
Delimiter::CurlyBracketBlock
};
input.reset(&state);
input.parse_until_before(delimiters, |input| parser.parse_prelude(input))
};

input.expect_curly_bracket_block()?;
// Do this here so that we consume the `{` even if the prelude is `Err`.
let prelude = prelude?;
Expand Down
Loading