-
Notifications
You must be signed in to change notification settings - Fork 889
Conversation
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.
just some minor comments
src/rules/noUnsafeFinallyRule.ts
Outdated
function isBreakBoundary(scope: IFinallyScope, node: ts.BreakStatement): boolean { | ||
return node.label ? scope.labels.indexOf(node.label.text) >= 0 : scope.isBreakBoundary; | ||
type JumpStatement = ts.BreakStatement | ts.ContinueStatement | ts.ThrowStatement | ts.ReturnStatement; | ||
function showKind(node: JumpStatement): string { |
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 naming this printJumpKind
src/rules/noUnsafeFinallyRule.ts
Outdated
if (isControlFlowBoundary(currentScope, node)) { | ||
return false; | ||
function jumpIsLocalToFinallyBlock(jump: JumpStatement): boolean { | ||
const isBreakContinue = jump.kind === ts.SyntaxKind.BreakStatement || jump.kind === ts.SyntaxKind.ContinueStatement; |
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.
isBreakOrContinue
src/rules/noUnsafeFinallyRule.ts
Outdated
} | ||
|
||
function isBreakBoundary(scope: IFinallyScope, node: ts.BreakStatement): boolean { | ||
return node.label ? scope.labels.indexOf(node.label.text) >= 0 : scope.isBreakBoundary; | ||
type JumpStatement = ts.BreakStatement | ts.ContinueStatement | ts.ThrowStatement | ts.ReturnStatement; |
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.
let's move this type to just before function walk
since it's used above too
src/rules/noUnsafeFinallyRule.ts
Outdated
case ts.SyntaxKind.TryStatement: | ||
const { tryBlock, catchClause, finallyBlock } = node as ts.TryStatement; | ||
ts.forEachChild(tryBlock, cb); | ||
if (catchClause) { |
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.
try to avoid implicit boolean coercion, check catchClause !== undefined
instead. same a few lines down
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.
We do have a lint rule for that (strict-boolean-expressions
), would you want to enable that in this repo?
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.
yeah, I'd like to enable it, I remember having some issues when trying to use it elsewhere but I haven't put in the time to investigate it fully.
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.
Your implementation is missing checks to prevent crashes on invalid code and proper handling of function boundaries. Added comments at the relevant sections of code.
src/rules/noUnsafeFinallyRule.ts
Outdated
break; | ||
|
||
default: | ||
ts.forEachChild(node, cb); |
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 (isFunctionScopeBoundary(node)) {
const old = inFinally;
inFinally = false;
ts.forEachChild(node, cb);
inFinally = old;
break;
} else {
return ts.forEachChild(node, cb);
}
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 don't think that's necessary. jumpIsLocalToFinallyBlock
checks for function scopes.
src/rules/noUnsafeFinallyRule.ts
Outdated
|
||
let node: ts.Node = jump; | ||
// This should only be called inside a finally block, so we'll eventually reach the TryStatement case and return. | ||
while (true) { |
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.
Avoid property access on undefined by ending the loop at SourceFile.
You should also stop at function scope boundary.
while(node.kind !== ts.SyntaxKind.SourceFile && !isFunctionScopeBoundary(node))
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.
We'll always exit this loop, as we only get here if we saw a TryStatement above.
src/rules/noUnsafeFinallyRule.ts
Outdated
case ts.SyntaxKind.ClassExpression: | ||
case ts.SyntaxKind.FunctionDeclaration: | ||
case ts.SyntaxKind.FunctionExpression: | ||
case ts.SyntaxKind.ArrowFunction: |
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.
this does not include all function scope boundaries. use isFunctionScopeBoundary(parent)
instead, see comment above
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.
Done
src/rules/noUnsafeFinallyRule.ts
Outdated
return false; | ||
function jumpIsLocalToFinallyBlock(jump: JumpStatement): boolean { | ||
const isBreakOrContinue = jump.kind === ts.SyntaxKind.BreakStatement || jump.kind === ts.SyntaxKind.ContinueStatement; | ||
const label = isBreakOrContinue ? (jump as ts.BreakStatement | ts.ContinueStatement).label : undefined; |
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.
nit: ts.BreakOrContinueStatement
src/rules/noUnsafeFinallyRule.ts
Outdated
ctx.addFailureAtNode(node, Rule.FAILURE_STRING(printJumpKind(node as JumpStatement))); | ||
} | ||
ts.forEachChild(node, cb); | ||
break; |
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.
nit: return ts.forEachChild(...)
src/rules/noUnsafeFinallyRule.ts
Outdated
case ts.SyntaxKind.ClassExpression: | ||
case ts.SyntaxKind.FunctionDeclaration: | ||
case ts.SyntaxKind.FunctionExpression: | ||
case ts.SyntaxKind.ArrowFunction: | ||
return true; |
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.
shouldn't this return false
?
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're climbing parents and see a function before you reach the try statement, that means there is a function inside the finally block. Jump statements can't leave a function, so the jump is definitely local to that finally block (because it's local to the function and the function is inside the finally 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.
just one change request and a comment.
other than that the code looks good.
|
||
let node: ts.Node = jump; | ||
// This should only be called inside a finally block, so we'll eventually reach the TryStatement case and return. | ||
while (true) { |
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.
you still need to check if you reached ts.SourceFile
to guard against invalid code crashing this rule.
take this code for example:
try {
} finally {
break; // break is not allowed here, tsc will complain but this rule will crash
}
// or
for(;;) {
try {
} finally {
break foo; // label does not exist, tsc will complain but this rule will crash
}
}
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 a test case. (It doesn't crash.)
// falls through | ||
|
||
default: | ||
return ts.forEachChild(node, cb); |
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.
you're right, you don't need to check for function boundaries here, but it would avoid doing unnecessary time spent in jumpIsLocalToFinallyBlock
and would make the code clearer.
You can of course avoid doing the additional check if inFinally === false
.
if (inFinally && isFunctionScopeBoundary(node)) {
inFinally = false;
ts.forEachChild(node, cb);
inFinally = true;
} else {
return ts.forEachChild(node, cb);
}
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.
jumpIsLocalToFinallyBlock
is only activated if a jump statement appears in a finally block, which with 99% certainty won't happen if people are linting with this rule. (Who on earth would put a loop with a break statement inside of a finally block?) So I don't think it's worth doing more work in cb
(which runs on every node) to try to save time in jumpIsLocalToFinallyBlock
(which will almost never be called).
e0ae485
to
7322185
Compare
PR checklist
Overview of change:
Just a rewrite. Should be simpler now.