All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.javascript.jscomp.ConstParamCheck Maven / Gradle / Ivy

/*
 * Copyright 2013 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;

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

import com.google.common.annotations.VisibleForTesting;
import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback;
import com.google.javascript.rhino.IR;
import com.google.javascript.rhino.Node;

/**
 * Enforces that invocations of the method {@code goog.string.Const.from} are done with an argument
 * which is a string literal.
 *
 * 

This function parameter checker enforces that for all invocations of method {@code * goog.string.Const.from} the actual argument satisfies one of the following conditions: * *

    *
  1. The argument is an expression that is a string literal or concatenation thereof, or *
  2. The argument is a constant variable previously assigned from a string literal or * concatenation thereof. *
*/ class ConstParamCheck extends AbstractPostOrderCallback implements CompilerPass { // NOTE: Comparison of Node qualified names is faster than comparing against strings. private static final Node CONST_FUNCTION_NAME = IR.getprop(IR.name("goog"), "string", "Const", "from"); private static final Node CONST_FUNCTION_NAME_COLLAPSED = IR.name("goog$string$Const$from"); @VisibleForTesting static final DiagnosticType CONST_NOT_STRING_LITERAL_ERROR = DiagnosticType.error( "JSC_CONSTANT_NOT_STRING_LITERAL_ERROR", "Function argument is not a string literal or a constant assigned " + "from a string literal or a concatenation of these."); private final AbstractCompiler compiler; public ConstParamCheck(AbstractCompiler compiler) { this.compiler = checkNotNull(compiler); } @Override public void process(Node externs, Node root) { checkState(compiler.getLifeCycleStage().isNormalized()); NodeTraversal.traverse(compiler, root, this); } /** * Callback to visit a node and check method call arguments of {@code goog.string.Const.from}. * * @param traversal The node traversal object that supplies context, such as the scope chain to * use in name lookups as well as error reporting. * @param node The node being visited. * @param parent The parent of the node. */ @Override public void visit(NodeTraversal traversal, Node node, Node parent) { if (node.isCall()) { Node name = node.getFirstChild(); Node argument = name.getNext(); if (argument == null) { return; } // Detect calls to an aliased goog.string.Const. if (name.isName() && !name.matchesQualifiedName(CONST_FUNCTION_NAME_COLLAPSED)) { Scope scope = traversal.getScope(); Var var = scope.getVar(name.getString()); if (var == null) { return; } name = var.getInitialValue(); if (name == null) { return; } } if (name.matchesQualifiedName(CONST_FUNCTION_NAME) || name.matchesQualifiedName(CONST_FUNCTION_NAME_COLLAPSED)) { if (!isSafeValue(traversal.getScope(), argument)) { compiler.report(JSError.make(argument, CONST_NOT_STRING_LITERAL_ERROR)); } } } } /** * Checks if the method call argument is made of constant string literals. * *

This function argument checker will return true if: * *

    *
  1. The argument is a constant variable assigned from a string literal, or *
  2. The argument is a template into which only string literals are inserted, or *
  3. The argument is an expression that is a string literal, or *
  4. The argument is a ternary expression choosing between string literals, or *
  5. The argument is a concatenation of the above. *
* * @param scope The scope chain to use in name lookups. * @param argument The node of function argument to check. */ private boolean isSafeValue(Scope scope, Node argument) { if (NodeUtil.isSomeCompileTimeConstStringValue(argument)) { return true; } else if (argument.isTemplateLit()) { // Each templateLit child is either a TemplateLitString, or has children which are substituted for (Node sub = argument.getFirstChild(); sub != null; sub = sub.getNext()) { if (sub.isTemplateLitString()) { continue; } if (!isSafeValue(scope, sub.getOnlyChild())) { return false; } } return true; } else if (argument.isAdd()) { Node left = argument.getFirstChild(); Node right = argument.getLastChild(); return isSafeValue(scope, left) && isSafeValue(scope, right); } else if (argument.isName()) { String name = argument.getString(); Var var = scope.getVar(name); if (var == null || !var.isDeclaredOrInferredConst()) { return false; } Node initialValue = var.getInitialValue(); if (initialValue == null) { return false; } return isSafeValue(var.getScope(), initialValue); } return false; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy