feat(css/lexer): Implement error recovery for functions (#3445)

This commit is contained in:
Alexander Akait 2022-02-04 06:29:56 +03:00 committed by GitHub
parent 283486dcd7
commit f1410fc09b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 857 additions and 254 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -231,6 +231,7 @@ where
}
fn parse_one_value_inner(&mut self) -> PResult<Value> {
// TODO remove me
self.input.skip_ws()?;
let span = self.input.cur_span()?;
@ -398,25 +399,6 @@ where
}
}
/// Parse comma separated values.
fn parse_comma_separated_value(&mut self) -> PResult<Vec<Value>> {
let mut args = vec![];
loop {
self.input.skip_ws()?;
if is_one_of!(self, ")") {
break;
}
let value = self.parse_one_value_inner()?;
args.push(value);
}
Ok(args)
}
fn parse_square_brackets_value(&mut self) -> PResult<SimpleBlock> {
let span = self.input.cur_span()?;
@ -917,14 +899,58 @@ where
allow_operation_in_value: is_math_function,
..self.ctx
};
let value = self.with_ctx(ctx).parse_comma_separated_value()?;
expect!(self, ")");
Ok(Function {
span: span!(self, span.lo),
// Create a function with its name equal to the value of the current input token
// and with its value initially set to an empty list.
let mut function = Function {
span: Default::default(),
name,
value,
})
value: vec![],
};
// Repeatedly consume the next input token and process it as follows:
loop {
// <EOF-token>
// This is a parse error. Return the function.
if is!(self, EOF) {
break;
}
match cur!(self) {
// <)-token>
// Return the function.
tok!(")") => {
bump!(self);
break;
}
// anything else
// Reconsume the current input token. Consume a component value and append the
// returned value to the functions value.
_ => {
let state = self.input.state();
let parsed = self.with_ctx(ctx).parse_one_value_inner();
let value = match parsed {
Ok(value) => {
self.input.skip_ws()?;
value
}
Err(err) => {
self.errors.push(err);
self.input.reset(&state);
self.parse_component_value()?
}
};
function.value.push(value);
}
}
}
function.span = span!(self, span.lo);
return Ok(function);
}
}

View File

@ -1,6 +1,14 @@
error: Expected "}"
error: Expected Declaration value
--> $DIR/tests/errors/rome/invalid/fit-content/invalid-call/input.css:3:1
|
3 | }
| ^
error: Unexpected end of file
--> $DIR/tests/errors/rome/invalid/fit-content/invalid-call/input.css:3:3
|
3 | }
| ^

View File

@ -4,3 +4,9 @@ error: Expected "]"
2 | grid-template-columns: repeat(4, [col-start);
| ^
error: Unexpected end of file
--> $DIR/tests/errors/rome/invalid/grid/repeat/unclosed-lint-name/input.css:3:3
|
3 | }
| ^

View File

@ -1,6 +1,14 @@
error: Expected "}"
error: Expected Declaration value
--> $DIR/tests/errors/rome/invalid/grid/repeat/unclosed/input.css:3:1
|
3 | }
| ^
error: Unexpected end of file
--> $DIR/tests/errors/rome/invalid/grid/repeat/unclosed/input.css:3:3
|
3 | }
| ^

View File

@ -1,6 +1,14 @@
error: Expected "}"
error: Expected Declaration value
--> $DIR/tests/errors/rome/invalid/min-or-max/input.css:3:1
|
3 | }
| ^
error: Unexpected end of file
--> $DIR/tests/errors/rome/invalid/min-or-max/input.css:3:3
|
3 | }
| ^

View File

@ -233,132 +233,87 @@
]
},
{
"type": "Tokens",
"type": "Declaration",
"span": {
"start": 39,
"end": 57,
"ctxt": 0
},
"tokens": [
{
"span": {
"start": 39,
"end": 44,
"ctxt": 0
},
"token": {
"Ident": {
"value": "prop2",
"raw": "prop2"
}
}
},
{
"span": {
"start": 44,
"end": 45,
"ctxt": 0
},
"token": "Colon"
},
{
"span": {
"start": 45,
"end": 46,
"ctxt": 0
},
"token": {
"WhiteSpace": {
"value": " "
}
}
"name": {
"type": "Identifier",
"span": {
"start": 39,
"end": 44,
"ctxt": 0
},
"value": "prop2",
"raw": "prop2"
},
"value": [
{
"type": "Function",
"span": {
"start": 46,
"end": 51,
"ctxt": 0
},
"token": {
"Function": {
"value": "func",
"raw": "func"
}
}
},
{
"span": {
"start": 51,
"end": 52,
"ctxt": 0
},
"token": {
"Num": {
"value": 1.0,
"raw": "1",
"type": "integer"
}
}
},
{
"span": {
"start": 52,
"end": 53,
"ctxt": 0
},
"token": {
"WhiteSpace": {
"value": " "
}
}
},
{
"span": {
"start": 53,
"end": 54,
"ctxt": 0
},
"token": {
"Delim": {
"value": "-"
}
}
},
{
"span": {
"start": 54,
"end": 55,
"ctxt": 0
},
"token": {
"WhiteSpace": {
"value": " "
}
}
},
{
"span": {
"start": 55,
"end": 56,
"ctxt": 0
},
"token": {
"Num": {
"value": 2.0,
"raw": "2",
"type": "integer"
}
}
},
{
"span": {
"start": 56,
"end": 57,
"ctxt": 0
},
"token": "RParen"
"name": {
"type": "Identifier",
"span": {
"start": 46,
"end": 50,
"ctxt": 0
},
"value": "func",
"raw": "func"
},
"value": [
{
"type": "Number",
"span": {
"start": 51,
"end": 52,
"ctxt": 0
},
"value": 1.0,
"raw": "1"
},
{
"type": "Tokens",
"span": {
"start": 53,
"end": 54,
"ctxt": 0
},
"tokens": [
{
"span": {
"start": 53,
"end": 54,
"ctxt": 0
},
"token": {
"Delim": {
"value": "-"
}
}
}
]
},
{
"type": "Number",
"span": {
"start": 55,
"end": 56,
"ctxt": 0
},
"value": 2.0,
"raw": "2"
}
]
}
]
],
"important": null
}
]
}

View File

@ -233,132 +233,87 @@
]
},
{
"type": "Tokens",
"type": "Declaration",
"span": {
"start": 39,
"end": 57,
"ctxt": 0
},
"tokens": [
{
"span": {
"start": 39,
"end": 44,
"ctxt": 0
},
"token": {
"Ident": {
"value": "prop2",
"raw": "prop2"
}
}
},
{
"span": {
"start": 44,
"end": 45,
"ctxt": 0
},
"token": "Colon"
},
{
"span": {
"start": 45,
"end": 46,
"ctxt": 0
},
"token": {
"WhiteSpace": {
"value": " "
}
}
"name": {
"type": "Identifier",
"span": {
"start": 39,
"end": 44,
"ctxt": 0
},
"value": "prop2",
"raw": "prop2"
},
"value": [
{
"type": "Function",
"span": {
"start": 46,
"end": 51,
"ctxt": 0
},
"token": {
"Function": {
"value": "func",
"raw": "func"
}
}
},
{
"span": {
"start": 51,
"end": 52,
"ctxt": 0
},
"token": {
"Num": {
"value": 1.0,
"raw": "1",
"type": "integer"
}
}
},
{
"span": {
"start": 52,
"end": 53,
"ctxt": 0
},
"token": {
"WhiteSpace": {
"value": " "
}
}
},
{
"span": {
"start": 53,
"end": 54,
"ctxt": 0
},
"token": {
"Delim": {
"value": "+"
}
}
},
{
"span": {
"start": 54,
"end": 55,
"ctxt": 0
},
"token": {
"WhiteSpace": {
"value": " "
}
}
},
{
"span": {
"start": 55,
"end": 56,
"ctxt": 0
},
"token": {
"Num": {
"value": 2.0,
"raw": "2",
"type": "integer"
}
}
},
{
"span": {
"start": 56,
"end": 57,
"ctxt": 0
},
"token": "RParen"
"name": {
"type": "Identifier",
"span": {
"start": 46,
"end": 50,
"ctxt": 0
},
"value": "func",
"raw": "func"
},
"value": [
{
"type": "Number",
"span": {
"start": 51,
"end": 52,
"ctxt": 0
},
"value": 1.0,
"raw": "1"
},
{
"type": "Tokens",
"span": {
"start": 53,
"end": 54,
"ctxt": 0
},
"tokens": [
{
"span": {
"start": 53,
"end": 54,
"ctxt": 0
},
"token": {
"Delim": {
"value": "+"
}
}
}
]
},
{
"type": "Number",
"span": {
"start": 55,
"end": 56,
"ctxt": 0
},
"value": 2.0,
"raw": "2"
}
]
}
]
],
"important": null
}
]
}

View File

@ -0,0 +1,8 @@
div {
prop: func(1 + 2);
prop: func(ident.ident);
prop: func3( ident.ident );
prop: func([foo bar]);
prop: func((foo, bar));
prop: func({ foo: bar });
}

View File

@ -0,0 +1,611 @@
{
"type": "Stylesheet",
"span": {
"start": 0,
"end": 181,
"ctxt": 0
},
"rules": [
{
"type": "QualifiedRule",
"span": {
"start": 0,
"end": 180,
"ctxt": 0
},
"prelude": {
"type": "SelectorList",
"span": {
"start": 0,
"end": 3,
"ctxt": 0
},
"children": [
{
"type": "ComplexSelector",
"span": {
"start": 0,
"end": 3,
"ctxt": 0
},
"children": [
{
"type": "CompoundSelector",
"span": {
"start": 0,
"end": 3,
"ctxt": 0
},
"nestingSelector": null,
"typeSelector": {
"type": "TypeSelector",
"span": {
"start": 0,
"end": 3,
"ctxt": 0
},
"prefix": null,
"name": {
"type": "Identifier",
"span": {
"start": 0,
"end": 3,
"ctxt": 0
},
"value": "div",
"raw": "div"
}
},
"subclassSelectors": []
}
]
}
]
},
"block": {
"type": "Block",
"span": {
"start": 4,
"end": 180,
"ctxt": 0
},
"value": [
{
"type": "Declaration",
"span": {
"start": 10,
"end": 27,
"ctxt": 0
},
"name": {
"type": "Identifier",
"span": {
"start": 10,
"end": 14,
"ctxt": 0
},
"value": "prop",
"raw": "prop"
},
"value": [
{
"type": "Function",
"span": {
"start": 16,
"end": 27,
"ctxt": 0
},
"name": {
"type": "Identifier",
"span": {
"start": 16,
"end": 20,
"ctxt": 0
},
"value": "func",
"raw": "func"
},
"value": [
{
"type": "Number",
"span": {
"start": 21,
"end": 22,
"ctxt": 0
},
"value": 1.0,
"raw": "1"
},
{
"type": "Tokens",
"span": {
"start": 23,
"end": 24,
"ctxt": 0
},
"tokens": [
{
"span": {
"start": 23,
"end": 24,
"ctxt": 0
},
"token": {
"Delim": {
"value": "+"
}
}
}
]
},
{
"type": "Number",
"span": {
"start": 25,
"end": 26,
"ctxt": 0
},
"value": 2.0,
"raw": "2"
}
]
}
],
"important": null
},
{
"type": "Declaration",
"span": {
"start": 33,
"end": 56,
"ctxt": 0
},
"name": {
"type": "Identifier",
"span": {
"start": 33,
"end": 37,
"ctxt": 0
},
"value": "prop",
"raw": "prop"
},
"value": [
{
"type": "Function",
"span": {
"start": 39,
"end": 56,
"ctxt": 0
},
"name": {
"type": "Identifier",
"span": {
"start": 39,
"end": 43,
"ctxt": 0
},
"value": "func",
"raw": "func"
},
"value": [
{
"type": "Identifier",
"span": {
"start": 44,
"end": 49,
"ctxt": 0
},
"value": "ident",
"raw": "ident"
},
{
"type": "Tokens",
"span": {
"start": 49,
"end": 50,
"ctxt": 0
},
"tokens": [
{
"span": {
"start": 49,
"end": 50,
"ctxt": 0
},
"token": {
"Delim": {
"value": "."
}
}
}
]
},
{
"type": "Identifier",
"span": {
"start": 50,
"end": 55,
"ctxt": 0
},
"value": "ident",
"raw": "ident"
}
]
}
],
"important": null
},
{
"type": "Declaration",
"span": {
"start": 62,
"end": 92,
"ctxt": 0
},
"name": {
"type": "Identifier",
"span": {
"start": 62,
"end": 66,
"ctxt": 0
},
"value": "prop",
"raw": "prop"
},
"value": [
{
"type": "Function",
"span": {
"start": 68,
"end": 92,
"ctxt": 0
},
"name": {
"type": "Identifier",
"span": {
"start": 68,
"end": 73,
"ctxt": 0
},
"value": "func3",
"raw": "func3"
},
"value": [
{
"type": "Identifier",
"span": {
"start": 77,
"end": 82,
"ctxt": 0
},
"value": "ident",
"raw": "ident"
},
{
"type": "Tokens",
"span": {
"start": 82,
"end": 83,
"ctxt": 0
},
"tokens": [
{
"span": {
"start": 82,
"end": 83,
"ctxt": 0
},
"token": {
"Delim": {
"value": "."
}
}
}
]
},
{
"type": "Identifier",
"span": {
"start": 83,
"end": 88,
"ctxt": 0
},
"value": "ident",
"raw": "ident"
}
]
}
],
"important": null
},
{
"type": "Declaration",
"span": {
"start": 98,
"end": 119,
"ctxt": 0
},
"name": {
"type": "Identifier",
"span": {
"start": 98,
"end": 102,
"ctxt": 0
},
"value": "prop",
"raw": "prop"
},
"value": [
{
"type": "Function",
"span": {
"start": 104,
"end": 119,
"ctxt": 0
},
"name": {
"type": "Identifier",
"span": {
"start": 104,
"end": 108,
"ctxt": 0
},
"value": "func",
"raw": "func"
},
"value": [
{
"type": "SimpleBlock",
"span": {
"start": 109,
"end": 118,
"ctxt": 0
},
"name": "[",
"value": [
{
"type": "Identifier",
"span": {
"start": 110,
"end": 113,
"ctxt": 0
},
"value": "foo",
"raw": "foo"
},
{
"type": "Identifier",
"span": {
"start": 114,
"end": 117,
"ctxt": 0
},
"value": "bar",
"raw": "bar"
}
]
}
]
}
],
"important": null
},
{
"type": "Declaration",
"span": {
"start": 125,
"end": 147,
"ctxt": 0
},
"name": {
"type": "Identifier",
"span": {
"start": 125,
"end": 129,
"ctxt": 0
},
"value": "prop",
"raw": "prop"
},
"value": [
{
"type": "Function",
"span": {
"start": 131,
"end": 147,
"ctxt": 0
},
"name": {
"type": "Identifier",
"span": {
"start": 131,
"end": 135,
"ctxt": 0
},
"value": "func",
"raw": "func"
},
"value": [
{
"type": "SimpleBlock",
"span": {
"start": 136,
"end": 146,
"ctxt": 0
},
"name": "(",
"value": [
{
"type": "Identifier",
"span": {
"start": 137,
"end": 140,
"ctxt": 0
},
"value": "foo",
"raw": "foo"
},
{
"type": "Delimiter",
"span": {
"start": 140,
"end": 141,
"ctxt": 0
},
"value": ","
},
{
"type": "Identifier",
"span": {
"start": 142,
"end": 145,
"ctxt": 0
},
"value": "bar",
"raw": "bar"
}
]
}
]
}
],
"important": null
},
{
"type": "Declaration",
"span": {
"start": 153,
"end": 177,
"ctxt": 0
},
"name": {
"type": "Identifier",
"span": {
"start": 153,
"end": 157,
"ctxt": 0
},
"value": "prop",
"raw": "prop"
},
"value": [
{
"type": "Function",
"span": {
"start": 159,
"end": 177,
"ctxt": 0
},
"name": {
"type": "Identifier",
"span": {
"start": 159,
"end": 163,
"ctxt": 0
},
"value": "func",
"raw": "func"
},
"value": [
{
"type": "SimpleBlock",
"span": {
"start": 164,
"end": 176,
"ctxt": 0
},
"name": "{",
"value": [
{
"type": "Tokens",
"span": {
"start": 165,
"end": 175,
"ctxt": 0
},
"tokens": [
{
"span": {
"start": 165,
"end": 166,
"ctxt": 0
},
"token": {
"WhiteSpace": {
"value": " "
}
}
},
{
"span": {
"start": 166,
"end": 169,
"ctxt": 0
},
"token": {
"Ident": {
"value": "foo",
"raw": "foo"
}
}
},
{
"span": {
"start": 169,
"end": 170,
"ctxt": 0
},
"token": "Colon"
},
{
"span": {
"start": 170,
"end": 171,
"ctxt": 0
},
"token": {
"WhiteSpace": {
"value": " "
}
}
},
{
"span": {
"start": 171,
"end": 174,
"ctxt": 0
},
"token": {
"Ident": {
"value": "bar",
"raw": "bar"
}
}
},
{
"span": {
"start": 174,
"end": 175,
"ctxt": 0
},
"token": {
"WhiteSpace": {
"value": " "
}
}
}
]
}
]
}
]
}
],
"important": null
}
]
}
}
]
}

View File

@ -0,0 +1,18 @@
error: Expected Declaration value
--> $DIR/tests/recovery/function/input.css:2:18
|
2 | prop: func(1 + 2);
| ^
error: Expected Declaration value
--> $DIR/tests/recovery/function/input.css:3:21
|
3 | prop: func(ident.ident);
| ^
error: Expected Declaration value
--> $DIR/tests/recovery/function/input.css:4:25
|
4 | prop: func3( ident.ident );
| ^