Skip to content

Commit

Permalink
feat(rome_js_parser): EcmaScript decorators rome rome#4252
Browse files Browse the repository at this point in the history
  • Loading branch information
denbezrukov committed Apr 13, 2023
1 parent 785c2b3 commit 5844c28
Show file tree
Hide file tree
Showing 17 changed files with 1,619 additions and 39 deletions.
2 changes: 1 addition & 1 deletion crates/rome_js_factory/src/generated/node_factory.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion crates/rome_js_factory/src/generated/syntax_factory.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 25 additions & 0 deletions crates/rome_js_formatter/src/generated.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12159,6 +12159,31 @@ impl IntoFormat<JsFormatContext> for rome_js_syntax::AnyJsCallArgument {
)
}
}
impl AsFormat<JsFormatContext> for rome_js_syntax::AnyJsDecorator {
type Format<'a> = FormatRefWithRule<
'a,
rome_js_syntax::AnyJsDecorator,
crate::js::any::decorator::FormatAnyJsDecorator,
>;
fn format(&self) -> Self::Format<'_> {
FormatRefWithRule::new(
self,
crate::js::any::decorator::FormatAnyJsDecorator::default(),
)
}
}
impl IntoFormat<JsFormatContext> for rome_js_syntax::AnyJsDecorator {
type Format = FormatOwnedWithRule<
rome_js_syntax::AnyJsDecorator,
crate::js::any::decorator::FormatAnyJsDecorator,
>;
fn into_format(self) -> Self::Format {
FormatOwnedWithRule::new(
self,
crate::js::any::decorator::FormatAnyJsDecorator::default(),
)
}
}
impl AsFormat<JsFormatContext> for rome_js_syntax::AnyTsName {
type Format<'a> =
FormatRefWithRule<'a, rome_js_syntax::AnyTsName, crate::ts::any::name::FormatAnyTsName>;
Expand Down
18 changes: 18 additions & 0 deletions crates/rome_js_formatter/src/js/any/decorator.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//! This is a generated file. Don't modify it by hand! Run 'cargo codegen formatter' to re-generate the file.

use crate::prelude::*;
use rome_js_syntax::AnyJsDecorator;
#[derive(Debug, Clone, Default)]
pub(crate) struct FormatAnyJsDecorator;
impl FormatRule<AnyJsDecorator> for FormatAnyJsDecorator {
type Context = JsFormatContext;
fn fmt(&self, node: &AnyJsDecorator, f: &mut JsFormatter) -> FormatResult<()> {
match node {
AnyJsDecorator::JsParenthesizedExpression(node) => node.format().fmt(f),
AnyJsDecorator::JsCallExpression(node) => node.format().fmt(f),
AnyJsDecorator::JsStaticMemberExpression(node) => node.format().fmt(f),
AnyJsDecorator::JsIdentifierExpression(node) => node.format().fmt(f),
AnyJsDecorator::JsBogusExpression(node) => node.format().fmt(f),
}
}
}
1 change: 1 addition & 0 deletions crates/rome_js_formatter/src/js/any/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ pub(crate) mod class_member_name;
pub(crate) mod constructor_parameter;
pub(crate) mod declaration;
pub(crate) mod declaration_clause;
pub(crate) mod decorator;
pub(crate) mod export_clause;
pub(crate) mod export_default_declaration;
pub(crate) mod export_named_specifier;
Expand Down
6 changes: 3 additions & 3 deletions crates/rome_js_parser/src/syntax/auxiliary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ pub(crate) fn parse_declaration_clause(p: &mut JsParser, stmt_start_pos: TextSiz
match p.cur() {
T![function] => parse_function_declaration(p, StatementContext::StatementList),
T![@] => {
let decorators_list = parse_decorators(p);
let decorator_list = parse_decorators(p);

match p.cur() {
T![class] | T![abstract] if !p.state().in_ambient_context() => {
Expand All @@ -92,15 +92,15 @@ pub(crate) fn parse_declaration_clause(p: &mut JsParser, stmt_start_pos: TextSiz
// export @first @second abstract class Foo {
// constructor() {}
// }
parse_class_declaration(p, decorators_list, StatementContext::StatementList)
parse_class_declaration(p, decorator_list, StatementContext::StatementList)
}
_ => {
// test_err decorator_export_class_clause
// @decorator
// export let a;
// @decorator1 @decorator2
// export function Foo() { }
decorators_list
decorator_list
.add_diagnostic_if_present(p, |p, range| {
p.err_builder("Decorators are not valid here.", range)
})
Expand Down
66 changes: 49 additions & 17 deletions crates/rome_js_parser/src/syntax/class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,16 @@ use crate::state::{
};
use crate::syntax::binding::parse_binding;
use crate::syntax::expr::{
parse_assignment_expression_or_higher, parse_lhs_expr, parse_private_name, ExpressionContext,
parse_assignment_expression_or_higher, parse_expression, parse_lhs_expr, parse_private_name,
ExpressionContext,
};
use crate::syntax::function::{
parse_any_parameter, parse_formal_parameter, parse_function_body, parse_parameter_list,
parse_parameters_list, parse_ts_type_annotation_or_error, ParameterContext,
};
use crate::syntax::js_parse_error;
use crate::syntax::js_parse_error::{
expected_binding, expected_expression, modifier_already_seen,
expected_binding, expected_expression, invalid_decorator_error, modifier_already_seen,
modifier_cannot_be_used_with_modifier, modifier_must_precede_modifier,
};
use crate::syntax::object::{
Expand Down Expand Up @@ -71,13 +72,13 @@ pub(crate) fn is_at_ts_abstract_class_declaration(
/// Parses a class expression, e.g. let a = class {}
pub(super) fn parse_class_expression(
p: &mut JsParser,
decorators_list: ParsedSyntax,
decorator_list: ParsedSyntax,
) -> ParsedSyntax {
if !p.at(T![class]) {
return Absent;
}

Present(parse_class(p, ClassKind::Expression, decorators_list))
Present(parse_class(p, ClassKind::Expression, decorator_list))
}

// test class_declaration
Expand Down Expand Up @@ -145,14 +146,14 @@ pub(super) fn parse_class_expression(
/// * It uses an illegal identifier name
pub(super) fn parse_class_declaration(
p: &mut JsParser,
decorators_list: ParsedSyntax,
decorator_list: ParsedSyntax,
context: StatementContext,
) -> ParsedSyntax {
if !matches!(p.cur(), T![abstract] | T![class]) {
return Absent;
}

let mut class = parse_class(p, ClassKind::Declaration, decorators_list);
let mut class = parse_class(p, ClassKind::Declaration, decorator_list);

if !class.kind(p).is_bogus() && context.is_single_statement() {
// test_err class_in_single_statement_context
Expand All @@ -177,13 +178,13 @@ pub(super) fn parse_class_declaration(
// export default abstract class {}
pub(super) fn parse_class_export_default_declaration(
p: &mut JsParser,
decorators_list: ParsedSyntax,
decorator_list: ParsedSyntax,
) -> ParsedSyntax {
if !matches!(p.cur(), T![abstract] | T![class]) {
return Absent;
}

Present(parse_class(p, ClassKind::ExportDefault, decorators_list))
Present(parse_class(p, ClassKind::ExportDefault, decorator_list))
}

#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
Expand Down Expand Up @@ -215,17 +216,13 @@ impl From<ClassKind> for JsSyntaxKind {
// test ts ts_class_named_abstract_is_valid_in_ts
// class abstract {}
#[inline]
fn parse_class(
p: &mut JsParser,
kind: ClassKind,
decorators_list: ParsedSyntax,
) -> CompletedMarker {
let decorators_list = decorators_list.or_else(|| {
fn parse_class(p: &mut JsParser, kind: ClassKind, decorator_list: ParsedSyntax) -> CompletedMarker {
let decorator_list = decorator_list.or_else(|| {
let m = p.start();
Present(m.complete(p, JS_DECORATOR_LIST))
});

let m = decorators_list.precede(p);
let m = decorator_list.precede(p);
let is_abstract = p.eat(T![abstract]);

let class_token_range = p.cur_range();
Expand Down Expand Up @@ -2421,6 +2418,28 @@ impl ClassMemberModifiers {
// function foo() {
// @dec class MyClass {}
// }

// test_err ts decorator
// @'dsads' class MyClass {}
// @1 class MyClass {}
// @++1 class MyClass {}
// @[] in 1 class MyClass {}
// @[] class MyClass {}
// @() => {} class MyClass {}
// @1 == 2 ? true : false class MyClass {}
// @await fn class MyClass {}
// @function(){} class MyClass {}
// @obj instanceof Object class MyClass {}
// @1 === 2 class MyClass {}
// @new Object() class MyClass {}
// @{} class MyClass {}
// @a++ class MyClass {}
// @a,b class MyClass {}
// @`${d}foo` class MyClass {}
// @obj as MyType class MyClass {}
// @<MyType>obj class MyClass {}
// @obj satisfies MyType class MyClass {}
// @obj! class MyClass {}
pub(crate) fn parse_decorators(p: &mut JsParser) -> ParsedSyntax {
if !p.at(T![@]) {
return Absent;
Expand All @@ -2442,8 +2461,21 @@ fn parse_decorator(p: &mut JsParser) -> ParsedSyntax {

let m = p.start();
p.bump(T![@]);
parse_lhs_expr(p, ExpressionContext::default().and_in_ts_decorator(true))
.or_add_diagnostic(p, expected_expression);
if let Some(mut complete_marker) =
parse_expression(p, ExpressionContext::default().and_in_ts_decorator(true))
.or_add_diagnostic(p, expected_expression)
{
if !matches!(
complete_marker.kind(p),
JS_PARENTHESIZED_EXPRESSION
| JS_CALL_EXPRESSION
| JS_STATIC_MEMBER_EXPRESSION
| JS_IDENTIFIER_EXPRESSION
) {
p.error(invalid_decorator_error(p, complete_marker.range(p)));
complete_marker.change_to_bogus(p);
}
}

Present(m.complete(p, JS_DECORATOR))
}
Expand Down
6 changes: 3 additions & 3 deletions crates/rome_js_parser/src/syntax/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1284,7 +1284,7 @@ fn parse_primary_expression(p: &mut JsParser, context: ExpressionContext) -> Par
m.complete(p, JS_THIS_EXPRESSION)
}
T![@] => {
let decorators_list = parse_decorators(p);
let decorator_list = parse_decorators(p);

return match p.cur() {
T![class] => {
Expand All @@ -1293,14 +1293,14 @@ fn parse_primary_expression(p: &mut JsParser, context: ExpressionContext) -> Par
// let b = @first @second class foo {
// constructor() {}
// }
parse_class_expression(p, decorators_list)
parse_class_expression(p, decorator_list)
}
_ => {
// test_err decorator_expression_class
// let a = @decorator () => {};
// let b = @first @second function foo {}
// let a = @decorator ( () => {} )
decorators_list
decorator_list
.add_diagnostic_if_present(p, |p, range| {
p.err_builder("Decorators are not valid here.", range)
})
Expand Down
7 changes: 7 additions & 0 deletions crates/rome_js_parser/src/syntax/js_parse_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -254,3 +254,10 @@ pub(crate) fn modifier_must_precede_modifier(
.detail(range, "move this modifier")
.detail(to_precede_modifier_range, "before this modifier")
}

pub(crate) fn invalid_decorator_error(p: &JsParser, range: TextRange) -> ParseDiagnostic {
p.err_builder(
format!("Invalid decorator `{}`", p.text(range.as_range()),),
range,
)
}
8 changes: 4 additions & 4 deletions crates/rome_js_parser/src/syntax/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ fn parse_module_item(p: &mut JsParser) -> ParsedSyntax {
}
T![export] => parse_export(p),
T![@] => {
let decorators_list = parse_decorators(p);
let decorator_list = parse_decorators(p);

match p.cur() {
T![class] => {
Expand All @@ -127,7 +127,7 @@ fn parse_module_item(p: &mut JsParser) -> ParsedSyntax {
// class Foo { }
// @first.field @second @(() => decorator)()
// class Bar {}
parse_class_declaration(p, decorators_list, StatementContext::StatementList)
parse_class_declaration(p, decorator_list, StatementContext::StatementList)
}
T![abstract] if is_at_ts_abstract_class_declaration(p, LineBreak::DoCheck) => {
// test ts decorator_abstract_class_declaration_top_level
Expand All @@ -139,7 +139,7 @@ fn parse_module_item(p: &mut JsParser) -> ParsedSyntax {
|p| {
parse_class_declaration(
p,
decorators_list,
decorator_list,
StatementContext::StatementList,
)
},
Expand All @@ -154,7 +154,7 @@ fn parse_module_item(p: &mut JsParser) -> ParsedSyntax {
// let a;
// @decorator1 @decorator2
// function Foo() { }
decorators_list
decorator_list
.add_diagnostic_if_present(p, |p, range| {
p.err_builder("Decorators are not valid here.", range)
})
Expand Down
8 changes: 4 additions & 4 deletions crates/rome_js_parser/src/syntax/stmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ pub(crate) fn parse_statement(p: &mut JsParser, context: StatementContext) -> Pa
// class and abstract class
T![class] => parse_class_declaration(p, Absent, context),
T![@] => {
let decorators_list = parse_decorators(p);
let decorator_list = parse_decorators(p);

match p.cur() {
T![class] => {
Expand All @@ -234,7 +234,7 @@ pub(crate) fn parse_statement(p: &mut JsParser, context: StatementContext) -> Pa
// @first.field @second @(() => decorator)()
// class Bar {}
// }
parse_class_declaration(p, decorators_list, context)
parse_class_declaration(p, decorator_list, context)
}
T![abstract] if is_at_ts_abstract_class_declaration(p, LineBreak::DoCheck) => {
// test ts decorator_abstract_class_declaration
Expand All @@ -245,7 +245,7 @@ pub(crate) fn parse_statement(p: &mut JsParser, context: StatementContext) -> Pa
// }
TypeScript.parse_exclusive_syntax(
p,
|p| parse_class_declaration(p, decorators_list, context),
|p| parse_class_declaration(p, decorator_list, context),
|p, abstract_class| {
ts_only_syntax_error(p, "abstract classes", abstract_class.range(p))
},
Expand All @@ -259,7 +259,7 @@ pub(crate) fn parse_statement(p: &mut JsParser, context: StatementContext) -> Pa
// @decorator @decorator2
// function Foo() { }
// }
decorators_list
decorator_list
.add_diagnostic_if_present(p, |p, range| {
p.err_builder("Decorators are not valid here.", range)
})
Expand Down
3 changes: 2 additions & 1 deletion crates/rome_js_parser/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -389,9 +389,10 @@ fn diagnostics_print_correctly() {
}
}

#[ignore]
#[test]
pub fn quick_test() {
let code = r"@(<ClassDecorator>bind)class Decorated {};
let code = r"@1 + 2 class MyClass {};
";
let root = parse(code, SourceType::ts());
let syntax = root.syntax();
Expand Down
Loading

0 comments on commit 5844c28

Please sign in to comment.