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
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -121,6 +121,37 @@ module.exports = class ScopeTracker { | |
return false; | ||
} | ||
|
||
// Safari raises a syntax error for a `let` or `const` declaration in a | ||
// `for` loop initialization that shadows a parent function's parameter. | ||
// https://github.com/babel/babili/issues/559 | ||
// https://bugs.webkit.org/show_bug.cgi?id=171041 | ||
// https://trac.webkit.org/changeset/217200/webkit/trunk/Source | ||
const isBlockScoped = binding.kind === "let" || binding.kind === "const"; | ||
const isForLoopDeclaration = | ||
(binding.path.parentPath.parent.type === "ForStatement" && | ||
binding.path.parentPath.key === "init") || | ||
(binding.path.parentPath.parent.type === "ForInStatement" && | ||
binding.path.parentPath.key === "left") || | ||
(binding.path.parentPath.parent.type === "ForOfStatement" && | ||
binding.path.parentPath.key === "left"); | ||
if (isBlockScoped && isForLoopDeclaration) { | ||
const functionParentScope = binding.scope.getFunctionParent(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. thing to note here: eg: for (let c;;); There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, good to know. Safari appears fine with |
||
const isIncorrectlyTopLevelInSafari = | ||
binding.scope.parent === functionParentScope; | ||
if (isIncorrectlyTopLevelInSafari) { | ||
const parentFunctionBinding = this.bindings | ||
.get(functionParentScope) | ||
.get(next); | ||
if (parentFunctionBinding) { | ||
const parentFunctionHasParamBinding = | ||
parentFunctionBinding.kind === "param"; | ||
if (parentFunctionHasParamBinding) { | ||
return false; | ||
} | ||
} | ||
} | ||
} | ||
|
||
for (let i = 0; i < binding.constantViolations.length; i++) { | ||
const violation = binding.constantViolations[i]; | ||
if (tracker.hasBindingOrReference(violation.scope, binding, next)) { | ||
|
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.
ForIn and ForOf can be combined as
ForXStatement
and it's better to use path's API to verify by passing the node -Wrote something up with a few more corner cases here - http://astexplorer.net/#/gist/1336be60e031cffc194b693ec358074f/c8d813db7e9ff40eb57be5ac807ce16751554fd8
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.
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.