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

fix: Ensure correct scope references after traversal #192

Merged
merged 2 commits into from Feb 18, 2019

Conversation

leebyron
Copy link
Contributor

(Replaces istanbuljs/istanbuljs#283)

This fixes a bug that can be seen when istanbul coverage is enabled for a babel@7 project which uses reference replacement, such as babel-plugin-lodash or babel-plugin-ramda.

The root issue is that plugins like these assume that scope references will always be Identifiers but Istanbul may replace an identifier with a SequenceExpression. In babel@7 this replacement overwrites the existing scope reference and later plugin scope reference replacement is at best unsafe and at worst may result in an invalid AST.

This seems like a pretty unfortunate situation, and ideally Babel automatically maintains these scope references during transforms or the many packages using scope would account for this situation. The pattern I see in other first-class babel plugins prefer to re-crawl scope after a transform which can affect identifiers (example: https://github.com/babel/minify/blob/master/packages/babel-plugin-minify-dead-code-elimination/src/index.js#L969)

To follow that example, I'm proposing this fix that crawls the scope after the path traversal - ensuring any subsequent references to scope get correct references.

I tested this in a project that used istanbul via jest along with babel-plugin-lodash that suffered from this issue and found that with this patch the issue was resolved and the inspected compiled output looked correct.

I'm also including two tests in this repo to illustrate the issue directly. One test illustrates it directly with the Babel plugin API and another with a popular babel plugin which uses this API and suffers from the issue. Both tests fail in master and pass with the fix applied.

@coreyfarrell
Copy link
Member

@leebyron thank you for submitting the failing test followed by the fix. So I'm a little unclear, are you suggesting that the fix should go into babel-plugin-istanbul only? This would assume that istanbul-lib-instrument would not be directly used when combining with other babel plugins - which does make sense to me.

@coveralls
Copy link

Coverage Status

Coverage increased (+0.08%) to 97.143% when pulling 57f63b0 on leebyron:test-scope into de54320 on istanbuljs:master.

5 similar comments
@coveralls
Copy link

Coverage Status

Coverage increased (+0.08%) to 97.143% when pulling 57f63b0 on leebyron:test-scope into de54320 on istanbuljs:master.

@coveralls
Copy link

Coverage Status

Coverage increased (+0.08%) to 97.143% when pulling 57f63b0 on leebyron:test-scope into de54320 on istanbuljs:master.

@coveralls
Copy link

Coverage Status

Coverage increased (+0.08%) to 97.143% when pulling 57f63b0 on leebyron:test-scope into de54320 on istanbuljs:master.

@coveralls
Copy link

Coverage Status

Coverage increased (+0.08%) to 97.143% when pulling 57f63b0 on leebyron:test-scope into de54320 on istanbuljs:master.

@coveralls
Copy link

Coverage Status

Coverage increased (+0.08%) to 97.143% when pulling 57f63b0 on leebyron:test-scope into de54320 on istanbuljs:master.

@leebyron
Copy link
Contributor Author

Hey @coreyfarrell you're fast on the comment :) I just closed the istanbul-lib-instrument PR as soon as I saw the tests passing here. On your suggestion to include more complete tests I realized that not only was this repo the appropriate place for those tests, it's probably also a more appropriate place for the fix since the problem is really using the istanbul transform in tandem with other babel transforms, which is likely to only be the case when using it as a babel plugin.

It also is a minor performance win in that it only recrawls the scope once, while the previous PR may have crawled multiple times as it encountered different untransformed subtrees.

@coreyfarrell
Copy link
Member

Thanks for quickly providing the failing test-case for this. Unfortunately I won't be able to review this immediately. Obviously not much to review for the implementation but I want to review the tests, dig a little deeper.

@leebyron
Copy link
Contributor Author

Understandable, no immediate rush. Some notes from my dig in:

I spent more time than I'd like to admit digging through Babel's scope and traversal implementation trying to gain a better understanding of the issue. My understanding is that Babel builds a scope tree once during parse which then tracks references to NodePaths. Since Babel transforms apply via mutation, these references are mostly maintained as transforms are applied in and around them, however the issue occurs when the reference itself is mutated, which Istanbul may do when replacing an expression (which could be a reference Identifier) with a SequenceExpression to add coverage tracking. I think that ideally Babel should handle this automatically, removing and replacing the reference or at least determining for itself that the Scope is corrupt and re-crawling, but I found no back references from those nodes, so it doesn't seem possible (without major surgery) to have Babel do this.

I stumbled onto this suggested fix by digging into what other Babel plugins do to solve this same issue, and came across a few just by grepping for other use of the API where it's solving a similar problem.

babel-plugin-minify-dead-code-elimination
babel-plugin-transform-react-remove-prop-types
babel-plugin-minify-mangle-names/lib/index.js
@babel/plugin-transform-parameters/lib/index.js

@coreyfarrell coreyfarrell merged commit 201a933 into istanbuljs:master Feb 18, 2019
@coreyfarrell
Copy link
Member

Thanks for troubleshooting and contributing this.

@coreyfarrell
Copy link
Member

@leebyron sorry for the delay, this is now released to npm under the next tag. This will be promoted to latest once we're done testing the new version of nyc.

@pi0
Copy link

pi0 commented Apr 22, 2019

Hey. This PR is introducing a regression described in #201.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants