fix(css/parser): Fix a bug with scope in block (#6402)

This commit is contained in:
Alexander Akait 2022-11-15 04:49:05 +03:00 committed by GitHub
parent fafc6257c8
commit 3d7545d89b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 299 additions and 224 deletions

View File

@ -565,7 +565,7 @@ where
| js_word!("-o-keyframes") | js_word!("-o-keyframes")
| js_word!("-ms-keyframes") => { | js_word!("-ms-keyframes") => {
let ctx = Ctx { let ctx = Ctx {
block_contents_grammar: BlockContentsGrammar::DeclarationList, block_contents_grammar: BlockContentsGrammar::RuleList,
is_top_level: false, is_top_level: false,
in_keyframes_at_rule: true, in_keyframes_at_rule: true,
..self.ctx ..self.ctx

View File

@ -134,24 +134,18 @@ where
unreachable!() unreachable!()
} }
}; };
let (normalized_at_rule_name, name) = if at_keyword_name.0.starts_with("--") { let name = if at_keyword_name.0.starts_with("--") {
( AtRuleName::DashedIdent(DashedIdent {
at_keyword_name.0.to_ascii_lowercase(), span: Span::new(span.lo + BytePos(1), span.hi, Default::default()),
AtRuleName::DashedIdent(DashedIdent { value: at_keyword_name.0,
span: Span::new(span.lo + BytePos(1), span.hi, Default::default()), raw: Some(at_keyword_name.1),
value: at_keyword_name.0, })
raw: Some(at_keyword_name.1),
}),
)
} else { } else {
( AtRuleName::Ident(Ident {
at_keyword_name.0.to_ascii_lowercase(), span: Span::new(span.lo + BytePos(1), span.hi, Default::default()),
AtRuleName::Ident(Ident { value: at_keyword_name.0,
span: Span::new(span.lo + BytePos(1), span.hi, Default::default()), raw: Some(at_keyword_name.1),
value: at_keyword_name.0, })
raw: Some(at_keyword_name.1),
}),
)
}; };
let mut prelude = vec![]; let mut prelude = vec![];
let mut at_rule = AtRule { let mut at_rule = AtRule {
@ -181,103 +175,31 @@ where
tok!(";") => { tok!(";") => {
self.input.bump(); self.input.bump();
let list_of_component_values = self.create_locv(prelude); at_rule.prelude = Some(Box::new(AtRulePrelude::ListOfComponentValues(
self.create_locv(prelude),
at_rule.prelude = match self )));
.parse_according_to_grammar(&list_of_component_values, |parser| {
parser.parse_at_rule_prelude(&normalized_at_rule_name)
}) {
Ok(at_rule_prelude) => match at_rule_prelude {
None if normalized_at_rule_name == js_word!("layer") => {
self.errors.push(Error::new(
span,
ErrorKind::Expected("at least one name"),
));
Some(Box::new(AtRulePrelude::ListOfComponentValues(
list_of_component_values,
)))
}
_ => at_rule_prelude.map(Box::new),
},
Err(err) => {
if *err.kind() != ErrorKind::Ignore {
self.errors.push(err);
}
if !list_of_component_values.children.is_empty() {
Some(Box::new(AtRulePrelude::ListOfComponentValues(
list_of_component_values,
)))
} else {
None
}
}
};
at_rule.span = span!(self, span.lo); at_rule.span = span!(self, span.lo);
// Canonicalization against a grammar
at_rule = self.canonicalize_at_rule_prelude(at_rule)?;
return Ok(at_rule); return Ok(at_rule);
} }
// <{-token> // <{-token>
// Consume a simple block and assign it to the at-rules block. Return the at-rule. // Consume a simple block and assign it to the at-rules block. Return the at-rule.
tok!("{") => { tok!("{") => {
let mut block = self.parse_as::<SimpleBlock>()?; let block = self.parse_as::<SimpleBlock>()?;
let list_of_component_values = self.create_locv(prelude); at_rule.prelude = Some(Box::new(AtRulePrelude::ListOfComponentValues(
self.create_locv(prelude),
at_rule.prelude = match self )));
.parse_according_to_grammar(&list_of_component_values, |parser| { at_rule.block = Some(block);
parser.parse_at_rule_prelude(&normalized_at_rule_name)
}) {
Ok(at_rule_prelude) => match at_rule_prelude {
Some(AtRulePrelude::LayerPrelude(LayerPrelude::NameList(
name_list,
))) if name_list.name_list.len() > 1 => {
self.errors.push(Error::new(
name_list.span,
ErrorKind::Expected("only one name"),
));
Some(Box::new(AtRulePrelude::ListOfComponentValues(
list_of_component_values,
)))
}
_ => at_rule_prelude.map(Box::new),
},
Err(err) => {
if *err.kind() != ErrorKind::Ignore {
self.errors.push(err);
}
if !list_of_component_values.children.is_empty() {
Some(Box::new(AtRulePrelude::ListOfComponentValues(
list_of_component_values,
)))
} else {
None
}
}
};
at_rule.block = match self.parse_according_to_grammar(
&self.create_locv(block.value.clone()),
|parser| parser.parse_at_rule_block(&normalized_at_rule_name),
) {
Ok(block_contents) => {
block.value = block_contents;
Some(block)
}
Err(err) => {
if *err.kind() != ErrorKind::Ignore {
self.errors.push(err);
}
Some(block)
}
};
at_rule.span = span!(self, span.lo); at_rule.span = span!(self, span.lo);
// Canonicalization against a grammar
at_rule = self.canonicalize_at_rule_prelude(at_rule)?;
at_rule = self.canonicalize_at_rule_block(at_rule)?;
return Ok(at_rule); return Ok(at_rule);
} }
// anything else // anything else
@ -299,50 +221,10 @@ where
{ {
fn parse(&mut self) -> PResult<QualifiedRule> { fn parse(&mut self) -> PResult<QualifiedRule> {
// To consume a qualified rule: // To consume a qualified rule:
let create_prelude =
|p: &mut Parser<I>, list: Vec<ComponentValue>| -> PResult<QualifiedRulePrelude> {
let list_of_component_values = p.create_locv(list);
if p.ctx.in_keyframes_at_rule {
Ok(QualifiedRulePrelude::ListOfComponentValues(
list_of_component_values,
))
} else if p.ctx.mixed_with_declarations {
match p.parse_according_to_grammar::<RelativeSelectorList>(
&list_of_component_values,
|parser| parser.parse(),
) {
Ok(relative_selector_list) => Ok(
QualifiedRulePrelude::RelativeSelectorList(relative_selector_list),
),
Err(err) => {
p.errors.push(err);
Ok(QualifiedRulePrelude::ListOfComponentValues(
list_of_component_values,
))
}
}
} else {
match p.parse_according_to_grammar::<SelectorList>(
&list_of_component_values,
|parser| parser.parse(),
) {
Ok(selector_list) => Ok(QualifiedRulePrelude::SelectorList(selector_list)),
Err(err) => {
p.errors.push(err);
Ok(QualifiedRulePrelude::ListOfComponentValues(
list_of_component_values,
))
}
}
}
};
let span = self.input.cur_span();
// Create a new qualified rule with its prelude initially set to an empty list, // Create a new qualified rule with its prelude initially set to an empty list,
// and its value initially set to nothing. // and its value initially set to nothing.
let span = self.input.cur_span();
let mut prelude = vec![]; let mut prelude = vec![];
// Repeatedly consume the next input token: // Repeatedly consume the next input token:
@ -375,30 +257,20 @@ where
// Consume a simple block and assign it to the qualified rules block. Return the // Consume a simple block and assign it to the qualified rules block. Return the
// qualified rule. // qualified rule.
tok!("{") => { tok!("{") => {
let mut block = self.parse_as::<SimpleBlock>()?; let block = self.parse_as::<SimpleBlock>()?;
let mut qualified_rule = QualifiedRule {
block.value = match self.ctx.block_contents_grammar { span: span!(self, span.lo),
BlockContentsGrammar::DeclarationList => self prelude: QualifiedRulePrelude::ListOfComponentValues(
.parse_according_to_grammar(&self.create_locv(block.value), |parser| { self.create_locv(prelude),
parser.parse_as::<Vec<DeclarationOrAtRule>>() ),
})? block,
.into_iter()
.map(ComponentValue::DeclarationOrAtRule)
.collect(),
_ => self
.parse_according_to_grammar(&self.create_locv(block.value), |parser| {
parser.parse_as::<Vec<StyleBlock>>()
})?
.into_iter()
.map(ComponentValue::StyleBlock)
.collect(),
}; };
return Ok(QualifiedRule { // Canonicalization against a grammar
span: span!(self, span.lo), qualified_rule = self.canonicalize_qualified_rule_prelude(qualified_rule)?;
prelude: create_prelude(self, prelude)?, qualified_rule = self.canonicalize_qualified_rule_block(qualified_rule)?;
block,
}); return Ok(qualified_rule);
} }
// Reconsume the current input token. Consume a component value. Append the returned // Reconsume the current input token. Consume a component value. Append the returned
// value to the qualified rules prelude. // value to the qualified rules prelude.
@ -458,12 +330,7 @@ where
// Reconsume the current input token. Consume an at-rule, and append the result to // Reconsume the current input token. Consume an at-rule, and append the result to
// rules. // rules.
tok!("@") => { tok!("@") => {
let at_rule = self let at_rule = self.parse()?;
.with_ctx(Ctx {
block_contents_grammar: BlockContentsGrammar::StyleBlock,
..self.ctx
})
.parse_as::<AtRule>()?;
rules.push(StyleBlock::AtRule(Box::new(at_rule))); rules.push(StyleBlock::AtRule(Box::new(at_rule)));
} }
@ -880,32 +747,8 @@ where
return Ok(declaration); return Ok(declaration);
} }
// Grammar parsing // Canonicalization against a grammar
let list_of_component_values = self.create_locv(declaration.value); declaration = self.canonicalize_declaration_value(declaration)?;
declaration.value =
match self.parse_according_to_grammar(&list_of_component_values, |parser| {
let mut values = vec![];
loop {
if is!(parser, EOF) {
break;
}
values.push(parser.parse_generic_value()?);
}
Ok(values)
}) {
Ok(values) => values,
Err(err) => {
if *err.kind() != ErrorKind::Ignore {
self.errors.push(err);
}
list_of_component_values.children
}
};
// 8. Return the declaration. // 8. Return the declaration.
Ok(declaration) Ok(declaration)
@ -1055,9 +898,8 @@ where
unreachable!() unreachable!()
} }
}; };
let function_name = &*ident.0.to_ascii_lowercase();
let name = Ident { let name = Ident {
span: swc_common::Span::new(span.lo, span.hi - BytePos(1), Default::default()), span: Span::new(span.lo, span.hi - BytePos(1), Default::default()),
value: ident.0, value: ident.0,
raw: Some(ident.1), raw: Some(ident.1),
}; };
@ -1101,26 +943,8 @@ where
function.span = span!(self, span.lo); function.span = span!(self, span.lo);
// Grammar parsing // Canonicalization against a grammar
match self.ctx.block_contents_grammar { function = self.canonicalize_function_value(function)?;
BlockContentsGrammar::DeclarationList => {}
_ => {
let locv = self.create_locv(function.value);
function.value = match self.parse_according_to_grammar(&locv, |parser| {
parser.parse_function_values(function_name)
}) {
Ok(values) => values,
Err(err) => {
if *err.kind() != ErrorKind::Ignore {
self.errors.push(err);
}
locv.children
}
};
}
}
return Ok(function); return Ok(function);
} }

View File

@ -1,5 +1,6 @@
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
use swc_atoms::js_word;
use swc_common::{Span, Spanned, SyntaxContext, DUMMY_SP}; use swc_common::{Span, Spanned, SyntaxContext, DUMMY_SP};
use swc_css_ast::*; use swc_css_ast::*;
@ -7,7 +8,7 @@ use super::{
input::{Input, InputType, ParserInput}, input::{Input, InputType, ParserInput},
Ctx, Error, PResult, Parse, Parser, Ctx, Error, PResult, Parse, Parser,
}; };
use crate::parser::BlockContentsGrammar; use crate::{error::ErrorKind, parser::BlockContentsGrammar};
impl<I> Parser<I> impl<I> Parser<I>
where where
@ -74,6 +75,255 @@ where
res res
} }
pub(super) fn canonicalize_at_rule_prelude(&mut self, mut at_rule: AtRule) -> PResult<AtRule> {
let normalized_at_rule_name = match &at_rule.name {
AtRuleName::Ident(Ident { value, .. }) => value.to_ascii_lowercase(),
AtRuleName::DashedIdent(_) => return Ok(at_rule),
};
let list_of_component_values = match at_rule.prelude {
Some(at_rule_prelude) => match *at_rule_prelude {
AtRulePrelude::ListOfComponentValues(list_of_component_values) => {
list_of_component_values
}
_ => {
unreachable!();
}
},
_ => {
unreachable!();
}
};
at_rule.prelude = match self
.parse_according_to_grammar(&list_of_component_values, |parser| {
parser.parse_at_rule_prelude(&normalized_at_rule_name)
}) {
Ok(at_rule_prelude) => match at_rule_prelude {
Some(AtRulePrelude::LayerPrelude(LayerPrelude::NameList(name_list)))
if name_list.name_list.len() > 1 && at_rule.block.is_some() =>
{
self.errors.push(Error::new(
name_list.span,
ErrorKind::Expected("only one name"),
));
Some(Box::new(AtRulePrelude::ListOfComponentValues(
list_of_component_values,
)))
}
None if *normalized_at_rule_name == js_word!("layer")
&& at_rule.block.is_none() =>
{
self.errors.push(Error::new(
at_rule.span,
ErrorKind::Expected("at least one name"),
));
Some(Box::new(AtRulePrelude::ListOfComponentValues(
list_of_component_values,
)))
}
_ => at_rule_prelude.map(Box::new),
},
Err(err) => {
if *err.kind() != ErrorKind::Ignore {
self.errors.push(err);
}
if !list_of_component_values.children.is_empty() {
Some(Box::new(AtRulePrelude::ListOfComponentValues(
list_of_component_values,
)))
} else {
None
}
}
};
Ok(at_rule)
}
pub(super) fn canonicalize_at_rule_block(&mut self, mut at_rule: AtRule) -> PResult<AtRule> {
let normalized_at_rule_name = match &at_rule.name {
AtRuleName::Ident(Ident { value, .. }) => value.to_ascii_lowercase(),
AtRuleName::DashedIdent(_) => return Ok(at_rule),
};
let mut block = match at_rule.block {
Some(simple_block) => simple_block,
_ => {
unreachable!();
}
};
let list_of_component_values = self.create_locv(block.value);
block.value = match self.parse_according_to_grammar(&list_of_component_values, |parser| {
parser.parse_at_rule_block(&normalized_at_rule_name)
}) {
Ok(block_contents) => block_contents,
Err(err) => {
if *err.kind() != ErrorKind::Ignore {
self.errors.push(err);
}
list_of_component_values.children
}
};
at_rule.block = Some(block);
Ok(at_rule)
}
pub(super) fn canonicalize_qualified_rule_prelude(
&mut self,
mut qualified_rule: QualifiedRule,
) -> PResult<QualifiedRule> {
let list_of_component_values = match qualified_rule.prelude {
QualifiedRulePrelude::ListOfComponentValues(list_of_component_values) => {
list_of_component_values
}
_ => {
unreachable!();
}
};
qualified_rule.prelude = if self.ctx.in_keyframes_at_rule {
QualifiedRulePrelude::ListOfComponentValues(list_of_component_values)
} else if self.ctx.mixed_with_declarations {
match self.parse_according_to_grammar::<RelativeSelectorList>(
&list_of_component_values,
|parser| parser.parse(),
) {
Ok(relative_selector_list) => {
QualifiedRulePrelude::RelativeSelectorList(relative_selector_list)
}
Err(err) => {
self.errors.push(err);
QualifiedRulePrelude::ListOfComponentValues(list_of_component_values)
}
}
} else {
match self
.parse_according_to_grammar::<SelectorList>(&list_of_component_values, |parser| {
parser.parse()
}) {
Ok(selector_list) => QualifiedRulePrelude::SelectorList(selector_list),
Err(err) => {
self.errors.push(err);
QualifiedRulePrelude::ListOfComponentValues(list_of_component_values)
}
}
};
Ok(qualified_rule)
}
pub(super) fn canonicalize_qualified_rule_block(
&mut self,
mut qualified_rule: QualifiedRule,
) -> PResult<QualifiedRule> {
qualified_rule.block.value = match self.ctx.block_contents_grammar {
BlockContentsGrammar::RuleList if self.ctx.in_keyframes_at_rule => self
.parse_according_to_grammar(
&self.create_locv(qualified_rule.block.value),
|parser| {
parser
.with_ctx(Ctx {
block_contents_grammar: BlockContentsGrammar::DeclarationList,
..parser.ctx
})
.parse_as::<Vec<DeclarationOrAtRule>>()
},
)?
.into_iter()
.map(ComponentValue::DeclarationOrAtRule)
.collect(),
_ => self
.parse_according_to_grammar(
&self.create_locv(qualified_rule.block.value),
|parser| {
parser
.with_ctx(Ctx {
block_contents_grammar: BlockContentsGrammar::StyleBlock,
..parser.ctx
})
.parse_as::<Vec<StyleBlock>>()
},
)?
.into_iter()
.map(ComponentValue::StyleBlock)
.collect(),
};
Ok(qualified_rule)
}
pub(super) fn canonicalize_function_value(
&mut self,
mut function: Function,
) -> PResult<Function> {
match self.ctx.block_contents_grammar {
BlockContentsGrammar::DeclarationList => {}
_ => {
let function_name = function.name.value.to_ascii_lowercase();
let locv = self.create_locv(function.value);
function.value = match self.parse_according_to_grammar(&locv, |parser| {
parser.parse_function_values(&function_name)
}) {
Ok(values) => values,
Err(err) => {
if *err.kind() != ErrorKind::Ignore {
self.errors.push(err);
}
locv.children
}
};
}
}
Ok(function)
}
pub(super) fn canonicalize_declaration_value(
&mut self,
mut declaration: Declaration,
) -> PResult<Declaration> {
let locv = self.create_locv(declaration.value);
declaration.value = match self.parse_according_to_grammar(&locv, |parser| {
let mut values = vec![];
loop {
if is!(parser, EOF) {
break;
}
values.push(parser.parse_generic_value()?);
}
Ok(values)
}) {
Ok(values) => values,
Err(err) => {
if *err.kind() != ErrorKind::Ignore {
self.errors.push(err);
}
locv.children
}
};
Ok(declaration)
}
pub(super) fn try_to_parse_legacy_nesting(&mut self) -> Option<QualifiedRule> { pub(super) fn try_to_parse_legacy_nesting(&mut self) -> Option<QualifiedRule> {
let state = self.input.state(); let state = self.input.state();
let qualified_rule = self let qualified_rule = self

View File

@ -266,6 +266,7 @@ where
Ok(tokens) Ok(tokens)
} }
// TODO use `JsWord`
pub fn parse_function_values(&mut self, function_name: &str) -> PResult<Vec<ComponentValue>> { pub fn parse_function_values(&mut self, function_name: &str) -> PResult<Vec<ComponentValue>> {
let mut values = vec![]; let mut values = vec![];

View File

@ -2,5 +2,5 @@
x Expected at least one name x Expected at least one name
,-[$DIR/tests/recovery/at-rule/layer/empty/input.css:1:1] ,-[$DIR/tests/recovery/at-rule/layer/empty/input.css:1:1]
1 | @layer ; 1 | @layer ;
: ^^^^^^ : ^^^^^^^^
`---- `----