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

Ignored catch parameter #17517

Merged
merged 17 commits into from
Aug 8, 2017
Merged

Conversation

tinganho
Copy link
Contributor

Fixes #17467

@tinganho tinganho force-pushed the IgnoredCatchParameter branch from 4d29920 to 58c8a2c Compare July 30, 2017 21:27
@@ -2196,6 +2196,10 @@
"category": "Error",
"code": 2713
},
"Duplicate identifier '_ignoredCatchParameter'. Compiler uses the parameter declaration '_ignoredCatchParameter' to bind ignored catched exceptions.": {
Copy link
Member

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed.

@@ -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);
Copy link
Member

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 😄 ).

Copy link
Member

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added createTempVariable(undefined) here.

@DanielRosenwasser
Copy link
Member

DanielRosenwasser commented Jul 31, 2017

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.

@DanielRosenwasser
Copy link
Member

DanielRosenwasser commented Jul 31, 2017

(Also, thanks for the PR @tinganho, long time no see! 😄)

if (!node.variableDeclaration) {
transformFlags |= TransformFlags.AssertESNext;
}
else if (/* node.variableDeclaration && */ isBindingPattern(node.variableDeclaration.name)) {
Copy link
Member

Choose a reason for hiding this comment

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

The comment is unnecessary

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed.

else if (/* !catchClause.variableDeclaration && */ languageVersion < ScriptTarget.ESNext) {
const blockLocals = catchClause.block.locals;
if (blockLocals) {
forEachKey(blockLocals, caughtName => {
Copy link
Member

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.

Copy link
Contributor Author

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.

@@ -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)) {
Copy link
Member

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.

Copy link
Member

@DanielRosenwasser DanielRosenwasser Aug 1, 2017

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.");

@@ -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);
Copy link
Member

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.

@@ -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
Copy link
Member

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.

Copy link
Contributor Author

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.

@DanielRosenwasser
Copy link
Member

DanielRosenwasser commented Aug 1, 2017

Sounds like you want createTempVariable, not createUniqueName (unless you have useful text in mind, but I'd keep it as short as possible). Is that accurate @rbuckton?

@rbuckton
Copy link
Member

rbuckton commented Aug 1, 2017

Yes, specifically createTempVariable(undefined).

@tinganho
Copy link
Contributor Author

tinganho commented Aug 1, 2017

(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 😄 )

@tinganho
Copy link
Contributor Author

tinganho commented Aug 1, 2017

I now, removed the named variable ignoredCatchParameter, it made more sense to make the suggested temporary unique variable. So I cleaned up diagnostics, tests and some other unnecessary stuff.

Copy link
Member

@weswigham weswigham left a 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.

@@ -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);
Copy link
Member

@weswigham weswigham Aug 1, 2017

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;
      }
    }
  }
}

Copy link
Member

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.

@tinganho
Copy link
Contributor Author

tinganho commented Aug 2, 2017

@weswigham I think I know why, it messes up checking with the lines after it:

screen shot 2017-08-02 at 11 14 51

@tinganho
Copy link
Contributor Author

tinganho commented Aug 2, 2017

I updated the code with recursive visits.

if (node.variableDeclaration) {
writeToken(SyntaxKind.OpenParenToken, openParenPos);
emit(node.variableDeclaration);
writeToken(SyntaxKind.CloseParenToken, node.variableDeclaration ? node.variableDeclaration.end : openParenPos);
Copy link
Member

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.

Copy link
Contributor Author

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();
Copy link
Member

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

✔️

@@ -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.");
Copy link
Member

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"?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

✔️

@@ -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
Copy link
Member

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

✔️

parent?: TryStatement;
variableDeclaration: VariableDeclaration;
parent?: TryStatement; // We make this optional to parse missing try statements
variableDeclaration?: VariableDeclaration;
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 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.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

✔️

@rbuckton
Copy link
Member

rbuckton commented Aug 2, 2017

@weswigham I think I know why, it messes up checking with the lines after it:

screen shot 2017-08-02 at 11 14 51

invalidTryStatements.ts covers grammar errors handled in the checker. invalidTryStatements2.ts covers parse errors. I think invalidTryStatements2.ts is a better place for try {} catch () {}.

@tinganho
Copy link
Contributor Author

tinganho commented Aug 2, 2017

invalidTryStatements.ts covers grammar errors handled in the checker. invalidTryStatements2.ts covers parse errors. I think invalidTryStatements2.ts is a better place for try {} catch () {}.

I added both the test cases try { } and try { } catch() { } in invalidTryStatements2.ts.

@rbuckton
Copy link
Member

rbuckton commented Aug 8, 2017

Looks great! Thanks for the contribution!

@rbuckton rbuckton merged commit 75c8ecb into microsoft:master Aug 8, 2017
@tinganho
Copy link
Contributor Author

tinganho commented Aug 9, 2017

Thanks for the great review!

@microsoft microsoft locked and limited conversation to collaborators Jun 14, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants