Skip to content

Commit

Permalink
fix(eslint-plugin): [consistent-indexed-object-style] don't report on…
Browse files Browse the repository at this point in the history
… indirect circular references (typescript-eslint#10537)

* initial implementation (squashed)

* revert unrelated changes

* remove now redundant eslint-disable comment

* slight formatting change

* remove unrelated changes

* add a test for missing coverage

* add indirect union recursive test

* remove additional unnecessary comments
  • Loading branch information
ronami authored Dec 30, 2024
1 parent 638c2e0 commit 7eba36e
Show file tree
Hide file tree
Showing 3 changed files with 377 additions and 13 deletions.
104 changes: 94 additions & 10 deletions packages/eslint-plugin/src/rules/consistent-indexed-object-style.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { ScopeVariable } from '@typescript-eslint/scope-manager';
import type { TSESLint, TSESTree } from '@typescript-eslint/utils';
import type { ReportFixFunction } from '@typescript-eslint/utils/ts-eslint';

Expand All @@ -6,6 +7,7 @@ import { AST_NODE_TYPES, ASTUtils } from '@typescript-eslint/utils';
import {
createRule,
getFixOrSuggest,
isNodeEqual,
isParenthesized,
nullThrows,
} from '../util';
Expand Down Expand Up @@ -78,16 +80,12 @@ export default createRule<Options, MessageIds>({
if (parentId) {
const scope = context.sourceCode.getScope(parentId);
const superVar = ASTUtils.findVariable(scope, parentId.name);
if (superVar) {
const isCircular = superVar.references.some(
item =>
item.isTypeReference &&
node.range[0] <= item.identifier.range[0] &&
node.range[1] >= item.identifier.range[1],
);
if (isCircular) {
return;
}

if (
superVar &&
isDeeplyReferencingType(node, superVar, new Set([parentId]))
) {
return;
}
}

Expand Down Expand Up @@ -269,3 +267,89 @@ function findParentDeclaration(
}
return undefined;
}

function isDeeplyReferencingType(
node: TSESTree.Node,
superVar: ScopeVariable,
visited: Set<TSESTree.Node>,
): boolean {
if (visited.has(node)) {
// something on the chain is circular but it's not the reference being checked
return false;
}

visited.add(node);

switch (node.type) {
case AST_NODE_TYPES.TSTypeLiteral:
return node.members.some(member =>
isDeeplyReferencingType(member, superVar, visited),
);
case AST_NODE_TYPES.TSTypeAliasDeclaration:
return isDeeplyReferencingType(node.typeAnnotation, superVar, visited);
case AST_NODE_TYPES.TSIndexedAccessType:
return [node.indexType, node.objectType].some(type =>
isDeeplyReferencingType(type, superVar, visited),
);
case AST_NODE_TYPES.TSConditionalType:
return [
node.checkType,
node.extendsType,
node.falseType,
node.trueType,
].some(type => isDeeplyReferencingType(type, superVar, visited));
case AST_NODE_TYPES.TSUnionType:
case AST_NODE_TYPES.TSIntersectionType:
return node.types.some(type =>
isDeeplyReferencingType(type, superVar, visited),
);
case AST_NODE_TYPES.TSInterfaceDeclaration:
return node.body.body.some(type =>
isDeeplyReferencingType(type, superVar, visited),
);
case AST_NODE_TYPES.TSTypeAnnotation:
return isDeeplyReferencingType(node.typeAnnotation, superVar, visited);
case AST_NODE_TYPES.TSIndexSignature: {
if (node.typeAnnotation) {
return isDeeplyReferencingType(node.typeAnnotation, superVar, visited);
}
break;
}
case AST_NODE_TYPES.TSTypeParameterInstantiation: {
return node.params.some(param =>
isDeeplyReferencingType(param, superVar, visited),
);
}
case AST_NODE_TYPES.TSTypeReference: {
if (isDeeplyReferencingType(node.typeName, superVar, visited)) {
return true;
}

if (
node.typeArguments &&
isDeeplyReferencingType(node.typeArguments, superVar, visited)
) {
return true;
}

break;
}
case AST_NODE_TYPES.Identifier: {
// check if the identifier is a reference of the type being checked
if (superVar.references.some(ref => isNodeEqual(ref.identifier, node))) {
return true;
}

// otherwise, follow its definition(s)
const refVar = ASTUtils.findVariable(superVar.scope, node.name);

if (refVar) {
return refVar.defs.some(def =>
isDeeplyReferencingType(def.node, superVar, visited),
);
}
}
}

return false;
}
Loading

0 comments on commit 7eba36e

Please sign in to comment.