diff --git a/packages/eslint-plugin/src/rules/naming-convention.ts b/packages/eslint-plugin/src/rules/naming-convention.ts index 635e1a625b80..e4a34ac75046 100644 --- a/packages/eslint-plugin/src/rules/naming-convention.ts +++ b/packages/eslint-plugin/src/rules/naming-convention.ts @@ -4,7 +4,7 @@ import { AST_NODE_TYPES, TSESLint } from '@typescript-eslint/utils'; import type { ScriptTarget } from 'typescript'; import { - collectUnusedVariables, + collectVariables, createRule, getParserServices, requiresQuoting as _requiresQuoting, @@ -159,7 +159,7 @@ export default createRule({ return modifiers; } - const unusedVariables = collectUnusedVariables(context); + const { unusedVariables } = collectVariables(context); function isUnused( name: string, initialScope: TSESLint.Scope.Scope | null, diff --git a/packages/eslint-plugin/src/rules/no-unused-vars.ts b/packages/eslint-plugin/src/rules/no-unused-vars.ts index ad6fd271952f..64b8a15f7ff0 100644 --- a/packages/eslint-plugin/src/rules/no-unused-vars.ts +++ b/packages/eslint-plugin/src/rules/no-unused-vars.ts @@ -1,9 +1,13 @@ -import { PatternVisitor } from '@typescript-eslint/scope-manager'; +import type { ScopeVariable } from '@typescript-eslint/scope-manager'; +import { + DefinitionType, + PatternVisitor, +} from '@typescript-eslint/scope-manager'; import type { TSESTree } from '@typescript-eslint/utils'; import { AST_NODE_TYPES, TSESLint } from '@typescript-eslint/utils'; import { - collectUnusedVariables as _collectUnusedVariables, + collectVariables, createRule, getNameLocationInGlobalDirectiveComment, isDefinitionFile, @@ -12,7 +16,7 @@ import { NullThrowsReasons, } from '../util'; -export type MessageIds = 'unusedVar'; +export type MessageIds = 'unusedVar' | 'usedIgnoredVar'; export type Options = [ | 'all' | 'local' @@ -25,6 +29,7 @@ export type Options = [ caughtErrors?: 'all' | 'none'; caughtErrorsIgnorePattern?: string; destructuredArrayIgnorePattern?: string; + reportUsedIgnorePattern?: boolean; }, ]; @@ -37,8 +42,15 @@ interface TranslatedOptions { caughtErrors: 'all' | 'none'; caughtErrorsIgnorePattern?: RegExp; destructuredArrayIgnorePattern?: RegExp; + reportUsedIgnorePattern: boolean; } +type VariableType = + | 'array-destructure' + | 'catch-clause' + | 'parameter' + | 'variable'; + export default createRule({ name: 'no-unused-vars', meta: { @@ -85,6 +97,9 @@ export default createRule({ destructuredArrayIgnorePattern: { type: 'string', }, + reportUsedIgnorePattern: { + type: 'boolean', + }, }, additionalProperties: false, }, @@ -93,6 +108,8 @@ export default createRule({ ], messages: { unusedVar: "'{{varName}}' is {{action}} but never used{{additional}}.", + usedIgnoredVar: + "'{{varName}}' is marked as ignored but is used{{additional}}.", }, }, defaultOptions: [{}], @@ -105,6 +122,7 @@ export default createRule({ args: 'after-used', ignoreRestSiblings: false, caughtErrors: 'all', + reportUsedIgnorePattern: false, }; if (typeof firstOption === 'string') { @@ -115,6 +133,9 @@ export default createRule({ options.ignoreRestSiblings = firstOption.ignoreRestSiblings ?? options.ignoreRestSiblings; options.caughtErrors = firstOption.caughtErrors ?? options.caughtErrors; + options.reportUsedIgnorePattern = + firstOption.reportUsedIgnorePattern ?? + options.reportUsedIgnorePattern; if (firstOption.varsIgnorePattern) { options.varsIgnorePattern = new RegExp( @@ -148,7 +169,158 @@ export default createRule({ return options; })(); - function collectUnusedVariables(): TSESLint.Scope.Variable[] { + /** + * Gets a given variable's description and configured ignore pattern + * based on the provided variableType + * @param variableType a simple name for the types of variables that this rule supports + * @returns the given variable's description and + * ignore pattern + */ + function getVariableDescription(variableType: VariableType): { + pattern: string | undefined; + variableDescription: string; + } { + switch (variableType) { + case 'array-destructure': + return { + pattern: options.destructuredArrayIgnorePattern?.toString(), + variableDescription: 'elements of array destructuring', + }; + + case 'catch-clause': + return { + pattern: options.caughtErrorsIgnorePattern?.toString(), + variableDescription: 'args', + }; + + case 'parameter': + return { + pattern: options.argsIgnorePattern?.toString(), + variableDescription: 'args', + }; + + case 'variable': + return { + pattern: options.varsIgnorePattern?.toString(), + variableDescription: 'vars', + }; + } + } + + /** + * Generates the message data about the variable being defined and unused, + * including the ignore pattern if configured. + * @param unusedVar eslint-scope variable object. + * @returns The message data to be used with this unused variable. + */ + function getDefinedMessageData( + unusedVar: ScopeVariable, + ): Record { + const def = unusedVar.defs.at(0)?.type; + let additionalMessageData = ''; + + if (def) { + const { variableDescription, pattern } = (() => { + switch (def) { + case DefinitionType.CatchClause: + if (options.caughtErrorsIgnorePattern) { + return getVariableDescription('catch-clause'); + } + break; + + case DefinitionType.Parameter: + if (options.argsIgnorePattern) { + return getVariableDescription('parameter'); + } + break; + + default: + if (options.varsIgnorePattern) { + return getVariableDescription('variable'); + } + break; + } + + return { pattern: undefined, variableDescription: undefined }; + })(); + + if (pattern && variableDescription) { + additionalMessageData = `. Allowed unused ${variableDescription} must match ${pattern}`; + } + } + + return { + varName: unusedVar.name, + action: 'defined', + additional: additionalMessageData, + }; + } + + /** + * Generate the warning message about the variable being + * assigned and unused, including the ignore pattern if configured. + * @param unusedVar eslint-scope variable object. + * @returns The message data to be used with this unused variable. + */ + function getAssignedMessageData( + unusedVar: ScopeVariable, + ): Record { + const def = unusedVar.defs.at(0); + let additionalMessageData = ''; + + if (def) { + const { variableDescription, pattern } = (() => { + if ( + def.name.parent.type === AST_NODE_TYPES.ArrayPattern && + options.destructuredArrayIgnorePattern + ) { + return getVariableDescription('array-destructure'); + } else if (options.varsIgnorePattern) { + return getVariableDescription('variable'); + } + + return { pattern: undefined, variableDescription: undefined }; + })(); + + if (pattern && variableDescription) { + additionalMessageData = `. Allowed unused ${variableDescription} must match ${pattern}`; + } + } + + return { + varName: unusedVar.name, + action: 'assigned a value', + additional: additionalMessageData, + }; + } + + /** + * Generate the warning message about a variable being used even though + * it is marked as being ignored. + * @param variable eslint-scope variable object + * @param variableType a simple name for the types of variables that this rule supports + * @returns The message data to be used with this used ignored variable. + */ + function getUsedIgnoredMessageData( + variable: ScopeVariable, + variableType: VariableType, + ): Record { + const { variableDescription, pattern } = + getVariableDescription(variableType); + + let additionalMessageData = ''; + + if (pattern && variableDescription) { + additionalMessageData = `. Used ${variableDescription} must not match ${pattern}`; + } + + return { + varName: variable.name, + additional: additionalMessageData, + }; + } + + function collectUnusedVariables(): ScopeVariable[] { /** * Checks whether a node is a sibling of the rest property or not. * @param node a node to check @@ -168,9 +340,7 @@ export default createRule({ * @param variable eslint-scope variable object. * @returns True if the variable is exported, false if not. */ - function hasRestSpreadSibling( - variable: TSESLint.Scope.Variable, - ): boolean { + function hasRestSpreadSibling(variable: ScopeVariable): boolean { if (options.ignoreRestSiblings) { const hasRestSiblingDefinition = variable.defs.some(def => hasRestSibling(def.name.parent), @@ -190,7 +360,7 @@ export default createRule({ * @param variable The variable to check. * @returns `true` if the variable is defined after the last used parameter. */ - function isAfterLastUsedArg(variable: TSESLint.Scope.Variable): boolean { + function isAfterLastUsedArg(variable: ScopeVariable): boolean { const def = variable.defs[0]; const params = context.sourceCode.getDeclaredVariables(def.node); const posteriorParams = params.slice(params.indexOf(variable) + 1); @@ -201,12 +371,25 @@ export default createRule({ ); } - const unusedVariablesOriginal = _collectUnusedVariables(context); - const unusedVariablesReturn: TSESLint.Scope.Variable[] = []; - for (const variable of unusedVariablesOriginal) { + const analysisResults = collectVariables(context); + const variables = [ + ...Array.from(analysisResults.unusedVariables, variable => ({ + used: false, + variable, + })), + ...Array.from(analysisResults.usedVariables, variable => ({ + used: true, + variable, + })), + ]; + const unusedVariablesReturn: ScopeVariable[] = []; + for (const { used, variable } of variables) { // explicit global variables don't have definitions. if (variable.defs.length === 0) { - unusedVariablesReturn.push(variable); + if (!used) { + unusedVariablesReturn.push(variable); + } + continue; } const def = variable.defs[0]; @@ -230,6 +413,13 @@ export default createRule({ 'name' in def.name && options.destructuredArrayIgnorePattern?.test(def.name.name) ) { + if (options.reportUsedIgnorePattern && used) { + context.report({ + node: def.name, + messageId: 'usedIgnoredVar', + data: getUsedIgnoredMessageData(variable, 'array-destructure'), + }); + } continue; } @@ -243,6 +433,13 @@ export default createRule({ 'name' in def.name && options.caughtErrorsIgnorePattern?.test(def.name.name) ) { + if (options.reportUsedIgnorePattern && used) { + context.report({ + node: def.name, + messageId: 'usedIgnoredVar', + data: getUsedIgnoredMessageData(variable, 'catch-clause'), + }); + } continue; } } else if (def.type === TSESLint.Scope.DefinitionType.Parameter) { @@ -255,6 +452,13 @@ export default createRule({ 'name' in def.name && options.argsIgnorePattern?.test(def.name.name) ) { + if (options.reportUsedIgnorePattern && used) { + context.report({ + node: def.name, + messageId: 'usedIgnoredVar', + data: getUsedIgnoredMessageData(variable, 'parameter'), + }); + } continue; } // if "args" option is "after-used", skip used variables @@ -271,6 +475,13 @@ export default createRule({ 'name' in def.name && options.varsIgnorePattern?.test(def.name.name) ) { + if (options.reportUsedIgnorePattern && used) { + context.report({ + node: def.name, + messageId: 'usedIgnoredVar', + data: getUsedIgnoredMessageData(variable, 'variable'), + }); + } continue; } } @@ -285,7 +496,9 @@ export default createRule({ continue; } - unusedVariablesReturn.push(variable); + if (!used) { + unusedVariablesReturn.push(variable); + } } return unusedVariablesReturn; @@ -334,78 +547,6 @@ export default createRule({ // collect 'Program:exit'(programNode): void { - /** - * Generates the message data about the variable being defined and unused, - * including the ignore pattern if configured. - * @param unusedVar eslint-scope variable object. - * @returns The message data to be used with this unused variable. - */ - function getDefinedMessageData( - unusedVar: TSESLint.Scope.Variable, - ): Record { - const defType = unusedVar.defs[0]?.type; - let type; - let pattern; - - if ( - defType === TSESLint.Scope.DefinitionType.CatchClause && - options.caughtErrorsIgnorePattern - ) { - type = 'args'; - pattern = options.caughtErrorsIgnorePattern.toString(); - } else if ( - defType === TSESLint.Scope.DefinitionType.Parameter && - options.argsIgnorePattern - ) { - type = 'args'; - pattern = options.argsIgnorePattern.toString(); - } else if ( - defType !== TSESLint.Scope.DefinitionType.Parameter && - options.varsIgnorePattern - ) { - type = 'vars'; - pattern = options.varsIgnorePattern.toString(); - } - - const additional = type - ? `. Allowed unused ${type} must match ${pattern}` - : ''; - - return { - varName: unusedVar.name, - action: 'defined', - additional, - }; - } - - /** - * Generate the warning message about the variable being - * assigned and unused, including the ignore pattern if configured. - * @param unusedVar eslint-scope variable object. - * @returns The message data to be used with this unused variable. - */ - function getAssignedMessageData( - unusedVar: TSESLint.Scope.Variable, - ): Record { - const def = unusedVar.defs.at(0); - let additional = ''; - - if ( - options.destructuredArrayIgnorePattern && - def?.name.parent.type === AST_NODE_TYPES.ArrayPattern - ) { - additional = `. Allowed unused elements of array destructuring patterns must match ${options.destructuredArrayIgnorePattern.toString()}`; - } else if (options.varsIgnorePattern) { - additional = `. Allowed unused vars must match ${options.varsIgnorePattern.toString()}`; - } - - return { - varName: unusedVar.name, - action: 'assigned a value', - additional, - }; - } - const unusedVars = collectUnusedVariables(); for (const unusedVar of unusedVars) { @@ -625,17 +766,7 @@ namespace Test { } type T = Test.Foo.T; // Error: Namespace 'Test' has no exported member 'Foo'. -*/ - -/* - -###### TODO ###### - -We currently extend base `no-unused-vars` implementation because it's easier and lighter-weight. - -Because of this, there are a few false-negatives which won't get caught. -We could fix these if we fork the base rule; but that's a lot of code (~650 lines) to add in. -I didn't want to do that just yet without some real-world issues, considering these are pretty rare edge-cases. +--- These cases are mishandled because the base rule assumes that each variable has one def, but type-value shadowing creates a variable with two defs diff --git a/packages/eslint-plugin/src/util/collectUnusedVariables.ts b/packages/eslint-plugin/src/util/collectUnusedVariables.ts index 443167a3e3c0..82dc0d55b3d2 100644 --- a/packages/eslint-plugin/src/util/collectUnusedVariables.ts +++ b/packages/eslint-plugin/src/util/collectUnusedVariables.ts @@ -1,3 +1,7 @@ +import type { + ScopeManager, + ScopeVariable, +} from '@typescript-eslint/scope-manager'; import { ImplicitLibVariable, ScopeType, @@ -11,42 +15,48 @@ import { TSESLint, } from '@typescript-eslint/utils'; -class UnusedVarsVisitor< - MessageIds extends string, - Options extends readonly unknown[], -> extends Visitor { +interface VariableAnalysis { + readonly unusedVariables: ReadonlySet; + readonly usedVariables: ReadonlySet; +} +interface MutableVariableAnalysis { + readonly unusedVariables: Set; + readonly usedVariables: Set; +} + +/** + * This class leverages an AST visitor to mark variables as used via the + * `eslintUsed` property. + */ +class UnusedVarsVisitor extends Visitor { + /** + * We keep a weak cache so that multiple rules can share the calculation + */ private static readonly RESULTS_CACHE = new WeakMap< TSESTree.Program, - ReadonlySet + VariableAnalysis >(); readonly #scopeManager: TSESLint.Scope.ScopeManager; - // readonly #unusedVariables = new Set(); - private constructor(context: TSESLint.RuleContext) { + private constructor(scopeManager: ScopeManager) { super({ visitChildrenEvenIfSelectorExists: true, }); - this.#scopeManager = ESLintUtils.nullThrows( - context.sourceCode.scopeManager, - 'Missing required scope manager', - ); + this.#scopeManager = scopeManager; } - public static collectUnusedVariables< - MessageIds extends string, - Options extends readonly unknown[], - >( - context: TSESLint.RuleContext, - ): ReadonlySet { - const program = context.sourceCode.ast; + public static collectUnusedVariables( + program: TSESTree.Program, + scopeManager: ScopeManager, + ): VariableAnalysis { const cached = this.RESULTS_CACHE.get(program); if (cached) { return cached; } - const visitor = new this(context); + const visitor = new this(scopeManager); visitor.visit(program); const unusedVars = visitor.collectUnusedVariables( @@ -58,34 +68,49 @@ class UnusedVarsVisitor< private collectUnusedVariables( scope: TSESLint.Scope.Scope, - unusedVariables = new Set(), - ): ReadonlySet { - for (const variable of scope.variables) { - if ( - // skip function expression names, - scope.functionExpressionScope || - // variables marked with markVariableAsUsed(), - variable.eslintUsed || - // implicit lib variables (from @typescript-eslint/scope-manager), - variable instanceof ImplicitLibVariable || - // basic exported variables - isExported(variable) || - // variables implicitly exported via a merged declaration - isMergableExported(variable) || - // used variables - isUsedVariable(variable) - ) { - continue; - } + variables: MutableVariableAnalysis = { + unusedVariables: new Set(), + usedVariables: new Set(), + }, + ): VariableAnalysis { + if ( + // skip function expression names + // this scope is created just to house the variable that allows a function + // expression to self-reference if it has a name defined + !scope.functionExpressionScope + ) { + for (const variable of scope.variables) { + // cases that we don't want to treat as used or unused + if ( + // implicit lib variables (from @typescript-eslint/scope-manager) + // these aren't variables that should be checked ever + variable instanceof ImplicitLibVariable + ) { + continue; + } - unusedVariables.add(variable); + if ( + // variables marked with markVariableAsUsed() + variable.eslintUsed || + // basic exported variables + isExported(variable) || + // variables implicitly exported via a merged declaration + isMergableExported(variable) || + // used variables + isUsedVariable(variable) + ) { + variables.usedVariables.add(variable); + } else { + variables.unusedVariables.add(variable); + } + } } for (const childScope of scope.childScopes) { - this.collectUnusedVariables(childScope, unusedVariables); + this.collectUnusedVariables(childScope, variables); } - return unusedVariables; + return variables; } //#region HELPERS @@ -114,14 +139,11 @@ class UnusedVarsVisitor< } private markVariableAsUsed( - variableOrIdentifier: TSESLint.Scope.Variable | TSESTree.Identifier, + variableOrIdentifier: ScopeVariable | TSESTree.Identifier, ): void; private markVariableAsUsed(name: string, parent: TSESTree.Node): void; private markVariableAsUsed( - variableOrIdentifierOrName: - | TSESLint.Scope.Variable - | TSESTree.Identifier - | string, + variableOrIdentifierOrName: ScopeVariable | TSESTree.Identifier | string, parent?: TSESTree.Node, ): void { if ( @@ -398,7 +420,7 @@ const MERGABLE_TYPES = new Set([ * Determine if the variable is directly exported * @param variable the variable to check */ -function isMergableExported(variable: TSESLint.Scope.Variable): boolean { +function isMergableExported(variable: ScopeVariable): boolean { // If all of the merged things are of the same type, TS will error if not all of them are exported - so we only need to find one for (const def of variable.defs) { // parameters can never be exported. @@ -425,7 +447,7 @@ function isMergableExported(variable: TSESLint.Scope.Variable): boolean { * @param variable eslint-scope variable object. * @returns True if the variable is exported, false if not. */ -function isExported(variable: TSESLint.Scope.Variable): boolean { +function isExported(variable: ScopeVariable): boolean { return variable.defs.some(definition => { let node = definition.node; @@ -448,15 +470,13 @@ const LOGICAL_ASSIGNMENT_OPERATORS = new Set(['&&=', '||=', '??=']); * @param variable The variable to check. * @returns True if the variable is used */ -function isUsedVariable(variable: TSESLint.Scope.Variable): boolean { +function isUsedVariable(variable: ScopeVariable): boolean { /** * Gets a list of function definitions for a specified variable. * @param variable eslint-scope variable object. * @returns Function nodes. */ - function getFunctionDefinitions( - variable: TSESLint.Scope.Variable, - ): Set { + function getFunctionDefinitions(variable: ScopeVariable): Set { const functionDefinitions = new Set(); variable.defs.forEach(def => { @@ -477,9 +497,7 @@ function isUsedVariable(variable: TSESLint.Scope.Variable): boolean { return functionDefinitions; } - function getTypeDeclarations( - variable: TSESLint.Scope.Variable, - ): Set { + function getTypeDeclarations(variable: ScopeVariable): Set { const nodes = new Set(); variable.defs.forEach(def => { @@ -494,9 +512,7 @@ function isUsedVariable(variable: TSESLint.Scope.Variable): boolean { return nodes; } - function getModuleDeclarations( - variable: TSESLint.Scope.Variable, - ): Set { + function getModuleDeclarations(variable: ScopeVariable): Set { const nodes = new Set(); variable.defs.forEach(def => { @@ -508,6 +524,18 @@ function isUsedVariable(variable: TSESLint.Scope.Variable): boolean { return nodes; } + function getEnumDeclarations(variable: ScopeVariable): Set { + const nodes = new Set(); + + variable.defs.forEach(def => { + if (def.node.type === AST_NODE_TYPES.TSEnumDeclaration) { + nodes.add(def.node); + } + }); + + return nodes; + } + /** * Checks if the ref is contained within one of the given nodes */ @@ -729,6 +757,9 @@ function isUsedVariable(variable: TSESLint.Scope.Variable): boolean { const moduleDeclNodes = getModuleDeclarations(variable); const isModuleDecl = moduleDeclNodes.size > 0; + const enumDeclNodes = getEnumDeclarations(variable); + const isEnumDecl = enumDeclNodes.size > 0; + let rhsNode: TSESTree.Node | null = null; return variable.references.some(ref => { @@ -741,7 +772,8 @@ function isUsedVariable(variable: TSESLint.Scope.Variable): boolean { !forItself && !(isFunctionDefinition && isSelfReference(ref, functionNodes)) && !(isTypeDecl && isInsideOneOf(ref, typeDeclNodes)) && - !(isModuleDecl && isSelfReference(ref, moduleDeclNodes)) + !(isModuleDecl && isSelfReference(ref, moduleDeclNodes)) && + !(isEnumDecl && isSelfReference(ref, enumDeclNodes)) ); }); } @@ -755,13 +787,19 @@ function isUsedVariable(variable: TSESLint.Scope.Variable): boolean { * - variables within declaration files * - variables within ambient module declarations */ -function collectUnusedVariables< +function collectVariables< MessageIds extends string, Options extends readonly unknown[], >( context: Readonly>, -): ReadonlySet { - return UnusedVarsVisitor.collectUnusedVariables(context); +): VariableAnalysis { + return UnusedVarsVisitor.collectUnusedVariables( + context.sourceCode.ast, + ESLintUtils.nullThrows( + context.sourceCode.scopeManager, + 'Missing required scope manager', + ), + ); } -export { collectUnusedVariables }; +export { collectVariables }; diff --git a/packages/eslint-plugin/tests/rules/no-unused-vars/no-unused-vars-eslint.test.ts b/packages/eslint-plugin/tests/rules/no-unused-vars/no-unused-vars-eslint.test.ts index af8c42b63408..9d2866a079d8 100644 --- a/packages/eslint-plugin/tests/rules/no-unused-vars/no-unused-vars-eslint.test.ts +++ b/packages/eslint-plugin/tests/rules/no-unused-vars/no-unused-vars-eslint.test.ts @@ -4,6 +4,7 @@ import { RuleTester } from '@typescript-eslint/rule-tester'; import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; +import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import type { MessageIds } from '../../../src/rules/no-unused-vars'; import rule from '../../../src/rules/no-unused-vars'; @@ -79,10 +80,46 @@ function assignedError( }; } +/** + * Returns an expected error for used-but-ignored variables. + * @param varName The name of the variable + * @param [additional] The additional text for the message data + * @param [type] The node type (defaults to "Identifier") + * @returns An expected error object + */ +function usedIgnoredError( + varName: string, + additional = '', + type: AST_NODE_TYPES = AST_NODE_TYPES.Identifier, +): TSESLint.TestCaseError { + return { + messageId: 'usedIgnoredVar', + data: { + varName, + additional, + }, + type, + }; +} + ruleTester.run('no-unused-vars', rule, { valid: [ - 'var foo = 5;\n\nlabel: while (true) {\n console.log(foo);\n break label;\n}', - 'var foo = 5;\n\nwhile (true) {\n console.log(foo);\n break;\n}', + ` +var foo = 5; + +label: while (true) { + console.log(foo); + break label; +} + `, + ` +var foo = 5; + +while (true) { + console.log(foo); + break; +} + `, { code: ` for (let prop in box) { @@ -987,28 +1024,27 @@ foo(); `, // https://github.com/eslint/eslint/issues/6576 - [ - 'var unregisterFooWatcher;', - '// ...', - 'unregisterFooWatcher = $scope.$watch( "foo", function() {', - ' // ...some code..', - ' unregisterFooWatcher();', - '});', - ].join('\n'), - [ - 'var ref;', - 'ref = setInterval(', - ' function(){', - ' clearInterval(ref);', - ' }, 10);', - ].join('\n'), - [ - 'var _timer;', - 'function f() {', - ' _timer = setTimeout(function () {}, _timer ? 100 : 0);', - '}', - 'f();', - ].join('\n'), + ` +var unregisterFooWatcher; +// ... +unregisterFooWatcher = $scope.$watch('foo', function () { + // ...some code.. + unregisterFooWatcher(); +}); + `, + ` +var ref; +ref = setInterval(function () { + clearInterval(ref); +}, 10); + `, + ` +var _timer; +function f() { + _timer = setTimeout(function () {}, _timer ? 100 : 0); +} +f(); + `, ` function foo(cb) { cb = (function () { @@ -1054,15 +1090,16 @@ foo(); `, // https://github.com/eslint/eslint/issues/6646 - [ - 'function someFunction() {', - ' var a = 0, i;', - ' for (i = 0; i < 2; i++) {', - ' a = myFunction(a);', - ' }', - '}', - 'someFunction();', - ].join('\n'), + ` +function someFunction() { + var a = 0, + i; + for (i = 0; i < 2; i++) { + a = myFunction(a); + } +} +someFunction(); + `, // https://github.com/eslint/eslint/issues/7124 { @@ -1131,7 +1168,11 @@ console.log(Foo); // https://github.com/eslint/eslint/issues/14163 { - code: 'let foo, rest;\n({ foo, ...rest } = something);\nconsole.log(rest);', + code: ` +let foo, rest; +({ foo, ...rest } = something); +console.log(rest); + `, options: [{ ignoreRestSiblings: true }], parserOptions: { ecmaVersion: 2020 }, }, @@ -1188,6 +1229,38 @@ a(); code: 'import.meta;', parserOptions: { ecmaVersion: 2020, sourceType: 'module' }, }, + + // https://github.com/eslint/eslint/issues/17568 + { + code: ` +const a = 5; +const _c = a + 5; + `, + options: [ + { args: 'all', varsIgnorePattern: '^_', reportUsedIgnorePattern: true }, + ], + parserOptions: { ecmaVersion: 6 }, + }, + { + code: ` +(function foo(a, _b) { + return a + 5; +})(5); + `, + options: [ + { args: 'all', argsIgnorePattern: '^_', reportUsedIgnorePattern: true }, + ], + }, + { + code: ` +const [a, _b, c] = items; +console.log(a + c); + `, + options: [ + { destructuredArrayIgnorePattern: '^_', reportUsedIgnorePattern: true }, + ], + parserOptions: { ecmaVersion: 6 }, + }, ], invalid: [ { @@ -1647,7 +1720,7 @@ const [a, _b, c] = array; { ...assignedError( 'a', - '. Allowed unused elements of array destructuring patterns must match /^_/u', + '. Allowed unused elements of array destructuring must match /^_/u', ), line: 3, column: 8, @@ -1655,7 +1728,7 @@ const [a, _b, c] = array; { ...assignedError( 'c', - '. Allowed unused elements of array destructuring patterns must match /^_/u', + '. Allowed unused elements of array destructuring must match /^_/u', ), line: 3, column: 15, @@ -1678,7 +1751,7 @@ const ignoreArray = ['ignore']; { ...assignedError( 'a', - '. Allowed unused elements of array destructuring patterns must match /^_/u', + '. Allowed unused elements of array destructuring must match /^_/u', ), line: 3, column: 8, @@ -1686,7 +1759,7 @@ const ignoreArray = ['ignore']; { ...assignedError( 'c', - '. Allowed unused elements of array destructuring patterns must match /^_/u', + '. Allowed unused elements of array destructuring must match /^_/u', ), line: 3, column: 15, @@ -2048,13 +2121,15 @@ console.log(coords); // https://github.com/eslint/eslint/issues/3714 { - // cspell:disable-next-line - code: '/* global a$fooz,$foo */\na$fooz;', + code: ` +/* global a$fooz,$foo */ +a$fooz; + `, errors: [ { - line: 1, + line: 2, column: 18, - endLine: 1, + endLine: 2, endColumn: 22, messageId: 'unusedVar', data: { @@ -2066,13 +2141,15 @@ console.log(coords); ], }, { - // cspell:disable-next-line - code: '/* globals a$fooz, $ */\na$fooz;', + code: ` +/* globals a$fooz, $ */ +a$fooz; + `, errors: [ { - line: 1, + line: 2, column: 20, - endLine: 1, + endLine: 2, endColumn: 21, messageId: 'unusedVar', data: { @@ -2137,12 +2214,16 @@ console.log(coords); // non ascii. { - code: '/*global 変数, 数*/\n変数;', + code: ` +/*global 変数, 数*/ + +変数; + `, errors: [ { - line: 1, + line: 2, column: 14, - endLine: 1, + endLine: 2, endColumn: 15, messageId: 'unusedVar', data: { @@ -2246,7 +2327,7 @@ try { } catch (err) {} `, options: [{ caughtErrors: 'all', varsIgnorePattern: '^err' }], - errors: [definedError('err', '. Allowed unused vars must match /^err/u')], + errors: [definedError('err')], }, { code: ` @@ -2254,7 +2335,7 @@ try { } catch (err) {} `, options: [{ caughtErrors: 'all', varsIgnorePattern: '^.' }], - errors: [definedError('err', '. Allowed unused vars must match /^./u')], + errors: [definedError('err')], }, // multiple try catch with one success @@ -2438,14 +2519,14 @@ foo(); // https://github.com/eslint/eslint/issues/6646 { - code: [ - 'while (a) {', - ' function foo(b) {', - ' b = b + 1;', - ' }', - ' foo()', - '}', - ].join('\n'), + code: ` +while (a) { + function foo(b) { + b = b + 1; + } + foo(); +} + `, errors: [assignedError('b')], }, @@ -2704,9 +2785,12 @@ const a = () => () => { // https://github.com/eslint/eslint/issues/14324 { - code: 'let x = [];\nx = x.concat(x);', + code: ` +let x = []; +x = x.concat(x); + `, parserOptions: { ecmaVersion: 2015 }, - errors: [{ ...assignedError('x'), line: 2, column: 1 }], + errors: [{ ...assignedError('x'), line: 3, column: 1 }], }, { code: ` @@ -2776,5 +2860,110 @@ c = foo1; }, ], }, + + // https://github.com/eslint/eslint/issues/17568 + { + code: ` +const _a = 5; +const _b = _a + 5; + `, + options: [ + { args: 'all', varsIgnorePattern: '^_', reportUsedIgnorePattern: true }, + ], + parserOptions: { ecmaVersion: 6 }, + errors: [usedIgnoredError('_a', '. Used vars must not match /^_/u')], + }, + { + code: ` +const _a = 42; +foo(() => _a); + `, + options: [ + { args: 'all', varsIgnorePattern: '^_', reportUsedIgnorePattern: true }, + ], + parserOptions: { ecmaVersion: 6 }, + errors: [usedIgnoredError('_a', '. Used vars must not match /^_/u')], + }, + { + code: ` +(function foo(_a) { + return _a + 5; +})(5); + `, + options: [ + { args: 'all', argsIgnorePattern: '^_', reportUsedIgnorePattern: true }, + ], + errors: [usedIgnoredError('_a', '. Used args must not match /^_/u')], + }, + { + code: ` +const [a, _b] = items; +console.log(a + _b); + `, + options: [ + { destructuredArrayIgnorePattern: '^_', reportUsedIgnorePattern: true }, + ], + parserOptions: { ecmaVersion: 6 }, + errors: [ + usedIgnoredError( + '_b', + '. Used elements of array destructuring must not match /^_/u', + ), + ], + }, + { + code: ` +let _x; +[_x] = arr; +foo(_x); + `, + options: [ + { + destructuredArrayIgnorePattern: '^_', + reportUsedIgnorePattern: true, + varsIgnorePattern: '[iI]gnored', + }, + ], + parserOptions: { ecmaVersion: 6 }, + errors: [ + usedIgnoredError( + '_x', + '. Used elements of array destructuring must not match /^_/u', + ), + ], + }, + { + code: ` +const [ignored] = arr; +foo(ignored); + `, + options: [ + { + destructuredArrayIgnorePattern: '^_', + reportUsedIgnorePattern: true, + varsIgnorePattern: '[iI]gnored', + }, + ], + parserOptions: { ecmaVersion: 6 }, + errors: [ + usedIgnoredError('ignored', '. Used vars must not match /[iI]gnored/u'), + ], + }, + { + code: ` +try { +} catch (_err) { + console.error(_err); +} + `, + options: [ + { + caughtErrors: 'all', + caughtErrorsIgnorePattern: '^_', + reportUsedIgnorePattern: true, + }, + ], + errors: [usedIgnoredError('_err', '. Used args must not match /^_/u')], + }, ], }); diff --git a/packages/eslint-plugin/tests/rules/no-unused-vars/no-unused-vars.test.ts b/packages/eslint-plugin/tests/rules/no-unused-vars/no-unused-vars.test.ts index b4706224efac..7202218bc962 100644 --- a/packages/eslint-plugin/tests/rules/no-unused-vars/no-unused-vars.test.ts +++ b/packages/eslint-plugin/tests/rules/no-unused-vars/no-unused-vars.test.ts @@ -1,7 +1,7 @@ import { noFormat, RuleTester } from '@typescript-eslint/rule-tester'; import rule from '../../../src/rules/no-unused-vars'; -import { collectUnusedVariables } from '../../../src/util'; +import { collectVariables } from '../../../src/util'; import { getFixturesRootDir } from '../../RuleTester'; const ruleTester = new RuleTester({ @@ -22,7 +22,7 @@ const withMetaParserOptions = { // this is used to ensure that the caching the utility does does not impact the results done by no-unused-vars ruleTester.defineRule('collect-unused-vars', { create(context) { - collectUnusedVariables(context); + collectVariables(context); return {}; }, defaultOptions: [], @@ -1143,6 +1143,13 @@ export namespace Bar { export import TheFoo = Foo; } `, + { + code: ` +type _Foo = 1; +export const x: _Foo = 1; + `, + options: [{ varsIgnorePattern: '^_', reportUsedIgnorePattern: false }], + }, ], invalid: [ @@ -1995,5 +2002,96 @@ const foo: number = 1; }, ], }, + { + code: ` +enum Foo { + A = 1, + B = Foo.A, +} + `, + errors: [ + { + messageId: 'unusedVar', + data: { + varName: 'Foo', + action: 'defined', + additional: '', + }, + line: 2, + }, + ], + }, + + // reportUsedIgnorePattern + { + code: ` +type _Foo = 1; +export const x: _Foo = 1; + `, + options: [{ varsIgnorePattern: '^_', reportUsedIgnorePattern: true }], + errors: [ + { + messageId: 'usedIgnoredVar', + data: { + varName: '_Foo', + additional: '. Used vars must not match /^_/u', + }, + line: 2, + }, + ], + }, + { + code: ` +interface _Foo {} +export const x: _Foo = 1; + `, + options: [{ varsIgnorePattern: '^_', reportUsedIgnorePattern: true }], + errors: [ + { + messageId: 'usedIgnoredVar', + data: { + varName: '_Foo', + additional: '. Used vars must not match /^_/u', + }, + line: 2, + }, + ], + }, + { + code: ` +enum _Foo { + A = 1, +} +export const x = _Foo.A; + `, + options: [{ varsIgnorePattern: '^_', reportUsedIgnorePattern: true }], + errors: [ + { + messageId: 'usedIgnoredVar', + data: { + varName: '_Foo', + additional: '. Used vars must not match /^_/u', + }, + line: 2, + }, + ], + }, + { + code: ` +namespace _Foo {} +export const x = _Foo; + `, + options: [{ varsIgnorePattern: '^_', reportUsedIgnorePattern: true }], + errors: [ + { + messageId: 'usedIgnoredVar', + data: { + varName: '_Foo', + additional: '. Used vars must not match /^_/u', + }, + line: 2, + }, + ], + }, ], }); diff --git a/packages/eslint-plugin/tests/schema-snapshots/no-unused-vars.shot b/packages/eslint-plugin/tests/schema-snapshots/no-unused-vars.shot index a87d2016784b..7717d324d3db 100644 --- a/packages/eslint-plugin/tests/schema-snapshots/no-unused-vars.shot +++ b/packages/eslint-plugin/tests/schema-snapshots/no-unused-vars.shot @@ -34,6 +34,9 @@ exports[`Rule schemas should be convertible to TS types for documentation purpos "ignoreRestSiblings": { "type": "boolean" }, + "reportUsedIgnorePattern": { + "type": "boolean" + }, "vars": { "enum": ["all", "local"], "type": "string" @@ -61,6 +64,7 @@ type Options = [ caughtErrorsIgnorePattern?: string; destructuredArrayIgnorePattern?: string; ignoreRestSiblings?: boolean; + reportUsedIgnorePattern?: boolean; vars?: 'all' | 'local'; varsIgnorePattern?: string; }, diff --git a/packages/scope-manager/src/referencer/Referencer.ts b/packages/scope-manager/src/referencer/Referencer.ts index f10263642fa1..07fbcce32562 100644 --- a/packages/scope-manager/src/referencer/Referencer.ts +++ b/packages/scope-manager/src/referencer/Referencer.ts @@ -669,13 +669,6 @@ class Referencer extends Visitor { // enum members can be referenced within the enum body this.scopeManager.nestTSEnumScope(node); - // define the enum name again inside the new enum scope - // references to the enum should not resolve directly to the enum - this.currentScope().defineIdentifier( - node.id, - new TSEnumNameDefinition(node.id, node), - ); - for (const member of node.body.members) { // TS resolves literal named members to be actual names // enum Foo { diff --git a/packages/scope-manager/src/variable/index.ts b/packages/scope-manager/src/variable/index.ts index 912aebcf05cd..80d3d9e485af 100644 --- a/packages/scope-manager/src/variable/index.ts +++ b/packages/scope-manager/src/variable/index.ts @@ -1,6 +1,11 @@ +import type { ESLintScopeVariable } from './ESLintScopeVariable'; +import type { Variable } from './Variable'; + export { ESLintScopeVariable } from './ESLintScopeVariable'; export { ImplicitLibVariable, ImplicitLibVariableOptions, } from './ImplicitLibVariable'; export { Variable } from './Variable'; + +export type ScopeVariable = Variable | ESLintScopeVariable; diff --git a/packages/scope-manager/tests/fixtures/ts-enum/external-ref.ts.shot b/packages/scope-manager/tests/fixtures/ts-enum/external-ref.ts.shot index 5702c7630c55..d280c28da478 100644 --- a/packages/scope-manager/tests/fixtures/ts-enum/external-ref.ts.shot +++ b/packages/scope-manager/tests/fixtures/ts-enum/external-ref.ts.shot @@ -27,19 +27,7 @@ ScopeManager { }, Variable$3 { defs: [ - TSEnumNameDefinition$2 { - name: Identifier<"Foo">, - node: TSEnumDeclaration$1, - }, - ], - name: "Foo", - references: [], - isValueVariable: true, - isTypeVariable: true, - }, - Variable$4 { - defs: [ - TSEnumMemberDefinition$3 { + TSEnumMemberDefinition$2 { name: Identifier<"a">, node: TSEnumMember$2, }, @@ -73,14 +61,12 @@ ScopeManager { isStrict: true, references: [], set: Map { - "Foo" => Variable$3, - "a" => Variable$4, + "a" => Variable$3, }, type: "tsEnum", upper: GlobalScope$1, variables: [ Variable$3, - Variable$4, ], }, ], diff --git a/packages/scope-manager/tests/fixtures/ts-enum/literal-member-ref.ts.shot b/packages/scope-manager/tests/fixtures/ts-enum/literal-member-ref.ts.shot index ced0d8421032..b2ad6e79966f 100644 --- a/packages/scope-manager/tests/fixtures/ts-enum/literal-member-ref.ts.shot +++ b/packages/scope-manager/tests/fixtures/ts-enum/literal-member-ref.ts.shot @@ -18,19 +18,7 @@ ScopeManager { }, Variable$3 { defs: [ - TSEnumNameDefinition$2 { - name: Identifier<"Foo">, - node: TSEnumDeclaration$1, - }, - ], - name: "Foo", - references: [], - isValueVariable: true, - isTypeVariable: true, - }, - Variable$4 { - defs: [ - TSEnumMemberDefinition$3 { + TSEnumMemberDefinition$2 { name: Literal$2, node: TSEnumMember$3, }, @@ -43,15 +31,15 @@ ScopeManager { isTypeReference: false, isValueReference: true, isWrite: false, - resolved: Variable$4, + resolved: Variable$3, }, ], isValueVariable: true, isTypeVariable: true, }, - Variable$5 { + Variable$4 { defs: [ - TSEnumMemberDefinition$4 { + TSEnumMemberDefinition$3 { name: Identifier<"b">, node: TSEnumMember$4, }, @@ -85,16 +73,14 @@ ScopeManager { Reference$1, ], set: Map { - "Foo" => Variable$3, - "a" => Variable$4, - "b" => Variable$5, + "a" => Variable$3, + "b" => Variable$4, }, type: "tsEnum", upper: GlobalScope$1, variables: [ Variable$3, Variable$4, - Variable$5, ], }, ], diff --git a/packages/scope-manager/tests/fixtures/ts-enum/literal-member.ts.shot b/packages/scope-manager/tests/fixtures/ts-enum/literal-member.ts.shot index 1d47840172b3..211dc5d3fef8 100644 --- a/packages/scope-manager/tests/fixtures/ts-enum/literal-member.ts.shot +++ b/packages/scope-manager/tests/fixtures/ts-enum/literal-member.ts.shot @@ -18,19 +18,7 @@ ScopeManager { }, Variable$3 { defs: [ - TSEnumNameDefinition$2 { - name: Identifier<"Foo">, - node: TSEnumDeclaration$1, - }, - ], - name: "Foo", - references: [], - isValueVariable: true, - isTypeVariable: true, - }, - Variable$4 { - defs: [ - TSEnumMemberDefinition$3 { + TSEnumMemberDefinition$2 { name: Literal$2, node: TSEnumMember$3, }, @@ -62,14 +50,12 @@ ScopeManager { isStrict: true, references: [], set: Map { - "Foo" => Variable$3, - "a" => Variable$4, + "a" => Variable$3, }, type: "tsEnum", upper: GlobalScope$1, variables: [ Variable$3, - Variable$4, ], }, ], diff --git a/packages/scope-manager/tests/fixtures/ts-enum/member-ref.ts.shot b/packages/scope-manager/tests/fixtures/ts-enum/member-ref.ts.shot index 71c949c32dc8..72e9fca37e02 100644 --- a/packages/scope-manager/tests/fixtures/ts-enum/member-ref.ts.shot +++ b/packages/scope-manager/tests/fixtures/ts-enum/member-ref.ts.shot @@ -18,19 +18,7 @@ ScopeManager { }, Variable$3 { defs: [ - TSEnumNameDefinition$2 { - name: Identifier<"Foo">, - node: TSEnumDeclaration$1, - }, - ], - name: "Foo", - references: [], - isValueVariable: true, - isTypeVariable: true, - }, - Variable$4 { - defs: [ - TSEnumMemberDefinition$3 { + TSEnumMemberDefinition$2 { name: Identifier<"a">, node: TSEnumMember$2, }, @@ -43,15 +31,15 @@ ScopeManager { isTypeReference: false, isValueReference: true, isWrite: false, - resolved: Variable$4, + resolved: Variable$3, }, ], isValueVariable: true, isTypeVariable: true, }, - Variable$5 { + Variable$4 { defs: [ - TSEnumMemberDefinition$4 { + TSEnumMemberDefinition$3 { name: Identifier<"b">, node: TSEnumMember$3, }, @@ -85,16 +73,14 @@ ScopeManager { Reference$1, ], set: Map { - "Foo" => Variable$3, - "a" => Variable$4, - "b" => Variable$5, + "a" => Variable$3, + "b" => Variable$4, }, type: "tsEnum", upper: GlobalScope$1, variables: [ Variable$3, Variable$4, - Variable$5, ], }, ], diff --git a/packages/scope-manager/tests/fixtures/ts-enum/scope.ts.shot b/packages/scope-manager/tests/fixtures/ts-enum/scope.ts.shot index 329ce74b931d..cef8795c18ee 100644 --- a/packages/scope-manager/tests/fixtures/ts-enum/scope.ts.shot +++ b/packages/scope-manager/tests/fixtures/ts-enum/scope.ts.shot @@ -18,19 +18,7 @@ ScopeManager { }, Variable$3 { defs: [ - TSEnumNameDefinition$2 { - name: Identifier<"Foo">, - node: TSEnumDeclaration$1, - }, - ], - name: "Foo", - references: [], - isValueVariable: true, - isTypeVariable: true, - }, - Variable$4 { - defs: [ - TSEnumMemberDefinition$3 { + TSEnumMemberDefinition$2 { name: Identifier<"a">, node: TSEnumMember$2, }, @@ -40,9 +28,9 @@ ScopeManager { isValueVariable: true, isTypeVariable: true, }, - Variable$5 { + Variable$4 { defs: [ - VariableDefinition$4 { + VariableDefinition$3 { name: Identifier<"unresolved">, node: VariableDeclarator$3, }, @@ -56,7 +44,7 @@ ScopeManager { isTypeReference: false, isValueReference: true, isWrite: true, - resolved: Variable$5, + resolved: Variable$4, writeExpr: Identifier<"a">, }, ], @@ -82,14 +70,14 @@ ScopeManager { set: Map { "const" => ImplicitGlobalConstTypeVariable, "Foo" => Variable$2, - "unresolved" => Variable$5, + "unresolved" => Variable$4, }, type: "global", upper: null, variables: [ ImplicitGlobalConstTypeVariable, Variable$2, - Variable$5, + Variable$4, ], }, TSEnumScope$2 { @@ -97,14 +85,12 @@ ScopeManager { isStrict: true, references: [], set: Map { - "Foo" => Variable$3, - "a" => Variable$4, + "a" => Variable$3, }, type: "tsEnum", upper: GlobalScope$1, variables: [ Variable$3, - Variable$4, ], }, ], diff --git a/packages/scope-manager/tests/fixtures/ts-enum/self-ref.ts.shot b/packages/scope-manager/tests/fixtures/ts-enum/self-ref.ts.shot index 541e5457731b..4b705897e680 100644 --- a/packages/scope-manager/tests/fixtures/ts-enum/self-ref.ts.shot +++ b/packages/scope-manager/tests/fixtures/ts-enum/self-ref.ts.shot @@ -12,18 +12,6 @@ ScopeManager { }, ], name: "Foo", - references: [], - isValueVariable: true, - isTypeVariable: true, - }, - Variable$3 { - defs: [ - TSEnumNameDefinition$2 { - name: Identifier<"Foo">, - node: TSEnumDeclaration$1, - }, - ], - name: "Foo", references: [ Reference$1 { identifier: Identifier<"Foo">, @@ -31,15 +19,15 @@ ScopeManager { isTypeReference: false, isValueReference: true, isWrite: false, - resolved: Variable$3, + resolved: Variable$2, }, ], isValueVariable: true, isTypeVariable: true, }, - Variable$4 { + Variable$3 { defs: [ - TSEnumMemberDefinition$3 { + TSEnumMemberDefinition$2 { name: Identifier<"a">, node: TSEnumMember$2, }, @@ -49,9 +37,9 @@ ScopeManager { isValueVariable: true, isTypeVariable: true, }, - Variable$5 { + Variable$4 { defs: [ - TSEnumMemberDefinition$4 { + TSEnumMemberDefinition$3 { name: Identifier<"b">, node: TSEnumMember$3, }, @@ -85,16 +73,14 @@ ScopeManager { Reference$1, ], set: Map { - "Foo" => Variable$3, - "a" => Variable$4, - "b" => Variable$5, + "a" => Variable$3, + "b" => Variable$4, }, type: "tsEnum", upper: GlobalScope$1, variables: [ Variable$3, Variable$4, - Variable$5, ], }, ], diff --git a/packages/scope-manager/tests/fixtures/type-declaration/signatures/method-computed-name2.ts.shot b/packages/scope-manager/tests/fixtures/type-declaration/signatures/method-computed-name2.ts.shot index c3664e85979e..b74e711bd4a0 100644 --- a/packages/scope-manager/tests/fixtures/type-declaration/signatures/method-computed-name2.ts.shot +++ b/packages/scope-manager/tests/fixtures/type-declaration/signatures/method-computed-name2.ts.shot @@ -27,19 +27,7 @@ ScopeManager { }, Variable$3 { defs: [ - TSEnumNameDefinition$2 { - name: Identifier<"Foo">, - node: TSEnumDeclaration$1, - }, - ], - name: "Foo", - references: [], - isValueVariable: true, - isTypeVariable: true, - }, - Variable$4 { - defs: [ - TSEnumMemberDefinition$3 { + TSEnumMemberDefinition$2 { name: Identifier<"a">, node: TSEnumMember$2, }, @@ -49,9 +37,9 @@ ScopeManager { isValueVariable: true, isTypeVariable: true, }, - Variable$5 { + Variable$4 { defs: [ - TypeDefinition$4 { + TypeDefinition$3 { name: Identifier<"A">, node: TSTypeAliasDeclaration$3, }, @@ -72,14 +60,14 @@ ScopeManager { set: Map { "const" => ImplicitGlobalConstTypeVariable, "Foo" => Variable$2, - "A" => Variable$5, + "A" => Variable$4, }, type: "global", upper: null, variables: [ ImplicitGlobalConstTypeVariable, Variable$2, - Variable$5, + Variable$4, ], }, TSEnumScope$2 { @@ -87,14 +75,12 @@ ScopeManager { isStrict: true, references: [], set: Map { - "Foo" => Variable$3, - "a" => Variable$4, + "a" => Variable$3, }, type: "tsEnum", upper: GlobalScope$1, variables: [ Variable$3, - Variable$4, ], }, FunctionTypeScope$3 { diff --git a/packages/scope-manager/tests/fixtures/type-declaration/signatures/property-computed-name2.ts.shot b/packages/scope-manager/tests/fixtures/type-declaration/signatures/property-computed-name2.ts.shot index 3bdcd2395537..b52012160ac1 100644 --- a/packages/scope-manager/tests/fixtures/type-declaration/signatures/property-computed-name2.ts.shot +++ b/packages/scope-manager/tests/fixtures/type-declaration/signatures/property-computed-name2.ts.shot @@ -27,19 +27,7 @@ ScopeManager { }, Variable$3 { defs: [ - TSEnumNameDefinition$2 { - name: Identifier<"Foo">, - node: TSEnumDeclaration$1, - }, - ], - name: "Foo", - references: [], - isValueVariable: true, - isTypeVariable: true, - }, - Variable$4 { - defs: [ - TSEnumMemberDefinition$3 { + TSEnumMemberDefinition$2 { name: Identifier<"a">, node: TSEnumMember$2, }, @@ -49,9 +37,9 @@ ScopeManager { isValueVariable: true, isTypeVariable: true, }, - Variable$5 { + Variable$4 { defs: [ - TypeDefinition$4 { + TypeDefinition$3 { name: Identifier<"A">, node: TSTypeAliasDeclaration$3, }, @@ -72,14 +60,14 @@ ScopeManager { set: Map { "const" => ImplicitGlobalConstTypeVariable, "Foo" => Variable$2, - "A" => Variable$5, + "A" => Variable$4, }, type: "global", upper: null, variables: [ ImplicitGlobalConstTypeVariable, Variable$2, - Variable$5, + Variable$4, ], }, TSEnumScope$2 { @@ -87,14 +75,12 @@ ScopeManager { isStrict: true, references: [], set: Map { - "Foo" => Variable$3, - "a" => Variable$4, + "a" => Variable$3, }, type: "tsEnum", upper: GlobalScope$1, variables: [ Variable$3, - Variable$4, ], }, ], diff --git a/packages/utils/src/ts-eslint/Scope.ts b/packages/utils/src/ts-eslint/Scope.ts index 8a89ca02bd59..8cde263e555c 100644 --- a/packages/utils/src/ts-eslint/Scope.ts +++ b/packages/utils/src/ts-eslint/Scope.ts @@ -5,9 +5,7 @@ import * as scopeManager from '@typescript-eslint/scope-manager'; namespace Scope { export type ScopeManager = scopeManager.ScopeManager; export type Reference = scopeManager.Reference; - export type Variable = - | scopeManager.ESLintScopeVariable - | scopeManager.Variable; + export type Variable = scopeManager.ScopeVariable; export type Scope = scopeManager.Scope; export const ScopeType = scopeManager.ScopeType; // TODO - in the next major, clean this up with a breaking change