<commit_before>/*
 * Copyright 2008 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 com.google.javascript.jscomp.NodeTraversal.AbstractShallowCallback;
import com.google.javascript.jscomp.ReferenceCollectingCallback.BasicBlock;
import com.google.javascript.jscomp.ReferenceCollectingCallback.Behavior;
import com.google.javascript.jscomp.ReferenceCollectingCallback.Reference;
import com.google.javascript.jscomp.ReferenceCollectingCallback.ReferenceCollection;
import com.google.javascript.jscomp.ReferenceCollectingCallback.ReferenceMap;
import com.google.javascript.rhino.Node;

import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

/**
 * Checks variables to see if they are referenced before their declaration, or
 * if they are redeclared in a way that is suspicious (i.e. not dictated by
 * control structures). This is a more aggressive version of {@link VarCheck},
 * but it lacks the cross-module checks.
 *
 * @author <EMAIL> (<NAME>)
 */
class VariableReferenceCheck implements HotSwapCompilerPass {

  static final DiagnosticType EARLY_REFERENCE =
      DiagnosticType.warning(
          "JSC_REFERENCE_BEFORE_DECLARE", "Variable referenced before declaration: {0}");

  static final DiagnosticType REDECLARED_VARIABLE =
      DiagnosticType.warning("JSC_REDECLARED_VARIABLE", "Redeclared variable: {0}");

  static final DiagnosticType AMBIGUOUS_FUNCTION_DECL =
      DiagnosticType.error("AMBIGUOUS_FUNCTION_DECL", "Ambiguous use of a named function: {0}.");

  static final DiagnosticType EARLY_REFERENCE_ERROR =
      DiagnosticType.error(
          "JSC_REFERENCE_BEFORE_DECLARE_ERROR",
          "Illegal variable reference before declaration: {0}");

  static final DiagnosticType REASSIGNED_CONSTANT =
      DiagnosticType.error("JSC_REASSIGNED_CONSTANT", "Constant reassigned: {0}");

  static final DiagnosticType REDECLARED_VARIABLE_ERROR =
      DiagnosticType.error("JSC_REDECLARED_VARIABLE_ERROR", "Illegal redeclared variable: {0}");

  static final DiagnosticType DECLARATION_NOT_DIRECTLY_IN_BLOCK =
      DiagnosticType.error(
          "JSC_DECLARATION_NOT_DIRECTLY_IN_BLOCK",
          "Block-scoped declaration not directly within block: {0}");

  static final DiagnosticType UNUSED_LOCAL_ASSIGNMENT =
      DiagnosticType.disabled(
          "JSC_UNUSED_LOCAL_ASSIGNMENT", "Value assigned to local variable {0} is never read");

  private final AbstractCompiler compiler;

  // NOTE(nicksantos): It's a lot faster to use a shared Set that
  // we clear after each method call, because the Set never gets too big.
  private final Set<BasicBlock> blocksWithDeclarations = new HashSet<>();

  public VariableReferenceCheck(AbstractCompiler compiler) {
    this.compiler = compiler;
  }

  @Override
  public void process(Node externs, Node root) {
    ReferenceCollectingCallback callback =
        new ReferenceCollectingCallback(compiler, new ReferenceCheckingBehavior());
    callback.process(externs, root);
  }

  @Override
  public void hotSwapScript(Node scriptRoot, Node originalRoot) {
    ReferenceCollectingCallback callback =
        new ReferenceCollectingCallback(compiler, new ReferenceCheckingBehavior());
    callback.hotSwapScript(scriptRoot, originalRoot);
  }

  /**
   * Behavior that checks variables for redeclaration or early references
   * just after they go out of scope.
   */
  private class ReferenceCheckingBehavior implements Behavior {

    private Set<String> varsInFunctionBody;

    private ReferenceCheckingBehavior() {
      varsInFunctionBody = new HashSet<>();
    }

    @Override
    public void afterExitScope(NodeTraversal t, ReferenceMap referenceMap) {
      // TODO(bashir) In hot-swap version this means that for global scope we
      // only go through all global variables accessed in the modified file not
      // all global variables. This should be fixed.

      // Check all vars after finishing a scope
      Scope scope = t.getScope();
      if (scope.isFunctionBlockScope()) {
        varsInFunctionBody.clear();
        for (Var v : scope.getVarIterable()) {
          varsInFunctionBody.add(v.name);
        }
      }
      for (Var v : scope.getVarIterable()) {
        ReferenceCollection referenceCollection = referenceMap.getReferences(v);
        // TODO(moz): Figure out why this could be null
        if (referenceCollection != null) {
          if (scope.getRootNode().isFunction() && v.isDefaultParam()) {
            checkDefaultParam(v, scope, varsInFunctionBody);
          }
          if (scope.getRootNode().isFunction()) {
            checkShadowParam(v, scope, referenceCollection.references);
          }
          checkVar(v, referenceCollection.references);
        }
      }
    }

    /**
     * Do a shallow check since cases like:
     *   function f(y = () => x, x = 5) { return y(); }
     * is legal. We are going to miss cases like:
     *   function f(y = (() => x)(), x = 5) { return y(); }
     * but this should be rare.
     */
    private class ShallowReferenceCollector extends AbstractShallowCallback {
      private final Set<Node> currParamReferences = new LinkedHashSet<>();

      @Override
      public void visit(NodeTraversal t, Node n, Node parent) {
        if (!NodeUtil.isReferenceName(n)) {
          return;
        }
        currParamReferences.add(n);
      }
    }

    private void checkDefaultParam(Var param, Scope scope, Set<String> varsInFunctionBody) {
      ShallowReferenceCollector check = new ShallowReferenceCollector();
      NodeTraversal.traverseEs6(compiler, param.getParentNode().getSecondChild(), check);
      for (Node ref : check.currParamReferences) {
        String refName = ref.getString();
        if (varsInFunctionBody.contains(refName) && !scope.isDeclared(refName, true)) {
          compiler.report(JSError.make(ref, EARLY_REFERENCE_ERROR, refName));
        }
      }
    }

    private void checkShadowParam(Var v, Scope functionScope, List<Reference> references) {
      Var maybeParam = functionScope.getVar(v.getName());
      if (maybeParam != null && maybeParam.isParam() && maybeParam.getScope() == functionScope) {
        for (Reference r : references) {
          if ((r.isVarDeclaration() || r.isHoistedFunction())
              && !r.getNode().equals(v.getNameNode())) {
            compiler.report(JSError.make(r.getNode(), REDECLARED_VARIABLE, v.name));
          }
        }
      }
    }

    /**
     * If the variable is declared more than once in a basic block, generate a
     * warning. Also check if a variable is used in a given scope before it is
     * declared, which suggest a likely error. Relies on the fact that
     * references is in parse-tree order.
     */
    private void checkVar(Var v, List<Reference> references) {
      blocksWithDeclarations.clear();<commit_msg>Don't build a hash set when we can just check<commit_after>/*
 * Copyright 2008 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 com.google.javascript.jscomp.NodeTraversal.AbstractShallowCallback;
import com.google.javascript.jscomp.ReferenceCollectingCallback.BasicBlock;
import com.google.javascript.jscomp.ReferenceCollectingCallback.Behavior;
import com.google.javascript.jscomp.ReferenceCollectingCallback.Reference;
import com.google.javascript.jscomp.ReferenceCollectingCallback.ReferenceCollection;
import com.google.javascript.jscomp.ReferenceCollectingCallback.ReferenceMap;
import com.google.javascript.rhino.Node;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * Checks variables to see if they are referenced before their declaration, or
 * if they are redeclared in a way that is suspicious (i.e. not dictated by
 * control structures). This is a more aggressive version of {@link VarCheck},
 * but it lacks the cross-module checks.
 *
 * @author <EMAIL> (<NAME>)
 */
class VariableReferenceCheck implements HotSwapCompilerPass {

  static final DiagnosticType EARLY_REFERENCE =
      DiagnosticType.warning(
          "JSC_REFERENCE_BEFORE_DECLARE", "Variable referenced before declaration: {0}");

  static final DiagnosticType REDECLARED_VARIABLE =
      DiagnosticType.warning("JSC_REDECLARED_VARIABLE", "Redeclared variable: {0}");

  static final DiagnosticType AMBIGUOUS_FUNCTION_DECL =
      DiagnosticType.error("AMBIGUOUS_FUNCTION_DECL", "Ambiguous use of a named function: {0}.");

  static final DiagnosticType EARLY_REFERENCE_ERROR =
      DiagnosticType.error(
          "JSC_REFERENCE_BEFORE_DECLARE_ERROR",
          "Illegal variable reference before declaration: {0}");

  static final DiagnosticType REASSIGNED_CONSTANT =
      DiagnosticType.error("JSC_REASSIGNED_CONSTANT", "Constant reassigned: {0}");

  static final DiagnosticType REDECLARED_VARIABLE_ERROR =
      DiagnosticType.error("JSC_REDECLARED_VARIABLE_ERROR", "Illegal redeclared variable: {0}");

  static final DiagnosticType DECLARATION_NOT_DIRECTLY_IN_BLOCK =
      DiagnosticType.error(
          "JSC_DECLARATION_NOT_DIRECTLY_IN_BLOCK",
          "Block-scoped declaration not directly within block: {0}");

  static final DiagnosticType UNUSED_LOCAL_ASSIGNMENT =
      DiagnosticType.disabled(
          "JSC_UNUSED_LOCAL_ASSIGNMENT", "Value assigned to local variable {0} is never read");

  private final AbstractCompiler compiler;

  // NOTE(nicksantos): It's a lot faster to use a shared Set that
  // we clear after each method call, because the Set never gets too big.
  private final Set<BasicBlock> blocksWithDeclarations = new HashSet<>();

  public VariableReferenceCheck(AbstractCompiler compiler) {
    this.compiler = compiler;
  }

  @Override
  public void process(Node externs, Node root) {
    ReferenceCollectingCallback callback =
        new ReferenceCollectingCallback(compiler, new ReferenceCheckingBehavior());
    callback.process(externs, root);
  }

  @Override
  public void hotSwapScript(Node scriptRoot, Node originalRoot) {
    ReferenceCollectingCallback callback =
        new ReferenceCollectingCallback(compiler, new ReferenceCheckingBehavior());
    callback.hotSwapScript(scriptRoot, originalRoot);
  }

  /**
   * Behavior that checks variables for redeclaration or early references
   * just after they go out of scope.
   */
  private class ReferenceCheckingBehavior implements Behavior {

    private Set<String> varsInFunctionBody;

    private ReferenceCheckingBehavior() {
      varsInFunctionBody = new HashSet<>();
    }

    @Override
    public void afterExitScope(NodeTraversal t, ReferenceMap referenceMap) {
      // TODO(bashir) In hot-swap version this means that for global scope we
      // only go through all global variables accessed in the modified file not
      // all global variables. This should be fixed.

      // Check all vars after finishing a scope
      Scope scope = t.getScope();
      if (scope.isFunctionBlockScope()) {
        varsInFunctionBody.clear();
        for (Var v : scope.getVarIterable()) {
          varsInFunctionBody.add(v.name);
        }
      }
      for (Var v : scope.getVarIterable()) {
        ReferenceCollection referenceCollection = referenceMap.getReferences(v);
        // TODO(moz): Figure out why this could be null
        if (referenceCollection != null) {
          if (scope.getRootNode().isFunction() && v.isDefaultParam()) {
            checkDefaultParam(v, scope, varsInFunctionBody);
          }
          if (scope.getRootNode().isFunction()) {
            checkShadowParam(v, scope, referenceCollection.references);
          }
          checkVar(v, referenceCollection.references);
        }
      }
    }

    private void checkDefaultParam(
        Var param, final Scope scope, final Set<String> varsInFunctionBody) {
      NodeTraversal.traverseEs6(
          compiler,
          param.getParentNode().getSecondChild(),
          /**
           * Do a shallow check since cases like:
           *   function f(y = () => x, x = 5) { return y(); }
           * is legal. We are going to miss cases like:
           *   function f(y = (() => x)(), x = 5) { return y(); }
           * but this should be rare.
           */
          new AbstractShallowCallback() {
            @Override
            public void visit(NodeTraversal t, Node n, Node parent) {
              if (!NodeUtil.isReferenceName(n)) {
                return;
              }
              String refName = n.getString();
              if (varsInFunctionBody.contains(refName) && !scope.isDeclared(refName, true)) {
                compiler.report(JSError.make(n, EARLY_REFERENCE_ERROR, refName));
              }
            }
          });
    }

    private void checkShadowParam(Var v, Scope functionScope, List<Reference> references) {
      Var maybeParam = functionScope.getVar(v.getName());
      if (maybeParam != null && maybeParam.isParam() && maybeParam.getScope() == functionScope) {
        for (Reference r : references) {
          if ((r.isVarDeclaration() || r.isHoistedFunction())
              && r.getNode() != v.getNameNode()) {
            compiler.report(JSError.make(r.getNode(), REDECLARED_VARIABLE, v.name));
          }
        }
      }
    }

    /**
     * If the variable is declared more than once in a basic block, generate a
     * warning. Also check if a variable is used in a given scope before it is
     * declared, which suggest a likely error. Relies on the fact that
     * references is in parse-tree order.
     */
    private void checkVar(Var v, List<Reference> references) {
      blocksWithDeclarations.clear();
{'fraction_non_alphanumeric': 0.08986316291102185, 'fraction_numerical': 0.002586969841377902, 'mean_word_length': 3.3193178476918552, 'pattern_counts': {'":': 0, '<': 23, '<?xml version=': 0, '>': 27, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5829908', 'n_tokens_mistral': 4047, 'n_tokens_neox': 3921, 'n_words': 1190}