Skip to content

Commit

Permalink
Clean up SimplifyDeclarations.shouldRemove and JsdocUtil
Browse files Browse the repository at this point in the history
Now it should be a little clearer where the warnings produced by
the i.js generation pass come from.
Also include a few other miscellaneous type fixes/simplifications.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=180994716
  • Loading branch information
blickly authored and brad4d committed Jan 8, 2018
1 parent cbeebfe commit f5e1fad
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 74 deletions.
2 changes: 1 addition & 1 deletion src/com/google/javascript/jscomp/ijs/ClassUtil.java
Expand Up @@ -76,7 +76,7 @@ static boolean hasNamedClass(Node functionNode) {
return getClassName(functionNode) != null;
}

static String getClassName(Node functionNode) {
private static String getClassName(Node functionNode) {
checkArgument(functionNode.isFunction());
if (isClassMethod(functionNode)) {
Node parent = functionNode.getParent();
Expand Down
69 changes: 39 additions & 30 deletions src/com/google/javascript/jscomp/ijs/ConvertToTypedInterface.java
Expand Up @@ -25,6 +25,7 @@
import com.google.javascript.jscomp.AbstractCompiler;
import com.google.javascript.jscomp.CompilerPass;
import com.google.javascript.jscomp.DiagnosticType;
import com.google.javascript.jscomp.JSError;
import com.google.javascript.jscomp.NodeTraversal;
import com.google.javascript.jscomp.NodeUtil;
import com.google.javascript.jscomp.Scope;
Expand Down Expand Up @@ -71,6 +72,16 @@ public ConvertToTypedInterface(AbstractCompiler compiler) {
this.compiler = compiler;
}

static void maybeWarnForConstWithoutExplicitType(
AbstractCompiler compiler, JSDocInfo jsdoc, Node nameNode) {
if (isConstToBeInferred(jsdoc, nameNode)
&& !nameNode.isFromExterns()
&& !JsdocUtil.isPrivate(jsdoc)) {
compiler.report(
JSError.make(nameNode, CONSTANT_WITHOUT_EXPLICIT_TYPE));
}
}

private void removeUselessFiles(Node externs, Node root) {
for (Node script : Iterables.concat(externs.children(), root.children())) {
if (!script.hasChildren()) {
Expand All @@ -88,7 +99,7 @@ public void process(Node externs, Node root) {
}
}

public void processFile(Node scriptNode) {
private void processFile(Node scriptNode) {
checkArgument(scriptNode.isScript());
FileInfo currentFile = new FileInfo();
NodeTraversal.traverseEs6(compiler, scriptNode, new RemoveNonDeclarations());
Expand Down Expand Up @@ -290,14 +301,14 @@ public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getToken()) {
case CLASS:
if (NodeUtil.isStatementParent(parent)) {
currentFile.recordNameDeclaration(n.getFirstChild(), t.getScope());
currentFile.recordNameDeclaration(n.getFirstChild());
}
break;
case FUNCTION:
if (NodeUtil.isStatementParent(parent)) {
currentFile.recordNameDeclaration(n.getFirstChild(), t.getScope());
currentFile.recordNameDeclaration(n.getFirstChild());
} else if (ClassUtil.isClassMethod(n) && ClassUtil.hasNamedClass(n)) {
currentFile.recordMethod(n, t.getScope());
currentFile.recordMethod(n);
}
break;
case EXPR_RESULT:
Expand All @@ -311,16 +322,16 @@ public void visit(NodeTraversal t, Node n, Node parent) {
} else if (callee.matchesQualifiedName("goog.require")) {
currentFile.recordImport(expr.getLastChild().getString());
} else if (callee.matchesQualifiedName("goog.define")) {
currentFile.recordDefine(expr, t.getScope());
currentFile.recordDefine(expr);
}
break;
case ASSIGN:
Node lhs = expr.getFirstChild();
propagateJsdocAtName(t, lhs);
currentFile.recordNameDeclaration(lhs, t.getScope());
currentFile.recordNameDeclaration(lhs);
break;
case GETPROP:
currentFile.recordNameDeclaration(expr, t.getScope());
currentFile.recordNameDeclaration(expr);
break;
default:
throw new RuntimeException("Unexpected declaration: " + expr);
Expand All @@ -331,7 +342,7 @@ public void visit(NodeTraversal t, Node n, Node parent) {
case LET:
checkState(n.hasOneChild());
propagateJsdocAtName(t, n.getFirstChild());
recordNameDeclaration(t, n);
recordNameDeclaration(n);
break;
case STRING_KEY:
if (n.hasOneChild()) {
Expand All @@ -343,15 +354,15 @@ public void visit(NodeTraversal t, Node n, Node parent) {
}
}

void recordNameDeclaration(NodeTraversal t, Node decl) {
private void recordNameDeclaration(Node decl) {
checkArgument(NodeUtil.isNameDeclaration(decl));
Node rhs = decl.getFirstChild().getLastChild();
boolean isImport = isImportRhs(rhs);
for (Node name : NodeUtil.findLhsNodesInNode(decl)) {
if (isImport) {
currentFile.recordImport(name.getString());
} else {
currentFile.recordNameDeclaration(name, t.getScope());
currentFile.recordNameDeclaration(name);
}
}
}
Expand All @@ -361,18 +372,18 @@ private void propagateJsdocAtName(NodeTraversal t, Node nameNode) {
nameNode.isQualifiedName() || nameNode.isStringKey() || nameNode.isDestructuringLhs(),
nameNode);
Node jsdocNode = NodeUtil.getBestJSDocInfoNode(nameNode);
JSDocInfo jsdoc = jsdocNode.getJSDocInfo();
if (!isConstToBeInferred(jsdoc, nameNode, false)) {
JSDocInfo originalJsdoc = jsdocNode.getJSDocInfo();
if (!isConstToBeInferred(originalJsdoc, nameNode)) {
return;
}
Node rhs = NodeUtil.getRValueOfLValue(nameNode);
if (rhs == null) {
return;
}
JSDocInfo newJsdoc = JsdocUtil.getJSDocForRhs(rhs, jsdoc);
JSDocInfo newJsdoc = JsdocUtil.getJSDocForRhs(rhs, originalJsdoc);
if (newJsdoc == null && ClassUtil.isThisProp(nameNode)) {
Var decl = findNameDeclaration(t.getScope(), rhs);
newJsdoc = JsdocUtil.getJSDocForName(decl, jsdoc);
newJsdoc = JsdocUtil.getJSDocForName(decl, originalJsdoc);
}
if (newJsdoc != null) {
jsdocNode.setJSDocInfo(newJsdoc);
Expand Down Expand Up @@ -495,11 +506,6 @@ private static boolean isClass(Node n) {
return n.isClass() || NodeUtil.isCallTo(n, "goog.defineClass");
}

private static boolean isExportLhs(Node lhs) {
return (lhs.isName() && lhs.matchesQualifiedName("exports"))
|| (lhs.isGetProp() && lhs.getFirstChild().matchesQualifiedName("exports"));
}

private static String rootName(String qualifiedName) {
int dotIndex = qualifiedName.indexOf('.');
if (dotIndex == -1) {
Expand All @@ -523,7 +529,6 @@ private RemovalType shouldRemove(PotentialDeclaration decl) {
return RemovalType.SIMPLIFY_RHS;
}
if (PotentialDeclaration.isTypedRhs(rhs)
|| NodeUtil.isCallTo(rhs, "goog.defineClass")
|| isImportRhs(rhs)
|| (isExport && (rhs.isQualifiedName() || rhs.isObjectLit()))
|| (jsdoc != null && jsdoc.isConstructor() && rhs.isQualifiedName())
Expand All @@ -536,27 +541,26 @@ private RemovalType shouldRemove(PotentialDeclaration decl) {
if (NodeUtil.isNamespaceDecl(nameNode)) {
return RemovalType.SIMPLIFY_RHS;
}
if (isConstToBeInferred(jsdoc, nameNode, isExport)) {
Node jsdocNode = NodeUtil.getBestJSDocInfoNode(nameNode);
jsdocNode.setJSDocInfo(JsdocUtil.pullJsdocTypeFromAst(compiler, jsdoc, nameNode));
Node jsdocNode = NodeUtil.getBestJSDocInfoNode(nameNode);
if (isConstToBeInferred(jsdoc, nameNode)) {
jsdocNode.setJSDocInfo(JsdocUtil.getUnusableTypeJSDoc(jsdoc));
maybeWarnForConstWithoutExplicitType(compiler, jsdoc, nameNode);
return RemovalType.SIMPLIFY_RHS;
}
if (jsdoc == null || !jsdoc.containsDeclaration()) {
Node jsdocNode = NodeUtil.getBestJSDocInfoNode(nameNode);
if (!isDeclaration(nameNode)
&& !currentFile.isPrefixProvided(fullyQualifiedName)
&& !currentFile.isStrictPrefixDeclared(fullyQualifiedName)) {
// This looks like an update rather than a declaration in this file.
return RemovalType.REMOVE_ALL;
}
jsdocNode.setJSDocInfo(JsdocUtil.getAllTypeJSDoc());
jsdocNode.setJSDocInfo(JsdocUtil.getUnusableTypeJSDoc(jsdoc));
}
return RemovalType.SIMPLIFY_RHS;
}

private boolean isAliasDefinition(PotentialDeclaration decl) {
boolean isExport = isExportLhs(decl.getLhs());
if (isConstToBeInferred(decl.getJsDoc(), decl.getLhs(), isExport)
if (isConstToBeInferred(decl.getJsDoc(), decl.getLhs())
&& decl.getRhs().isQualifiedName()) {
String aliasedName = decl.getRhs().getQualifiedName();
return currentFile.isPrefixRequired(aliasedName) || currentFile.isNameDeclared(aliasedName);
Expand All @@ -582,16 +586,21 @@ private static boolean isDeclaration(Node nameNode) {
}

static boolean isConstToBeInferred(
JSDocInfo jsdoc, Node nameNode, boolean isImpliedConst) {
JSDocInfo jsdoc, Node nameNode) {
boolean isConst =
isImpliedConst
|| nameNode.getParent().isConst()
nameNode.getParent().isConst()
|| isExportLhs(nameNode)
|| (jsdoc != null && jsdoc.hasConstAnnotation());
return isConst
&& !JsdocUtil.hasAnnotatedType(jsdoc)
&& !NodeUtil.isNamespaceDecl(nameNode);
}

private static boolean isExportLhs(Node lhs) {
return (lhs.isName() && lhs.matchesQualifiedName("exports"))
|| (lhs.isGetProp() && lhs.getFirstChild().matchesQualifiedName("exports"));
}

private static boolean isImportRhs(@Nullable Node rhs) {
if (rhs == null || !rhs.isCall()) {
return false;
Expand Down
11 changes: 5 additions & 6 deletions src/com/google/javascript/jscomp/ijs/FileInfo.java
Expand Up @@ -19,7 +19,6 @@

import com.google.common.collect.ListMultimap;
import com.google.common.collect.MultimapBuilder;
import com.google.javascript.jscomp.Scope;
import com.google.javascript.rhino.Node;
import java.util.HashSet;
import java.util.Set;
Expand All @@ -33,23 +32,23 @@ final class FileInfo {
private final ListMultimap<String, PotentialDeclaration> declarations =
MultimapBuilder.linkedHashKeys().arrayListValues().build();

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

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

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

ListMultimap<String, PotentialDeclaration> getDeclarations() {
return declarations;
}

void recordDeclaration(PotentialDeclaration decl) {
private void recordDeclaration(PotentialDeclaration decl) {
declarations.put(decl.getFullyQualifiedName(), decl);
}

Expand Down
41 changes: 7 additions & 34 deletions src/com/google/javascript/jscomp/ijs/JsdocUtil.java
Expand Up @@ -15,10 +15,6 @@
*/
package com.google.javascript.jscomp.ijs;

import static com.google.common.base.Preconditions.checkArgument;

import com.google.javascript.jscomp.AbstractCompiler;
import com.google.javascript.jscomp.JSError;
import com.google.javascript.jscomp.NodeUtil;
import com.google.javascript.jscomp.Var;
import com.google.javascript.rhino.IR;
Expand All @@ -36,38 +32,12 @@
final class JsdocUtil {
private JsdocUtil() {}

static JSDocInfo updateJsdoc(AbstractCompiler compiler, Node nameNode) {
checkArgument(nameNode.isStringKey(), nameNode);
Node jsdocNode = nameNode;
JSDocInfo jsdoc = jsdocNode.getJSDocInfo();
if (jsdoc == null) {
jsdoc = JsdocUtil.getAllTypeJSDoc();
} else if (ConvertToTypedInterface.isConstToBeInferred(jsdoc, nameNode, false)) {
jsdoc = JsdocUtil.pullJsdocTypeFromAst(compiler, jsdoc, nameNode);
}
jsdocNode.setJSDocInfo(jsdoc);
return jsdoc;
}

static JSDocInfo pullJsdocTypeFromAst(
AbstractCompiler compiler, JSDocInfo oldJSDoc, Node nameNode) {
checkArgument(nameNode.isQualifiedName() || nameNode.isStringKey(), nameNode);
if (oldJSDoc != null && oldJSDoc.getDescription() != null) {
return getConstJSDoc(oldJSDoc, "string");
}
if (!nameNode.isFromExterns() && !isPrivate(oldJSDoc)) {
compiler.report(
JSError.make(nameNode, ConvertToTypedInterface.CONSTANT_WITHOUT_EXPLICIT_TYPE));
}
return getConstJSDoc(oldJSDoc, new Node(Token.STAR));
}

private static boolean isPrivate(@Nullable JSDocInfo jsdoc) {
static boolean isPrivate(@Nullable JSDocInfo jsdoc) {
return jsdoc != null && jsdoc.getVisibility().equals(Visibility.PRIVATE);
}

static JSDocInfo getAllTypeJSDoc() {
return getConstJSDoc(null, new Node(Token.STAR));
static JSDocInfo getUnusableTypeJSDoc(JSDocInfo oldJSDoc) {
return getConstJSDoc(oldJSDoc, new Node(Token.STAR));
}

static JSDocInfo getQmarkTypeJSDoc() {
Expand Down Expand Up @@ -122,7 +92,10 @@ static JSDocInfo getJSDocForRhs(Node rhs, JSDocInfo oldJSDoc) {
return getConstJSDoc(oldJSDoc, new Node(Token.BANG, IR.string("RegExp")));
}
break;
default:
case UNDETERMINED:
if (oldJSDoc != null && oldJSDoc.getDescription() != null) {
return getConstJSDoc(oldJSDoc, "string");
}
break;
}
return null;
Expand Down
16 changes: 14 additions & 2 deletions src/com/google/javascript/jscomp/ijs/PotentialDeclaration.java
Expand Up @@ -125,7 +125,7 @@ final void remove(AbstractCompiler compiler) {
* A potential declaration that has a fully qualified name to describe it.
* This includes things like:
* var/let/const/function/class declarations,
* assignments to a fully qualfied name,
* assignments to a fully qualified name,
* and goog.module exports
* This is the most common type of potential declaration.
*/
Expand Down Expand Up @@ -162,8 +162,10 @@ void simplify(AbstractCompiler compiler) {
if (objLit.hasChildren()) {
for (Node key : objLit.children()) {
if (!isTypedRhs(key.getLastChild())) {
ConvertToTypedInterface
.maybeWarnForConstWithoutExplicitType(compiler, key.getJSDocInfo(), key);
removeStringKeyValue(key);
JsdocUtil.updateJsdoc(compiler, key);
maybeUpdateJsdoc(key);
compiler.reportChangeToEnclosingScope(key);
}
}
Expand Down Expand Up @@ -196,6 +198,15 @@ private static void removeStringKeyValue(Node stringKey) {
stringKey.replaceChild(value, replacementValue);
}

private static void maybeUpdateJsdoc(Node jsdocNode) {
checkArgument(jsdocNode.isStringKey(), jsdocNode);
JSDocInfo jsdoc = jsdocNode.getJSDocInfo();
if (jsdoc == null
|| !jsdoc.containsDeclaration()
|| ConvertToTypedInterface.isConstToBeInferred(jsdoc, jsdocNode)) {
jsdocNode.setJSDocInfo(JsdocUtil.getUnusableTypeJSDoc(jsdoc));
}
}
}

/**
Expand Down Expand Up @@ -259,6 +270,7 @@ Node getRemovableNode() {
static boolean isTypedRhs(Node rhs) {
return rhs.isFunction()
|| rhs.isClass()
|| NodeUtil.isCallTo(rhs, "goog.defineClass")
|| (rhs.isQualifiedName() && rhs.matchesQualifiedName("goog.abstractMethod"))
|| (rhs.isQualifiedName() && rhs.matchesQualifiedName("goog.nullFunction"));
}
Expand Down
Expand Up @@ -1353,7 +1353,7 @@ public void testRedeclarationOfClassMethodDoesntCrash() {
"class Foo {",
" constructor() {}",
"}",
// TODO(blickly): Would be better if this included the type anntoation.
// TODO(blickly): Would be better if this included the type annotation.
"/** @private */",
"Foo.prototype.handleEvent_",
""));
Expand Down

0 comments on commit f5e1fad

Please sign in to comment.