Skip to content
This repository has been archived by the owner on Aug 31, 2023. It is now read-only.

feat: unknown bindings and unknown patterns #1804

Merged
merged 14 commits into from
Nov 23, 2021
34 changes: 34 additions & 0 deletions crates/rslint_parser/src/syntax/class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ pub(super) fn class_expression(p: &mut Parser) -> CompletedMarker {
// class extends {}
// class
// class foo { set {} }
// class A extends bar extends foo {}
// class A extends bar, foo {}
/// Parses a class declaration
pub(super) fn class_declaration(p: &mut Parser) -> CompletedMarker {
class(p, ClassKind::Declaration)
Expand Down Expand Up @@ -483,6 +485,38 @@ fn class_member(p: &mut Parser) -> CompletedMarker {
if matches!(member_name, "get" | "set") && !is_at_line_break_or_generator {
let is_getter = member_name == "get";

// test getter_class_member
// class Getters {
// get foo() {}
// get static() {}
// static get bar() {}
// get "baz"() {}
// get ["a" + "b"]() {}
// get 5() {}
// get #private() {}
// }
// class NotGetters {
// get() {}
// async get() {}
// static get() {}
// }

// test setter_class_number
// class Setters {
// set foo(a) {}
// set static(a) {}
// static set bar(a) {}
// set "baz"(a) {}
// set ["a" + "b"](a) {}
// set 5(a) {}
// set #private(a) {}
// }
// class NotSetters {
// set(a) {}
// async set(a) {}
// static set(a) {}
// }

// The tree currently holds a STATIC_MEMBER_NAME node that wraps a ident token but we now found
// out that the 'get' or 'set' isn't a member name in this context but instead are the
// 'get'/'set' keywords for getters/setters. That's why we need to undo the member name node,
Expand Down
2 changes: 2 additions & 0 deletions crates/rslint_parser/src/syntax/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ fn assign_expr_recursive(
// function *foo() {
// yield foo;
// yield* foo;
// yield;
// }
pub fn yield_expr(p: &mut Parser) -> CompletedMarker {
let m = p.start();
Expand Down Expand Up @@ -679,6 +680,7 @@ pub fn args(p: &mut Parser) -> CompletedMarker {

// test_err paren_or_arrow_expr_invalid_params
// (5 + 5) => {}
// (a, ,b) => {}
pub fn paren_or_arrow_expr(p: &mut Parser, can_be_arrow: bool) -> CompletedMarker {
let m = p.start();
let checkpoint = p.checkpoint();
Expand Down
9 changes: 6 additions & 3 deletions crates/rslint_parser/src/syntax/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ pub(super) fn object_expr(p: &mut Parser) -> CompletedMarker {
/// An individual object property such as `"a": b` or `5: 6 + 6`.
fn object_member(p: &mut Parser) -> Option<CompletedMarker> {
match p.cur() {
// test object_expr_getter_setter
// test object_expr_getter_getter
ematipico marked this conversation as resolved.
Show resolved Hide resolved
// let a = {
// get foo() {
// return foo;
Expand Down Expand Up @@ -106,6 +106,10 @@ fn object_member(p: &mut Parser) -> Option<CompletedMarker> {
// test object_expr_method
// let b = {
// foo() {},
// foo() {},
// "bar"(a, b, c) {},
// ["foo" + "bar"](a) {},
// 5(...rest) {}
ematipico marked this conversation as resolved.
Show resolved Hide resolved
// }
if p.at(T!['(']) || p.at(T![<]) {
method_object_member_body(p).ok()?;
Expand Down Expand Up @@ -212,7 +216,7 @@ pub fn object_prop_name(p: &mut Parser, binding: bool) -> Option<CompletedMarker
}
}

// test object_prop_name
// test object_member_name
// let a = {"foo": foo, [6 + 6]: foo, bar: foo, 7: foo}
/// Parses a `JsAnyObjectMemberName` and returns its completion marker
fn object_member_name(p: &mut Parser) -> Option<CompletedMarker> {
Expand All @@ -233,7 +237,6 @@ pub(crate) fn computed_member_name(p: &mut Parser) -> CompletedMarker {

pub(super) fn literal_member_name(p: &mut Parser) -> Option<CompletedMarker> {
let m = p.start();

match p.cur() {
JS_STRING_LITERAL_TOKEN | JS_NUMBER_LITERAL_TOKEN | T![ident] => {
p.bump_any();
Expand Down
24 changes: 18 additions & 6 deletions crates/rslint_parser/src/syntax/pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,14 +70,16 @@ pub fn pattern(p: &mut Parser, parameters: bool, assignment: bool) -> Option<Com
m.complete(p, SINGLE_PATTERN)
}
_ => {
let mut unknown_node_kind = JS_UNKNOWN_BINDING;
let err = p
.err_builder("Expected an identifier or pattern, but found none")
.primary(p.cur_tok().range, "");
let mut ts = token_set![T![ident], T![yield], T![await], T!['['],];
if p.state.allow_object_expr {
unknown_node_kind = JS_UNKNOWN_PATTERN;
ematipico marked this conversation as resolved.
Show resolved Hide resolved
ts = ts.union(token_set![T!['{']]);
}
ParseRecoverer::with_error(ts, ERROR, err).recover(p);
ParseRecoverer::with_error(ts, unknown_node_kind, err).recover(p);
return None;
}
})
Expand All @@ -100,19 +102,21 @@ pub fn opt_binding_identifier(p: &mut Parser) -> Option<CompletedMarker> {
// }
// let eval = 5;
pub fn binding_identifier(p: &mut Parser) -> Option<CompletedMarker> {
let mut kind_to_change = NAME;
if p.at(T![yield]) && p.state.in_generator {
let err = p
.err_builder("Illegal use of `yield` as an identifier in generator function")
.primary(p.cur_tok().range, "");

kind_to_change = JS_UNKNOWN_BINDING;
p.error(err);
}

if p.at(T![await]) && p.state.in_async {
let err = p
.err_builder("Illegal use of `await` as an identifier in an async context")
.primary(p.cur_tok().range, "");

kind_to_change = JS_UNKNOWN_BINDING;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There were some more unknown bindings ;)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @MichaReiser , I make this change later in another PR

p.error(err);
}

Expand All @@ -125,12 +129,12 @@ pub fn binding_identifier(p: &mut Parser) -> Option<CompletedMarker> {
p.cur_src()
))
.primary(p.cur_tok().range, "");

kind_to_change = JS_UNKNOWN_BINDING;
p.error(err);
}

let mut m = identifier_reference(p)?;
m.change_kind(p, NAME);
m.change_kind(p, kind_to_change);
Some(m)
}

Expand All @@ -152,6 +156,8 @@ pub fn binding_element(
left
}

// test_err
// let [ default: , hey , ] = []
pub fn array_binding_pattern(
p: &mut Parser,
parameters: bool,
Expand All @@ -175,9 +181,10 @@ pub fn array_binding_pattern(
m.complete(p, REST_PATTERN);
break;
} else if binding_element(p, parameters, assignment).is_none() {
// TODO: find a away to land
ematipico marked this conversation as resolved.
Show resolved Hide resolved
ParseRecoverer::new(
token_set![T![await], T![ident], T![yield], T![:], T![=], T![']']],
ERROR,
JS_UNKNOWN_PATTERN,
)
.recover(p);
}
Expand All @@ -192,6 +199,11 @@ pub fn array_binding_pattern(
m.complete(p, ARRAY_PATTERN)
}

// test_err object_binding_pattern
// let { 5 } } = { eval: "foo" };
// let { eval } = { eval: "foo" };
// let { 5, 6 } = { eval: "foo" };
// let { default: , bar } = {};
pub fn object_binding_pattern(p: &mut Parser, parameters: bool) -> CompletedMarker {
let m = p.start();
p.expect(T!['{']);
Expand Down Expand Up @@ -247,7 +259,7 @@ fn object_binding_prop(p: &mut Parser, parameters: bool) -> Option<CompletedMark
m.abandon(p);
ParseRecoverer::new(
token_set![T![await], T![ident], T![yield], T![:], T![=], T!['}']],
ERROR,
JS_UNKNOWN_BINDING,
)
.recover(p);
return None;
Expand Down
23 changes: 22 additions & 1 deletion crates/rslint_parser/src/syntax/stmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,13 @@ pub fn stmt(p: &mut Parser, recovery_set: impl Into<Option<TokenSet>>) -> Option
Some(res)
}

// test_err double_label
// label1: {
// label2: {
// label1: {}
// }
// }

fn expr_stmt(p: &mut Parser) -> Option<CompletedMarker> {
let start = p.cur_tok().range.start;
// this is *technically* wrong because it would be an expr stmt in js but for our purposes
Expand Down Expand Up @@ -222,7 +229,7 @@ fn expr_stmt(p: &mut Parser) -> Option<CompletedMarker> {
&format!("`{}` is first used as a label here", text),
)
.primary(
p.cur_tok().range,
text_range,
&format!("a second use of `{}` here is not allowed", text),
);

Expand All @@ -246,6 +253,14 @@ fn expr_stmt(p: &mut Parser) -> Option<CompletedMarker> {
/// A debugger statement such as `debugger;`
// test debugger_stmt
// debugger;

// test_err debugger_stmt
// function foo() {
// debugger {
// var something = "lorem";
// }
// }

pub fn debugger_stmt(p: &mut Parser) -> CompletedMarker {
let m = p.start();
let range = p.cur_tok().range;
Expand Down Expand Up @@ -595,6 +610,7 @@ pub fn if_stmt(p: &mut Parser) -> CompletedMarker {
// if (true) else
// if else {}
// if () {} else {}
// if (true)}}}} {}
let m = p.start();
p.expect(T![if]);

Expand Down Expand Up @@ -671,6 +687,7 @@ pub fn while_stmt(p: &mut Parser) -> CompletedMarker {
// let bar, foo;
// const a = 5;
// const { foo: [bar], baz } = {};
// let foo = "lorem", bar = "ipsum", third = "value", fourth = 6;
pub fn variable_declaration_statement(p: &mut Parser) -> CompletedMarker {
// test_err var_decl_err
// var a =;
Expand Down Expand Up @@ -940,6 +957,7 @@ pub fn for_stmt(p: &mut Parser) -> CompletedMarker {
// test_err for_stmt_err
// for ;; {}
// for let i = 5; i < 10; i++ {}
// for let i = 5; i < 10; ++i {}
let m = p.start();
p.expect(T![for]);
// FIXME: This should emit an error for non-for-of
Expand Down Expand Up @@ -1126,8 +1144,11 @@ fn catch_declaration(p: &mut Parser) {
/// }
/// ```
// test try_stmt
// try {} catch {}
// try {} catch (e) {}
// try {} catch {} finally {}
// try {} catch (e) {} finally {}
// try {} finally {}
pub fn try_stmt(p: &mut Parser) -> CompletedMarker {
// TODO: recover from `try catch` and `try finally`. The issue is block_items
// will cause infinite recursion because parsing a stmt would not consume the catch token
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
1: LIST@18..27
0: JS_VARIABLE_DECLARATOR@18..27
0: SINGLE_PATTERN@18..24
0: NAME@18..24
0: JS_UNKNOWN_BINDING@18..24
0: IDENT@18..24 "await" [] [Whitespace(" ")]
1: JS_EQUAL_VALUE_CLAUSE@24..27
0: EQ@24..26 "=" [] [Whitespace(" ")]
Expand Down Expand Up @@ -48,7 +48,7 @@
1: LIST@56..65
0: JS_VARIABLE_DECLARATOR@56..65
0: SINGLE_PATTERN@56..62
0: NAME@56..62
0: JS_UNKNOWN_BINDING@56..62
0: IDENT@56..62 "yield" [] [Whitespace(" ")]
1: JS_EQUAL_VALUE_CLAUSE@62..65
0: EQ@62..64 "=" [] [Whitespace(" ")]
Expand All @@ -62,7 +62,7 @@
1: LIST@73..81
0: JS_VARIABLE_DECLARATOR@73..81
0: SINGLE_PATTERN@73..78
0: NAME@73..78
0: JS_UNKNOWN_BINDING@73..78
0: IDENT@73..78 "eval" [] [Whitespace(" ")]
1: JS_EQUAL_VALUE_CLAUSE@78..81
0: EQ@78..80 "=" [] [Whitespace(" ")]
Expand Down
5 changes: 5 additions & 0 deletions crates/rslint_parser/test_data/inline/err/debugger_stmt.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
function foo() {
debugger {
var something = "lorem";
}
}
53 changes: 53 additions & 0 deletions crates/rslint_parser/test_data/inline/err/debugger_stmt.rast
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
0: JS_ROOT@0..61
0: (empty)
1: LIST@0..0
2: LIST@0..60
0: JS_FUNCTION_DECLARATION@0..60
0: FUNCTION_KW@0..9 "function" [] [Whitespace(" ")]
1: JS_IDENTIFIER_BINDING@9..12
0: IDENT@9..12 "foo" [] []
2: JS_PARAMETER_LIST@12..15
0: L_PAREN@12..13 "(" [] []
1: LIST@13..13
2: R_PAREN@13..15 ")" [] [Whitespace(" ")]
3: JS_FUNCTION_BODY@15..60
0: L_CURLY@15..16 "{" [] []
1: LIST@16..16
2: LIST@16..58
0: JS_DEBUGGER_STATEMENT@16..27
0: DEBUGGER_KW@16..27 "debugger" [Whitespace("\n\t")] [Whitespace(" ")]
1: (empty)
1: JS_BLOCK_STATEMENT@27..58
0: L_CURLY@27..28 "{" [] []
1: LIST@28..55
0: JS_VARIABLE_DECLARATION_STATEMENT@28..55
0: JS_VARIABLE_DECLARATION@28..54
0: VAR_KW@28..35 "var" [Whitespace("\n\t\t")] [Whitespace(" ")]
1: LIST@35..54
0: JS_VARIABLE_DECLARATOR@35..54
0: SINGLE_PATTERN@35..45
0: NAME@35..45
0: IDENT@35..45 "something" [] [Whitespace(" ")]
1: JS_EQUAL_VALUE_CLAUSE@45..54
0: EQ@45..47 "=" [] [Whitespace(" ")]
1: JS_STRING_LITERAL@47..54
0: JS_STRING_LITERAL_TOKEN@47..54 "\"lorem\"" [] []
1: SEMICOLON@54..55 ";" [] []
2: R_CURLY@55..58 "}" [Whitespace("\n\t")] []
3: R_CURLY@58..60 "}" [Whitespace("\n")] []
3: EOF@60..61 "" [Whitespace("\n")] []
--
error[SyntaxError]: Expected a semicolon or an implicit semicolon after a statement, but found none
┌─ debugger_stmt.js:2:11
2 │ debugger {
│ -------- ^ An explicit or implicit semicolon is expected here...
│ │
│ ...Which is required to end this statement

--
function foo() {
debugger {
var something = "lorem";
}
}
5 changes: 5 additions & 0 deletions crates/rslint_parser/test_data/inline/err/double_label.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
label1: {
label2: {
label1: {}
}
}
Loading