From d7183d82e2956fa7632bda074dae405a7775dad3 Mon Sep 17 00:00:00 2001 From: Alexander Akait <4567934+alexander-akait@users.noreply.github.com> Date: Mon, 29 Nov 2021 18:26:57 +0300 Subject: [PATCH] feat(css/parser): Add more error recovery (#2849) --- crates/swc_css_parser/src/parser/mod.rs | 2 - crates/swc_css_parser/src/parser/value/mod.rs | 6 +- .../tests/recovery/declaration/input.css | 9 + .../tests/recovery/declaration/output.json | 318 ++++++++++++++++++ .../recovery/declaration/output.swc-stderr | 14 + .../delim-token/bang/output.swc-stderr | 10 +- .../tests/recovery/whitespaces/output.json | 224 ++++++------ .../recovery/whitespaces/output.swc-stderr | 29 +- 8 files changed, 471 insertions(+), 141 deletions(-) create mode 100644 crates/swc_css_parser/tests/recovery/declaration/input.css create mode 100644 crates/swc_css_parser/tests/recovery/declaration/output.json create mode 100644 crates/swc_css_parser/tests/recovery/declaration/output.swc-stderr diff --git a/crates/swc_css_parser/src/parser/mod.rs b/crates/swc_css_parser/src/parser/mod.rs index 7cd6d4b66db..4549cdc3976 100644 --- a/crates/swc_css_parser/src/parser/mod.rs +++ b/crates/swc_css_parser/src/parser/mod.rs @@ -38,8 +38,6 @@ struct Ctx { disallow_comma_in_media_query: bool, - is_in_delimited_value: bool, - allow_at_selector: bool, recover_from_property_value: bool, diff --git a/crates/swc_css_parser/src/parser/value/mod.rs b/crates/swc_css_parser/src/parser/value/mod.rs index 9e260637839..ed5f444b63b 100644 --- a/crates/swc_css_parser/src/parser/value/mod.rs +++ b/crates/swc_css_parser/src/parser/value/mod.rs @@ -344,9 +344,7 @@ where _ => {} } - if is_one_of!(self, "") - || (self.ctx.is_in_delimited_value && is_one_of!(self, "!", ";")) - { + if is_one_of!(self, "", "!", ";") { let token = self.input.bump()?.unwrap(); return Ok(Value::Lazy(Tokens { span, @@ -526,7 +524,6 @@ where self.input.skip_ws()?; let ctx = Ctx { - is_in_delimited_value: true, allow_separating_value_with_space: false, ..self.ctx }; @@ -555,7 +552,6 @@ where } else { let ctx = Ctx { allow_operation_in_value: true, - is_in_delimited_value: true, ..self.ctx }; diff --git a/crates/swc_css_parser/tests/recovery/declaration/input.css b/crates/swc_css_parser/tests/recovery/declaration/input.css new file mode 100644 index 00000000000..3d3cfdd6d86 --- /dev/null +++ b/crates/swc_css_parser/tests/recovery/declaration/input.css @@ -0,0 +1,9 @@ +.class { + prop: ; + color: red; +} + +.class { + prop: !!; + color: red; +} diff --git a/crates/swc_css_parser/tests/recovery/declaration/output.json b/crates/swc_css_parser/tests/recovery/declaration/output.json new file mode 100644 index 00000000000..2119a1eb3bb --- /dev/null +++ b/crates/swc_css_parser/tests/recovery/declaration/output.json @@ -0,0 +1,318 @@ +{ + "type": "Stylesheet", + "span": { + "start": 0, + "end": 81, + "ctxt": 0 + }, + "rules": [ + { + "type": "QualifiedRule", + "span": { + "start": 0, + "end": 38, + "ctxt": 0 + }, + "prelude": { + "type": "SelectorList", + "span": { + "start": 0, + "end": 6, + "ctxt": 0 + }, + "children": [ + { + "type": "ComplexSelector", + "span": { + "start": 0, + "end": 6, + "ctxt": 0 + }, + "children": [ + { + "type": "CompoundSelector", + "span": { + "start": 0, + "end": 6, + "ctxt": 0 + }, + "nestingSelector": null, + "typeSelector": null, + "subclassSelectors": [ + { + "type": "ClassSelector", + "span": { + "start": 0, + "end": 6, + "ctxt": 0 + }, + "text": { + "type": "Identifier", + "span": { + "start": 1, + "end": 6, + "ctxt": 0 + }, + "value": "class", + "raw": "class" + } + } + ] + } + ] + } + ] + }, + "block": { + "type": "Block", + "span": { + "start": 7, + "end": 38, + "ctxt": 0 + }, + "items": [ + { + "type": "Declaration", + "span": { + "start": 13, + "end": 30, + "ctxt": 0 + }, + "property": { + "type": "Identifier", + "span": { + "start": 13, + "end": 17, + "ctxt": 0 + }, + "value": "prop", + "raw": "prop" + }, + "value": [ + { + "type": "Tokens", + "span": { + "start": 18, + "end": 19, + "ctxt": 0 + }, + "tokens": [ + { + "span": { + "start": 18, + "end": 19, + "ctxt": 0 + }, + "token": { + "WhiteSpace": { + "value": " " + } + } + } + ] + } + ], + "important": null + }, + { + "type": "Declaration", + "span": { + "start": 25, + "end": 35, + "ctxt": 0 + }, + "property": { + "type": "Identifier", + "span": { + "start": 25, + "end": 30, + "ctxt": 0 + }, + "value": "color", + "raw": "color" + }, + "value": [ + { + "type": "Identifier", + "span": { + "start": 32, + "end": 35, + "ctxt": 0 + }, + "value": "red", + "raw": "red" + } + ], + "important": null + } + ] + } + }, + { + "type": "QualifiedRule", + "span": { + "start": 40, + "end": 80, + "ctxt": 0 + }, + "prelude": { + "type": "SelectorList", + "span": { + "start": 40, + "end": 46, + "ctxt": 0 + }, + "children": [ + { + "type": "ComplexSelector", + "span": { + "start": 40, + "end": 46, + "ctxt": 0 + }, + "children": [ + { + "type": "CompoundSelector", + "span": { + "start": 40, + "end": 46, + "ctxt": 0 + }, + "nestingSelector": null, + "typeSelector": null, + "subclassSelectors": [ + { + "type": "ClassSelector", + "span": { + "start": 40, + "end": 46, + "ctxt": 0 + }, + "text": { + "type": "Identifier", + "span": { + "start": 41, + "end": 46, + "ctxt": 0 + }, + "value": "class", + "raw": "class" + } + } + ] + } + ] + } + ] + }, + "block": { + "type": "Block", + "span": { + "start": 47, + "end": 80, + "ctxt": 0 + }, + "items": [ + { + "type": "Tokens", + "span": { + "start": 53, + "end": 61, + "ctxt": 0 + }, + "tokens": [ + { + "span": { + "start": 53, + "end": 57, + "ctxt": 0 + }, + "token": { + "Ident": { + "value": "prop", + "raw": "prop" + } + } + }, + { + "span": { + "start": 57, + "end": 58, + "ctxt": 0 + }, + "token": "Colon" + }, + { + "span": { + "start": 58, + "end": 59, + "ctxt": 0 + }, + "token": { + "WhiteSpace": { + "value": " " + } + } + }, + { + "span": { + "start": 59, + "end": 60, + "ctxt": 0 + }, + "token": { + "Delim": { + "value": "!" + } + } + }, + { + "span": { + "start": 60, + "end": 61, + "ctxt": 0 + }, + "token": { + "Delim": { + "value": "!" + } + } + } + ] + }, + { + "type": "Declaration", + "span": { + "start": 67, + "end": 77, + "ctxt": 0 + }, + "property": { + "type": "Identifier", + "span": { + "start": 67, + "end": 72, + "ctxt": 0 + }, + "value": "color", + "raw": "color" + }, + "value": [ + { + "type": "Identifier", + "span": { + "start": 74, + "end": 77, + "ctxt": 0 + }, + "value": "red", + "raw": "red" + } + ], + "important": null + } + ] + } + } + ] +} diff --git a/crates/swc_css_parser/tests/recovery/declaration/output.swc-stderr b/crates/swc_css_parser/tests/recovery/declaration/output.swc-stderr new file mode 100644 index 00000000000..bbe014ae884 --- /dev/null +++ b/crates/swc_css_parser/tests/recovery/declaration/output.swc-stderr @@ -0,0 +1,14 @@ +error: Expected a property value + --> $DIR/tests/recovery/declaration/input.css:2:10 + | +2 | prop: ; + | ^ + +error: Expected !important + --> $DIR/tests/recovery/declaration/input.css:7:12 + | +7 | prop: !!; + | ____________^ +8 | | color: red; + | |____^ + diff --git a/crates/swc_css_parser/tests/recovery/delim-token/bang/output.swc-stderr b/crates/swc_css_parser/tests/recovery/delim-token/bang/output.swc-stderr index 721a851ae8b..c2c769c52ac 100644 --- a/crates/swc_css_parser/tests/recovery/delim-token/bang/output.swc-stderr +++ b/crates/swc_css_parser/tests/recovery/delim-token/bang/output.swc-stderr @@ -1,6 +1,8 @@ -error: Expected Declaration value - --> $DIR/tests/recovery/delim-token/bang/input.css:2:12 +error: Expected !important + --> $DIR/tests/recovery/delim-token/bang/input.css:2:13 | -2 | color: !!; - | ^ +2 | color: !!; + | _____________^ +3 | | } + | |_ diff --git a/crates/swc_css_parser/tests/recovery/whitespaces/output.json b/crates/swc_css_parser/tests/recovery/whitespaces/output.json index ad86ef38c04..e471ecfec51 100644 --- a/crates/swc_css_parser/tests/recovery/whitespaces/output.json +++ b/crates/swc_css_parser/tests/recovery/whitespaces/output.json @@ -71,176 +71,172 @@ }, "items": [ { - "type": "Tokens", + "type": "Declaration", "span": { "start": 8, - "end": 14, + "end": 24, "ctxt": 0 }, - "tokens": [ - { - "span": { - "start": 8, - "end": 12, - "ctxt": 0 - }, - "token": { - "Ident": { - "value": "prop", - "raw": "prop" - } - } - }, - { - "span": { - "start": 12, - "end": 13, - "ctxt": 0 - }, - "token": "Colon" + "property": { + "type": "Identifier", + "span": { + "start": 8, + "end": 12, + "ctxt": 0 }, + "value": "prop", + "raw": "prop" + }, + "value": [ { + "type": "Tokens", "span": { "start": 13, "end": 14, "ctxt": 0 }, - "token": { - "WhiteSpace": { - "value": " " + "tokens": [ + { + "span": { + "start": 13, + "end": 14, + "ctxt": 0 + }, + "token": { + "WhiteSpace": { + "value": " " + } + } } - } + ] } - ] + ], + "important": null }, { - "type": "Tokens", + "type": "Declaration", "span": { "start": 20, - "end": 29, + "end": 39, "ctxt": 0 }, - "tokens": [ - { - "span": { - "start": 20, - "end": 24, - "ctxt": 0 - }, - "token": { - "Ident": { - "value": "prop", - "raw": "prop" - } - } - }, - { - "span": { - "start": 24, - "end": 25, - "ctxt": 0 - }, - "token": "Colon" + "property": { + "type": "Identifier", + "span": { + "start": 20, + "end": 24, + "ctxt": 0 }, + "value": "prop", + "raw": "prop" + }, + "value": [ { + "type": "Tokens", "span": { "start": 25, "end": 29, "ctxt": 0 }, - "token": { - "WhiteSpace": { - "value": " " + "tokens": [ + { + "span": { + "start": 25, + "end": 29, + "ctxt": 0 + }, + "token": { + "WhiteSpace": { + "value": " " + } + } } - } + ] } - ] + ], + "important": null }, { - "type": "Tokens", + "type": "Declaration", "span": { "start": 35, - "end": 42, + "end": 52, "ctxt": 0 }, - "tokens": [ - { - "span": { - "start": 35, - "end": 39, - "ctxt": 0 - }, - "token": { - "Ident": { - "value": "prop", - "raw": "prop" - } - } - }, - { - "span": { - "start": 39, - "end": 40, - "ctxt": 0 - }, - "token": "Colon" + "property": { + "type": "Identifier", + "span": { + "start": 35, + "end": 39, + "ctxt": 0 }, + "value": "prop", + "raw": "prop" + }, + "value": [ { + "type": "Tokens", "span": { "start": 40, "end": 42, "ctxt": 0 }, - "token": { - "WhiteSpace": { - "value": "\n\n" + "tokens": [ + { + "span": { + "start": 40, + "end": 42, + "ctxt": 0 + }, + "token": { + "WhiteSpace": { + "value": "\n\n" + } + } } - } + ] } - ] + ], + "important": null }, { - "type": "Tokens", + "type": "Declaration", "span": { "start": 48, - "end": 63, + "end": 64, "ctxt": 0 }, - "tokens": [ - { - "span": { - "start": 48, - "end": 52, - "ctxt": 0 - }, - "token": { - "Ident": { - "value": "prop", - "raw": "prop" - } - } + "property": { + "type": "Identifier", + "span": { + "start": 48, + "end": 52, + "ctxt": 0 }, + "value": "prop", + "raw": "prop" + }, + "value": [ { + "type": "Tokens", "span": { - "start": 52, - "end": 53, + "start": 63, + "end": 64, "ctxt": 0 }, - "token": "Colon" - }, - { - "span": { - "start": 53, - "end": 63, - "ctxt": 0 - }, - "token": { - "WhiteSpace": { - "value": " \n\n " + "tokens": [ + { + "span": { + "start": 63, + "end": 64, + "ctxt": 0 + }, + "token": "Semi" } - } + ] } - ] + ], + "important": null } ] } diff --git a/crates/swc_css_parser/tests/recovery/whitespaces/output.swc-stderr b/crates/swc_css_parser/tests/recovery/whitespaces/output.swc-stderr index b4e1c4e80b2..98e76846d77 100644 --- a/crates/swc_css_parser/tests/recovery/whitespaces/output.swc-stderr +++ b/crates/swc_css_parser/tests/recovery/whitespaces/output.swc-stderr @@ -1,24 +1,21 @@ -error: Expected Declaration value - --> $DIR/tests/recovery/whitespaces/input.css:2:11 +error: Expected a property value + --> $DIR/tests/recovery/whitespaces/input.css:2:10 | 2 | prop: ; - | ^ + | ^ -error: Expected Declaration value - --> $DIR/tests/recovery/whitespaces/input.css:3:14 +error: Expected a property value + --> $DIR/tests/recovery/whitespaces/input.css:3:10 | 3 | prop: ; - | ^ + | ^^^^ -error: Expected Declaration value - --> $DIR/tests/recovery/whitespaces/input.css:6:1 +error: Expected a property value + --> $DIR/tests/recovery/whitespaces/input.css:4:10 | -6 | ; - | ^ - -error: Expected Declaration value - --> $DIR/tests/recovery/whitespaces/input.css:9:5 - | -9 | ; - | ^ +4 | prop: + | __________^ +5 | | +6 | | ; + | |_