From 37ad6f3a15eaeb490b0a0fce0f49e2461cc0c7bc Mon Sep 17 00:00:00 2001 From: yangchenye Date: Thu, 16 Mar 2023 20:33:04 -0500 Subject: [PATCH] feat(parser): support decorators in class expression (#190) feat(parser): support decorator in class expression --- crates/oxc_ast/src/visit.rs | 7 +- crates/oxc_parser/src/js/expression.rs | 4 + tasks/coverage/babel | 2 +- tasks/coverage/babel.snap | 18 +-- tasks/coverage/test262.snap | 90 +------------ tasks/coverage/typescript | 2 +- tasks/coverage/typescript.snap | 170 +------------------------ 7 files changed, 22 insertions(+), 271 deletions(-) diff --git a/crates/oxc_ast/src/visit.rs b/crates/oxc_ast/src/visit.rs index 482236a99..d9875ead2 100644 --- a/crates/oxc_ast/src/visit.rs +++ b/crates/oxc_ast/src/visit.rs @@ -356,11 +356,14 @@ pub trait Visit<'a>: Sized { } fn visit_class(&mut self, class: &'a Class<'a>) { - let kind = AstKind::Class(class); - self.enter_node(kind); + // Class level decorators are transpiled as functions outside of the class taking the class + // itself as argument. They should be visited before class is entered. E.g., they inherit + // strict mode from the enclosing scope rather than from class. for decorator in &class.decorators { self.visit_decorator(decorator); } + let kind = AstKind::Class(class); + self.enter_node(kind); if let Some(id) = &class.id { self.visit_binding_identifier(id); } diff --git a/crates/oxc_parser/src/js/expression.rs b/crates/oxc_parser/src/js/expression.rs index d2b9100ff..2f49b0f4c 100644 --- a/crates/oxc_parser/src/js/expression.rs +++ b/crates/oxc_parser/src/js/expression.rs @@ -147,6 +147,10 @@ impl<'a> Parser<'a> { fn parse_primary_expression(&mut self) -> Result> { let span = self.start_span(); + if self.at(Kind::At) { + self.eat_decorators()?; + } + // AsyncFunctionExpression // AsyncGeneratorExpression if self.at_function_with_async() { diff --git a/tasks/coverage/babel b/tasks/coverage/babel index d38f415c9..6925ac555 160000 --- a/tasks/coverage/babel +++ b/tasks/coverage/babel @@ -1 +1 @@ -Subproject commit d38f415c9f7704a201cbfb1d73e6884738931bb2 +Subproject commit 6925ac555c62c67886b86e38c0c6a7dca60b3cb7 diff --git a/tasks/coverage/babel.snap b/tasks/coverage/babel.snap index 168ce5a8b..9f79a01eb 100644 --- a/tasks/coverage/babel.snap +++ b/tasks/coverage/babel.snap @@ -1,6 +1,6 @@ Babel Summary: -AST Parsed : 2050/2071 (98.99%) -Positive Passed: 2050/2071 (98.99%) +AST Parsed : 2052/2071 (99.08%) +Positive Passed: 2052/2071 (99.08%) Negative Passed: 1335/1502 (88.88%) Expect Syntax Error: "annex-b/disabled/1.1-html-comments-close/input.js" Expect Syntax Error: "annex-b/disabled/3.1-sloppy-labeled-functions-if-body/input.js" @@ -349,20 +349,6 @@ Expect to Parse: "typescript/regression/nested-extends-in-arrow-type-param/input · ───┬─── · ╰── Expect `,` here, but found `extends` ╰──── -Expect to Parse: "typescript/type-arguments-bit-shift-left-like-babel-7/decorator-call-expression/input.ts" - - × Unexpected token - ╭─[typescript/type-arguments-bit-shift-left-like-babel-7/decorator-call-expression/input.ts:1:1] - 1 │ (@f<(v: T) => void>() class {}); - · ─ - ╰──── -Expect to Parse: "typescript/type-arguments-bit-shift-left-like/decorator-call-expression/input.ts" - - × Unexpected token - ╭─[typescript/type-arguments-bit-shift-left-like/decorator-call-expression/input.ts:1:1] - 1 │ (@f<(v: T) => void>() class {}); - · ─ - ╰──── Expect to Parse: "typescript/types/const-type-parameters-babel-7/input.ts" × Unexpected token diff --git a/tasks/coverage/test262.snap b/tasks/coverage/test262.snap index 2eb0b20b0..0ead8ffb9 100644 --- a/tasks/coverage/test262.snap +++ b/tasks/coverage/test262.snap @@ -1,6 +1,6 @@ Test262 Summary: -AST Parsed : 44005/44024 (99.96%) -Positive Passed: 44005/44024 (99.96%) +AST Parsed : 44015/44024 (99.98%) +Positive Passed: 44015/44024 (99.98%) Negative Passed: 3915/3915 (100.00%) Expect to Parse: "language/block-scope/syntax/redeclaration-global/allowed-to-redeclare-function-declaration-with-var.js" @@ -32,69 +32,9 @@ Expect to Parse: "language/expressions/class/decorator/syntax/class-valid/decora ╭─[language/expressions/class/decorator/syntax/class-valid/decorator-member-expr-private-identifier.js:43:1] 43 │ static { 44 │ var C = @#$ - · ─ + · ── 45 │ @#_ ╰──── -Expect to Parse: "language/expressions/class/decorator/syntax/valid/decorator-call-expr-identifier-reference-yield.js" - - × Unexpected token - ╭─[language/expressions/class/decorator/syntax/valid/decorator-call-expr-identifier-reference-yield.js:43:1] - 43 │ - 44 │ var C = @yield() class {}; - · ─ - ╰──── -Expect to Parse: "language/expressions/class/decorator/syntax/valid/decorator-call-expr-identifier-reference.js" - - × Unexpected token - ╭─[language/expressions/class/decorator/syntax/valid/decorator-call-expr-identifier-reference.js:50:1] - 50 │ - 51 │ var C = @$() - · ─ - 52 │ @_() - ╰──── -Expect to Parse: "language/expressions/class/decorator/syntax/valid/decorator-member-expr-decorator-member-expr.js" - - × Unexpected token - ╭─[language/expressions/class/decorator/syntax/valid/decorator-member-expr-decorator-member-expr.js:42:1] - 42 │ - 43 │ var C = @ns.$ - · ─ - 44 │ @ns._ - ╰──── -Expect to Parse: "language/expressions/class/decorator/syntax/valid/decorator-member-expr-identifier-reference-yield.js" - - × Unexpected token - ╭─[language/expressions/class/decorator/syntax/valid/decorator-member-expr-identifier-reference-yield.js:32:1] - 32 │ - 33 │ var C = @yield class {}; - · ─ - ╰──── -Expect to Parse: "language/expressions/class/decorator/syntax/valid/decorator-member-expr-identifier-reference.js" - - × Unexpected token - ╭─[language/expressions/class/decorator/syntax/valid/decorator-member-expr-identifier-reference.js:39:1] - 39 │ - 40 │ var C = @$ - · ─ - 41 │ @_ - ╰──── -Expect to Parse: "language/expressions/class/decorator/syntax/valid/decorator-parenthesized-expr-identifier-reference-yield.js" - - × Unexpected token - ╭─[language/expressions/class/decorator/syntax/valid/decorator-parenthesized-expr-identifier-reference-yield.js:50:1] - 50 │ - 51 │ var C = @(yield) class {}; - · ─ - ╰──── -Expect to Parse: "language/expressions/class/decorator/syntax/valid/decorator-parenthesized-expr-identifier-reference.js" - - × Unexpected token - ╭─[language/expressions/class/decorator/syntax/valid/decorator-parenthesized-expr-identifier-reference.js:52:1] - 52 │ - 53 │ var C = @($) - · ─ - 54 │ @(_) - ╰──── Expect to Parse: "language/global-code/decl-func-dup.js" × Identifier `f` has already been declared @@ -115,30 +55,6 @@ Expect to Parse: "language/statements/class/decorator/syntax/class-valid/decorat · ── 46 │ @#_ ╰──── -Expect to Parse: "language/statements/class/decorator/syntax/valid/decorator-call-expr-identifier-reference-yield.js" - - × The keyword 'yield' is reserved - ╭─[language/statements/class/decorator/syntax/valid/decorator-call-expr-identifier-reference-yield.js:44:1] - 44 │ - 45 │ @yield() class C {} - · ───── - ╰──── -Expect to Parse: "language/statements/class/decorator/syntax/valid/decorator-member-expr-identifier-reference-yield.js" - - × The keyword 'yield' is reserved - ╭─[language/statements/class/decorator/syntax/valid/decorator-member-expr-identifier-reference-yield.js:33:1] - 33 │ - 34 │ @yield class C {} - · ───── - ╰──── -Expect to Parse: "language/statements/class/decorator/syntax/valid/decorator-parenthesized-expr-identifier-reference-yield.js" - - × The keyword 'yield' is reserved - ╭─[language/statements/class/decorator/syntax/valid/decorator-parenthesized-expr-identifier-reference-yield.js:51:1] - 51 │ - 52 │ @(yield) class C {} - · ───── - ╰──── Expect to Parse: "language/statements/function/S13_A6_T1.js" × Identifier `__func` has already been declared diff --git a/tasks/coverage/typescript b/tasks/coverage/typescript index 9ccf47fec..7f292bf2a 160000 --- a/tasks/coverage/typescript +++ b/tasks/coverage/typescript @@ -1 +1 @@ -Subproject commit 9ccf47fec5c1c87061956ac6e1446677654a4d4b +Subproject commit 7f292bf2a19aa14ed69a55e646111af9533d8f1c diff --git a/tasks/coverage/typescript.snap b/tasks/coverage/typescript.snap index 02a12884a..6af982e35 100644 --- a/tasks/coverage/typescript.snap +++ b/tasks/coverage/typescript.snap @@ -1,7 +1,7 @@ TypeScript Summary: -AST Parsed : 2305/2338 (98.59%) -Positive Passed: 2305/2338 (98.59%) -Negative Passed: 683/2532 (26.97%) +AST Parsed : 2323/2340 (99.27%) +Positive Passed: 2323/2340 (99.27%) +Negative Passed: 681/2532 (26.90%) Expect Syntax Error: "Symbols/ES5SymbolProperty2.ts" Expect Syntax Error: "Symbols/ES5SymbolProperty6.ts" Expect Syntax Error: "additionalChecks/noPropertyAccessFromIndexSignature1.ts" @@ -282,6 +282,7 @@ Expect Syntax Error: "decorators/class/property/decoratorOnClassProperty7.ts" Expect Syntax Error: "decorators/decoratorCallGeneric.ts" Expect Syntax Error: "decorators/invalid/decoratorOnEnum.ts" Expect Syntax Error: "decorators/invalid/decoratorOnFunctionDeclaration.ts" +Expect Syntax Error: "decorators/invalid/decoratorOnFunctionExpression.ts" Expect Syntax Error: "decorators/invalid/decoratorOnFunctionParameter.ts" Expect Syntax Error: "decorators/invalid/decoratorOnImportEquals1.ts" Expect Syntax Error: "decorators/invalid/decoratorOnInterface.ts" @@ -651,6 +652,7 @@ Expect Syntax Error: "esDecorators/classDeclaration/fields/esDecorators-classDec Expect Syntax Error: "esDecorators/classDeclaration/fields/esDecorators-classDeclaration-fields-staticAmbient.ts" Expect Syntax Error: "esDecorators/classDeclaration/methods/esDecorators-classDeclaration-methods-nonStaticAbstract.ts" Expect Syntax Error: "esDecorators/esDecorators-arguments.ts" +Expect Syntax Error: "esDecorators/esDecorators-emitDecoratorMetadata.ts" Expect Syntax Error: "expressions/arrayLiterals/arrayLiterals.ts" Expect Syntax Error: "expressions/arrayLiterals/arrayLiterals3.ts" Expect Syntax Error: "expressions/asOperator/asOperator2.ts" @@ -1918,150 +1920,6 @@ Expect to Parse: "es6/for-ofStatements/for-of53.ts" · ╰── It can not be redeclared here 4 │ } ╰──── -Expect to Parse: "esDecorators/classExpression/classSuper/esDecorators-classExpression-classSuper.1.ts" - - × Unexpected token - ╭─[esDecorators/classExpression/classSuper/esDecorators-classExpression-classSuper.1.ts:12:1] - 12 │ - 13 │ (@dec - · ─ - 14 │ class C extends Base { - ╰──── -Expect to Parse: "esDecorators/classExpression/classSuper/esDecorators-classExpression-classSuper.2.ts" - - × Unexpected token - ╭─[esDecorators/classExpression/classSuper/esDecorators-classExpression-classSuper.2.ts:7:1] - 7 │ // class expression in extends should not get an assigned name - 8 │ (@dec - · ─ - 9 │ class C1 extends class { } { - ╰──── -Expect to Parse: "esDecorators/classExpression/classSuper/esDecorators-classExpression-classSuper.3.ts" - - × Unexpected token - ╭─[esDecorators/classExpression/classSuper/esDecorators-classExpression-classSuper.3.ts:12:1] - 12 │ - 13 │ (@dec - · ─ - 14 │ class C extends Base { - ╰──── -Expect to Parse: "esDecorators/classExpression/classSuper/esDecorators-classExpression-classSuper.4.ts" - - × Unexpected token - ╭─[esDecorators/classExpression/classSuper/esDecorators-classExpression-classSuper.4.ts:12:1] - 12 │ - 13 │ (@dec - · ─ - 14 │ class C extends Base { - ╰──── -Expect to Parse: "esDecorators/classExpression/classSuper/esDecorators-classExpression-classSuper.5.ts" - - × Unexpected token - ╭─[esDecorators/classExpression/classSuper/esDecorators-classExpression-classSuper.5.ts:12:1] - 12 │ - 13 │ (@dec - · ─ - 14 │ class C1 extends Base { - ╰──── -Expect to Parse: "esDecorators/classExpression/classSuper/esDecorators-classExpression-classSuper.6.ts" - - × Unexpected token - ╭─[esDecorators/classExpression/classSuper/esDecorators-classExpression-classSuper.6.ts:12:1] - 12 │ // none of the following should result in caching `super` - 13 │ (@dec - · ─ - 14 │ class C extends Base { - ╰──── -Expect to Parse: "esDecorators/classExpression/esDecorators-classExpression-commentPreservation.ts" - - × Unexpected token - ╭─[esDecorators/classExpression/esDecorators-classExpression-commentPreservation.ts:10:1] - 10 │ /*2*/ - 11 │ @dec - · ─ - 12 │ /*3*/ - ╰──── -Expect to Parse: "esDecorators/classExpression/namedEvaluation/esDecorators-classExpression-namedEvaluation.1.ts" - - × Unexpected token - ╭─[esDecorators/classExpression/namedEvaluation/esDecorators-classExpression-namedEvaluation.1.ts:10:1] - 10 │ - 11 │ x = @dec class { }; - · ─ - 12 │ x = class { @dec y: any; }; - ╰──── -Expect to Parse: "esDecorators/classExpression/namedEvaluation/esDecorators-classExpression-namedEvaluation.10.ts" - - × Unexpected token - ╭─[esDecorators/classExpression/namedEvaluation/esDecorators-classExpression-namedEvaluation.10.ts:9:1] - 9 │ - 10 │ { class C { static x = @dec class {}; } } - · ─ - 11 │ { class C { static "x" = @dec class {}; } } - ╰──── -Expect to Parse: "esDecorators/classExpression/namedEvaluation/esDecorators-classExpression-namedEvaluation.11.ts" - - × Unexpected token - ╭─[esDecorators/classExpression/namedEvaluation/esDecorators-classExpression-namedEvaluation.11.ts:8:1] - 8 │ - 9 │ (@dec class {}); - · ─ - 10 │ (class { @dec y: any }); - ╰──── -Expect to Parse: "esDecorators/classExpression/namedEvaluation/esDecorators-classExpression-namedEvaluation.2.ts" - - × Unexpected token - ╭─[esDecorators/classExpression/namedEvaluation/esDecorators-classExpression-namedEvaluation.2.ts:10:1] - 10 │ - 11 │ ({ x: @dec class { } }); - · ─ - 12 │ ({ x: class { @dec y: any; } }); - ╰──── -Expect to Parse: "esDecorators/classExpression/namedEvaluation/esDecorators-classExpression-namedEvaluation.3.ts" - - × Unexpected token - ╭─[esDecorators/classExpression/namedEvaluation/esDecorators-classExpression-namedEvaluation.3.ts:8:1] - 8 │ - 9 │ { let x = @dec class { }; } - · ─ - 10 │ { let x = class { @dec y: any; }; } - ╰──── -Expect to Parse: "esDecorators/classExpression/namedEvaluation/esDecorators-classExpression-namedEvaluation.4.ts" - - × Unexpected token - ╭─[esDecorators/classExpression/namedEvaluation/esDecorators-classExpression-namedEvaluation.4.ts:8:1] - 8 │ - 9 │ { const [x = @dec class { }] = obj; } - · ─ - 10 │ { const [x = class { @dec y: any; }] = obj; } - ╰──── -Expect to Parse: "esDecorators/classExpression/namedEvaluation/esDecorators-classExpression-namedEvaluation.5.ts" - - × Unexpected token - ╭─[esDecorators/classExpression/namedEvaluation/esDecorators-classExpression-namedEvaluation.5.ts:8:1] - 8 │ - 9 │ ({ x = @dec class { } } = obj); - · ─ - 10 │ ({ x = class { @dec y: any; } } = obj); - ╰──── -Expect to Parse: "esDecorators/classExpression/namedEvaluation/esDecorators-classExpression-namedEvaluation.6.ts" - - × Unexpected token - ╭─[esDecorators/classExpression/namedEvaluation/esDecorators-classExpression-namedEvaluation.6.ts:8:1] - 8 │ - 9 │ ({ y: x = @dec class { } } = obj); - · ─ - 10 │ ({ y: x = class { @dec y: any; } } = obj); - ╰──── -Expect to Parse: "esDecorators/classExpression/namedEvaluation/esDecorators-classExpression-namedEvaluation.7.ts" - - × Unexpected token - ╭─[esDecorators/classExpression/namedEvaluation/esDecorators-classExpression-namedEvaluation.7.ts:8:1] - 8 │ - 9 │ [x = @dec class { }] = obj; - · ─ - 10 │ [x = class { @dec y: any; }] = obj; - ╰──── Expect to Parse: "expressions/typeSatisfaction/typeSatisfaction_contextualTyping1.ts" × Automatic Semicolon Insertion @@ -4409,7 +4267,7 @@ Expect to Parse: "salsa/privateIdentifierExpando.ts" ╭─[decorators/invalid/decoratorOnArrowFunction.ts:2:1] 2 │ 3 │ var F = @dec () => { - · ─ + · ── 4 │ } ╰──── @@ -4421,14 +4279,6 @@ Expect to Parse: "salsa/privateIdentifierExpando.ts" 5 │ } ╰──── - × Unexpected token - ╭─[decorators/invalid/decoratorOnFunctionExpression.ts:2:1] - 2 │ - 3 │ var F = @dec function () { - · ─ - 4 │ } - ╰──── - × Unexpected token ╭─[dynamicImport/importCallExpressionGrammarError.ts:8:1] 8 │ var a = ["./0"]; @@ -7654,14 +7504,6 @@ Expect to Parse: "salsa/privateIdentifierExpando.ts" 3 │ } ╰──── - × Unexpected token - ╭─[esDecorators/esDecorators-emitDecoratorMetadata.ts:30:1] - 30 │ - 31 │ (@dec class C { - · ─ - 32 │ constructor(x: number) {} - ╰──── - × Invalid assignment ╭─[expressions/assignmentOperator/assignmentLHSIsValue.ts:5:1] 5 │ class C {