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

feat(rome_js_parser): instantiation expressions #3152

Merged
merged 26 commits into from
Sep 13, 2022

Conversation

IWANABETHATGUY
Copy link
Contributor

@IWANABETHATGUY IWANABETHATGUY commented Sep 3, 2022

Summary

  1. Part of 📎 Tracking tasks for implementing Instantiation expressions #3147
  2. Finishing the first task, align to the implementation of Typescript Instantiation expressions microsoft/TypeScript#47607
  3. Adding this extra SyntaxKind TsExpressionWithTypeArguments to align typescript AST shape.
  4. Modifying the shape of TsTypeOfType,adding optional type_arguments,
    now, the TsTypeOfType could be written like:
    type T21 = typeof Array<string>;
    
  5. The newly added SyntaxKind is used to support Typescript instantiation expression, also it would be sometimes
    ambiguous when try to parse

Newly Supported Syntax

let f1 = fx<string>;
let f2 = fx<string, number>;
let f3 = fx['test']<string>;
const a2 = f.g<number>;  // () => number
const a3 = f<number>.g;  // <U>() => U
const a4 = f<number>.g<number>;  // () => number
const a5 = f['g']<number>;  // () => number
const a7 = (f<number>)['g'];
const a6 = f<number>['g'];  // type Error
const b2 = f?.<number>();
const b3 = f<number>?.();
const b4 = f<number>?.<number>();  // Type Error, expected no type arguments
const x1 = f<true>
(true);
// Parsed as relational expression
const x2 = f<true>
true;
// Parsed as instantiation expression
const x3 = f<true>;
true;

test ts ts_type_instantiation_expression
type StringBox = Box<string>;

test_err ts ts_instantiation_expressions1
const a8 = f<number><number>;  // Relational operator error
const b1 = f?.<number>;  // Error, `(` expected

Test Plan

  1. CI should pass
  2. Checking the newly added snapshot

Prettier Compatibility

Main

Average compatibility: 85.67
Compatible lines: 84.09

PR

Average compatibility: 85.81
Compatible lines: 84.10

@netlify
Copy link

netlify bot commented Sep 3, 2022

Deploy Preview for docs-rometools ready!

Name Link
🔨 Latest commit 487ba17
🔍 Latest deploy log https://app.netlify.com/sites/docs-rometools/deploys/631fed3d7082f70009e41b54
😎 Deploy Preview https://deploy-preview-3152--docs-rometools.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site settings.

@IWANABETHATGUY
Copy link
Contributor Author

!bench_parser

@github-actions
Copy link

github-actions bot commented Sep 5, 2022

Parser Benchmark Results

group                                 main                                   pr
-----                                 ----                                   --
parser/checker.ts                     1.02    134.3±4.69ms    19.4 MB/sec    1.00    132.0±6.21ms    19.7 MB/sec
parser/compiler.js                    1.00     79.8±3.98ms    13.1 MB/sec    1.06     84.7±2.63ms    12.4 MB/sec
parser/d3.min.js                      1.00     45.3±1.96ms     5.8 MB/sec    1.04     47.1±1.65ms     5.6 MB/sec
parser/dojo.js                        1.00      4.0±0.01ms    17.4 MB/sec    1.00      4.0±0.02ms    17.3 MB/sec
parser/ios.d.ts                       1.01    110.8±2.46ms    16.8 MB/sec    1.00    109.9±3.01ms    17.0 MB/sec
parser/jquery.min.js                  1.00     11.8±0.12ms     7.0 MB/sec    1.01     11.9±0.09ms     6.9 MB/sec
parser/math.js                        1.00     90.4±2.96ms     7.2 MB/sec    1.00     90.8±2.44ms     7.1 MB/sec
parser/parser.ts                      1.00      2.8±0.00ms    17.7 MB/sec    1.01      2.8±0.01ms    17.5 MB/sec
parser/pixi.min.js                    1.00     55.5±1.09ms     7.9 MB/sec    1.02     56.6±1.39ms     7.8 MB/sec
parser/react-dom.production.min.js    1.00     16.5±0.44ms     7.0 MB/sec    1.00     16.5±0.21ms     7.0 MB/sec
parser/react.production.min.js        1.00    882.7±3.32µs     7.0 MB/sec    1.00    880.7±0.53µs     7.0 MB/sec
parser/router.ts                      1.00      2.4±0.01ms    26.0 MB/sec    1.00      2.4±0.00ms    25.9 MB/sec
parser/tex-chtml-full.js              1.00    119.4±2.68ms     7.6 MB/sec    1.03    123.1±3.58ms     7.4 MB/sec
parser/three.min.js                   1.00     62.0±2.38ms     9.5 MB/sec    1.02     63.2±1.92ms     9.3 MB/sec
parser/typescript.js                  1.00   543.9±16.68ms    17.5 MB/sec    1.02   555.1±18.29ms    17.1 MB/sec
parser/vue.global.prod.js             1.00     20.3±0.32ms     5.9 MB/sec    1.02     20.6±0.46ms     5.8 MB/sec

@IWANABETHATGUY IWANABETHATGUY marked this pull request as ready for review September 8, 2022 14:36
@IWANABETHATGUY IWANABETHATGUY requested a review from a team September 8, 2022 14:36
@@ -316,6 +316,7 @@ JsAnyExpression =
| TsTypeAssertionExpression
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Adding this extra SyntaxKind to align typescript AST shape. Also, this is necessary for instantiation expression due to instantiation expression parsing sometimes needs an intermediate AST which may reparse in different contexts.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@IWANABETHATGUY
Copy link
Contributor Author

!bench_parser

@MichaReiser
Copy link
Contributor

Would you mind extening the PR summary by:

  • Adding some examples of the syntax that is supported now
  • New AST nodes added and where they are being used.
  • Mention of breaking AST changes (ExpressionWithArguments was used in other contexts before)

@github-actions
Copy link

github-actions bot commented Sep 8, 2022

Parser Benchmark Results

group                                 main                                   pr
-----                                 ----                                   --
parser/checker.ts                     1.00    118.6±5.58ms    21.9 MB/sec    1.02    120.8±7.32ms    21.5 MB/sec
parser/compiler.js                    1.00     74.3±3.73ms    14.1 MB/sec    1.03     76.8±3.77ms    13.7 MB/sec
parser/d3.min.js                      1.00     43.2±2.07ms     6.1 MB/sec    1.00     43.3±2.12ms     6.0 MB/sec
parser/dojo.js                        1.00      3.9±0.15ms    17.4 MB/sec    1.00      3.9±0.16ms    17.4 MB/sec
parser/ios.d.ts                       1.02    104.8±4.69ms    17.8 MB/sec    1.00    103.0±5.08ms    18.1 MB/sec
parser/jquery.min.js                  1.00     11.7±0.55ms     7.0 MB/sec    1.01     11.9±0.50ms     6.9 MB/sec
parser/math.js                        1.00     82.5±4.12ms     7.9 MB/sec    1.02     84.4±4.72ms     7.7 MB/sec
parser/parser.ts                      1.00      2.8±0.09ms    17.2 MB/sec    1.02      2.9±0.12ms    16.9 MB/sec
parser/pixi.min.js                    1.01     51.4±2.99ms     8.5 MB/sec    1.00     51.0±2.07ms     8.6 MB/sec
parser/react-dom.production.min.js    1.01     16.6±0.76ms     6.9 MB/sec    1.00     16.4±0.63ms     7.0 MB/sec
parser/react.production.min.js        1.02   905.2±44.27µs     6.8 MB/sec    1.00   890.8±50.91µs     6.9 MB/sec
parser/router.ts                      1.03      2.4±0.09ms    25.1 MB/sec    1.00      2.4±0.10ms    26.0 MB/sec
parser/tex-chtml-full.js              1.00    111.6±6.68ms     8.2 MB/sec    1.02    113.5±7.13ms     8.0 MB/sec
parser/three.min.js                   1.00     58.1±3.22ms    10.1 MB/sec    1.00     58.1±3.28ms    10.1 MB/sec
parser/typescript.js                  1.02   517.1±17.42ms    18.4 MB/sec    1.00   504.8±20.90ms    18.8 MB/sec
parser/vue.global.prod.js             1.00     19.7±0.88ms     6.1 MB/sec    1.03     20.3±1.18ms     5.9 MB/sec

.or_add_diagnostic(p, expected_expression)
.map(|expr| parse_member_expression_rest(p, expr, context, false, &mut false))
{
if let TS_EXPRESSION_WITH_TYPE_ARGUMENTS = lhs.kind() {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

if !matches!(p.cur(), T![?.] | T![<] | T![<<] | T!['(']) {
break lhs;
}

// Cloning here is necessary because parsing out the type arguments may rewind in which
// case we want to return the `lhs`.
let m = lhs.clone().precede(p);
let m = match lhs.kind() {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@@ -1255,6 +1289,38 @@ pub(crate) fn parse_ts_type_arguments_in_expression(p: &mut Parser) -> ParsedSyn
.unwrap_or(Absent)
}

#[inline]
pub fn can_follow_type_arguments_in_expr(cur_kind: JsSyntaxKind) -> bool {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@IWANABETHATGUY
Copy link
Contributor Author

  • ExpressionWithArguments

You said that

ExpressionWithArguments was used in other contexts before

But I can't search it through the codebase.

@IWANABETHATGUY
Copy link
Contributor Author

Would you mind extening the PR summary by:

  • Adding some examples of the syntax that is supported now
  • New AST nodes added and where they are being used.
  • Mention of breaking AST changes (ExpressionWithArguments was used in other contexts before)

Done

Copy link
Contributor

@MichaReiser MichaReiser left a comment

Choose a reason for hiding this comment

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

The PR overall is looking good to me. But I do have a few follow up questions:

  • ExpressionWithTypeArguments: My understanding is that this is a new expression type and represents instantiation expressions. What's your thinking behind calling it ExpressionWithTypeArguments instead of TsInstantiationExpression? Are there other places where it should be used that aren't instantiation expressions?
  • ExpressionWithTypeArguments should end in Expression to align with our naming convention that all expressions are named ...Expression: JsCallExpression, JsIdentifierExpression, ...

crates/rome_js_parser/src/syntax/expr.rs Show resolved Hide resolved
crates/rome_js_parser/src/syntax/expr.rs Outdated Show resolved Hide resolved
xtask/codegen/js.ungram Outdated Show resolved Hide resolved
@IWANABETHATGUY
Copy link
Contributor Author

  • ExpressionWithTypeArguments: My understanding is that this is a new expression type and represents instantiation expressions. What's your thinking behind calling it ExpressionWithTypeArguments instead of TsInstantiationExpression? Are there other places where it should be used that aren't instantiation expressions?
  1. I do agree with you, but ExpressionWithTypeArguments is not the newly added SyntaxKind in Typescript parser, we don't have this syntaxKind because our parser is not a 100% port of Typescript. I add this new SyntaxKind because ExpressionWithTypeArguments are widely used in parsing TsInstantiationExpression (mostly as a middle SyntaxKind, maybe split in different contexts). I could give two solution
    • Aligning to typescript, keep the ExpressionWithTypeArguments as is, (cons: violate our naming convention)
    • Rename it to TsInstantiationExpression, this may increase the burden of mind of the maintainer when porting typescript in the future
      (maybe we could add some comments.)

@MichaReiser
Copy link
Contributor

MichaReiser commented Sep 10, 2022

  • ExpressionWithTypeArguments: My understanding is that this is a new expression type and represents instantiation expressions. What's your thinking behind calling it ExpressionWithTypeArguments instead of TsInstantiationExpression? Are there other places where it should be used that aren't instantiation expressions?
1. I do agree with you, but `ExpressionWithTypeArguments` is not the newly added `SyntaxKind` in `Typescript` parser, we don't have this `syntaxKind` because our parser is not a 100% port of Typescript. I add this new `SyntaxKind` because `ExpressionWithTypeArguments` are widely used in parsing `TsInstantiationExpression` (mostly as a middle SyntaxKind, maybe split in different contexts). I could give two solution
   
   * Aligning to typescript, keep the `ExpressionWithTypeArguments` as is, (cons: violate our naming convention)
   * Rename it to `TsInstantiationExpression`, this may increase the burden of mind of the maintainer when porting typescript in  the future
     (maybe we could add some comments.)

I'm in favour of renaming the node to TsInstantiationExpression for the following reasons:

  • It aligns with our *Expression naming convention
  • I can google for typescript instantiation expression and find resources about the syntax. Searching for expressions with type arguments doesn't yield any useful search results.
  • TypeScript may mainly use the ExpressionWithTypeArguments name for historical reasons. It used to be an existing node for them. We don't have this historical context and can, therefore, more freely pick a new.
  • Our AST already differs in many places from TypeScript's because of our decision to use a CST and explicitly model required children. Being compatible with existing AST's hasn't been a goal yet, which is why I consider it OK to diverge from TypeScript.

We can ease the transition for people familiar with TypeScript's AST by adding a comment to TsInstantiationExpression in js.ungram that this is the same as TypeScript's ExpressionWithTypeArguments

@IWANABETHATGUY
Copy link
Contributor Author

Done d2d9e60

@IWANABETHATGUY IWANABETHATGUY requested review from MichaReiser and removed request for xunilrj and ematipico September 10, 2022 10:02
Copy link
Contributor

@MichaReiser MichaReiser left a comment

Choose a reason for hiding this comment

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

Thank you for the updates.

I think we're good to merge this PR after you included the TsTypeofType changes in the PR summary, add a new test for a TsTypeofType with a line break before the following <, and once the NeedsParentheses implementation has been double checked in which cases parentheses are necessary.

@IWANABETHATGUY
Copy link
Contributor Author

Let me explain these two commits:
f45c26f
79d2af3


impl NeedsParentheses for TsInstantiationExpression {
fn needs_parentheses_with_parent(&self, parent: &rome_js_syntax::JsSyntaxNode) -> bool {
unary_like_expression_needs_parentheses(self.syntax(), parent)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Why I write the `needs_parenthesis_with_parent like this?

  1. here is how typescript parse PostUpdateExpression https://github.dev/microsoft/TypeScript/blob/77374732df82c9d5c1319677dc595868bbc648b5/src/compiler/parser.ts#L5345-L5354, almost the same as how they parse ExpressionWithTypeArguments https://github.dev/microsoft/TypeScript/blob/77374732df82c9d5c1319677dc595868bbc648b5/src/compiler/parser.ts#L7504-L7514.

  2. So I think the needs_parentheses_with_parent should be the same as JsPostUpdateExpression

    impl NeedsParentheses for JsPostUpdateExpression {
    fn needs_parentheses_with_parent(&self, parent: &JsSyntaxNode) -> bool {
    unary_like_expression_needs_parentheses(self.syntax(), parent)
    }
    }

@IWANABETHATGUY
Copy link
Contributor Author

IWANABETHATGUY commented Sep 12, 2022

@MichaReiser , All issues have been addressed.

@MichaReiser MichaReiser merged commit 5654fae into rome:main Sep 13, 2022
@MichaReiser MichaReiser added the A-Parser Area: parser label Sep 13, 2022
@MichaReiser MichaReiser added this to the 0.10.0 milestone Sep 13, 2022
@IWANABETHATGUY IWANABETHATGUY deleted the feat/instantiation_expressions branch September 13, 2022 05:51
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
A-Parser Area: parser
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants