diff --git a/src/com/google/javascript/jscomp/ClosureCheckModule.java b/src/com/google/javascript/jscomp/ClosureCheckModule.java index f0539abfe11..f0e396538e4 100644 --- a/src/com/google/javascript/jscomp/ClosureCheckModule.java +++ b/src/com/google/javascript/jscomp/ClosureCheckModule.java @@ -128,9 +128,20 @@ public final class ClosureCheckModule extends AbstractModuleCallback private final AbstractCompiler compiler; - private String currentModuleName = null; - private Map shortRequiredNamespaces = new HashMap<>(); - private Node defaultExportNode = null; + private static class ModuleInfo { + // Name of the module in question (i.e. the argument to goog.module) + private final String name; + // Mapping from fully qualified goog.required names to the import LHS node + private final Map importsByLongRequiredName = new HashMap<>(); + // The node of the default (non-named) export of this module, if it exists. + private Node defaultExportNode = null; + + ModuleInfo(String moduleName) { + this.name = moduleName; + } + } + + private ModuleInfo currentModule = null; public ClosureCheckModule(AbstractCompiler compiler) { this.compiler = compiler; @@ -153,22 +164,25 @@ public void enterModule(NodeTraversal t, Node scopeRoot) { Node call = firstStatement.getFirstChild(); Node callee = call.getFirstChild(); if (callee.matchesQualifiedName("goog.module")) { - Preconditions.checkState(currentModuleName == null); - currentModuleName = extractFirstArgumentName(call); + Preconditions.checkState(currentModule == null); + String moduleName = extractFirstArgumentName(call); + if (moduleName == null) { + t.report(scopeRoot, ClosureRewriteModule.INVALID_MODULE_NAMESPACE); + } else { + currentModule = new ModuleInfo(moduleName); + } } } } @Override public void exitModule(NodeTraversal t, Node scopeRoot) { - currentModuleName = null; - shortRequiredNamespaces.clear(); - defaultExportNode = null; + currentModule = null; } @Override public void visit(NodeTraversal t, Node n, Node parent) { - if (currentModuleName == null) { + if (currentModule == null) { return; } JSDocInfo jsDoc = n.getJSDocInfo(); @@ -179,7 +193,7 @@ public void visit(NodeTraversal t, Node n, Node parent) { case CALL: Node callee = n.getFirstChild(); if (callee.matchesQualifiedName("goog.module") - && !currentModuleName.equals(extractFirstArgumentName(n))) { + && !currentModule.name.equals(extractFirstArgumentName(n))) { t.report(n, MULTIPLE_MODULES_IN_FILE); } else if (callee.matchesQualifiedName("goog.provide")) { t.report(n, MODULE_AND_PROVIDES); @@ -224,15 +238,15 @@ public void visit(NodeTraversal t, Node n, Node parent) { } break; case GETPROP: - if (currentModuleName != null && n.matchesQualifiedName(currentModuleName)) { + if (n.matchesQualifiedName(currentModule.name)) { t.report(n, REFERENCE_TO_MODULE_GLOBAL_NAME); - } else if (shortRequiredNamespaces.containsKey(n.getQualifiedName())) { - String shortName = shortRequiredNamespaces.get(n.getQualifiedName()); - if (shortName == null) { + } else if (currentModule.importsByLongRequiredName.containsKey(n.getQualifiedName())) { + Node importLhs = currentModule.importsByLongRequiredName.get(n.getQualifiedName()); + if (importLhs == null || !importLhs.isName()) { t.report(n, REFERENCE_TO_FULLY_QUALIFIED_IMPORT_NAME, n.getQualifiedName()); } else { t.report(n, REFERENCE_TO_SHORT_IMPORT_BY_LONG_NAME_INCLUDING_SHORT_NAME, - n.getQualifiedName(), shortName); + n.getQualifiedName(), importLhs.getQualifiedName()); } } break; @@ -240,6 +254,7 @@ public void visit(NodeTraversal t, Node n, Node parent) { break; } } + private void checkJSDoc(NodeTraversal t, JSDocInfo jsDoc) { for (Node typeNode : jsDoc.getTypeNodes()) { checkTypeExpression(t, typeNode); @@ -257,16 +272,16 @@ public void visit(Node node) { } String type = node.getString(); while (true) { - if (shortRequiredNamespaces.containsKey(type)) { - String shortName = shortRequiredNamespaces.get(type); - if (shortName == null) { + if (currentModule.importsByLongRequiredName.containsKey(type)) { + Node importLhs = currentModule.importsByLongRequiredName.get(type); + if (importLhs == null || !importLhs.isName()) { t.report(node, JSDOC_REFERENCE_TO_FULLY_QUALIFIED_IMPORT_NAME, type); - } else if (!shortName.equals(type)) { + } else if (!importLhs.getString().equals(type)) { t.report( node, JSDOC_REFERENCE_TO_SHORT_IMPORT_BY_LONG_NAME_INCLUDING_SHORT_NAME, type, - shortName); + importLhs.getString()); } } if (type.contains(".")) { @@ -293,16 +308,17 @@ private void checkModuleExport(NodeTraversal t, Node n, Node parent) { Preconditions.checkArgument(n.isAssign()); Node lhs = n.getFirstChild(); Preconditions.checkState(isExportLhs(lhs)); - if (defaultExportNode == null && (!t.inModuleScope() || !parent.isExprResult())) { + if (currentModule.defaultExportNode == null && (!t.inModuleScope() || !parent.isExprResult())) { // Invalid export location. t.report(n, EXPORT_NOT_A_MODULE_LEVEL_STATEMENT); } if (lhs.isName()) { - if (defaultExportNode != null) { + if (currentModule.defaultExportNode != null) { // Multiple exports - t.report(n, EXPORT_REPEATED_ERROR, String.valueOf(defaultExportNode.getLineno())); + int previousLine = currentModule.defaultExportNode.getLineno(); + t.report(n, EXPORT_REPEATED_ERROR, String.valueOf(previousLine)); } - defaultExportNode = lhs; + currentModule.defaultExportNode = lhs; } if ((lhs.isName() || !NodeUtil.isPrototypeProperty(lhs)) && !NodeUtil.isLegacyGoogModuleFile(NodeUtil.getEnclosingScript(n))) { @@ -338,7 +354,7 @@ private void checkRequireCall(NodeTraversal t, Node callNode, Node parent) { } private void checkShortGoogRequireCall(NodeTraversal t, Node callNode, Node declaration) { - String shortName = null; + Node lhs = null; if (NodeUtil.isNameDeclaration(declaration)) { if (declaration.isLet() && !callNode.getFirstChild().matchesQualifiedName("goog.forwardDeclare")) { @@ -347,13 +363,12 @@ private void checkShortGoogRequireCall(NodeTraversal t, Node callNode, Node decl if (!declaration.hasOneChild()) { t.report(declaration, ONE_REQUIRE_PER_DECLARATION); } - Node lhs = declaration.getFirstChild(); + lhs = declaration.getFirstChild(); if (lhs.isDestructuringLhs() && !isValidDestructuringImport(lhs)) { t.report(declaration, INVALID_DESTRUCTURING_REQUIRE); } - shortName = lhs.isName() ? lhs.getString() : null; } - shortRequiredNamespaces.put(extractFirstArgumentName(callNode), shortName); + currentModule.importsByLongRequiredName.put(extractFirstArgumentName(callNode), lhs); } private static boolean isValidDestructuringImport(Node destructuringLhs) {