-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
Ignored catch parameter #17517
Ignored catch parameter #17517
Conversation
4d29920
to
58c8a2c
Compare
src/compiler/diagnosticMessages.json
Outdated
@@ -2196,6 +2196,10 @@ | |||
"category": "Error", | |||
"code": 2713 | |||
}, | |||
"Duplicate identifier '_ignoredCatchParameter'. Compiler uses the parameter declaration '_ignoredCatchParameter' to bind ignored catched exceptions.": { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you still need this:
Compiler uses the declaration '_ignoredCatchBinding` to bind ignored 'catch' clause parameters.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed.
src/compiler/transformers/esnext.ts
Outdated
@@ -212,6 +214,13 @@ namespace ts { | |||
return visitEachChild(node, noDestructuringValue ? visitorNoDestructuringValue : visitor, context); | |||
} | |||
|
|||
function visitCatchClause(node: CatchClause): CatchClause { | |||
if (!node.variableDeclaration) { | |||
return updateCatchClause(node, createVariableDeclaration("_ignoredCatchParameter"), node.block); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of createVariableDeclaration
, we have a createUniqueName
that you can call, which relieves you of having to write the error out. I think @RyanCavanaugh was just joking about reserving a variable name (but I'm not sure 😄 ).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, you should either use createUniqueName or createTempVariable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added createTempVariable(undefined)
here.
Looks great other than the unique identifier change I suggested (which may lead you to remove some tests relating to reserving these identifiers). I think @rbuckton may want to review the changes. |
(Also, thanks for the PR @tinganho, long time no see! 😄) |
src/compiler/binder.ts
Outdated
if (!node.variableDeclaration) { | ||
transformFlags |= TransformFlags.AssertESNext; | ||
} | ||
else if (/* node.variableDeclaration && */ isBindingPattern(node.variableDeclaration.name)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comment is unnecessary
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed.
src/compiler/checker.ts
Outdated
else if (/* !catchClause.variableDeclaration && */ languageVersion < ScriptTarget.ESNext) { | ||
const blockLocals = catchClause.block.locals; | ||
if (blockLocals) { | ||
forEachKey(blockLocals, caughtName => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need a name? We can generate a name in the transforms.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I removed this block of change. Since we don't need check and error on a named variable anymore.
src/compiler/transformers/es2015.ts
Outdated
@@ -3173,7 +3173,7 @@ namespace ts { | |||
function visitCatchClause(node: CatchClause): CatchClause { | |||
const ancestorFacts = enterSubtree(HierarchyFacts.BlockScopeExcludes, HierarchyFacts.BlockScopeIncludes); | |||
let updated: CatchClause; | |||
if (isBindingPattern(node.variableDeclaration.name)) { | |||
if (node.variableDeclaration && isBindingPattern(node.variableDeclaration.name)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By the time we get to the es2015 transform we must already have a variableDeclaration. Filling in the missing binding should be handled in the esnext transform. At best we should have a debug assertion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed!
Debug.assert(!!node.variableDeclaration, "Catch clauses should always be present when downleveling ES2015 code.");
src/compiler/transformers/esnext.ts
Outdated
@@ -212,6 +214,13 @@ namespace ts { | |||
return visitEachChild(node, noDestructuringValue ? visitorNoDestructuringValue : visitor, context); | |||
} | |||
|
|||
function visitCatchClause(node: CatchClause): CatchClause { | |||
if (!node.variableDeclaration) { | |||
return updateCatchClause(node, createVariableDeclaration("_ignoredCatchParameter"), node.block); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, you should either use createUniqueName or createTempVariable.
src/compiler/types.ts
Outdated
@@ -1807,8 +1807,8 @@ namespace ts { | |||
|
|||
export interface CatchClause extends Node { | |||
kind: SyntaxKind.CatchClause; | |||
parent?: TryStatement; | |||
variableDeclaration: VariableDeclaration; | |||
parent?: TryStatement; // We parse missing try statements |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does this comment mean? This property is present to reduce costs when walking up the AST.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It wasn't apparent to me that the parent
can be missing in a catch clause.
Sounds like you want |
Yes, specifically |
(Thanks @DanielRosenwasser , I have been quite busy for a while, so I haven't have time to participate so much here. Though, working with TS projects all day 😄 ) |
I now, removed the named variable |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also bemoan apparently having no coverage in the conformance suite for
try {} catch() {}
which is an error both before and after this change.
src/compiler/transformers/esnext.ts
Outdated
@@ -212,6 +214,13 @@ namespace ts { | |||
return visitEachChild(node, noDestructuringValue ? visitorNoDestructuringValue : visitor, context); | |||
} | |||
|
|||
function visitCatchClause(node: CatchClause): CatchClause { | |||
if (!node.variableDeclaration) { | |||
return updateCatchClause(node, createVariableDeclaration(createTempVariable(/*recordTempVariable*/ undefined)), node.block); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
node.block
still needs to be recursively visited here, no?
function* doFoo(foo: any) {
try {
throw "whatever";
}
catch {
try {
throw "again"
}
catch {
for await (const c of foo) {
c;
}
}
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@weswigham is correct, you should recursively visit the block of the catch clause.
@weswigham I think I know why, it messes up checking with the lines after it: |
I updated the code with recursive visits. |
src/compiler/emitter.ts
Outdated
if (node.variableDeclaration) { | ||
writeToken(SyntaxKind.OpenParenToken, openParenPos); | ||
emit(node.variableDeclaration); | ||
writeToken(SyntaxKind.CloseParenToken, node.variableDeclaration ? node.variableDeclaration.end : openParenPos); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The conditional on this line is no longer required since you are checking node.variableDeclaration
on 2134.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✔️
@@ -4778,11 +4778,12 @@ namespace ts { | |||
function parseCatchClause(): CatchClause { | |||
const result = <CatchClause>createNode(SyntaxKind.CatchClause); | |||
parseExpected(SyntaxKind.CatchKeyword); | |||
if (parseExpected(SyntaxKind.OpenParenToken)) { | |||
|
|||
if (parseOptional(SyntaxKind.OpenParenToken)) { | |||
result.variableDeclaration = parseVariableDeclaration(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Conditionally setting result.variableDeclaration
introduces polymorphism as we now have two CatchClause
shapes, one with the property and one without. This can cause performance degradation in V8 (NodeJS). I would recommend you add an else
clause that sets result.variableDeclaration = undefined
to keep the shape of CatchClause
the same regardless of which form the user writes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✔️
src/compiler/transformers/es2015.ts
Outdated
@@ -3173,6 +3173,7 @@ namespace ts { | |||
function visitCatchClause(node: CatchClause): CatchClause { | |||
const ancestorFacts = enterSubtree(HierarchyFacts.BlockScopeExcludes, HierarchyFacts.BlockScopeIncludes); | |||
let updated: CatchClause; | |||
Debug.assert(!!node.variableDeclaration, "Catch clauses should always be present when downleveling ES2015 code."); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about "Catch clause variable should always be present when downleveling ES2015"
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✔️
src/compiler/types.ts
Outdated
@@ -1807,8 +1807,8 @@ namespace ts { | |||
|
|||
export interface CatchClause extends Node { | |||
kind: SyntaxKind.CatchClause; | |||
parent?: TryStatement; | |||
variableDeclaration: VariableDeclaration; | |||
parent?: TryStatement; // We make this optional to parse missing try statements |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comment is unnecessary. We only re-introduce parent
here to refine its type for methods in the checker. parent
is optional because it parent pointers are not always set, especially during tree transformations.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✔️
src/compiler/types.ts
Outdated
parent?: TryStatement; | ||
variableDeclaration: VariableDeclaration; | ||
parent?: TryStatement; // We make this optional to parse missing try statements | ||
variableDeclaration?: VariableDeclaration; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you update createCatchClause
and updateCatchClause
in factory.ts and add | undefined
to the variableDeclaration
parameters in both cases? This is needed for those using the Compiler API with -strictNullChecks
enabled for their project.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✔️
invalidTryStatements.ts covers grammar errors handled in the checker. invalidTryStatements2.ts covers parse errors. I think invalidTryStatements2.ts is a better place for |
I added both the test cases |
Looks great! Thanks for the contribution! |
Thanks for the great review! |
Fixes #17467