You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
136 lines
4.2 KiB
136 lines
4.2 KiB
/**
|
|
* @fileoverview Rule to check for "block scoped" variables by binding context
|
|
* @author Matt DuVall <http://www.mattduvall.com>
|
|
*/
|
|
"use strict";
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Rule Definition
|
|
//------------------------------------------------------------------------------
|
|
|
|
/** @type {import('../shared/types').Rule} */
|
|
module.exports = {
|
|
meta: {
|
|
type: "suggestion",
|
|
|
|
docs: {
|
|
description: "Enforce the use of variables within the scope they are defined",
|
|
recommended: false,
|
|
url: "https://eslint.org/docs/latest/rules/block-scoped-var"
|
|
},
|
|
|
|
schema: [],
|
|
|
|
messages: {
|
|
outOfScope: "'{{name}}' declared on line {{definitionLine}} column {{definitionColumn}} is used outside of binding context."
|
|
}
|
|
},
|
|
|
|
create(context) {
|
|
let stack = [];
|
|
const sourceCode = context.sourceCode;
|
|
|
|
/**
|
|
* Makes a block scope.
|
|
* @param {ASTNode} node A node of a scope.
|
|
* @returns {void}
|
|
*/
|
|
function enterScope(node) {
|
|
stack.push(node.range);
|
|
}
|
|
|
|
/**
|
|
* Pops the last block scope.
|
|
* @returns {void}
|
|
*/
|
|
function exitScope() {
|
|
stack.pop();
|
|
}
|
|
|
|
/**
|
|
* Reports a given reference.
|
|
* @param {eslint-scope.Reference} reference A reference to report.
|
|
* @param {eslint-scope.Definition} definition A definition for which to report reference.
|
|
* @returns {void}
|
|
*/
|
|
function report(reference, definition) {
|
|
const identifier = reference.identifier;
|
|
const definitionPosition = definition.name.loc.start;
|
|
|
|
context.report({
|
|
node: identifier,
|
|
messageId: "outOfScope",
|
|
data: {
|
|
name: identifier.name,
|
|
definitionLine: definitionPosition.line,
|
|
definitionColumn: definitionPosition.column + 1
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Finds and reports references which are outside of valid scopes.
|
|
* @param {ASTNode} node A node to get variables.
|
|
* @returns {void}
|
|
*/
|
|
function checkForVariables(node) {
|
|
if (node.kind !== "var") {
|
|
return;
|
|
}
|
|
|
|
// Defines a predicate to check whether or not a given reference is outside of valid scope.
|
|
const scopeRange = stack[stack.length - 1];
|
|
|
|
/**
|
|
* Check if a reference is out of scope
|
|
* @param {ASTNode} reference node to examine
|
|
* @returns {boolean} True is its outside the scope
|
|
* @private
|
|
*/
|
|
function isOutsideOfScope(reference) {
|
|
const idRange = reference.identifier.range;
|
|
|
|
return idRange[0] < scopeRange[0] || idRange[1] > scopeRange[1];
|
|
}
|
|
|
|
// Gets declared variables, and checks its references.
|
|
const variables = sourceCode.getDeclaredVariables(node);
|
|
|
|
for (let i = 0; i < variables.length; ++i) {
|
|
|
|
// Reports.
|
|
variables[i]
|
|
.references
|
|
.filter(isOutsideOfScope)
|
|
.forEach(ref => report(ref, variables[i].defs.find(def => def.parent === node)));
|
|
}
|
|
}
|
|
|
|
return {
|
|
Program(node) {
|
|
stack = [node.range];
|
|
},
|
|
|
|
// Manages scopes.
|
|
BlockStatement: enterScope,
|
|
"BlockStatement:exit": exitScope,
|
|
ForStatement: enterScope,
|
|
"ForStatement:exit": exitScope,
|
|
ForInStatement: enterScope,
|
|
"ForInStatement:exit": exitScope,
|
|
ForOfStatement: enterScope,
|
|
"ForOfStatement:exit": exitScope,
|
|
SwitchStatement: enterScope,
|
|
"SwitchStatement:exit": exitScope,
|
|
CatchClause: enterScope,
|
|
"CatchClause:exit": exitScope,
|
|
StaticBlock: enterScope,
|
|
"StaticBlock:exit": exitScope,
|
|
|
|
// Finds and reports references which are outside of valid scope.
|
|
VariableDeclaration: checkForVariables
|
|
};
|
|
|
|
}
|
|
};
|