Skip to content

Commit

Permalink
fix(js_parser): handle unterminated JSX_STRING_LITERAL properly (#4425)
Browse files Browse the repository at this point in the history
  • Loading branch information
denbezrukov authored Oct 29, 2024
1 parent 6a8ff77 commit fe792ed
Show file tree
Hide file tree
Showing 4 changed files with 224 additions and 6 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,13 @@ our [guidelines for writing a good changelog entry](https://github.com/biomejs/b

Contributed by @fireairforce

- Fix [#342](https://github.com/biomejs/biome/issues/342), js parser handle unterminated `JSX_STRING_LITERAL` properly

```jsx
function Comp() {
return (
<a rel="
```
- Fix [#342](https://github.com/biomejs/biome/issues/342), js parser is no longer progressing for an invalid object
member name:
Expand Down
15 changes: 9 additions & 6 deletions crates/biome_js_parser/src/lexer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -440,8 +440,11 @@ impl<'src> JsLexer<'src> {

match chr {
b'\'' | b'"' => {
self.consume_str_literal(true);
JSX_STRING_LITERAL
if self.consume_str_literal(true) {
JSX_STRING_LITERAL
} else {
ERROR_TOKEN
}
}
_ => self.lex_token(),
}
Expand Down Expand Up @@ -571,8 +574,8 @@ impl<'src> JsLexer<'src> {
// We should not yield diagnostics on a unicode char boundary. That wont make codespan panic
// but it may cause a panic for other crates which just consume the diagnostics
let invalid = self.current_char_unchecked();
let err = ParseDiagnostic::new( "expected hex digits for a unicode code point escape, but encountered an invalid character",
self.position..self.position + invalid.len_utf8() );
let err = ParseDiagnostic::new("expected hex digits for a unicode code point escape, but encountered an invalid character",
self.position..self.position + invalid.len_utf8());
self.push_diagnostic(err);
self.position -= 1;
return Err(());
Expand Down Expand Up @@ -1894,8 +1897,8 @@ impl<'src> JsLexer<'src> {
self.current_flags |= TokenFlags::UNICODE_ESCAPE;
self.resolve_identifier(chr)
} else {
let err = ParseDiagnostic::new( "unexpected unicode escape",
start..self.position).with_hint("this escape is unexpected, as it does not designate the start of an identifier");
let err = ParseDiagnostic::new("unexpected unicode escape",
start..self.position).with_hint("this escape is unexpected, as it does not designate the start of an identifier");
self.push_diagnostic(err);
self.next_byte();
JsSyntaxKind::ERROR_TOKEN
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
function Comp() {
return (
<a
rel="
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
---
source: crates/biome_js_parser/tests/spec_test.rs
expression: snapshot
---
## Input

```jsx
function Comp() {
return (
<a
rel="
```
## AST
```
JsModule {
bom_token: missing (optional),
interpreter_token: missing (optional),
directives: JsDirectiveList [],
items: JsModuleItemList [
JsFunctionDeclaration {
async_token: missing (optional),
function_token: FUNCTION_KW@0..9 "function" [] [Whitespace(" ")],
star_token: missing (optional),
id: JsIdentifierBinding {
name_token: IDENT@9..13 "Comp" [] [],
},
type_parameters: missing (optional),
parameters: JsParameters {
l_paren_token: L_PAREN@13..14 "(" [] [],
items: JsParameterList [],
r_paren_token: R_PAREN@14..16 ")" [] [Whitespace(" ")],
},
return_type_annotation: missing (optional),
body: JsFunctionBody {
l_curly_token: L_CURLY@16..17 "{" [] [],
directives: JsDirectiveList [],
statements: JsStatementList [
JsReturnStatement {
return_token: RETURN_KW@17..26 "return" [Newline("\n"), Whitespace("\t")] [Whitespace(" ")],
argument: JsParenthesizedExpression {
l_paren_token: L_PAREN@26..27 "(" [] [],
expression: JsBogusExpression {
items: [
JsBogus {
items: [
JsBogus {
items: [
L_ANGLE@27..31 "<" [Newline("\n"), Whitespace("\t\t")] [],
JsxName {
value_token: JSX_IDENT@31..32 "a" [] [],
},
JsBogus {
items: [
JsxAttribute {
name: JsxName {
value_token: JSX_IDENT@32..39 "rel" [Newline("\n"), Whitespace("\t\t\t")] [],
},
initializer: JsxAttributeInitializerClause {
eq_token: EQ@39..40 "=" [] [],
value: missing (required),
},
},
JsBogus {
items: [
ERROR_TOKEN@40..42 "\"\n" [] [],
],
},
],
},
],
},
JsxChildList [],
JsxClosingElement {
l_angle_token: missing (required),
slash_token: missing (required),
name: missing (required),
r_angle_token: missing (required),
},
],
},
],
},
r_paren_token: missing (required),
},
semicolon_token: missing (optional),
},
],
r_curly_token: missing (required),
},
},
],
eof_token: EOF@42..42 "" [] [],
}
```
## CST
```
0: JS_MODULE@0..42
0: (empty)
1: (empty)
2: JS_DIRECTIVE_LIST@0..0
3: JS_MODULE_ITEM_LIST@0..42
0: JS_FUNCTION_DECLARATION@0..42
0: (empty)
1: FUNCTION_KW@0..9 "function" [] [Whitespace(" ")]
2: (empty)
3: JS_IDENTIFIER_BINDING@9..13
0: IDENT@9..13 "Comp" [] []
4: (empty)
5: JS_PARAMETERS@13..16
0: L_PAREN@13..14 "(" [] []
1: JS_PARAMETER_LIST@14..14
2: R_PAREN@14..16 ")" [] [Whitespace(" ")]
6: (empty)
7: JS_FUNCTION_BODY@16..42
0: L_CURLY@16..17 "{" [] []
1: JS_DIRECTIVE_LIST@17..17
2: JS_STATEMENT_LIST@17..42
0: JS_RETURN_STATEMENT@17..42
0: RETURN_KW@17..26 "return" [Newline("\n"), Whitespace("\t")] [Whitespace(" ")]
1: JS_PARENTHESIZED_EXPRESSION@26..42
0: L_PAREN@26..27 "(" [] []
1: JS_BOGUS_EXPRESSION@27..42
0: JS_BOGUS@27..42
0: JS_BOGUS@27..42
0: L_ANGLE@27..31 "<" [Newline("\n"), Whitespace("\t\t")] []
1: JSX_NAME@31..32
0: JSX_IDENT@31..32 "a" [] []
2: JS_BOGUS@32..42
0: JSX_ATTRIBUTE@32..40
0: JSX_NAME@32..39
0: JSX_IDENT@32..39 "rel" [Newline("\n"), Whitespace("\t\t\t")] []
1: JSX_ATTRIBUTE_INITIALIZER_CLAUSE@39..40
0: EQ@39..40 "=" [] []
1: (empty)
1: JS_BOGUS@40..42
0: ERROR_TOKEN@40..42 "\"\n" [] []
1: JSX_CHILD_LIST@42..42
2: JSX_CLOSING_ELEMENT@42..42
0: (empty)
1: (empty)
2: (empty)
3: (empty)
2: (empty)
2: (empty)
3: (empty)
4: EOF@42..42 "" [] []
```
## Diagnostics
```
jsx_element_attribute_string_literal_err.jsx:4:8 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
× Expected a JSX attribute value but instead found '"
'.
2return (
3<a
> 4rel="
^
> 5
i Expected a JSX attribute value here.
2return (
3<a
> 4rel="
^
> 5
jsx_element_attribute_string_literal_err.jsx:5:1 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
× unterminated string literal
3<a
4rel="
> 5 │
i input ends here
3<a
4rel="
> 5 │
i string literal starts here
2return (
3<a
> 4rel="
^
5
```

0 comments on commit fe792ed

Please sign in to comment.