Skip to content

Commit

Permalink
Small NodeUtil changes:
Browse files Browse the repository at this point in the history
 - Added NodeUtil.isNamedFunctionExpression
 - deprecated isVarArgsFunction in favor of doesFunctionReferenceOwnArgumentsObject
 - small cleanup of isExpressionResultUsed

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=173940187
  • Loading branch information
concavelenz authored and brad4d committed Oct 31, 2017
1 parent 467c690 commit a933e16
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 20 deletions.
63 changes: 47 additions & 16 deletions src/com/google/javascript/jscomp/NodeUtil.java
Expand Up @@ -3061,6 +3061,13 @@ static boolean isFunctionExpression(Node n) {
return n.isFunction() && (!isNamedFunction(n) || !isDeclarationParent(n.getParent()));
}

/**
* @return Whether the node is both a function expression and the function is named.
*/
static boolean isNamedFunctionExpression(Node n) {
return NodeUtil.isFunctionExpression(n) && !n.getFirstChild().getString().isEmpty();
}

/**
* see {@link #isFunctionExpression}
*
Expand Down Expand Up @@ -3124,13 +3131,42 @@ static boolean isEmptyFunctionExpression(Node node) {
* Determines if a function takes a variable number of arguments by
* looking for references to the "arguments" var_args object.
*/
static boolean isVarArgsFunction(Node function) {
// TODO(johnlenz): rename this function
checkArgument(function.isFunction());
return !function.isArrowFunction() && isNameReferenced(
function.getLastChild(),
"arguments",
MATCH_NOT_THIS_BINDING);
@Deprecated
static boolean isVarArgsFunction(Node fn) {
return doesFunctionReferenceOwnArgumentsObject(fn);
}

/**
* @return Whether a function has a reference to its own "arguments" object.
*/
static boolean doesFunctionReferenceOwnArgumentsObject(Node fn) {
checkArgument(fn.isFunction());
if (fn.isArrowFunction()) {
return false;
}
return referencesArgumentsHelper(fn.getLastChild());
}

/** @return Whether any child is a reference to the "arguments" object of the root. Effectively,
* this includes arrow method bodies (which don't have their own) and excludes other functions
* which shadow the "arguments" value with their own.
*/
private static boolean referencesArgumentsHelper(Node node) {
if (node.isName() && node.getString().equals("arguments")) {
return true;
}

if (NodeUtil.isVanillaFunction(node)) {
return false;
}

for (Node c = node.getFirstChild(); c != null; c = c.getNext()) {
if (referencesArgumentsHelper(c)) {
return true;
}
}

return false;
}

/**
Expand Down Expand Up @@ -4890,10 +4926,9 @@ static String getBestLValueName(@Nullable Node lValue) {
}

/**
* @return false iff the result of the expression is not consumed.
* @return true iff the result of the expression is consumed.
*/
static boolean isExpressionResultUsed(Node expr) {
// TODO(johnlenz): consider sharing some code with trySimpleUnusedResult.
Node parent = expr.getParent();
switch (parent.getToken()) {
case BLOCK:
Expand Down Expand Up @@ -4922,13 +4957,9 @@ static boolean isExpressionResultUsed(Node expr) {

return (expr == parent.getFirstChild()) ? false : isExpressionResultUsed(parent);
case FOR:
case FOR_IN:
if (!parent.isForIn()) {
// Only an expression whose result is in the condition part of the
// expression is used.
return (parent.getSecondChild() == expr);
}
break;
// Only an expression whose result is in the condition part of the
// expression is used.
return (parent.getSecondChild() == expr);
default:
break;
}
Expand Down
33 changes: 29 additions & 4 deletions test/com/google/javascript/jscomp/NodeUtilTest.java
Expand Up @@ -2927,13 +2927,38 @@ public void testGetAllVars2() {
}

public void testIsVarArgs() {
assertTrue(NodeUtil.isVarArgsFunction(getNode("function() {return () => arguments}")));
assertFalse(NodeUtil.isVarArgsFunction(getNode("() => arguments")));
assertTrue(NodeUtil.doesFunctionReferenceOwnArgumentsObject(
getNode("function() {return () => arguments}")));
assertFalse(NodeUtil.doesFunctionReferenceOwnArgumentsObject(
getNode("() => arguments")));
}

private boolean executedOnceTestCase(String code) {
/**
* When the left side is a destructuring pattern, generally it's not possible to identify the
* RHS for a specific name on the LHS.
*/
public void testIsExpressionResultUsed() {
assertThat(NodeUtil.isExpressionResultUsed(getNameNodeFrom("for (x in y) z", "x"))).isTrue();
assertThat(NodeUtil.isExpressionResultUsed(getNameNodeFrom("for (x in y) z", "y"))).isTrue();
assertThat(NodeUtil.isExpressionResultUsed(getNameNodeFrom("for (x in y) z", "z"))).isFalse();

assertThat(NodeUtil.isExpressionResultUsed(getNameNodeFrom("for (x of y) z", "x"))).isTrue();
assertThat(NodeUtil.isExpressionResultUsed(getNameNodeFrom("for (x of y) z", "y"))).isTrue();
assertThat(NodeUtil.isExpressionResultUsed(getNameNodeFrom("for (x of y) z", "z"))).isFalse();

assertThat(NodeUtil.isExpressionResultUsed(getNameNodeFrom("for (x; y; z) a", "x"))).isFalse();
assertThat(NodeUtil.isExpressionResultUsed(getNameNodeFrom("for (x; y; z) a", "y"))).isTrue();
assertThat(NodeUtil.isExpressionResultUsed(getNameNodeFrom("for (x; y; z) a", "z"))).isFalse();
}

private Node getNameNodeFrom(String code, String name) {
Node ast = parse(code);
Node nameNode = getNameNode(ast, "x");
Node nameNode = getNameNode(ast, name);
return nameNode;
}

private boolean executedOnceTestCase(String code) {
Node nameNode = getNameNodeFrom(code, "x");
return NodeUtil.isExecutedExactlyOnce(nameNode);
}

Expand Down

0 comments on commit a933e16

Please sign in to comment.