Skip to content

Commit

Permalink
Extract out a callback that visits inferrable const declarations.
Browse files Browse the repository at this point in the history
The intent is to use this to create a mode for giving the unannotated
@const diagnostic outside of type-summary generation, such as after
type-checking.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=189212245
  • Loading branch information
blickly committed Mar 15, 2018
1 parent acf1076 commit b487391
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 81 deletions.
83 changes: 3 additions & 80 deletions src/com/google/javascript/jscomp/ijs/ConvertToTypedInterface.java
Expand Up @@ -310,97 +310,20 @@ static void splitNameDeclarationsAndRemoveDestructuring(Node n, NodeTraversal t)
}
}

private static class PropagateConstJsdoc extends NodeTraversal.AbstractPostOrderCallback {
final FileInfo currentFile;
private static class PropagateConstJsdoc extends ProcessConstJsdocCallback {

PropagateConstJsdoc(FileInfo currentFile) {
this.currentFile = currentFile;
super(currentFile);
}

@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getToken()) {
case CLASS:
if (NodeUtil.isStatementParent(parent)) {
currentFile.recordNameDeclaration(n.getFirstChild());
}
break;
case FUNCTION:
if (NodeUtil.isStatementParent(parent)) {
currentFile.recordNameDeclaration(n.getFirstChild());
} else if (ClassUtil.isClassMethod(n) && ClassUtil.hasNamedClass(n)) {
currentFile.recordMethod(n);
}
break;
case EXPR_RESULT:
Node expr = n.getFirstChild();
switch (expr.getToken()) {
case CALL:
Node callee = expr.getFirstChild();
checkState(CALLS_TO_PRESERVE.contains(callee.getQualifiedName()));
if (callee.matchesQualifiedName("goog.provide")) {
currentFile.markProvided(expr.getLastChild().getString());
} else if (callee.matchesQualifiedName("goog.require")) {
currentFile.recordImport(expr.getLastChild().getString());
} else if (callee.matchesQualifiedName("goog.define")) {
currentFile.recordDefine(expr);
}
break;
case ASSIGN:
Node lhs = expr.getFirstChild();
propagateJsdocAtName(t, lhs);
currentFile.recordNameDeclaration(lhs);
break;
case GETPROP:
currentFile.recordNameDeclaration(expr);
break;
default:
throw new RuntimeException("Unexpected declaration: " + expr);
}
break;
case VAR:
case CONST:
case LET:
checkState(n.hasOneChild(), n);
propagateJsdocAtName(t, n.getFirstChild());
recordNameDeclaration(n);
break;
case STRING_KEY:
if (n.hasOneChild()) {
propagateJsdocAtName(t, n);
}
break;
default:
break;
}
}

private void recordNameDeclaration(Node decl) {
checkArgument(NodeUtil.isNameDeclaration(decl));
Node rhs = decl.getFirstChild().getLastChild();
boolean isImport = PotentialDeclaration.isImportRhs(rhs);
for (Node name : NodeUtil.findLhsNodesInNode(decl)) {
if (isImport) {
currentFile.recordImport(name.getString());
} else {
currentFile.recordNameDeclaration(name);
}
}
}

private void propagateJsdocAtName(NodeTraversal t, Node nameNode) {
protected void processConstWithRhs(NodeTraversal t, Node nameNode) {
checkArgument(
nameNode.isQualifiedName() || nameNode.isStringKey() || nameNode.isDestructuringLhs(),
nameNode);
Node jsdocNode = NodeUtil.getBestJSDocInfoNode(nameNode);
JSDocInfo originalJsdoc = jsdocNode.getJSDocInfo();
if (!PotentialDeclaration.isConstToBeInferred(originalJsdoc, nameNode)) {
return;
}
Node rhs = NodeUtil.getRValueOfLValue(nameNode);
if (rhs == null) {
return;
}
JSDocInfo newJsdoc = JsdocUtil.getJSDocForRhs(rhs, originalJsdoc);
if (newJsdoc == null && ClassUtil.isThisProp(nameNode)) {
Var decl = findNameDeclaration(t.getScope(), rhs);
Expand Down
2 changes: 1 addition & 1 deletion src/com/google/javascript/jscomp/ijs/FileInfo.java
Expand Up @@ -48,7 +48,7 @@ ListMultimap<String, PotentialDeclaration> getDeclarations() {
return declarations;
}

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

Expand Down
127 changes: 127 additions & 0 deletions src/com/google/javascript/jscomp/ijs/ProcessConstJsdocCallback.java
@@ -0,0 +1,127 @@
/*
* Copyright 2018 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp.ijs;

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

import com.google.javascript.jscomp.NodeTraversal;
import com.google.javascript.jscomp.NodeUtil;
import com.google.javascript.rhino.JSDocInfo;
import com.google.javascript.rhino.Node;

/**
* A callback that calls the abstract method on every "inferrable const". This is a constant
* declaration for which there is no declared type and an RHS is present. This is useful for giving
* warnings like the CONSTANT_WITHOUT_EXPLICIT_TYPE diagnostic.
*
* As a side effect, this callback also populates the given FileInfo (assumed empty) with all of
* the declarations found throughout the compilation.
*/
abstract class ProcessConstJsdocCallback extends NodeTraversal.AbstractPostOrderCallback {

private final FileInfo currentFile;

ProcessConstJsdocCallback(FileInfo currentFile) {
this.currentFile = currentFile;
}

@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getToken()) {
case CLASS:
if (NodeUtil.isStatementParent(parent)) {
currentFile.recordNameDeclaration(n.getFirstChild());
}
break;
case FUNCTION:
if (NodeUtil.isStatementParent(parent)) {
currentFile.recordNameDeclaration(n.getFirstChild());
} else if (ClassUtil.isClassMethod(n) && ClassUtil.hasNamedClass(n)) {
currentFile.recordMethod(n);
}
break;
case EXPR_RESULT:
Node expr = n.getFirstChild();
switch (expr.getToken()) {
case CALL:
Node callee = expr.getFirstChild();
if (callee.matchesQualifiedName("goog.provide")) {
currentFile.markProvided(expr.getLastChild().getString());
} else if (callee.matchesQualifiedName("goog.require")) {
currentFile.recordImport(expr.getLastChild().getString());
} else if (callee.matchesQualifiedName("goog.define")) {
currentFile.recordDefine(expr);
}
break;
case ASSIGN:
Node lhs = expr.getFirstChild();
currentFile.recordNameDeclaration(lhs);
processDeclarationWithRhs(t, lhs);
break;
case GETPROP:
currentFile.recordNameDeclaration(expr);
break;
default:
throw new RuntimeException("Unexpected declaration: " + expr);
}
break;
case VAR:
case CONST:
case LET:
checkState(n.hasOneChild(), n);
recordNameDeclaration(n);
if (n.getFirstChild().isName() && n.getFirstChild().hasChildren()) {
processDeclarationWithRhs(t, n.getFirstChild());
}
break;
case STRING_KEY:
if (parent.isObjectLit() && n.hasOneChild()) {
processDeclarationWithRhs(t, n);
}
break;
default:
break;
}
}

private void recordNameDeclaration(Node decl) {
checkArgument(NodeUtil.isNameDeclaration(decl));
Node rhs = decl.getFirstChild().getLastChild();
boolean isImport = PotentialDeclaration.isImportRhs(rhs);
for (Node name : NodeUtil.findLhsNodesInNode(decl)) {
if (isImport) {
currentFile.recordImport(name.getString());
} else {
currentFile.recordNameDeclaration(name);
}
}
}

private void processDeclarationWithRhs(NodeTraversal t, Node lhs) {
checkArgument(
lhs.isQualifiedName() || lhs.isStringKey() || lhs.isDestructuringLhs(),
lhs);
checkState(NodeUtil.getRValueOfLValue(lhs) != null, lhs);
JSDocInfo originalJsdoc = NodeUtil.getBestJSDocInfo(lhs);
if (!PotentialDeclaration.isConstToBeInferred(originalJsdoc, lhs)) {
return;
}
processConstWithRhs(t, lhs);
}

protected abstract void processConstWithRhs(NodeTraversal t, Node lhs);
}

0 comments on commit b487391

Please sign in to comment.