Skip to content

Commit

Permalink
Make PotentialDeclaration abstract with concrete subclasses
Browse files Browse the repository at this point in the history
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=179945629
  • Loading branch information
blickly committed Jan 2, 2018
1 parent 547778a commit cc99576
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 82 deletions.
Expand Up @@ -441,10 +441,6 @@ void simplifyAll() {
} }


private void processDeclaration(PotentialDeclaration decl) { private void processDeclaration(PotentialDeclaration decl) {
if (NodeUtil.isCallTo(decl.getLhs(), "goog.define")) {
NodeUtil.deleteNode(decl.getLhs().getLastChild(), compiler);
return;
}
switch (shouldRemove(decl)) { switch (shouldRemove(decl)) {
case PRESERVE_ALL: case PRESERVE_ALL:
if (decl.getRhs() != null && decl.getRhs().isFunction()) { if (decl.getRhs() != null && decl.getRhs().isFunction()) {
Expand Down
6 changes: 3 additions & 3 deletions src/com/google/javascript/jscomp/ijs/FileInfo.java
Expand Up @@ -39,15 +39,15 @@ final class FileInfo {
MultimapBuilder.linkedHashKeys().arrayListValues().build(); MultimapBuilder.linkedHashKeys().arrayListValues().build();


void recordNameDeclaration(Node qnameNode, Scope scope) { void recordNameDeclaration(Node qnameNode, Scope scope) {
recordDeclaration(PotentialDeclaration.fromName(qnameNode, scope)); recordDeclaration(PotentialDeclaration.fromName(qnameNode));
} }


void recordMethod(Node functionNode, Scope scope) { void recordMethod(Node functionNode, Scope scope) {
recordDeclaration(PotentialDeclaration.fromMethod(functionNode, scope)); recordDeclaration(PotentialDeclaration.fromMethod(functionNode));
} }


void recordDefine(Node callNode, Scope scope) { void recordDefine(Node callNode, Scope scope) {
recordDeclaration(PotentialDeclaration.fromDefine(callNode, scope)); recordDeclaration(PotentialDeclaration.fromDefine(callNode));
} }


ListMultimap<String, PotentialDeclaration> getDeclarations() { ListMultimap<String, PotentialDeclaration> getDeclarations() {
Expand Down
187 changes: 112 additions & 75 deletions src/com/google/javascript/jscomp/ijs/PotentialDeclaration.java
Expand Up @@ -20,7 +20,6 @@


import com.google.javascript.jscomp.AbstractCompiler; import com.google.javascript.jscomp.AbstractCompiler;
import com.google.javascript.jscomp.NodeUtil; import com.google.javascript.jscomp.NodeUtil;
import com.google.javascript.jscomp.Scope;
import com.google.javascript.rhino.IR; import com.google.javascript.rhino.IR;
import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.JSDocInfo;
import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Node;
Expand All @@ -37,51 +36,55 @@
* goog.define calls, * goog.define calls,
* and even valueless property accesses (e.g. `/** @type {number} * / Foo.prototype.bar`) * and even valueless property accesses (e.g. `/** @type {number} * / Foo.prototype.bar`)
*/ */
class PotentialDeclaration { abstract class PotentialDeclaration {
// The fully qualified name of the declaration. // The fully qualified name of the declaration.
private final String fullyQualifiedName; private final String fullyQualifiedName;
// The LHS node of the declaration. // The LHS node of the declaration.
private final Node lhs; private final Node lhs;
// The RHS node of the declaration, if it exists. // The RHS node of the declaration, if it exists.
private final @Nullable Node rhs; private final @Nullable Node rhs;
// The scope in which the declaration is defined.
private final Scope scope;


private PotentialDeclaration(String fullyQualifiedName, Node lhs, Node rhs, Scope scope) { private PotentialDeclaration(String fullyQualifiedName, Node lhs, @Nullable Node rhs) {
this.fullyQualifiedName = checkNotNull(fullyQualifiedName); this.fullyQualifiedName = checkNotNull(fullyQualifiedName);
this.lhs = checkNotNull(lhs); this.lhs = checkNotNull(lhs);
this.rhs = rhs; this.rhs = rhs;
this.scope = checkNotNull(scope);
} }


static PotentialDeclaration fromName(Node nameNode, Scope scope) { static PotentialDeclaration fromName(Node nameNode) {
checkArgument(nameNode.isQualifiedName(), nameNode); checkArgument(nameNode.isQualifiedName(), nameNode);
Node rhs = NodeUtil.getRValueOfLValue(nameNode); Node rhs = NodeUtil.getRValueOfLValue(nameNode);
String name = String name =
ClassUtil.isThisProp(nameNode) ClassUtil.isThisProp(nameNode)
? ClassUtil.getPrototypeNameOfThisProp(nameNode) ? ClassUtil.getPrototypeNameOfThisProp(nameNode)
: nameNode.getQualifiedName(); : nameNode.getQualifiedName();
return new PotentialDeclaration(name, nameNode, rhs, scope); return new NameDeclaration(name, nameNode, rhs);
} }


static PotentialDeclaration fromMethod(Node functionNode, Scope scope) { static PotentialDeclaration fromMethod(Node functionNode) {
checkArgument(ClassUtil.isClassMethod(functionNode)); checkArgument(ClassUtil.isClassMethod(functionNode));
String name = ClassUtil.getPrototypeNameOfMethod(functionNode); String name = ClassUtil.getPrototypeNameOfMethod(functionNode);
return new PotentialDeclaration(name, functionNode.getParent(), functionNode, scope); return new MethodDeclaration(name, functionNode);
} }


static PotentialDeclaration fromDefine(Node callNode, Scope scope) { static PotentialDeclaration fromDefine(Node callNode) {
checkArgument(NodeUtil.isCallTo(callNode, "goog.define")); checkArgument(NodeUtil.isCallTo(callNode, "goog.define"));
String name = callNode.getSecondChild().getString(); return new DefineDeclaration(callNode);
Node rhs = callNode.getLastChild();
return new PotentialDeclaration(name, callNode, rhs, scope);
} }


String getFullyQualifiedName() { String getFullyQualifiedName() {
return fullyQualifiedName; return fullyQualifiedName;
} }


private Node getStatement() { Node getLhs() {
return lhs;
}

@Nullable
Node getRhs() {
return rhs;
}

Node getStatement() {
return NodeUtil.getEnclosingStatement(lhs); return NodeUtil.getEnclosingStatement(lhs);
} }


Expand Down Expand Up @@ -111,84 +114,118 @@ void remove(AbstractCompiler compiler) {
statement.removeChildren(); statement.removeChildren();
} }


private void removeStringKeyValue(Node stringKey) {
Node value = stringKey.getOnlyChild();
Node replacementValue = IR.number(0).srcrefTree(value);
stringKey.replaceChild(value, replacementValue);
}

/** /**
* Simplify this declaration to only include what's necessary for typing. * Simplify this declaration to only include what's necessary for typing.
* Usually, this means removing the RHS and leaving a type annotation. * Usually, this means removing the RHS and leaving a type annotation.
*/ */
void simplify(AbstractCompiler compiler) { abstract void simplify(AbstractCompiler compiler);
Node nameNode = getLhs();
JSDocInfo jsdoc = getJsDoc(); /**
if (jsdoc != null && jsdoc.hasEnumParameterType()) { * A potential declaration that has a fully qualified name to describe it.
// Remove values from enums * This includes things like:
if (getRhs().isObjectLit() && getRhs().hasChildren()) { * var/let/const/function/class declarations,
for (Node key : getRhs().children()) { * assignments to a fully qualfied name,
removeStringKeyValue(key); * and goog.module exports
} * This is the most common type of potential declaration.
compiler.reportChangeToEnclosingScope(getRhs()); */
} static class NameDeclaration extends PotentialDeclaration {
return;
NameDeclaration(String fullyQualifiedName, Node lhs, Node rhs) {
super(fullyQualifiedName, lhs, rhs);
} }
if (NodeUtil.isNamespaceDecl(nameNode)) {
Node objLit = getRhs(); @Override
if (getRhs().isOr()) { void simplify(AbstractCompiler compiler) {
objLit = getRhs().getLastChild().detach(); Node nameNode = getLhs();
getRhs().replaceWith(objLit); JSDocInfo jsdoc = getJsDoc();
compiler.reportChangeToEnclosingScope(nameNode); if (jsdoc != null && jsdoc.hasEnumParameterType()) {
} // Remove values from enums
if (objLit.hasChildren()) { if (getRhs().isObjectLit() && getRhs().hasChildren()) {
for (Node key : objLit.children()) { for (Node key : getRhs().children()) {
if (!isTypedRhs(key.getLastChild())) {
removeStringKeyValue(key); removeStringKeyValue(key);
JsdocUtil.updateJsdoc(compiler, key);
compiler.reportChangeToEnclosingScope(key);
} }
compiler.reportChangeToEnclosingScope(getRhs());
} }
return;
} }
return; if (NodeUtil.isNamespaceDecl(nameNode)) {
Node objLit = getRhs();
if (getRhs().isOr()) {
objLit = getRhs().getLastChild().detach();
getRhs().replaceWith(objLit);
compiler.reportChangeToEnclosingScope(nameNode);
}
if (objLit.hasChildren()) {
for (Node key : objLit.children()) {
if (!isTypedRhs(key.getLastChild())) {
removeStringKeyValue(key);
JsdocUtil.updateJsdoc(compiler, key);
compiler.reportChangeToEnclosingScope(key);
}
}
}
return;
}
if (nameNode.matchesQualifiedName("exports")) {
// Replace the RHS of a default goog.module export with Unknown
replaceRhsWithUnknown(getRhs());
compiler.reportChangeToEnclosingScope(nameNode);
return;
}
// Just completely remove the RHS, and replace with a getprop.
Node newStatement =
NodeUtil.newQNameDeclaration(compiler, nameNode.getQualifiedName(), null, jsdoc);
newStatement.useSourceInfoIfMissingFromForTree(nameNode);
Node oldStatement = getStatement();
NodeUtil.deleteChildren(oldStatement, compiler);
oldStatement.replaceWith(newStatement);
compiler.reportChangeToEnclosingScope(newStatement);
} }
if (nameNode.matchesQualifiedName("exports")) {
// Replace the RHS of a default goog.module export with Unknown private static void replaceRhsWithUnknown(Node rhs) {
replaceRhsWithUnknown(getRhs()); rhs.replaceWith(IR.cast(IR.number(0), JsdocUtil.getQmarkTypeJSDoc()).srcrefTree(rhs));
compiler.reportChangeToEnclosingScope(nameNode);
return;
} }
// Just completely remove the RHS, and replace with a getprop.
Node newStatement =
NodeUtil.newQNameDeclaration(compiler, nameNode.getQualifiedName(), null, jsdoc);
newStatement.useSourceInfoIfMissingFromForTree(nameNode);
Node oldStatement = getStatement();
NodeUtil.deleteChildren(oldStatement, compiler);
oldStatement.replaceWith(newStatement);
compiler.reportChangeToEnclosingScope(newStatement);
}


static boolean isTypedRhs(Node rhs) { private static void removeStringKeyValue(Node stringKey) {
return rhs.isFunction() Node value = stringKey.getOnlyChild();
|| rhs.isClass() Node replacementValue = IR.number(0).srcrefTree(value);
|| (rhs.isQualifiedName() && rhs.matchesQualifiedName("goog.abstractMethod")) stringKey.replaceChild(value, replacementValue);
|| (rhs.isQualifiedName() && rhs.matchesQualifiedName("goog.nullFunction")); }
}


private static void replaceRhsWithUnknown(Node rhs) {
rhs.replaceWith(IR.cast(IR.number(0), JsdocUtil.getQmarkTypeJSDoc()).srcrefTree(rhs));
} }


Node getLhs() { /**
return lhs; * A declaration declared by a call to `goog.define`. Note that a let, const, or var declaration
* annotated with @define in its JSDoc would be a NameDeclaration instead.
*/
private static class DefineDeclaration extends PotentialDeclaration {
DefineDeclaration(Node callNode) {
super(callNode.getSecondChild().getString(), callNode, callNode.getLastChild());
}

@Override
void simplify(AbstractCompiler compiler) {
NodeUtil.deleteNode(getLhs().getLastChild(), compiler);
}
} }


@Nullable /**
Node getRhs() { * A declaration of a method defined using the ES6 method syntax or goog.defineClass. Note that
return rhs; * a method defined as an assignment to a prototype property would be a NameDeclaration instead.
*/
private static class MethodDeclaration extends PotentialDeclaration {
MethodDeclaration(String name, Node functionNode) {
super(name, functionNode.getParent(), functionNode);
}

@Override
void simplify(AbstractCompiler compiler) {}
} }


Scope getScope() { static boolean isTypedRhs(Node rhs) {
return scope; return rhs.isFunction()
|| rhs.isClass()
|| (rhs.isQualifiedName() && rhs.matchesQualifiedName("goog.abstractMethod"))
|| (rhs.isQualifiedName() && rhs.matchesQualifiedName("goog.nullFunction"));
} }
} }

0 comments on commit cc99576

Please sign in to comment.