-
Notifications
You must be signed in to change notification settings - Fork 0
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
Track using violations #1
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 |
---|---|---|
|
@@ -8,15 +8,16 @@ function removeRvalIfUndefined(declaratorPath) { | |
} | ||
} | ||
|
||
function areAllBindingsNotSeen(declaratorPath, seenNames, t) { | ||
const id = declaratorPath.node.id; | ||
const names = t.getBindingIdentifiers(id); | ||
for (const name in names) { | ||
if (seenNames.has(name)) { | ||
return false; | ||
} | ||
} | ||
return true; | ||
function getLoopParent(path, scopeParent) { | ||
const parent = path.findParent((p) => p.isLoop() || p === scopeParent); | ||
// don't traverse higher than the function the var is defined in. | ||
return parent === scopeParent ? null : parent; | ||
} | ||
|
||
function getFunctionParent(path, scopeParent) { | ||
const parent = path.findParent((p) => p.isFunction()); | ||
// don't traverse higher than the function the var is defined in. | ||
return parent === scopeParent ? null : parent; | ||
} | ||
|
||
module.exports = function({ types: t }) { | ||
|
@@ -25,27 +26,6 @@ module.exports = function({ types: t }) { | |
return { | ||
name: "remove-undefined-if-possible", | ||
visitor: { | ||
Program: { | ||
enter() { | ||
names = new Set(); | ||
functionNesting = 0; | ||
}, | ||
exit() { | ||
names = null; | ||
functionNesting = 0; | ||
}, | ||
}, | ||
Function: { | ||
enter() { | ||
functionNesting++; | ||
}, | ||
exit() { | ||
functionNesting--; | ||
}, | ||
}, | ||
Identifier(path) { | ||
names.add(path.node.name); | ||
}, | ||
ReturnStatement(path) { | ||
if (path.node.argument !== null) { | ||
const rval = path.get("argument") | ||
|
@@ -55,6 +35,7 @@ module.exports = function({ types: t }) { | |
} | ||
} | ||
}, | ||
|
||
VariableDeclaration(path) { | ||
switch (path.node.kind) { | ||
case "const": | ||
|
@@ -65,12 +46,46 @@ module.exports = function({ types: t }) { | |
} | ||
break; | ||
case "var": | ||
if (functionNesting === 0) { | ||
// ignore global vars | ||
break; | ||
} | ||
const { node, scope } = path; | ||
for (const declarator of path.get("declarations")) { | ||
if (areAllBindingsNotSeen(declarator, names, t)) { | ||
const binding = scope.getBinding(declarator.node.id.name); | ||
if (!binding) { | ||
continue; | ||
} | ||
|
||
const { start } = node; | ||
const scopeParent = declarator.getFunctionParent(); | ||
|
||
const violation = binding.constantViolations.some(v => { | ||
const violationStart = v.node.start; | ||
if (violationStart < start) { | ||
return true; | ||
} | ||
|
||
for (let func = getFunctionParent(v, scopeParent); func; func = getFunctionParent(func, scopeParent)) { | ||
const id = func.node.id; | ||
const binding = id && scope.getBinding(id.name); | ||
|
||
if (!binding) { | ||
continue; | ||
} | ||
|
||
const funcViolation = binding.referencePaths.some((p) => { | ||
return p.node.start < start; | ||
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. Would this fail for references through multiple levels of indirection? e.g. function foo() {
bar();
var a = undefined;
console.log(a);
function bar() {
g();
}
function g() {
a = 4;
}
} We cannot remove the 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. That'll be easy enough to add. 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. Done, but it's unoptimized. |
||
}); | ||
if (funcViolation) { | ||
return true; | ||
} | ||
} | ||
|
||
for (let loop = getLoopParent(declarator, scopeParent); loop; loop = getLoopParent(loop, scopeParent)) { | ||
if (loop.node.end > violationStart) { | ||
return true; | ||
} | ||
} | ||
}); | ||
|
||
if (!violation) { | ||
removeRvalIfUndefined(declarator); | ||
} | ||
} | ||
|
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 think the
start
andend
attributes won't work for nodes that are runtime-generated: https://astexplorer.net/#/Pl6ASeZRlL. This is still probably better than any other way of determining relative ordering though, if we add some existence checks for these attributes for safety.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.
Ah, I thought doing a
#replaceWith
auto-inherited the location. Looks like you have tot.inherits(newNode, oldNode)
in order to get that.