Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Don't treat a colon in a conditional expression branch as part of an arrow function #47550

Merged
merged 18 commits into from
Feb 25, 2022

Conversation

jakebailey
Copy link
Member

@jakebailey jakebailey commented Jan 21, 2022

@typescript-bot typescript-bot added Author: Team For Milestone Bug PRs that fix a bug with a specific milestone labels Jan 21, 2022
@@ -4147,7 +4147,7 @@ namespace ts {
return parseOptional(SyntaxKind.EqualsToken) ? parseAssignmentExpressionOrHigher() : undefined;
}

function parseAssignmentExpressionOrHigher(): Expression {
function parseAssignmentExpressionOrHigher(parsingConditionalTrueSide = false): Expression {
Copy link
Member Author

Choose a reason for hiding this comment

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

I am not attached to this parameter name. Please suggest something better if you think of one!

@jakebailey

This comment has been minimized.

@typescript-bot

This comment has been minimized.

@typescript-bot

This comment has been minimized.

@jakebailey
Copy link
Member Author

I didn't create a context flag for this as I wasn't sure that it would be clearer to go and figure out all of the places where I want to exit the context. The parameter naturally unsets itself when it's no longer needed, at the cost of some plumbing and maybe the potential to forget to pass it down if the code changes.

I can try and switch it to a context flag if that's more desirable, but I'm not entirely convinced it's the better of the two options.

colonToken = parseExpectedToken(SyntaxKind.ColonToken),
nodeIsPresent(colonToken)
? parseAssignmentExpressionOrHigher()
? parseAssignmentExpressionOrHigher(/*parsingConditionalTrueSide*/ false)
Copy link
Member Author

Choose a reason for hiding this comment

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

This is false because I believe this to be unambiguous after the first colon, and one of the original test cases exhibits this.

(I could have omitted the parameter, but I'm being explicit in this one place for clarity.)

Copy link
Contributor

Choose a reason for hiding this comment

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

This might be problematic in a ? b ? c : (d) : e => f

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks for the test case. This should be parsed the same as:

a ? (b ? (c) : (d)) : (e => f)

I guess it's still ambiguous in the right side.

@jakebailey jakebailey changed the title Don't treat a colon in the true side of conditional as part of an arrow function Don't treat a colon in a conditional expression branch as part of an arrow function Jan 24, 2022
@DanielRosenwasser
Copy link
Member

An alternative approach - one idea @ahejlsberg had was to disallow return-annotated function expressions in conditionals today. In that case, we would continue parsing it as we do today, but issue an error that the arrow function has to be parenthesized.

@jakebailey
Copy link
Member Author

jakebailey commented Jan 24, 2022

Unless I'm misunderstanding, that's exactly what my PR does; you can't stick a return type in an unparenthesized branch of a conditional expression, as it'll always assume it's terminating.

@jakebailey

This comment has been minimized.

@typescript-bot

This comment has been minimized.

@typescript-bot

This comment has been minimized.

// false side, we may still be within the true side of a parent conditional
// expression, so don't allow it to be a return type either.
if (parsingConditionalExpression && token() === SyntaxKind.ColonToken) {
Debug.assert(!allowAmbiguity);
Copy link
Member

Choose a reason for hiding this comment

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

can you issue an error in the allowAmbiguity case? At least I think that's what @DanielRosenwasser was proposing.

Edit: I'm not sure an error here will be (1) always correct (2) sensible even if correct. If it's not both, then making this happen is probably difficult.

Copy link
Member Author

Choose a reason for hiding this comment

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

It can never be the case that we're parsing a conditional and allowing ambiguity, as the conditional only tryParse's these to see if they are, then accepts them, otherwise tries some other expression type, hence the assert.

colonToken = parseExpectedToken(SyntaxKind.ColonToken),
nodeIsPresent(colonToken)
? parseAssignmentExpressionOrHigher()
? parseAssignmentExpressionOrHigher(/*parsingConditionalExpression*/ true)
Copy link
Member

Choose a reason for hiding this comment

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

I agree, I think a context flag would be easier to read and possibly faster. These are currently the only two places that are needed, right?

Actually, why are they both true? Where is false ever provided?

Edit: Oh, the parameter is optional. Yeah, I'd much prefer a context flag to a lot of identical optional parameters.

Copy link
Member Author

Choose a reason for hiding this comment

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

I was worried you'd say that, darn.

I guess my main annoyance was how many "okay, it's time to not be in this context" now calls I would have to add to all of the calls that parseAssignmentExpressionOrHigher (and all of the function it calls), but that's no better than the for in thing I'm already not a fan of...

@jakebailey
Copy link
Member Author

@typescript-bot pack this

@typescript-bot
Copy link
Collaborator

typescript-bot commented Jan 28, 2022

Heya @jakebailey, I've started to run the tarball bundle task on this PR at 4d44f07. You can monitor the build here.

@typescript-bot
Copy link
Collaborator

typescript-bot commented Jan 28, 2022

Heya @jakebailey, I've started to run the perf test suite on this PR at 74a5786. You can monitor the build here.

Update: The results are in!

@typescript-bot
Copy link
Collaborator

@jakebailey
Great news! no new errors were found between main..refs/pull/47550/merge

@tjjfvi
Copy link
Contributor

tjjfvi commented Jan 28, 2022

One option that would minimize breaking existing code would be leaving the handling as-is in TS files, and parsing it compliantly only in JS files -- like how the ambiguity with regard to generics is handled.

@typescript-bot
Copy link
Collaborator

@jakebailey
The results of the perf run you requested are in!

Here they are:

Comparison Report - main..47550

Metric main 47550 Delta Best Worst
Angular - node (v10.16.3, x64)
Memory used 356,221k (± 0.01%) 356,221k (± 0.02%) +1k (+ 0.00%) 356,089k 356,431k
Parse Time 1.96s (± 0.59%) 1.97s (± 0.25%) +0.01s (+ 0.46%) 1.95s 1.97s
Bind Time 0.86s (± 0.77%) 0.86s (± 1.02%) 0.00s ( 0.00%) 0.84s 0.88s
Check Time 5.60s (± 0.32%) 5.59s (± 0.59%) -0.01s (- 0.11%) 5.52s 5.66s
Emit Time 5.89s (± 0.70%) 5.87s (± 0.49%) -0.02s (- 0.36%) 5.80s 5.95s
Total Time 14.31s (± 0.40%) 14.29s (± 0.36%) -0.02s (- 0.16%) 14.15s 14.39s
Compiler-Unions - node (v10.16.3, x64)
Memory used 204,255k (± 0.02%) 204,302k (± 0.03%) +47k (+ 0.02%) 204,155k 204,435k
Parse Time 0.79s (± 0.78%) 0.79s (± 1.17%) +0.00s (+ 0.38%) 0.77s 0.81s
Bind Time 0.52s (± 1.39%) 0.53s (± 1.17%) +0.01s (+ 1.34%) 0.52s 0.54s
Check Time 7.85s (± 0.47%) 7.88s (± 0.56%) +0.03s (+ 0.43%) 7.80s 8.00s
Emit Time 2.47s (± 1.07%) 2.47s (± 0.85%) +0.00s (+ 0.04%) 2.44s 2.53s
Total Time 11.63s (± 0.47%) 11.67s (± 0.48%) +0.05s (+ 0.40%) 11.57s 11.84s
Monaco - node (v10.16.3, x64)
Memory used 342,675k (± 0.02%) 342,659k (± 0.02%) -16k (- 0.00%) 342,538k 342,811k
Parse Time 1.49s (± 0.51%) 1.49s (± 0.63%) +0.00s (+ 0.13%) 1.48s 1.52s
Bind Time 0.75s (± 0.59%) 0.75s (± 0.59%) 0.00s ( 0.00%) 0.74s 0.76s
Check Time 5.52s (± 0.71%) 5.55s (± 0.51%) +0.03s (+ 0.53%) 5.50s 5.61s
Emit Time 3.23s (± 0.87%) 3.19s (± 0.56%) -0.04s (- 1.15%) 3.16s 3.25s
Total Time 10.99s (± 0.36%) 10.98s (± 0.28%) -0.00s (- 0.02%) 10.92s 11.07s
TFS - node (v10.16.3, x64)
Memory used 305,680k (± 0.02%) 305,635k (± 0.02%) -45k (- 0.01%) 305,498k 305,799k
Parse Time 1.20s (± 0.68%) 1.21s (± 0.79%) +0.01s (+ 0.50%) 1.19s 1.22s
Bind Time 0.71s (± 0.96%) 0.71s (± 0.66%) -0.00s (- 0.70%) 0.70s 0.72s
Check Time 5.05s (± 0.41%) 5.06s (± 0.56%) +0.01s (+ 0.28%) 5.00s 5.13s
Emit Time 3.40s (± 1.05%) 3.44s (± 1.07%) +0.03s (+ 0.94%) 3.35s 3.51s
Total Time 10.37s (± 0.50%) 10.41s (± 0.49%) +0.04s (+ 0.41%) 10.33s 10.53s
material-ui - node (v10.16.3, x64)
Memory used 471,931k (± 0.01%) 471,952k (± 0.01%) +21k (+ 0.00%) 471,803k 472,068k
Parse Time 1.77s (± 0.46%) 1.79s (± 0.65%) +0.02s (+ 1.02%) 1.77s 1.82s
Bind Time 0.66s (± 0.79%) 0.65s (± 1.70%) -0.01s (- 1.06%) 0.64s 0.69s
Check Time 14.29s (± 0.49%) 14.28s (± 0.42%) -0.01s (- 0.04%) 14.16s 14.44s
Emit Time 0.00s (± 0.00%) 0.00s (± 0.00%) 0.00s ( NaN%) 0.00s 0.00s
Total Time 16.72s (± 0.41%) 16.72s (± 0.33%) +0.00s (+ 0.02%) 16.60s 16.89s
xstate - node (v10.16.3, x64)
Memory used 570,507k (± 0.02%) 574,040k (± 1.38%) +3,533k (+ 0.62%) 570,160k 606,090k
Parse Time 2.55s (± 0.36%) 2.56s (± 0.28%) +0.01s (+ 0.55%) 2.55s 2.58s
Bind Time 1.01s (± 0.44%) 1.02s (± 0.55%) +0.01s (+ 0.59%) 1.01s 1.03s
Check Time 1.50s (± 0.45%) 1.50s (± 0.59%) +0.01s (+ 0.53%) 1.49s 1.53s
Emit Time 0.07s (± 4.66%) 0.07s (± 4.13%) -0.00s (- 1.37%) 0.07s 0.08s
Total Time 5.13s (± 0.28%) 5.16s (± 0.29%) +0.03s (+ 0.60%) 5.13s 5.19s
Angular - node (v12.1.0, x64)
Memory used 333,695k (± 0.13%) 334,089k (± 0.02%) +394k (+ 0.12%) 333,926k 334,207k
Parse Time 1.94s (± 0.60%) 1.96s (± 0.68%) +0.02s (+ 1.18%) 1.94s 2.00s
Bind Time 0.83s (± 0.72%) 0.83s (± 1.82%) -0.00s (- 0.36%) 0.82s 0.89s
Check Time 5.42s (± 0.67%) 5.41s (± 0.37%) -0.00s (- 0.06%) 5.38s 5.46s
Emit Time 6.17s (± 0.84%) 6.14s (± 0.76%) -0.03s (- 0.49%) 6.05s 6.26s
Total Time 14.36s (± 0.43%) 14.35s (± 0.40%) -0.01s (- 0.10%) 14.20s 14.49s
Compiler-Unions - node (v12.1.0, x64)
Memory used 191,708k (± 0.03%) 191,660k (± 0.12%) -48k (- 0.02%) 191,047k 191,931k
Parse Time 0.78s (± 0.63%) 0.79s (± 0.82%) +0.01s (+ 1.55%) 0.77s 0.80s
Bind Time 0.53s (± 0.92%) 0.53s (± 1.04%) -0.00s (- 0.37%) 0.52s 0.54s
Check Time 7.31s (± 0.33%) 7.34s (± 0.53%) +0.04s (+ 0.49%) 7.25s 7.41s
Emit Time 2.47s (± 0.56%) 2.48s (± 0.99%) +0.00s (+ 0.12%) 2.41s 2.53s
Total Time 11.09s (± 0.31%) 11.14s (± 0.37%) +0.05s (+ 0.42%) 11.04s 11.21s
Monaco - node (v12.1.0, x64)
Memory used 325,667k (± 0.03%) 325,675k (± 0.02%) +8k (+ 0.00%) 325,493k 325,882k
Parse Time 1.47s (± 0.75%) 1.46s (± 1.18%) -0.01s (- 0.54%) 1.43s 1.52s
Bind Time 0.74s (± 0.40%) 0.74s (± 0.88%) -0.00s (- 0.00%) 0.72s 0.75s
Check Time 5.41s (± 0.36%) 5.43s (± 0.50%) +0.02s (+ 0.39%) 5.35s 5.48s
Emit Time 3.21s (± 0.50%) 3.22s (± 0.57%) +0.01s (+ 0.25%) 3.18s 3.26s
Total Time 10.83s (± 0.33%) 10.85s (± 0.30%) +0.02s (+ 0.19%) 10.76s 10.93s
TFS - node (v12.1.0, x64)
Memory used 290,352k (± 0.02%) 290,369k (± 0.02%) +17k (+ 0.01%) 290,198k 290,546k
Parse Time 1.21s (± 0.68%) 1.22s (± 0.61%) +0.02s (+ 1.33%) 1.21s 1.24s
Bind Time 0.70s (± 0.68%) 0.71s (± 0.87%) +0.01s (+ 2.01%) 0.70s 0.73s
Check Time 4.99s (± 0.46%) 4.99s (± 0.55%) 0.00s ( 0.00%) 4.91s 5.05s
Emit Time 3.43s (± 0.56%) 3.42s (± 0.72%) -0.01s (- 0.29%) 3.35s 3.47s
Total Time 10.32s (± 0.33%) 10.34s (± 0.42%) +0.02s (+ 0.17%) 10.22s 10.41s
material-ui - node (v12.1.0, x64)
Memory used 450,680k (± 0.02%) 450,644k (± 0.01%) -36k (- 0.01%) 450,506k 450,737k
Parse Time 1.78s (± 0.26%) 1.78s (± 0.43%) +0.00s (+ 0.28%) 1.77s 1.81s
Bind Time 0.64s (± 1.04%) 0.64s (± 0.78%) -0.01s (- 0.78%) 0.63s 0.65s
Check Time 12.73s (± 0.41%) 12.81s (± 0.59%) +0.08s (+ 0.61%) 12.69s 13.00s
Emit Time 0.00s (± 0.00%) 0.00s (± 0.00%) 0.00s ( NaN%) 0.00s 0.00s
Total Time 15.15s (± 0.34%) 15.23s (± 0.54%) +0.08s (+ 0.53%) 15.09s 15.43s
xstate - node (v12.1.0, x64)
Memory used 539,927k (± 1.44%) 536,377k (± 0.02%) -3,549k (- 0.66%) 536,080k 536,640k
Parse Time 2.50s (± 0.77%) 2.51s (± 0.55%) +0.00s (+ 0.16%) 2.47s 2.53s
Bind Time 1.05s (± 0.50%) 1.04s (± 0.72%) -0.01s (- 0.57%) 1.03s 1.06s
Check Time 1.43s (± 0.47%) 1.43s (± 0.54%) +0.00s (+ 0.28%) 1.42s 1.45s
Emit Time 0.07s (± 0.00%) 0.07s (± 0.00%) 0.00s ( 0.00%) 0.07s 0.07s
Total Time 5.05s (± 0.41%) 5.05s (± 0.24%) -0.00s (- 0.04%) 5.03s 5.07s
Angular - node (v14.15.1, x64)
Memory used 332,298k (± 0.01%) 332,306k (± 0.01%) +9k (+ 0.00%) 332,237k 332,349k
Parse Time 1.95s (± 0.44%) 1.96s (± 0.70%) +0.01s (+ 0.41%) 1.93s 1.99s
Bind Time 0.88s (± 0.73%) 0.87s (± 0.56%) -0.01s (- 0.91%) 0.86s 0.88s
Check Time 5.44s (± 0.58%) 5.43s (± 0.57%) -0.01s (- 0.24%) 5.37s 5.50s
Emit Time 6.18s (± 0.65%) 6.19s (± 0.64%) +0.01s (+ 0.15%) 6.12s 6.28s
Total Time 14.46s (± 0.47%) 14.45s (± 0.44%) -0.00s (- 0.03%) 14.32s 14.55s
Compiler-Unions - node (v14.15.1, x64)
Memory used 191,346k (± 0.58%) 192,011k (± 0.62%) +664k (+ 0.35%) 190,368k 193,659k
Parse Time 0.81s (± 0.61%) 0.82s (± 0.70%) +0.01s (+ 1.74%) 0.81s 0.83s
Bind Time 0.56s (± 0.40%) 0.56s (± 0.59%) +0.00s (+ 0.18%) 0.55s 0.57s
Check Time 7.41s (± 1.00%) 7.41s (± 0.56%) -0.00s (- 0.07%) 7.33s 7.50s
Emit Time 2.48s (± 0.47%) 2.49s (± 1.09%) +0.00s (+ 0.20%) 2.44s 2.55s
Total Time 11.26s (± 0.72%) 11.28s (± 0.56%) +0.02s (+ 0.16%) 11.17s 11.42s
Monaco - node (v14.15.1, x64)
Memory used 324,494k (± 0.01%) 324,499k (± 0.01%) +5k (+ 0.00%) 324,462k 324,581k
Parse Time 1.50s (± 0.55%) 1.52s (± 0.66%) +0.01s (+ 0.80%) 1.50s 1.55s
Bind Time 0.77s (± 0.75%) 0.76s (± 0.48%) -0.01s (- 0.78%) 0.76s 0.77s
Check Time 5.34s (± 0.63%) 5.37s (± 0.53%) +0.03s (+ 0.56%) 5.31s 5.43s
Emit Time 3.27s (± 0.78%) 3.27s (± 0.61%) +0.01s (+ 0.18%) 3.24s 3.33s
Total Time 10.88s (± 0.49%) 10.93s (± 0.32%) +0.05s (+ 0.42%) 10.86s 11.01s
TFS - node (v14.15.1, x64)
Memory used 289,264k (± 0.01%) 289,241k (± 0.01%) -23k (- 0.01%) 289,201k 289,276k
Parse Time 1.23s (± 0.72%) 1.28s (± 1.59%) +0.04s (+ 3.40%) 1.23s 1.32s
Bind Time 0.74s (± 0.67%) 0.72s (± 0.66%) -0.01s (- 1.77%) 0.72s 0.74s
Check Time 4.97s (± 0.48%) 4.98s (± 0.65%) +0.02s (+ 0.32%) 4.89s 5.07s
Emit Time 3.53s (± 0.45%) 3.47s (± 1.93%) -0.06s (- 1.56%) 3.34s 3.61s
Total Time 10.47s (± 0.34%) 10.46s (± 0.83%) -0.01s (- 0.10%) 10.32s 10.70s
material-ui - node (v14.15.1, x64)
Memory used 448,882k (± 0.01%) 448,891k (± 0.00%) +10k (+ 0.00%) 448,856k 448,928k
Parse Time 1.83s (± 0.55%) 1.84s (± 0.53%) +0.02s (+ 0.88%) 1.83s 1.87s
Bind Time 0.68s (± 0.53%) 0.70s (± 0.98%) +0.01s (+ 1.75%) 0.68s 0.71s
Check Time 12.83s (± 0.33%) 12.96s (± 0.48%) +0.13s (+ 1.01%) 12.85s 13.14s
Emit Time 0.00s (± 0.00%) 0.00s (± 0.00%) 0.00s ( NaN%) 0.00s 0.00s
Total Time 15.34s (± 0.25%) 15.49s (± 0.44%) +0.16s (+ 1.02%) 15.38s 15.70s
xstate - node (v14.15.1, x64)
Memory used 534,100k (± 0.00%) 534,184k (± 0.01%) +85k (+ 0.02%) 534,110k 534,270k
Parse Time 2.54s (± 0.41%) 2.57s (± 0.61%) +0.03s (+ 1.18%) 2.53s 2.61s
Bind Time 1.15s (± 0.82%) 1.15s (± 0.63%) -0.00s (- 0.09%) 1.14s 1.17s
Check Time 1.48s (± 0.33%) 1.48s (± 0.45%) +0.00s (+ 0.27%) 1.47s 1.50s
Emit Time 0.07s (± 4.92%) 0.07s (± 4.92%) 0.00s ( 0.00%) 0.07s 0.08s
Total Time 5.25s (± 0.29%) 5.28s (± 0.32%) +0.03s (+ 0.55%) 5.25s 5.32s
System
Machine Namets-ci-ubuntu
Platformlinux 4.4.0-210-generic
Architecturex64
Available Memory16 GB
Available Memory6 GB
CPUs4 × Intel(R) Core(TM) i7-4770 CPU @ 3.40GHz
Hosts
  • node (v10.16.3, x64)
  • node (v12.1.0, x64)
  • node (v14.15.1, x64)
Scenarios
  • Angular - node (v10.16.3, x64)
  • Angular - node (v12.1.0, x64)
  • Angular - node (v14.15.1, x64)
  • Compiler-Unions - node (v10.16.3, x64)
  • Compiler-Unions - node (v12.1.0, x64)
  • Compiler-Unions - node (v14.15.1, x64)
  • Monaco - node (v10.16.3, x64)
  • Monaco - node (v12.1.0, x64)
  • Monaco - node (v14.15.1, x64)
  • TFS - node (v10.16.3, x64)
  • TFS - node (v12.1.0, x64)
  • TFS - node (v14.15.1, x64)
  • material-ui - node (v10.16.3, x64)
  • material-ui - node (v12.1.0, x64)
  • material-ui - node (v14.15.1, x64)
  • xstate - node (v10.16.3, x64)
  • xstate - node (v12.1.0, x64)
  • xstate - node (v14.15.1, x64)
Benchmark Name Iterations
Current 47550 10
Baseline main 10

Developer Information:

Download Benchmark

@jakebailey
Copy link
Member Author

@typescript-bot pack this

@typescript-bot
Copy link
Collaborator

typescript-bot commented Feb 8, 2022

Heya @jakebailey, I've started to run the tarball bundle task on this PR at 2958b46. You can monitor the build here.

@typescript-bot
Copy link
Collaborator

Hey @jakebailey, I've packed this into an installable tgz. You can install it for testing by referencing it in your package.json like so:

{
    "devDependencies": {
        "typescript": "https://typescript.visualstudio.com/cf7ac146-d525-443c-b23c-0d58337efebc/_apis/build/builds/119476/artifacts?artifactName=tgz&fileId=D8C12F369BC95A45F20FCB3D172DB4B45E55761944BFC63ABAD7B33E77F4B18502&fileName=/typescript-4.6.0-insiders.20220208.tgz"
    }
}

and then running npm install.

@jakebailey
Copy link
Member Author

Pushed a change that is explicitly #16241 (comment).

@jakebailey
Copy link
Member Author

Wow, passes on my machine because I had renamed the test to something with different casing. Oops.

@jakebailey
Copy link
Member Author

FYI, this PR has been reverted in #48940, pending a "real" fix.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Author: Team For Milestone Bug PRs that fix a bug with a specific milestone
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

[bug] Ternary + arrow function is not correctly parsed
5 participants