<commit_before>"use strict";

module.exports = Scope;

var includes = require("lodash/collection/includes");
var traverse = require("./index");
var defaults = require("lodash/object/defaults");
var messages = require("../messages");
var globals  = require("globals");
var flatten  = require("lodash/array/flatten");
var extend   = require("lodash/object/extend");
var object   = require("../helpers/object");
var each     = require("lodash/collection/each");
var t        = require("../types");

/**
 * This searches the current "scope" and collects all references/bindings
 * within.
 *
 * @param {Node} block
 * @param {Node} parentBlock
 * @param {Scope} [parent]
 * @param {File} [file]
 */

function Scope(block, parentBlock, parent, file) {
  this.parent = parent;
  this.file   = parent ? parent.file : file;

  this.parentBlock = parentBlock;
  this.block       = block;

  this.crawl();
}

Scope.defaultDeclarations = flatten([globals.builtin, globals.browser, globals.node].map(Object.keys));

/**
 * Description
 *
 * @param {Object} node
 * @param {Object} opts
 * @param [state]
 */

Scope.prototype.traverse = function (node, opts, state) {
  traverse(node, opts, this, state);
};

/**
 * Description
 *
 * @param {File} file
 * @param {String} [name="temp"]
 */

Scope.prototype.generateTemp = function (file, name) {
  var id = file.generateUidIdentifier(name || "temp", this);
  this.push({
    key: id.name,
    id: id
  });
  return id;
};

/**
 * Description
 *
 * @param {String} name
 */

Scope.prototype.generateUidIdentifier = function (name) {
  return this.file.generateUidIdentifier(name, this);
};

/*
 * Description
 *
 * @param {Object} parent
 * @returns {Object}
 */

Scope.prototype.generateUidBasedOnNode = function (parent) {
  var node = parent;

  if (t.isAssignmentExpression(parent)) {
    node = parent.left;
  } else if (t.isVariableDeclarator(parent)) {
    node = parent.id;
  } else if (t.isProperty(node)) {
    node = node.key;
  }

  var parts = [];

  var add = function (node) {
    if (t.isMemberExpression(node)) {
      add(node.object);
      add(node.property);
    } else if (t.isIdentifier(node)) {
      parts.push(node.name);
    } else if (t.isLiteral(node)) {
      parts.push(node.value);
    } else if (t.isCallExpression(node)) {
      add(node.callee);
    }
  };

  add(node);

  var id = parts.join("$");
  id = id.replace(/^_/, "") || "ref";

  return this.file.generateUidIdentifier(id, this);
};

/**
 * Description
 *
 * @param {Object} node
 * @returns {Object}
 */

Scope.prototype.generateTempBasedOnNode = function (node) {
  if (t.isIdentifier(node) && this.hasBinding(node.name)) {
    return null;
  }

  var id = this.generateUidBasedOnNode(node);
  this.push({
    key: id.name,
    id: id
  });
  return id;
};

Scope.prototype.checkBlockScopedCollisions = function (kind, name, id) {
  var local = this.getOwnBindingInfo(name);
  if (!local) return;

  if (kind === "param") return;
  if (kind === "hoisted" && local.kind === "let") return;

  if (local.kind === "let" || local.kind === "const") {
    throw this.file.errorWithNode(id, messages.get("scopeDuplicateDeclaration", name), TypeError);
  }
};

Scope.prototype.rename = function (oldName, newName) {
  newName = newName || this.generateUidIdentifier(oldName).name;

  var info = this.getBindingInfo(oldName);
  if (!info) return;

  var binding = info.identifier;
  var scope = info.scope;

  scope.traverse(scope.block, {
    enter: function (node, parent, scope) {
      if (t.isReferencedIdentifier(node, parent) && node.name === oldName) {
        node.name = newName;
      } else if (t.isScope(node, parent)) {
        if (!scope.bindingIdentifierEquals(oldName, binding)) {
          this.skip();
        }
      }<commit_msg>add general support for declarations to Scope::rename - closes #751<commit_after>"use strict";

module.exports = Scope;

var includes = require("lodash/collection/includes");
var traverse = require("./index");
var defaults = require("lodash/object/defaults");
var messages = require("../messages");
var globals  = require("globals");
var flatten  = require("lodash/array/flatten");
var extend   = require("lodash/object/extend");
var object   = require("../helpers/object");
var each     = require("lodash/collection/each");
var t        = require("../types");

/**
 * This searches the current "scope" and collects all references/bindings
 * within.
 *
 * @param {Node} block
 * @param {Node} parentBlock
 * @param {Scope} [parent]
 * @param {File} [file]
 */

function Scope(block, parentBlock, parent, file) {
  this.parent = parent;
  this.file   = parent ? parent.file : file;

  this.parentBlock = parentBlock;
  this.block       = block;

  this.crawl();
}

Scope.defaultDeclarations = flatten([globals.builtin, globals.browser, globals.node].map(Object.keys));

/**
 * Description
 *
 * @param {Object} node
 * @param {Object} opts
 * @param [state]
 */

Scope.prototype.traverse = function (node, opts, state) {
  traverse(node, opts, this, state);
};

/**
 * Description
 *
 * @param {File} file
 * @param {String} [name="temp"]
 */

Scope.prototype.generateTemp = function (file, name) {
  var id = file.generateUidIdentifier(name || "temp", this);
  this.push({
    key: id.name,
    id: id
  });
  return id;
};

/**
 * Description
 *
 * @param {String} name
 */

Scope.prototype.generateUidIdentifier = function (name) {
  return this.file.generateUidIdentifier(name, this);
};

/*
 * Description
 *
 * @param {Object} parent
 * @returns {Object}
 */

Scope.prototype.generateUidBasedOnNode = function (parent) {
  var node = parent;

  if (t.isAssignmentExpression(parent)) {
    node = parent.left;
  } else if (t.isVariableDeclarator(parent)) {
    node = parent.id;
  } else if (t.isProperty(node)) {
    node = node.key;
  }

  var parts = [];

  var add = function (node) {
    if (t.isMemberExpression(node)) {
      add(node.object);
      add(node.property);
    } else if (t.isIdentifier(node)) {
      parts.push(node.name);
    } else if (t.isLiteral(node)) {
      parts.push(node.value);
    } else if (t.isCallExpression(node)) {
      add(node.callee);
    }
  };

  add(node);

  var id = parts.join("$");
  id = id.replace(/^_/, "") || "ref";

  return this.file.generateUidIdentifier(id, this);
};

/**
 * Description
 *
 * @param {Object} node
 * @returns {Object}
 */

Scope.prototype.generateTempBasedOnNode = function (node) {
  if (t.isIdentifier(node) && this.hasBinding(node.name)) {
    return null;
  }

  var id = this.generateUidBasedOnNode(node);
  this.push({
    key: id.name,
    id: id
  });
  return id;
};

Scope.prototype.checkBlockScopedCollisions = function (kind, name, id) {
  var local = this.getOwnBindingInfo(name);
  if (!local) return;

  if (kind === "param") return;
  if (kind === "hoisted" && local.kind === "let") return;

  if (local.kind === "let" || local.kind === "const") {
    throw this.file.errorWithNode(id, messages.get("scopeDuplicateDeclaration", name), TypeError);
  }
};

Scope.prototype.rename = function (oldName, newName) {
  newName = newName || this.generateUidIdentifier(oldName).name;

  var info = this.getBindingInfo(oldName);
  if (!info) return;

  var binding = info.identifier;
  var scope = info.scope;

  scope.traverse(scope.block, {
    enter: function (node, parent, scope) {
      if (t.isReferencedIdentifier(node, parent) && node.name === oldName) {
        node.name = newName;
      } else if (t.isDeclaration(node)) {
        var ids = t.getBindingIdentifiers(node);
        for (var name in ids) {
          if (name === oldName) ids[name].name = newName;
        }
      } else if (t.isScope(node, parent)) {
        if (!scope.bindingIdentifierEquals(oldName, binding)) {
          this.skip();
        }
      }
{'fraction_non_alphanumeric': 0.1647632490696779, 'fraction_numerical': 0.00038496086231233157, 'mean_word_length': 3.3909859154929576, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, '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': '2945110', 'n_tokens_mistral': 2595, 'n_tokens_neox': 2552, 'n_words': 584}