diff --git a/eslint.config.mjs b/eslint.config.mjs index 9241cbee26a5..3e5efbf853cd 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -236,7 +236,9 @@ export default defineConfig( 'no-lonely-if': 'error', 'no-mixed-operators': 'error', 'no-process-exit': 'error', + 'no-unassigned-vars': 'error', 'no-unreachable-loop': 'error', + 'no-useless-assignment': 'error', 'no-useless-call': 'error', 'no-useless-computed-key': 'error', 'no-useless-concat': 'error', diff --git a/packages/eslint-plugin/src/util/getFunctionHeadLoc.ts b/packages/eslint-plugin/src/util/getFunctionHeadLoc.ts index 49ab2b742dd9..53987cddc37b 100644 --- a/packages/eslint-plugin/src/util/getFunctionHeadLoc.ts +++ b/packages/eslint-plugin/src/util/getFunctionHeadLoc.ts @@ -154,8 +154,8 @@ export function getFunctionHeadLoc( sourceCode: TSESLint.SourceCode, ): TSESTree.SourceLocation { const parent = node.parent; - let start: TSESTree.Position | null = null; - let end: TSESTree.Position | null = null; + let start: TSESTree.Position; + let end: TSESTree.Position; if ( parent.type === AST_NODE_TYPES.MethodDefinition || diff --git a/packages/eslint-plugin/tools/generate-breaking-changes.mts b/packages/eslint-plugin/tools/generate-breaking-changes.mts index 0ddb3563b976..0d3d0b4a6b1f 100644 --- a/packages/eslint-plugin/tools/generate-breaking-changes.mts +++ b/packages/eslint-plugin/tools/generate-breaking-changes.mts @@ -22,6 +22,7 @@ async function getNewRulesAsOfMajorVersion( // Normally we wouldn't condone using the 'eval' API... // But this is an internal-only script and it's the easiest way to convert // the JS raw text into a runtime object. 🤷 + // eslint-disable-next-line no-unassigned-vars -- assigned by eval let oldRulesObject!: { rules: TypeScriptESLintRules }; eval(`oldRulesObject = ${oldObjectText}`); const oldRuleNames = new Set(Object.keys(oldRulesObject.rules)); diff --git a/packages/rule-tester/src/RuleTester.ts b/packages/rule-tester/src/RuleTester.ts index c81f7296e189..ccf143c1f506 100644 --- a/packages/rule-tester/src/RuleTester.ts +++ b/packages/rule-tester/src/RuleTester.ts @@ -761,8 +761,8 @@ export class RuleTester extends TestFramework { // Verify the code. let initialMessages: Linter.LintMessage[] | null = null; - let messages: Linter.LintMessage[] | null = null; - let fixedResult: SourceCodeFixer.AppliedFixes | null = null; + let messages: Linter.LintMessage[]; + let fixedResult: SourceCodeFixer.AppliedFixes; let passNumber = 0; const outputs: string[] = []; const configWithoutCustomKeys = omitCustomConfigProperties(config); diff --git a/packages/scope-manager/src/referencer/ClassVisitor.ts b/packages/scope-manager/src/referencer/ClassVisitor.ts index 0a8d0b9cf82c..83f44ccc5f33 100644 --- a/packages/scope-manager/src/referencer/ClassVisitor.ts +++ b/packages/scope-manager/src/referencer/ClassVisitor.ts @@ -9,23 +9,18 @@ import { TypeVisitor } from './TypeVisitor'; import { Visitor } from './Visitor'; export class ClassVisitor extends Visitor { - readonly #classNode: TSESTree.ClassDeclaration | TSESTree.ClassExpression; readonly #referencer: Referencer; - constructor( - referencer: Referencer, - node: TSESTree.ClassDeclaration | TSESTree.ClassExpression, - ) { + constructor(referencer: Referencer) { super(referencer); this.#referencer = referencer; - this.#classNode = node; } static visit( referencer: Referencer, node: TSESTree.ClassDeclaration | TSESTree.ClassExpression, ): void { - const classVisitor = new ClassVisitor(referencer, node); + const classVisitor = new ClassVisitor(referencer); classVisitor.visitClass(node); } @@ -97,7 +92,7 @@ export class ClassVisitor extends Visitor { } if (node.value.type === AST_NODE_TYPES.FunctionExpression) { - this.visitMethodFunction(node.value, node); + this.visitMethodFunction(node.value); } else { this.#referencer.visit(node.value); } @@ -105,10 +100,7 @@ export class ClassVisitor extends Visitor { node.decorators.forEach(d => this.#referencer.visit(d)); } - protected visitMethodFunction( - node: TSESTree.FunctionExpression, - methodNode: TSESTree.MethodDefinition, - ): void { + protected visitMethodFunction(node: TSESTree.FunctionExpression): void { if (node.id) { // FunctionExpression with name creates its special scope; // FunctionExpressionNameScope. @@ -122,71 +114,6 @@ export class ClassVisitor extends Visitor { // Consider this function is in the MethodDefinition. this.#referencer.scopeManager.nestFunctionScope(node, true); - /** - * class A { - * @meta // <--- check this - * foo(a: Type) {} - * - * @meta // <--- check this - * foo(): Type {} - * } - */ - let withMethodDecorators = !!methodNode.decorators.length; - /** - * class A { - * foo( - * @meta // <--- check this - * a: Type - * ) {} - * - * set foo( - * @meta // <--- EXCEPT this. TS do nothing for this - * a: Type - * ) {} - * } - */ - withMethodDecorators ||= - methodNode.kind !== 'set' && - node.params.some(param => param.decorators.length); - if (!withMethodDecorators && methodNode.kind === 'set') { - const keyName = getLiteralMethodKeyName(methodNode); - - /** - * class A { - * @meta // <--- check this - * get a() {} - * set ['a'](v: Type) {} - * } - */ - if ( - keyName != null && - this.#classNode.body.body.find( - (node): node is TSESTree.MethodDefinition => - node !== methodNode && - node.type === AST_NODE_TYPES.MethodDefinition && - // Node must both be static or not - node.static === methodNode.static && - getLiteralMethodKeyName(node) === keyName, - )?.decorators.length - ) { - withMethodDecorators = true; - } - } - - /** - * @meta // <--- check this - * class A { - * constructor(a: Type) {} - * } - */ - if ( - !withMethodDecorators && - methodNode.kind === 'constructor' && - this.#classNode.decorators.length - ) { - withMethodDecorators = true; - } - // Process parameter declarations. for (const param of node.params) { this.visitPattern( @@ -337,39 +264,3 @@ export class ClassVisitor extends Visitor { this.visitType(node); } } - -/** - * Only if key is one of [identifier, string, number], ts will combine metadata of accessors . - * class A { - * get a() {} - * set ['a'](v: Type) {} - * - * get [1]() {} - * set [1](v: Type) {} - * - * // Following won't be combined - * get [key]() {} - * set [key](v: Type) {} - * - * get [true]() {} - * set [true](v: Type) {} - * - * get ['a'+'b']() {} - * set ['a'+'b']() {} - * } - */ -function getLiteralMethodKeyName( - node: TSESTree.MethodDefinition, -): number | string | null { - if (node.computed && node.key.type === AST_NODE_TYPES.Literal) { - if ( - typeof node.key.value === 'string' || - typeof node.key.value === 'number' - ) { - return node.key.value; - } - } else if (!node.computed && node.key.type === AST_NODE_TYPES.Identifier) { - return node.key.name; - } - return null; -} diff --git a/packages/scope-manager/tests/eslint-scope/es6-class.test.ts b/packages/scope-manager/tests/eslint-scope/es6-class.test.ts index c7ab81d99077..eaae8dd0210b 100644 --- a/packages/scope-manager/tests/eslint-scope/es6-class.test.ts +++ b/packages/scope-manager/tests/eslint-scope/es6-class.test.ts @@ -140,6 +140,7 @@ describe('ES6 class', () => { assert.isScopeOfType(scope, ScopeType.global); expect(scope.block.type).toBe(AST_NODE_TYPES.Program); expect(scope.isStrict).toBe(false); + expect(variables).toHaveLength(0); scope = scopeManager.scopes[1]; variables = getRealVariables(scope.variables); diff --git a/packages/scope-manager/tests/eslint-scope/es6-object.test.ts b/packages/scope-manager/tests/eslint-scope/es6-object.test.ts index 7f26c43ff780..ffdc8541978d 100644 --- a/packages/scope-manager/tests/eslint-scope/es6-object.test.ts +++ b/packages/scope-manager/tests/eslint-scope/es6-object.test.ts @@ -19,6 +19,7 @@ describe('ES6 object', () => { assert.isScopeOfType(scope, ScopeType.global); expect(scope.block.type).toBe(AST_NODE_TYPES.Program); expect(scope.isStrict).toBe(false); + expect(variables).toHaveLength(0); scope = scopeManager.scopes[1]; variables = getRealVariables(scope.variables); @@ -51,6 +52,7 @@ describe('ES6 object', () => { assert.isScopeOfType(scope, ScopeType.global); expect(scope.block.type).toBe(AST_NODE_TYPES.Program); expect(scope.isStrict).toBe(false); + expect(variables).toHaveLength(0); scope = scopeManager.scopes[1]; variables = getRealVariables(scope.variables); diff --git a/packages/website/src/components/editor/createProvideTwoslashInlay.ts b/packages/website/src/components/editor/createProvideTwoslashInlay.ts index e775e80e519a..7ec378b4aff7 100644 --- a/packages/website/src/components/editor/createProvideTwoslashInlay.ts +++ b/packages/website/src/components/editor/createProvideTwoslashInlay.ts @@ -8,14 +8,9 @@ import type * as ts from 'typescript'; import type { SandboxInstance } from './useSandboxServices'; function findTwoshashQueries(code: string): RegExpExecArray[] { - let match: RegExpExecArray | null = null; - const matches: RegExpExecArray[] = []; // RegExp that matches '^//?$' - const twoslashQueryRegex = /^(\s*\/\/\s*\^\?)\s*$/gm; - while ((match = twoslashQueryRegex.exec(code))) { - matches.push(match); - } - return matches; + const twoslashQueryRegex = /^(\s*\/\/\s*\^\?)\s*$/m; + return [...code.matchAll(twoslashQueryRegex)]; } export function createTwoslashInlayProvider(