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

Add workaround for Safari for loop lexical scope bug #567

Merged
merged 4 commits into from Jun 13, 2017

Conversation

btmills
Copy link
Contributor

@btmills btmills commented Jun 6, 2017

Safari raises a syntax error for a let or const declaration in a for loop initalization that shadows a parent function's parameter:

function a(b) {
  for (let b;;);
}

This is valid code, but Safari throws a syntax error. The bug has been reported and since fixed in WebKit, so future versions of Safari will not have this problem.

This modifies the scope tracker's canUseInReferencedScopes method to detect cases where a rename would trigger this bug and use a different name.

Fixes #559

This is my first time dealing with Babel internals, so please let me know if there's a better way to do this!

Safari raises a syntax error for a `let` or `const` declaration in a
`for` loop initalization that shadows a parent function's parameter:

```js
function a(b) {
  for (let b;;);
}
```

This is valid code, but Safari throws a syntax error. The bug has been
[reported](https://bugs.webkit.org/show_bug.cgi?id=171041) and since
[fixed](https://trac.webkit.org/changeset/217200/webkit/trunk/Source) in
WebKit, so future versions of Safari will not have this problem.

This modifies the scope tracker's `canUseInReferencedScopes` method to
detect cases where a rename would trigger this bug and use a different
name.

Fixes babel#559
@codecov
Copy link

codecov bot commented Jun 6, 2017

Codecov Report

Merging #567 into master will increase coverage by 0.05%.
The diff coverage is 93.33%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #567      +/-   ##
==========================================
+ Coverage   82.93%   82.99%   +0.05%     
==========================================
  Files          41       41              
  Lines        2743     2758      +15     
  Branches      958      967       +9     
==========================================
+ Hits         2275     2289      +14     
  Misses        282      282              
- Partials      186      187       +1
Impacted Files Coverage Δ
...el-plugin-minify-mangle-names/src/scope-tracker.js 75% <93.33%> (+3.98%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update be3f3a7...4665255. Read the comment docs.

(binding.path.parentPath.parent.type === "ForOfStatement" &&
binding.path.parentPath.key === "left");
if (isBlockScoped && isForLoopDeclaration) {
const functionParentScope = binding.scope.getFunctionParent();
Copy link
Member

@boopathi boopathi Jun 6, 2017

Choose a reason for hiding this comment

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

thing to note here: getFunctionParent in babel returns either a Function or Program. Is this okay? or de we specifically look for function?

eg:

for (let c;;);

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah, good to know. Safari appears fine with let a; for (let a; false;); - no syntax error there. I found .isFunction() and used that to make sure that we're inside a function scope.

(binding.path.parentPath.parent.type === "ForInStatement" &&
binding.path.parentPath.key === "left") ||
(binding.path.parentPath.parent.type === "ForOfStatement" &&
binding.path.parentPath.key === "left");
Copy link
Member

Choose a reason for hiding this comment

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

ForIn and ForOf can be combined as ForXStatement and it's better to use path's API to verify by passing the node -

path.isForStatement({ init: varDeclaration.node });
path.isForXStatement({ left: varDeclaration.node });

Wrote something up with a few more corner cases here - http://astexplorer.net/#/gist/1336be60e031cffc194b693ec358074f/c8d813db7e9ff40eb57be5ac807ce16751554fd8

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for the tips! I've updated this PR with what I hope is more idiomatic traversal code. The only intentional change to the logic is ensuring that the parent scope belongs to a function and not Program.

I also added a whole bunch of other tests including the other corner cases you identified.

@boopathi
Copy link
Member

boopathi commented Jun 6, 2017

Thanks for the PR. It looks great. I have added some review inline. I'll try this out tomorrow.

@jason-codaio
Copy link

Any eta on this PR going in and being released? Old babili releases have some breaking bugs and this is blocking us from upgrading.

@alicoding
Copy link

I just encountered this one as well :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Tag: Bug Fix Pull Request fixes a bug
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Cannot redeclare let variable in Safari
4 participants