Skip to content

Commit

Permalink
fix(eslint-plugin): backport upstream changes
Browse files Browse the repository at this point in the history
  • Loading branch information
Josh-Cena committed Mar 1, 2022
1 parent ede0e15 commit 49800bb
Show file tree
Hide file tree
Showing 2 changed files with 591 additions and 0 deletions.
125 changes: 125 additions & 0 deletions packages/eslint-plugin/src/rules/no-shadow.ts
Expand Up @@ -24,6 +24,9 @@ type Options = [
},
];

const SENTINEL_TYPE =
/^(?:(?:Function|Class)(?:Declaration|Expression)|ArrowFunctionExpression|CatchClause|ImportDeclaration|ExportNamedDeclaration)$/u;

export default util.createRule<Options, MessageIds>({
name: 'no-shadow',
meta: {
Expand Down Expand Up @@ -319,6 +322,124 @@ export default util.createRule<Options, MessageIds>({
);
}

/**
* Checks whether or not a given location is inside of the range of a given node.
* @param node An node to check.
* @param location A location to check.
* @returns `true` if the location is inside of the range of the node.
*/
function isInRange(
node: TSESTree.Node | null,
location: number,
): boolean | null {
return node && node.range[0] <= location && location <= node.range[1];
}

/**
* Searches from the current node through its ancestry to find a matching node.
* @param node a node to get.
* @param match a callback that checks whether or not the node verifies its condition or not.
* @returns the matching node.
*/
function findSelfOrAncestor(
node: TSESTree.Node | undefined,
match: (node: TSESTree.Node) => boolean,
): TSESTree.Node | undefined {
let currentNode = node;

while (currentNode && !match(currentNode)) {
currentNode = currentNode.parent;
}
return currentNode;
}

/**
* Finds function's outer scope.
* @param scope Function's own scope.
* @returns Function's outer scope.
*/
function getOuterScope(
scope: TSESLint.Scope.Scope,
): TSESLint.Scope.Scope | null {
const upper = scope.upper;

if (upper?.type === 'function-expression-name') {
return upper.upper;
}
return upper;
}

/**
* Checks if a variable and a shadowedVariable have the same init pattern ancestor.
* @param variable a variable to check.
* @param shadowedVariable a shadowedVariable to check.
* @returns Whether or not the variable and the shadowedVariable have the same init pattern ancestor.
*/
function isInitPatternNode(
variable: TSESLint.Scope.Variable,
shadowedVariable: TSESLint.Scope.Variable,
): boolean {
const outerDef = shadowedVariable.defs[0];

if (!outerDef) {
return false;
}

const { variableScope } = variable.scope;

if (
!(
(variableScope.block.type ===
AST_NODE_TYPES.ArrowFunctionExpression ||
variableScope.block.type === AST_NODE_TYPES.FunctionExpression) &&
getOuterScope(variableScope) === shadowedVariable.scope
)
) {
return false;
}

const fun = variableScope.block;
const { parent } = fun;

const callExpression = findSelfOrAncestor(
parent,
node => node.type === AST_NODE_TYPES.CallExpression,
);

if (!callExpression) {
return false;
}

let node: TSESTree.Node | undefined = outerDef.name;
const location = callExpression.range[1];

while (node) {
if (node.type === AST_NODE_TYPES.VariableDeclarator) {
if (isInRange(node.init, location)) {
return true;
}
if (
(node.parent?.parent?.type === AST_NODE_TYPES.ForInStatement ||
node.parent?.parent?.type === AST_NODE_TYPES.ForOfStatement) &&
isInRange(node.parent.parent.right, location)
) {
return true;
}
break;
} else if (node.type === AST_NODE_TYPES.AssignmentPattern) {
if (isInRange(node.right, location)) {
return true;
}
} else if (SENTINEL_TYPE.test(node.type)) {
break;
}

node = node.parent;
}

return false;
}

/**
* Checks if a variable is inside the initializer of scopeVar.
*
Expand Down Expand Up @@ -460,6 +581,10 @@ export default util.createRule<Options, MessageIds>({
(shadowed.identifiers.length > 0 ||
(options.builtinGlobals && isESLintGlobal)) &&
!isOnInitializer(variable, shadowed) &&
!(
options.ignoreOnInitialization &&
isInitPatternNode(variable, shadowed)
) &&
!(options.hoist !== 'all' && isInTdz(variable, shadowed))
) {
context.report({
Expand Down

0 comments on commit 49800bb

Please sign in to comment.