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

Commit

Permalink
feat(rome_js_parser): EcmaScript @decorators #4252
Browse files Browse the repository at this point in the history
  • Loading branch information
denbezrukov committed Apr 30, 2023
1 parent d528ffc commit ea68d64
Show file tree
Hide file tree
Showing 8 changed files with 382 additions and 110 deletions.
49 changes: 11 additions & 38 deletions crates/rome_js_parser/src/syntax/class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1646,7 +1646,6 @@ pub(crate) fn is_nth_at_modifier(p: &mut JsParser, n: usize, constructor_paramet
| T![accessor]
| T![readonly]
| T![abstract]
| T![@]
) {
return false;
}
Expand All @@ -1658,12 +1657,8 @@ pub(crate) fn is_nth_at_modifier(p: &mut JsParser, n: usize, constructor_paramet
let followed_by_any_member = is_at_class_member_name(p, n + 1);
let followed_by_class_member = !constructor_parameter && p.nth_at(n + 1, T![*]);
let followed_by_parameter = constructor_parameter && matches!(p.nth(n + 1), T!['{'] | T!['[']);
let followed_by_decorator = p.nth_at(n + 1, T![@]);

followed_by_any_member
|| followed_by_class_member
|| followed_by_parameter
|| followed_by_decorator
followed_by_any_member || followed_by_class_member || followed_by_parameter
}

// test static_generator_constructor_method
Expand Down Expand Up @@ -1711,7 +1706,10 @@ fn parse_class_member_modifiers(
// test_err class_member_modifier
// class A { abstract foo; }
fn parse_modifier(p: &mut JsParser, constructor_parameter: bool) -> Option<ClassMemberModifier> {
if !is_nth_at_modifier(p, 0, constructor_parameter) {
// decorator modifiers can't be valid member names.
let is_at_decorator_modifier = p.cur() == T![@] || p.nth_at(1, T![@]);

if !is_nth_at_modifier(p, 0, constructor_parameter) && !is_at_decorator_modifier {
// all modifiers can also be valid member names. That's why we shouldn't parse a modifier
// if it isn't followed by a valid member name or another modifier
return None;
Expand Down Expand Up @@ -2578,6 +2576,12 @@ impl ClassMemberModifiers {
// @dec class MyClass {}
// }

// test ts ts_decorator_call_expression_with_arrow
// export class Foo {
// @Decorator((val) => val)
// badField!: number
// }

// test_err ts decorator
// @'dsads' class MyClass {}
// @1 class MyClass {}
Expand Down Expand Up @@ -2638,34 +2642,3 @@ fn parse_decorator(p: &mut JsParser) -> ParsedSyntax {

Present(m.complete(p, JS_DECORATOR))
}

/// Skips over any TypeScript decorator syntax.
pub(crate) fn skip_ts_decorators(p: &mut JsParser) {
if !p.at(T![@]) {
return;
}

p.parse_as_skipped_trivia_tokens(|p| {
while p.at(T![@]) {
parse_decorator_bogus(p).ok();
}
});
}

fn parse_decorator_bogus(p: &mut JsParser) -> ParsedSyntax {
if p.at(T![@]) {
let m = p.start();
p.bump(T![@]);
// test ts ts_decorator_call_expression_with_arrow
// export class Foo {
// @Decorator((val) => val)
// badField!: number
// }
parse_lhs_expr(p, ExpressionContext::default().and_in_decorator(true))
.or_add_diagnostic(p, expected_expression);

Present(m.complete(p, JS_BOGUS))
} else {
Absent
}
}
16 changes: 13 additions & 3 deletions crates/rome_js_parser/src/syntax/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@ use crate::state::{EnterFunction, EnterParameters, SignatureFlags};
use crate::syntax::binding::{
is_at_identifier_binding, is_nth_at_identifier_binding, parse_binding, parse_binding_pattern,
};
use crate::syntax::class::{parse_initializer_clause, skip_ts_decorators};
use crate::syntax::class::{parse_decorators, parse_initializer_clause};
use crate::syntax::expr::{
is_nth_at_identifier, parse_assignment_expression_or_higher, ExpressionContext,
};
use crate::syntax::js_parse_error;
use crate::syntax::js_parse_error::{expected_binding, expected_parameter, expected_parameters};
use crate::syntax::js_parse_error::{
decorators_not_allowed, expected_binding, expected_parameter, expected_parameters,
};
use crate::syntax::stmt::{is_semi, parse_block_impl, semi, StatementContext};
use crate::syntax::typescript::ts_parse_error::ts_only_syntax_error;
use crate::syntax::typescript::{
Expand Down Expand Up @@ -1024,7 +1026,15 @@ pub(crate) fn parse_formal_parameter(
parameter_context: ParameterContext,
expression_context: ExpressionContext,
) -> ParsedSyntax {
skip_ts_decorators(p);
// test_err ts ts_formal_parameter_decorator
// function a(@dec x) {}
// class Foo {
// constructor(@dec x) {}
// method(@dec x) {}
// }
let decorator_list = parse_decorators(p);
decorator_list.add_diagnostic_if_present(p, decorators_not_allowed);

parse_binding_pattern(p, expression_context).map(|binding| {
let binding_kind = binding.kind(p);
let binding_range = binding.range(p);
Expand Down
2 changes: 1 addition & 1 deletion crates/rome_js_parser/src/syntax/js_parse_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ pub(crate) fn decorators_not_allowed(p: &JsParser, range: TextRange) -> ParseDia

pub(crate) fn decorator_must_precede_modifier(p: &JsParser, range: TextRange) -> ParseDiagnostic {
p.err_builder(
"Decorators must precede the name and all keywords of property declarations",
"Decorators must precede the name and all keywords of property declarations.",
range,
)
}
6 changes: 2 additions & 4 deletions crates/rome_js_parser/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -393,10 +393,8 @@ fn diagnostics_print_correctly() {
#[test]
pub fn quick_test() {
let code = r#"
export default class MyComponent {
@task
*foo() {
}
class A {
@(dec()) private method(){}
}
"#;
Expand Down
107 changes: 53 additions & 54 deletions crates/rome_js_parser/test_data/inline/err/decorator_parameters.rast
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,18 @@ JsModule {
implements_clause: missing (optional),
l_curly_token: [email protected] "{" [] [],
members: JsClassMemberList [
JsConstructorClassMember {
modifiers: JsConstructorModifierList [],
name: JsLiteralMemberName {
value: [email protected] "constructor" [Newline("\n"), Whitespace(" ")] [],
},
parameters: JsConstructorParameters {
l_paren_token: [email protected] "(" [] [],
parameters: JsConstructorParameterList [
JsBogusParameter {
items: [
JsBogus {
items: [
JsBogusMember {
items: [
JsConstructorModifierList [],
JsLiteralMemberName {
value: [email protected] "constructor" [Newline("\n"), Whitespace(" ")] [],
},
JsBogus {
items: [
[email protected] "(" [] [],
JsBogus {
items: [
JsDecoratorList [
JsDecorator {
at_token: [email protected] "@" [] [],
expression: JsIdentifierExpression {
Expand All @@ -35,31 +35,31 @@ JsModule {
},
},
],
},
JsFormalParameter {
binding: JsIdentifierBinding {
name_token: [email protected] "a" [] [],
},
question_mark_token: missing (optional),
type_annotation: TsTypeAnnotation {
colon_token: [email protected] ":" [] [Whitespace(" ")],
ty: TsStringType {
string_token: [email protected] "string" [] [],
JsFormalParameter {
binding: JsIdentifierBinding {
name_token: [email protected] "a" [] [],
},
question_mark_token: missing (optional),
type_annotation: TsTypeAnnotation {
colon_token: [email protected] ":" [] [Whitespace(" ")],
ty: TsStringType {
string_token: [email protected] "string" [] [],
},
},
initializer: missing (optional),
},
initializer: missing (optional),
},
],
},
],
r_paren_token: [email protected] ")" [] [Whitespace(" ")],
},
body: JsFunctionBody {
l_curly_token: [email protected] "{" [] [],
directives: JsDirectiveList [],
statements: JsStatementList [],
r_curly_token: [email protected] "}" [] [],
},
],
},
[email protected] ")" [] [Whitespace(" ")],
],
},
JsFunctionBody {
l_curly_token: [email protected] "{" [] [],
directives: JsDirectiveList [],
statements: JsStatementList [],
r_curly_token: [email protected] "}" [] [],
},
],
},
],
r_curly_token: [email protected] "}" [Newline("\n")] [],
Expand All @@ -83,29 +83,28 @@ JsModule {
6: (empty)
7: [email protected] "{" [] []
8: [email protected]
0: JS_CONSTRUCTOR_CLASS_MEMBER@11..45
0: JS_BOGUS_MEMBER@11..45
0: [email protected]
1: [email protected]
0: [email protected] "constructor" [Newline("\n"), Whitespace(" ")] []
2: JS_CONSTRUCTOR_PARAMETERS@26..43
2: JS_BOGUS@26..43
0: [email protected] "(" [] []
1: [email protected]
0: [email protected]
0: [email protected]
0: [email protected]
0: [email protected] "@" [] []
1: [email protected]
0: [email protected]
0: [email protected] "dec" [] [Whitespace(" ")]
1: [email protected]
0: [email protected]
0: [email protected] "a" [] []
1: (empty)
2: [email protected]
0: [email protected] ":" [] [Whitespace(" ")]
1: [email protected]
0: [email protected] "string" [] []
3: (empty)
1: [email protected]
0: [email protected]
0: [email protected]
0: [email protected] "@" [] []
1: [email protected]
0: [email protected]
0: [email protected] "dec" [] [Whitespace(" ")]
1: [email protected]
0: [email protected]
0: [email protected] "a" [] []
1: (empty)
2: [email protected]
0: [email protected] ":" [] [Whitespace(" ")]
1: [email protected]
0: [email protected] "string" [] []
3: (empty)
2: [email protected] ")" [] [Whitespace(" ")]
3: [email protected]
0: [email protected] "{" [] []
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -575,7 +575,7 @@ JsModule {
--
decorator_precede_class_member.ts:2:10 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

× Decorators must precede the name and all keywords of property declarations
× Decorators must precede the name and all keywords of property declarations.

1 │ class Bar {
> 2 │ public @dec get foo() {}
Expand All @@ -586,7 +586,7 @@ decorator_precede_class_member.ts:2:10 parse ━━━━━━━━━━━
--
decorator_precede_class_member.ts:3:10 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

× Decorators must precede the name and all keywords of property declarations
× Decorators must precede the name and all keywords of property declarations.

1 │ class Bar {
2 │ public @dec get foo() {}
Expand All @@ -610,7 +610,7 @@ decorator_precede_class_member.ts:4:3 parse ━━━━━━━━━━━━
--
decorator_precede_class_member.ts:4:12 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

× Decorators must precede the name and all keywords of property declarations
× Decorators must precede the name and all keywords of property declarations.

2 │ public @dec get foo() {}
3 │ static @dec foo: string;
Expand All @@ -622,7 +622,7 @@ decorator_precede_class_member.ts:4:12 parse ━━━━━━━━━━━
--
decorator_precede_class_member.ts:5:11 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

× Decorators must precede the name and all keywords of property declarations
× Decorators must precede the name and all keywords of property declarations.

3 │ static @dec foo: string;
4 │ readonly @dec test() {}
Expand All @@ -634,7 +634,7 @@ decorator_precede_class_member.ts:5:11 parse ━━━━━━━━━━━
--
decorator_precede_class_member.ts:6:13 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

× Decorators must precede the name and all keywords of property declarations
× Decorators must precede the name and all keywords of property declarations.

4 │ readonly @dec test() {}
5 │ private @dec test() {}
Expand All @@ -646,7 +646,7 @@ decorator_precede_class_member.ts:6:13 parse ━━━━━━━━━━━
--
decorator_precede_class_member.ts:9:10 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

× Decorators must precede the name and all keywords of property declarations
× Decorators must precede the name and all keywords of property declarations.

7 │ }
8 │ class Qux extends Bar {
Expand All @@ -658,7 +658,7 @@ decorator_precede_class_member.ts:9:10 parse ━━━━━━━━━━━
--
decorator_precede_class_member.ts:10:10 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

× Decorators must precede the name and all keywords of property declarations
× Decorators must precede the name and all keywords of property declarations.

8 │ class Qux extends Bar {
9 │ public @dec get foo() {}
Expand All @@ -682,7 +682,7 @@ decorator_precede_class_member.ts:11:3 parse ━━━━━━━━━━━
--
decorator_precede_class_member.ts:11:12 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

× Decorators must precede the name and all keywords of property declarations
× Decorators must precede the name and all keywords of property declarations.

9 │ public @dec get foo() {}
10 │ static @dec foo: string;
Expand All @@ -694,7 +694,7 @@ decorator_precede_class_member.ts:11:12 parse ━━━━━━━━━━━
--
decorator_precede_class_member.ts:12:11 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

× Decorators must precede the name and all keywords of property declarations
× Decorators must precede the name and all keywords of property declarations.

10 │ static @dec foo: string;
11 │ readonly @dec test() {}
Expand All @@ -718,7 +718,7 @@ decorator_precede_class_member.ts:13:3 parse ━━━━━━━━━━━
--
decorator_precede_class_member.ts:13:12 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

× Decorators must precede the name and all keywords of property declarations
× Decorators must precede the name and all keywords of property declarations.

11 │ readonly @dec test() {}
12 │ private @dec test() {}
Expand Down
Loading

0 comments on commit ea68d64

Please sign in to comment.