Skip to content
This repository has been archived by the owner on Mar 25, 2021. It is now read-only.

Implement rule to forbid return statements in finally blocks #1097 #1349

Merged

Conversation

lowkay
Copy link
Contributor

@lowkay lowkay commented Jun 26, 2016

No description provided.

@lowkay
Copy link
Contributor Author

lowkay commented Jun 27, 2016

Hmm, looking at this again and while I believe the code is easily understandable and simple, it does require 2 passes over finally blocks, which is not ideal.

I am thinking of an alternative approach to do it in a single pass by tracking current scope and flagging if the current scope is in a finally by taking advantage of scopeAwareRuleWalker.

@jkillian
Copy link
Contributor

Good idea for a rule! I wasn't aware of the weird semantics until I looked it up:

image

@jkillian
Copy link
Contributor

Could you add this rule to src/configs/latest.ts?

ruleName: "no-return-in-finally",
description: "Disallows return statements in finally blocks.",
descriptionDetails: "",
rationale: "Return statements inside finally blocks have confusing semantics.",
Copy link
Contributor

Choose a reason for hiding this comment

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

You could add a link (example) or some more details about what these confusing semantics are. Not a blocking issue though

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Would it be acceptable to link to eslint.org (seems a bit weird)? Or maybe: "Using control-flow statements such as return, continue, break and throws inside finally blocks overrides any control-flow statements in the same try ... catch scope. This is considered confusing and unexpected behavior."

Copy link
Contributor

Choose a reason for hiding this comment

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

@lowkay looks good, here are a few small edit suggestions:

When used inside finally blocks, control flow statements such as return, continue, break and throws override any other control flow statements in the same try/catch scope. This is confusing and unexpected behavior.

@jkillian
Copy link
Contributor

jkillian commented Jul 18, 2016

Should we broaden this rule to watch for other statements beyond just return? ESLint watches for throw, break, and continue as well.

return super.isScopeBoundary(node) || this.isFinallyBlock(node);
}

private isFinallyBlock(node: ts.Node): boolean {
Copy link
Contributor

Choose a reason for hiding this comment

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

You can move isFinallyBlock and isTryStatement outside of the class as they don't reference any class members. Very minor though, not a blocking issue for merging this

@jkillian
Copy link
Contributor

Code looks really good @lowkay! Only major thing here is to decide if the rule should handle more than just return statements as mentioned above. That'll affect the name of the rule, so we need to get it figured out before merging

@lowkay
Copy link
Contributor Author

lowkay commented Jul 18, 2016

I agree with you that it should handle the same as eslint, take the rule name from theirs "no-unsafe-finally"?

@jkillian
Copy link
Contributor

I like that it's consistent with ESLint (easier for users who use both), but I feel like "unsafe" implies the wrong idea. It's not so much unsafe as unexpected behavior. I'll ponder the name some more, feel free to make changes under the current name for now

@lowkay
Copy link
Contributor Author

lowkay commented Jul 18, 2016

Cool... Looking at this further, the new semantics mean that handling continue/break need additional scopes :(

So for example if we see a continue/break inside a switch inside a finally that's fine, but see a return anywhere outside a new scope boundary and we need to fail.

Then how about handling breaking out of labels? We need to track where the label is with respect to the finally block.

Essentially handling the different scoping of continue/break over return makes this rule much more complex to implement.

@lowkay
Copy link
Contributor Author

lowkay commented Jul 18, 2016

I think I know how to handle it though - each scope indicates it's scoping impact (full boundary for return/throws vs partial boundary for continue/break) then we walk up the scopes:

  1. for return/throws that means walking up to the first full boundary and if that's a finally then error,
  2. for continue/break that means walking up to the first applicable scope (tracking labels) , if we reach a full boundary and that is a finally then error.

@lowkay
Copy link
Contributor Author

lowkay commented Jul 18, 2016

Naming-wise I thought perhaps no-control-flow-in-finally but that's awkward...

@jkillian
Copy link
Contributor

@lowkay That implementation strategy seems good! If some weird edge case is overly complex (for example, tracking labels) we can opt for code simplicity for this initial PR and enhance later.

@adidahiya any thoughts on rule names?

@adidahiya
Copy link
Contributor

using the same rule name as eslint ("no-unsafe-finally") seems fine here since it has the same semantics

@jkillian
Copy link
Contributor

Seems good to me, let's go with no-unsafe-finally then

@lowkay
Copy link
Contributor Author

lowkay commented Jul 20, 2016

@jkillian @adidahiya I believe I have made all the changes mentioned, additionally I managed to get label support in too, so continue label; and break label; will cause an error if they reference a label outside of the finally scope.

@jkillian
Copy link
Contributor

Thanks @lowkay! Solid work on this which turned out to be trickier than expected

@jkillian jkillian merged commit 1b6ed08 into palantir:master Jul 21, 2016
soniro pushed a commit to soniro/tslint that referenced this pull request Aug 31, 2016
…#1097 (palantir#1349)

* Implement rule to forbid return statements in finally blocks palantir#1097

* explore the try statement fully to pick up violations in nested scopes

* switch to a scope aware rule walker to reduce number of passes over the AST

* Add support for other control flow statements in finally blocks (break, continue, throws)

* improve the rationale description for the no unsafe finally rule

* rename rule to no-unsafe-finally to be inline with eslint

* add new rule to latest config

* pull out helper functions from the walker class.

* fix tslint violations (which didn't occur locally)
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.

None yet

3 participants