fix(css/parser): Add more error recovery (#6240)

This commit is contained in:
Donny/강동윤 2022-10-24 12:11:27 +09:00 committed by GitHub
parent 1a3ceaa968
commit 6341554628
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 670 additions and 15 deletions

View File

@ -1,4 +1,4 @@
use std::{cell::RefCell, char::REPLACEMENT_CHARACTER, mem::take, rc::Rc};
use std::{cell::RefCell, char::REPLACEMENT_CHARACTER, rc::Rc};
use swc_atoms::{js_word, JsWord};
use swc_common::{input::Input, BytePos, Span};
@ -11,7 +11,7 @@ use crate::{
pub(crate) type LexResult<T> = Result<T, ErrorKind>;
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct Lexer<I>
where
I: Input,
@ -27,7 +27,7 @@ where
raw_buf: Rc<RefCell<String>>,
sub_buf: Rc<RefCell<String>>,
sub_raw_buf: Rc<RefCell<String>>,
errors: Vec<Error>,
errors: Rc<RefCell<Vec<Error>>>,
}
impl<I> Lexer<I>
@ -48,7 +48,7 @@ where
raw_buf: Rc::new(RefCell::new(String::with_capacity(256))),
sub_buf: Rc::new(RefCell::new(String::with_capacity(32))),
sub_raw_buf: Rc::new(RefCell::new(String::with_capacity(32))),
errors: vec![],
errors: Default::default(),
}
}
@ -155,7 +155,7 @@ where
}
fn take_errors(&mut self) -> Vec<Error> {
take(&mut self.errors)
self.errors.take()
}
fn skip_ws(&mut self) -> Option<BytePos> {
@ -220,7 +220,7 @@ where
#[cold]
fn emit_error(&mut self, kind: ErrorKind) {
self.errors.push(Error::new(
self.errors.borrow_mut().push(Error::new(
Span::new(self.cur_pos, self.input.cur_pos(), Default::default()),
kind,
));
@ -510,6 +510,7 @@ where
let span = Span::new(self.start_pos, end, Default::default());
self.errors
.borrow_mut()
.push(Error::new(span, ErrorKind::UnterminatedBlockComment));
return;

View File

@ -7,7 +7,7 @@ use swc_css_ast::{ComponentValue, ListOfComponentValues, Token, TokenAndSpan};
use super::PResult;
use crate::error::{Error, ErrorKind};
pub trait ParserInput: Iterator<Item = TokenAndSpan> {
pub trait ParserInput: Clone + Iterator<Item = TokenAndSpan> {
type State: Debug;
fn start_pos(&mut self) -> BytePos;
@ -22,7 +22,7 @@ pub trait ParserInput: Iterator<Item = TokenAndSpan> {
fn skip_ws(&mut self) -> Option<BytePos>;
}
#[derive(Debug)]
#[derive(Debug, Clone)]
pub(super) struct Buffer<I>
where
I: ParserInput,
@ -184,7 +184,7 @@ pub struct TokensState {
idx: usize,
}
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct TokensInput<'a> {
tokens: &'a Tokens,
idx: usize,
@ -272,7 +272,7 @@ pub struct ListOfComponentValuesState {
balance_stack: Vec<BalanceToken>,
}
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct ListOfComponentValuesInput<'a> {
list: &'a ListOfComponentValues,
idx: Vec<usize>,

View File

@ -78,7 +78,7 @@ struct Ctx {
is_trying_legacy_nesting: bool,
}
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct Parser<I>
where
I: ParserInput,

View File

@ -31,6 +31,21 @@ where
self.parse()
}
pub(super) fn try_parse<Ret>(
&mut self,
op: impl FnOnce(&mut Parser<I>) -> PResult<Ret>,
) -> Option<Ret> {
let mut parser = self.clone();
match op(&mut parser) {
Ok(v) => {
*self = parser;
Some(v)
}
Err(..) => None,
}
}
pub(super) fn create_locv(&self, children: Vec<ComponentValue>) -> ListOfComponentValues {
let span = match (children.first(), children.last()) {
(Some(first), Some(last)) => {

View File

@ -1273,12 +1273,29 @@ where
break;
}
let ctx = Ctx {
block_contents_grammar: BlockContentsGrammar::DeclarationValue,
..self.ctx
let value = match self.try_parse(|p| {
let ctx = Ctx {
block_contents_grammar: BlockContentsGrammar::DeclarationValue,
..p.ctx
};
p.with_ctx(ctx).parse_as::<ComponentValue>()
}) {
Some(v) => v,
None => {
if is_one_of!(self, ";", ":") {
let tok = self.input.bump().unwrap();
ComponentValue::PreservedToken(tok)
} else {
return Err(Error::new(
self.input.cur_span(),
ErrorKind::Expected("Declaration value"),
));
}
}
};
values.push(self.with_ctx(ctx).parse_as::<ComponentValue>()?);
values.push(value);
},
};

View File

@ -0,0 +1,5 @@
@media screen and(-webkit-min-device-pixel-ratio:0) {
.gradientWrapper {
-chrome-: only(; z-index: 10;);
}
}

View File

@ -0,0 +1,301 @@
{
"type": "Stylesheet",
"span": {
"start": 1,
"end": 123,
"ctxt": 0
},
"rules": [
{
"type": "AtRule",
"span": {
"start": 1,
"end": 123,
"ctxt": 0
},
"name": {
"type": "Ident",
"span": {
"start": 2,
"end": 7,
"ctxt": 0
},
"value": "media",
"raw": "media"
},
"prelude": {
"type": "MediaQueryList",
"span": {
"start": 15,
"end": 52,
"ctxt": 0
},
"queries": [
{
"type": "MediaQuery",
"span": {
"start": 15,
"end": 52,
"ctxt": 0
},
"modifier": null,
"mediaType": null,
"keyword": null,
"condition": {
"type": "MediaCondition",
"span": {
"start": 15,
"end": 52,
"ctxt": 0
},
"conditions": [
{
"type": "Function",
"span": {
"start": 15,
"end": 52,
"ctxt": 0
},
"name": {
"type": "Ident",
"span": {
"start": 15,
"end": 18,
"ctxt": 0
},
"value": "and",
"raw": "and"
},
"value": [
{
"type": "PreservedToken",
"span": {
"start": 19,
"end": 49,
"ctxt": 0
},
"token": {
"Ident": {
"value": "-webkit-min-device-pixel-ratio",
"raw": "-webkit-min-device-pixel-ratio"
}
}
},
{
"type": "PreservedToken",
"span": {
"start": 49,
"end": 50,
"ctxt": 0
},
"token": "Colon"
},
{
"type": "PreservedToken",
"span": {
"start": 50,
"end": 51,
"ctxt": 0
},
"token": {
"Number": {
"value": 0.0,
"raw": "0",
"type": "integer"
}
}
}
]
}
]
}
}
]
},
"block": {
"type": "SimpleBlock",
"span": {
"start": 53,
"end": 123,
"ctxt": 0
},
"name": {
"type": "PreservedToken",
"span": {
"start": 53,
"end": 54,
"ctxt": 0
},
"token": "LBrace"
},
"value": [
{
"type": "QualifiedRule",
"span": {
"start": 59,
"end": 121,
"ctxt": 0
},
"prelude": {
"type": "SelectorList",
"span": {
"start": 59,
"end": 75,
"ctxt": 0
},
"children": [
{
"type": "ComplexSelector",
"span": {
"start": 59,
"end": 75,
"ctxt": 0
},
"children": [
{
"type": "CompoundSelector",
"span": {
"start": 59,
"end": 75,
"ctxt": 0
},
"nestingSelector": null,
"typeSelector": null,
"subclassSelectors": [
{
"type": "ClassSelector",
"span": {
"start": 59,
"end": 75,
"ctxt": 0
},
"text": {
"type": "Ident",
"span": {
"start": 60,
"end": 75,
"ctxt": 0
},
"value": "gradientWrapper",
"raw": "gradientWrapper"
}
}
]
}
]
}
]
},
"block": {
"type": "SimpleBlock",
"span": {
"start": 76,
"end": 121,
"ctxt": 0
},
"name": {
"type": "PreservedToken",
"span": {
"start": 76,
"end": 77,
"ctxt": 0
},
"token": "LBrace"
},
"value": [
{
"type": "Declaration",
"span": {
"start": 84,
"end": 114,
"ctxt": 0
},
"name": {
"type": "Ident",
"span": {
"start": 84,
"end": 92,
"ctxt": 0
},
"value": "-chrome-",
"raw": "-chrome-"
},
"value": [
{
"type": "Function",
"span": {
"start": 94,
"end": 114,
"ctxt": 0
},
"name": {
"type": "Ident",
"span": {
"start": 94,
"end": 98,
"ctxt": 0
},
"value": "only",
"raw": "only"
},
"value": [
{
"type": "Delimiter",
"span": {
"start": 99,
"end": 100,
"ctxt": 0
},
"value": ";"
},
{
"type": "Ident",
"span": {
"start": 101,
"end": 108,
"ctxt": 0
},
"value": "z-index",
"raw": "z-index"
},
{
"type": "PreservedToken",
"span": {
"start": 108,
"end": 109,
"ctxt": 0
},
"token": "Colon"
},
{
"type": "Integer",
"span": {
"start": 110,
"end": 112,
"ctxt": 0
},
"value": 10,
"raw": "10"
},
{
"type": "Delimiter",
"span": {
"start": 112,
"end": 113,
"ctxt": 0
},
"value": ";"
}
]
}
],
"important": null
}
]
}
}
]
}
}
]
}

View File

@ -0,0 +1,310 @@
x Stylesheet
,-[$DIR/tests/fixture/only/1/input.css:1:1]
1 | ,-> @media screen and(-webkit-min-device-pixel-ratio:0) {
2 | | .gradientWrapper {
3 | | -chrome-: only(; z-index: 10;);
4 | | }
5 | `-> }
`----
x Rule
,-[$DIR/tests/fixture/only/1/input.css:1:1]
1 | ,-> @media screen and(-webkit-min-device-pixel-ratio:0) {
2 | | .gradientWrapper {
3 | | -chrome-: only(; z-index: 10;);
4 | | }
5 | `-> }
`----
x AtRule
,-[$DIR/tests/fixture/only/1/input.css:1:1]
1 | ,-> @media screen and(-webkit-min-device-pixel-ratio:0) {
2 | | .gradientWrapper {
3 | | -chrome-: only(; z-index: 10;);
4 | | }
5 | `-> }
`----
x AtRuleName
,-[$DIR/tests/fixture/only/1/input.css:1:1]
1 | @media screen and(-webkit-min-device-pixel-ratio:0) {
: ^^^^^
`----
x Ident
,-[$DIR/tests/fixture/only/1/input.css:1:1]
1 | @media screen and(-webkit-min-device-pixel-ratio:0) {
: ^^^^^
`----
x MediaQueryList
,-[$DIR/tests/fixture/only/1/input.css:1:1]
1 | @media screen and(-webkit-min-device-pixel-ratio:0) {
: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
`----
x MediaQuery
,-[$DIR/tests/fixture/only/1/input.css:1:1]
1 | @media screen and(-webkit-min-device-pixel-ratio:0) {
: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
`----
x MediaCondition
,-[$DIR/tests/fixture/only/1/input.css:1:1]
1 | @media screen and(-webkit-min-device-pixel-ratio:0) {
: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
`----
x MediaConditionAllType
,-[$DIR/tests/fixture/only/1/input.css:1:1]
1 | @media screen and(-webkit-min-device-pixel-ratio:0) {
: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
`----
x MediaInParens
,-[$DIR/tests/fixture/only/1/input.css:1:1]
1 | @media screen and(-webkit-min-device-pixel-ratio:0) {
: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
`----
x Function
,-[$DIR/tests/fixture/only/1/input.css:1:1]
1 | @media screen and(-webkit-min-device-pixel-ratio:0) {
: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
`----
x Ident
,-[$DIR/tests/fixture/only/1/input.css:1:1]
1 | @media screen and(-webkit-min-device-pixel-ratio:0) {
: ^^^
`----
x ComponentValue
,-[$DIR/tests/fixture/only/1/input.css:1:1]
1 | @media screen and(-webkit-min-device-pixel-ratio:0) {
: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
`----
x Ident { value: Atom('-webkit-min-device-pixel-ratio' type=dynamic), raw: Atom('-webkit-min-device-pixel-ratio' type=dynamic) }
,-[$DIR/tests/fixture/only/1/input.css:1:1]
1 | @media screen and(-webkit-min-device-pixel-ratio:0) {
: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
`----
x ComponentValue
,-[$DIR/tests/fixture/only/1/input.css:1:1]
1 | @media screen and(-webkit-min-device-pixel-ratio:0) {
: ^
`----
x Colon
,-[$DIR/tests/fixture/only/1/input.css:1:1]
1 | @media screen and(-webkit-min-device-pixel-ratio:0) {
: ^
`----
x ComponentValue
,-[$DIR/tests/fixture/only/1/input.css:1:1]
1 | @media screen and(-webkit-min-device-pixel-ratio:0) {
: ^
`----
x Number { value: 0.0, raw: Atom('0' type=inline), type_flag: Integer }
,-[$DIR/tests/fixture/only/1/input.css:1:1]
1 | @media screen and(-webkit-min-device-pixel-ratio:0) {
: ^
`----
x SimpleBlock
,-[$DIR/tests/fixture/only/1/input.css:1:1]
1 | ,-> @media screen and(-webkit-min-device-pixel-ratio:0) {
2 | | .gradientWrapper {
3 | | -chrome-: only(; z-index: 10;);
4 | | }
5 | `-> }
`----
x LBrace
,-[$DIR/tests/fixture/only/1/input.css:1:1]
1 | @media screen and(-webkit-min-device-pixel-ratio:0) {
: ^
`----
x ComponentValue
,-[$DIR/tests/fixture/only/1/input.css:2:5]
2 | ,-> .gradientWrapper {
3 | | -chrome-: only(; z-index: 10;);
4 | `-> }
`----
x Rule
,-[$DIR/tests/fixture/only/1/input.css:2:5]
2 | ,-> .gradientWrapper {
3 | | -chrome-: only(; z-index: 10;);
4 | `-> }
`----
x QualifiedRule
,-[$DIR/tests/fixture/only/1/input.css:2:5]
2 | ,-> .gradientWrapper {
3 | | -chrome-: only(; z-index: 10;);
4 | `-> }
`----
x SelectorList
,-[$DIR/tests/fixture/only/1/input.css:2:5]
2 | .gradientWrapper {
: ^^^^^^^^^^^^^^^^
`----
x ComplexSelector
,-[$DIR/tests/fixture/only/1/input.css:2:5]
2 | .gradientWrapper {
: ^^^^^^^^^^^^^^^^
`----
x CompoundSelector
,-[$DIR/tests/fixture/only/1/input.css:2:5]
2 | .gradientWrapper {
: ^^^^^^^^^^^^^^^^
`----
x SubclassSelector
,-[$DIR/tests/fixture/only/1/input.css:2:5]
2 | .gradientWrapper {
: ^^^^^^^^^^^^^^^^
`----
x ClassSelector
,-[$DIR/tests/fixture/only/1/input.css:2:5]
2 | .gradientWrapper {
: ^^^^^^^^^^^^^^^^
`----
x Ident
,-[$DIR/tests/fixture/only/1/input.css:2:5]
2 | .gradientWrapper {
: ^^^^^^^^^^^^^^^
`----
x SimpleBlock
,-[$DIR/tests/fixture/only/1/input.css:2:5]
2 | ,-> .gradientWrapper {
3 | | -chrome-: only(; z-index: 10;);
4 | `-> }
`----
x LBrace
,-[$DIR/tests/fixture/only/1/input.css:2:5]
2 | .gradientWrapper {
: ^
`----
x ComponentValue
,-[$DIR/tests/fixture/only/1/input.css:3:7]
3 | -chrome-: only(; z-index: 10;);
: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
`----
x StyleBlock
,-[$DIR/tests/fixture/only/1/input.css:3:7]
3 | -chrome-: only(; z-index: 10;);
: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
`----
x Declaration
,-[$DIR/tests/fixture/only/1/input.css:3:7]
3 | -chrome-: only(; z-index: 10;);
: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
`----
x DeclarationName
,-[$DIR/tests/fixture/only/1/input.css:3:7]
3 | -chrome-: only(; z-index: 10;);
: ^^^^^^^^
`----
x Ident
,-[$DIR/tests/fixture/only/1/input.css:3:7]
3 | -chrome-: only(; z-index: 10;);
: ^^^^^^^^
`----
x ComponentValue
,-[$DIR/tests/fixture/only/1/input.css:3:7]
3 | -chrome-: only(; z-index: 10;);
: ^^^^^^^^^^^^^^^^^^^^
`----
x Function
,-[$DIR/tests/fixture/only/1/input.css:3:7]
3 | -chrome-: only(; z-index: 10;);
: ^^^^^^^^^^^^^^^^^^^^
`----
x Ident
,-[$DIR/tests/fixture/only/1/input.css:3:7]
3 | -chrome-: only(; z-index: 10;);
: ^^^^
`----
x ComponentValue
,-[$DIR/tests/fixture/only/1/input.css:3:7]
3 | -chrome-: only(; z-index: 10;);
: ^
`----
x Delimiter
,-[$DIR/tests/fixture/only/1/input.css:3:7]
3 | -chrome-: only(; z-index: 10;);
: ^
`----
x ComponentValue
,-[$DIR/tests/fixture/only/1/input.css:3:7]
3 | -chrome-: only(; z-index: 10;);
: ^^^^^^^
`----
x Ident
,-[$DIR/tests/fixture/only/1/input.css:3:7]
3 | -chrome-: only(; z-index: 10;);
: ^^^^^^^
`----
x ComponentValue
,-[$DIR/tests/fixture/only/1/input.css:3:7]
3 | -chrome-: only(; z-index: 10;);
: ^
`----
x Colon
,-[$DIR/tests/fixture/only/1/input.css:3:7]
3 | -chrome-: only(; z-index: 10;);
: ^
`----
x ComponentValue
,-[$DIR/tests/fixture/only/1/input.css:3:7]
3 | -chrome-: only(; z-index: 10;);
: ^^
`----
x Integer
,-[$DIR/tests/fixture/only/1/input.css:3:7]
3 | -chrome-: only(; z-index: 10;);
: ^^
`----
x ComponentValue
,-[$DIR/tests/fixture/only/1/input.css:3:7]
3 | -chrome-: only(; z-index: 10;);
: ^
`----
x Delimiter
,-[$DIR/tests/fixture/only/1/input.css:3:7]
3 | -chrome-: only(; z-index: 10;);
: ^
`----

View File

@ -1,4 +1,10 @@
x Expected Declaration value
,-[$DIR/tests/recovery/at-rule/document/input.css:1:1]
1 | @document domain(http://www.example.com/) {}
: ^
`----
x Expected Declaration value
,-[$DIR/tests/recovery/at-rule/document/input.css:1:1]
1 | @document domain(http://www.example.com/) {}