From 5117057ee5498df63a135c0e2b3386ff14e9f5d8 Mon Sep 17 00:00:00 2001 From: ScriptedAlchemy Date: Thu, 19 Feb 2026 19:29:26 -0800 Subject: [PATCH 1/5] feat: port rule @typescript-eslint/max-params --- internal/config/config.go | 2 + .../typescript/rules/max_params/max_params.go | 141 ++++++++++ .../typescript/rules/max_params/max_params.md | 29 ++ .../rules/max_params/max_params_test.go | 157 +++++++++++ packages/rslint-test-tools/rstest.config.mts | 2 +- .../__snapshots__/max-params.test.ts.snap | 251 ++++++++++++++++++ rslint.json | 3 +- 7 files changed, 583 insertions(+), 2 deletions(-) create mode 100644 internal/plugins/typescript/rules/max_params/max_params.go create mode 100644 internal/plugins/typescript/rules/max_params/max_params.md create mode 100644 internal/plugins/typescript/rules/max_params/max_params_test.go create mode 100644 packages/rslint-test-tools/tests/typescript-eslint/rules/__snapshots__/max-params.test.ts.snap diff --git a/internal/config/config.go b/internal/config/config.go index 2fb18ab35..d23042c22 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -25,6 +25,7 @@ import ( "github.com/web-infra-dev/rslint/internal/plugins/typescript/rules/consistent_type_imports" "github.com/web-infra-dev/rslint/internal/plugins/typescript/rules/default_param_last" "github.com/web-infra-dev/rslint/internal/plugins/typescript/rules/dot_notation" + "github.com/web-infra-dev/rslint/internal/plugins/typescript/rules/max_params" "github.com/web-infra-dev/rslint/internal/plugins/typescript/rules/no_array_constructor" "github.com/web-infra-dev/rslint/internal/plugins/typescript/rules/no_array_delete" "github.com/web-infra-dev/rslint/internal/plugins/typescript/rules/no_base_to_string" @@ -391,6 +392,7 @@ func registerAllTypeScriptEslintPluginRules() { GlobalRuleRegistry.Register("@typescript-eslint/consistent-type-imports", consistent_type_imports.ConsistentTypeImportsRule) GlobalRuleRegistry.Register("@typescript-eslint/default-param-last", default_param_last.DefaultParamLastRule) GlobalRuleRegistry.Register("@typescript-eslint/dot-notation", dot_notation.DotNotationRule) + GlobalRuleRegistry.Register("@typescript-eslint/max-params", max_params.MaxParamsRule) GlobalRuleRegistry.Register("@typescript-eslint/no-array-constructor", no_array_constructor.NoArrayConstructorRule) GlobalRuleRegistry.Register("@typescript-eslint/no-array-delete", no_array_delete.NoArrayDeleteRule) GlobalRuleRegistry.Register("@typescript-eslint/no-base-to-string", no_base_to_string.NoBaseToStringRule) diff --git a/internal/plugins/typescript/rules/max_params/max_params.go b/internal/plugins/typescript/rules/max_params/max_params.go new file mode 100644 index 000000000..7e3d6b709 --- /dev/null +++ b/internal/plugins/typescript/rules/max_params/max_params.go @@ -0,0 +1,141 @@ +package max_params + +import ( + "fmt" + + "github.com/microsoft/typescript-go/shim/ast" + "github.com/web-infra-dev/rslint/internal/rule" +) + +type MaxParamsOptions struct { + Max int `json:"max"` + CountVoidThis bool `json:"countVoidThis"` +} + +func parseNumericOption(value interface{}) (int, bool) { + switch v := value.(type) { + case int: + return v, true + case int32: + return int(v), true + case int64: + return int(v), true + case float32: + return int(v), true + case float64: + return int(v), true + default: + return 0, false + } +} + +func parseOptions(options any) MaxParamsOptions { + opts := MaxParamsOptions{ + Max: 3, + CountVoidThis: false, + } + + if options == nil { + return opts + } + + var optsMap map[string]interface{} + if arr, ok := options.([]interface{}); ok && len(arr) > 0 { + optsMap, _ = arr[0].(map[string]interface{}) + } else { + optsMap, _ = options.(map[string]interface{}) + } + + if optsMap == nil { + return opts + } + + hasMax := false + if value, ok := optsMap["max"]; ok { + if maxValue, ok := parseNumericOption(value); ok { + opts.Max = maxValue + hasMax = true + } + } + if !hasMax { + if value, ok := optsMap["maximum"]; ok { + if maxValue, ok := parseNumericOption(value); ok { + opts.Max = maxValue + } + } + } + if value, ok := optsMap["countVoidThis"]; ok { + if flag, ok := value.(bool); ok { + opts.CountVoidThis = flag + } + } + + return opts +} + +func isVoidThisParameter(param *ast.Node) bool { + if param == nil || !ast.IsParameter(param) { + return false + } + + decl := param.AsParameterDeclaration() + if decl == nil { + return false + } + + name := decl.Name() + if name == nil || name.Kind != ast.KindIdentifier { + return false + } + + if name.AsIdentifier().Text != "this" { + return false + } + + return decl.Type != nil && decl.Type.Kind == ast.KindVoidKeyword +} + +func buildExceedMessage(count int, maxCount int) rule.RuleMessage { + return rule.RuleMessage{ + Id: "exceed", + Description: fmt.Sprintf("Function has too many parameters (%d). Maximum allowed is %d.", count, maxCount), + } +} + +var MaxParamsRule = rule.CreateRule(rule.Rule{ + Name: "max-params", + Run: func(ctx rule.RuleContext, options any) rule.RuleListeners { + opts := parseOptions(options) + + checkParameters := func(node *ast.Node) { + params := node.Parameters() + if params == nil { + return + } + + count := len(params) + if !opts.CountVoidThis && count > 0 && isVoidThisParameter(params[0]) { + count-- + } + + if count > opts.Max { + ctx.ReportNode(node, buildExceedMessage(count, opts.Max)) + } + } + + return rule.RuleListeners{ + ast.KindFunctionDeclaration: checkParameters, + ast.KindFunctionExpression: checkParameters, + ast.KindArrowFunction: checkParameters, + ast.KindMethodDeclaration: checkParameters, + ast.KindMethodSignature: checkParameters, + ast.KindConstructor: checkParameters, + ast.KindGetAccessor: checkParameters, + ast.KindSetAccessor: checkParameters, + ast.KindCallSignature: checkParameters, + ast.KindConstructSignature: checkParameters, + ast.KindFunctionType: checkParameters, + ast.KindConstructorType: checkParameters, + } + }, +}) diff --git a/internal/plugins/typescript/rules/max_params/max_params.md b/internal/plugins/typescript/rules/max_params/max_params.md new file mode 100644 index 000000000..af952892a --- /dev/null +++ b/internal/plugins/typescript/rules/max_params/max_params.md @@ -0,0 +1,29 @@ +# max-params + +## Rule Details + +Enforce a maximum number of parameters in function-like declarations. + +Examples of **incorrect** code for this rule: + +```typescript +function foo(a, b, c, d) {} + +class Foo { + method(this: Foo, a, b, c) {} +} +``` + +Examples of **correct** code for this rule: + +```typescript +function foo(a, b, c) {} + +class Foo { + method(this: void, a, b, c) {} +} +``` + +## Original Documentation + +https://typescript-eslint.io/rules/max-params diff --git a/internal/plugins/typescript/rules/max_params/max_params_test.go b/internal/plugins/typescript/rules/max_params/max_params_test.go new file mode 100644 index 000000000..405c900dc --- /dev/null +++ b/internal/plugins/typescript/rules/max_params/max_params_test.go @@ -0,0 +1,157 @@ +package max_params + +import ( + "testing" + + "github.com/web-infra-dev/rslint/internal/plugins/typescript/rules/fixtures" + "github.com/web-infra-dev/rslint/internal/rule_tester" +) + +func TestMaxParamsRule(t *testing.T) { + rule_tester.RunRuleTester(fixtures.GetRootDir(), "tsconfig.json", t, &MaxParamsRule, []rule_tester.ValidTestCase{ + {Code: `function foo() {}`}, + {Code: `const foo = function () {};`}, + {Code: `const foo = () => {};`}, + {Code: `function foo(a) {}`}, + { + Code: ` +class Foo { + constructor(a) {} +} + `, + }, + { + Code: ` +class Foo { + method(this: void, a, b, c) {} +} + `, + }, + { + Code: ` +class Foo { + method(this: Foo, a, b) {} +} + `, + }, + { + Code: `function foo(a, b, c, d) {}`, + Options: []interface{}{map[string]interface{}{"max": 4}}, + }, + { + Code: `function foo(a, b, c, d) {}`, + Options: []interface{}{map[string]interface{}{"maximum": 4}}, + }, + { + Code: ` +class Foo { + method(this: void) {} +} + `, + Options: []interface{}{map[string]interface{}{"max": 0}}, + }, + { + Code: ` +class Foo { + method(this: void, a) {} +} + `, + Options: []interface{}{map[string]interface{}{"max": 1}}, + }, + { + Code: ` +class Foo { + method(this: void, a) {} +} + `, + Options: []interface{}{map[string]interface{}{"countVoidThis": true, "max": 2}}, + }, + { + Code: ` +declare function makeDate(m: number, d: number, y: number): Date; + `, + Options: []interface{}{map[string]interface{}{"max": 3}}, + }, + { + Code: ` +type sum = (a: number, b: number) => number; + `, + Options: []interface{}{map[string]interface{}{"max": 2}}, + }, + }, []rule_tester.InvalidTestCase{ + { + Code: `function foo(a, b, c, d) {}`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "exceed", Line: 1, Column: 1, EndLine: 1, EndColumn: 28}, + }, + }, + { + Code: `const foo = function (a, b, c, d) {};`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "exceed", Line: 1, Column: 13, EndLine: 1, EndColumn: 37}, + }, + }, + { + Code: `const foo = (a, b, c, d) => {};`, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "exceed", Line: 1, Column: 13, EndLine: 1, EndColumn: 31}, + }, + }, + { + Code: `const foo = a => {};`, + Options: []interface{}{map[string]interface{}{"max": 0}}, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "exceed", Line: 1, Column: 13, EndLine: 1, EndColumn: 20}, + }, + }, + { + Code: ` +class Foo { + method(this: void, a, b, c, d) {} +} + `, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "exceed", Line: 3, Column: 3, EndLine: 3, EndColumn: 36}, + }, + }, + { + Code: ` +class Foo { + method(this: void, a) {} +} + `, + Options: []interface{}{map[string]interface{}{"countVoidThis": true, "max": 1}}, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "exceed", Line: 3, Column: 3, EndLine: 3, EndColumn: 27}, + }, + }, + { + Code: ` +class Foo { + method(this: Foo, a, b, c) {} +} + `, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "exceed", Line: 3, Column: 3, EndLine: 3, EndColumn: 32}, + }, + }, + { + Code: ` +declare function makeDate(m: number, d: number, y: number): Date; + `, + Options: []interface{}{map[string]interface{}{"max": 1}}, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "exceed", Line: 2, Column: 1, EndLine: 2, EndColumn: 66}, + }, + }, + { + Code: ` +type sum = (a: number, b: number) => number; + `, + Options: []interface{}{map[string]interface{}{"max": 1}}, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "exceed", Line: 2, Column: 12, EndLine: 2, EndColumn: 44}, + }, + }, + }) +} diff --git a/packages/rslint-test-tools/rstest.config.mts b/packages/rslint-test-tools/rstest.config.mts index c1b8a03d5..31af2e6ce 100644 --- a/packages/rslint-test-tools/rstest.config.mts +++ b/packages/rslint-test-tools/rstest.config.mts @@ -36,7 +36,7 @@ export default defineConfig({ // './tests/typescript-eslint/rules/explicit-member-accessibility.test.ts', // './tests/typescript-eslint/rules/explicit-module-boundary-types.test.ts', // './tests/typescript-eslint/rules/init-declarations.test.ts', - // './tests/typescript-eslint/rules/max-params.test.ts', + './tests/typescript-eslint/rules/max-params.test.ts', // './tests/typescript-eslint/rules/member-ordering.test.ts', // './tests/typescript-eslint/rules/member-ordering/member-ordering-alphabetically-case-insensitive-order.test.ts', // './tests/typescript-eslint/rules/member-ordering/member-ordering-alphabetically-order.test.ts', diff --git a/packages/rslint-test-tools/tests/typescript-eslint/rules/__snapshots__/max-params.test.ts.snap b/packages/rslint-test-tools/tests/typescript-eslint/rules/__snapshots__/max-params.test.ts.snap new file mode 100644 index 000000000..189363f06 --- /dev/null +++ b/packages/rslint-test-tools/tests/typescript-eslint/rules/__snapshots__/max-params.test.ts.snap @@ -0,0 +1,251 @@ +// Rstest Snapshot v1 + +exports[`max-params > invalid 1`] = ` +{ + "code": "function foo(a, b, c, d) {}", + "diagnostics": [ + { + "message": "Function has too many parameters (4). Maximum allowed is 3.", + "messageId": "exceed", + "range": { + "end": { + "column": 28, + "line": 1, + }, + "start": { + "column": 1, + "line": 1, + }, + }, + "ruleName": "@typescript-eslint/max-params", + }, + ], + "errorCount": 1, + "fileCount": 1, + "ruleCount": 1, +} +`; + +exports[`max-params > invalid 2`] = ` +{ + "code": "const foo = function (a, b, c, d) {};", + "diagnostics": [ + { + "message": "Function has too many parameters (4). Maximum allowed is 3.", + "messageId": "exceed", + "range": { + "end": { + "column": 37, + "line": 1, + }, + "start": { + "column": 13, + "line": 1, + }, + }, + "ruleName": "@typescript-eslint/max-params", + }, + ], + "errorCount": 1, + "fileCount": 1, + "ruleCount": 1, +} +`; + +exports[`max-params > invalid 3`] = ` +{ + "code": "const foo = (a, b, c, d) => {};", + "diagnostics": [ + { + "message": "Function has too many parameters (4). Maximum allowed is 3.", + "messageId": "exceed", + "range": { + "end": { + "column": 31, + "line": 1, + }, + "start": { + "column": 13, + "line": 1, + }, + }, + "ruleName": "@typescript-eslint/max-params", + }, + ], + "errorCount": 1, + "fileCount": 1, + "ruleCount": 1, +} +`; + +exports[`max-params > invalid 4`] = ` +{ + "code": "const foo = a => {};", + "diagnostics": [ + { + "message": "Function has too many parameters (1). Maximum allowed is 0.", + "messageId": "exceed", + "range": { + "end": { + "column": 20, + "line": 1, + }, + "start": { + "column": 13, + "line": 1, + }, + }, + "ruleName": "@typescript-eslint/max-params", + }, + ], + "errorCount": 1, + "fileCount": 1, + "ruleCount": 1, +} +`; + +exports[`max-params > invalid 5`] = ` +{ + "code": " +class Foo { + method(this: void, a, b, c, d) {} +} + ", + "diagnostics": [ + { + "message": "Function has too many parameters (4). Maximum allowed is 3.", + "messageId": "exceed", + "range": { + "end": { + "column": 36, + "line": 3, + }, + "start": { + "column": 3, + "line": 3, + }, + }, + "ruleName": "@typescript-eslint/max-params", + }, + ], + "errorCount": 1, + "fileCount": 1, + "ruleCount": 1, +} +`; + +exports[`max-params > invalid 6`] = ` +{ + "code": " +class Foo { + method(this: void, a) {} +} + ", + "diagnostics": [ + { + "message": "Function has too many parameters (2). Maximum allowed is 1.", + "messageId": "exceed", + "range": { + "end": { + "column": 27, + "line": 3, + }, + "start": { + "column": 3, + "line": 3, + }, + }, + "ruleName": "@typescript-eslint/max-params", + }, + ], + "errorCount": 1, + "fileCount": 1, + "ruleCount": 1, +} +`; + +exports[`max-params > invalid 7`] = ` +{ + "code": " +class Foo { + method(this: Foo, a, b, c) {} +} + ", + "diagnostics": [ + { + "message": "Function has too many parameters (4). Maximum allowed is 3.", + "messageId": "exceed", + "range": { + "end": { + "column": 32, + "line": 3, + }, + "start": { + "column": 3, + "line": 3, + }, + }, + "ruleName": "@typescript-eslint/max-params", + }, + ], + "errorCount": 1, + "fileCount": 1, + "ruleCount": 1, +} +`; + +exports[`max-params > invalid 8`] = ` +{ + "code": " +declare function makeDate(m: number, d: number, y: number): Date; + ", + "diagnostics": [ + { + "message": "Function has too many parameters (3). Maximum allowed is 1.", + "messageId": "exceed", + "range": { + "end": { + "column": 66, + "line": 2, + }, + "start": { + "column": 1, + "line": 2, + }, + }, + "ruleName": "@typescript-eslint/max-params", + }, + ], + "errorCount": 1, + "fileCount": 1, + "ruleCount": 1, +} +`; + +exports[`max-params > invalid 9`] = ` +{ + "code": " +type sum = (a: number, b: number) => number; + ", + "diagnostics": [ + { + "message": "Function has too many parameters (2). Maximum allowed is 1.", + "messageId": "exceed", + "range": { + "end": { + "column": 44, + "line": 2, + }, + "start": { + "column": 12, + "line": 2, + }, + }, + "ruleName": "@typescript-eslint/max-params", + }, + ], + "errorCount": 1, + "fileCount": 1, + "ruleCount": 1, +} +`; diff --git a/rslint.json b/rslint.json index 46461fc17..ae566097e 100644 --- a/rslint.json +++ b/rslint.json @@ -55,7 +55,8 @@ "@typescript-eslint/no-unused-vars": "warn", "@typescript-eslint/require-await": "warn", "@typescript-eslint/prefer-readonly": "warn", - "@typescript-eslint/no-non-null-assertion": "warn" + "@typescript-eslint/no-non-null-assertion": "warn", + "@typescript-eslint/max-params": "off" }, "plugins": ["@typescript-eslint"] } From 94ffd147f384deee2e719f85bfdcf44921ca64c4 Mon Sep 17 00:00:00 2001 From: ScriptedAlchemy Date: Wed, 25 Feb 2026 14:25:21 -0800 Subject: [PATCH 2/5] fix(typescript/max-params): align signature node parity --- .../typescript/rules/max_params/max_params.go | 12 +++++------- .../rules/max_params/max_params_test.go | 19 +++++++++++++++++++ 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/internal/plugins/typescript/rules/max_params/max_params.go b/internal/plugins/typescript/rules/max_params/max_params.go index 7e3d6b709..9a95d2313 100644 --- a/internal/plugins/typescript/rules/max_params/max_params.go +++ b/internal/plugins/typescript/rules/max_params/max_params.go @@ -41,9 +41,11 @@ func parseOptions(options any) MaxParamsOptions { var optsMap map[string]interface{} if arr, ok := options.([]interface{}); ok && len(arr) > 0 { - optsMap, _ = arr[0].(map[string]interface{}) - } else { - optsMap, _ = options.(map[string]interface{}) + if m, ok := arr[0].(map[string]interface{}); ok { + optsMap = m + } + } else if m, ok := options.(map[string]interface{}); ok { + optsMap = m } if optsMap == nil { @@ -128,14 +130,10 @@ var MaxParamsRule = rule.CreateRule(rule.Rule{ ast.KindFunctionExpression: checkParameters, ast.KindArrowFunction: checkParameters, ast.KindMethodDeclaration: checkParameters, - ast.KindMethodSignature: checkParameters, ast.KindConstructor: checkParameters, ast.KindGetAccessor: checkParameters, ast.KindSetAccessor: checkParameters, - ast.KindCallSignature: checkParameters, - ast.KindConstructSignature: checkParameters, ast.KindFunctionType: checkParameters, - ast.KindConstructorType: checkParameters, } }, }) diff --git a/internal/plugins/typescript/rules/max_params/max_params_test.go b/internal/plugins/typescript/rules/max_params/max_params_test.go index 405c900dc..2029e85fe 100644 --- a/internal/plugins/typescript/rules/max_params/max_params_test.go +++ b/internal/plugins/typescript/rules/max_params/max_params_test.go @@ -78,6 +78,25 @@ type sum = (a: number, b: number) => number; `, Options: []interface{}{map[string]interface{}{"max": 2}}, }, + { + Code: ` +interface Foo { + method(a: number, b: number, c: number, d: number): void; +} + `, + }, + { + Code: ` +type CallSig = { + (a: number, b: number, c: number, d: number): void; +}; + `, + }, + { + Code: ` +type Ctor = new (a: number, b: number, c: number, d: number) => Foo; + `, + }, }, []rule_tester.InvalidTestCase{ { Code: `function foo(a, b, c, d) {}`, From 536b1db22255eb9644b42ae7663a9120a4f1977b Mon Sep 17 00:00:00 2001 From: ScriptedAlchemy Date: Wed, 25 Feb 2026 20:49:08 -0800 Subject: [PATCH 3/5] fix: support max-params shorthand options --- .../typescript/rules/max_params/max_params.go | 10 +++++++++- .../typescript/rules/max_params/max_params.md | 13 +++++++++++++ .../typescript/rules/max_params/max_params_test.go | 13 ++++++++++++- 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/internal/plugins/typescript/rules/max_params/max_params.go b/internal/plugins/typescript/rules/max_params/max_params.go index 9a95d2313..3a55976d0 100644 --- a/internal/plugins/typescript/rules/max_params/max_params.go +++ b/internal/plugins/typescript/rules/max_params/max_params.go @@ -41,9 +41,17 @@ func parseOptions(options any) MaxParamsOptions { var optsMap map[string]interface{} if arr, ok := options.([]interface{}); ok && len(arr) > 0 { - if m, ok := arr[0].(map[string]interface{}); ok { + first := arr[0] + if maxValue, ok := parseNumericOption(first); ok { + opts.Max = maxValue + return opts + } + if m, ok := first.(map[string]interface{}); ok { optsMap = m } + } else if maxValue, ok := parseNumericOption(options); ok { + opts.Max = maxValue + return opts } else if m, ok := options.(map[string]interface{}); ok { optsMap = m } diff --git a/internal/plugins/typescript/rules/max_params/max_params.md b/internal/plugins/typescript/rules/max_params/max_params.md index af952892a..a1bf4c007 100644 --- a/internal/plugins/typescript/rules/max_params/max_params.md +++ b/internal/plugins/typescript/rules/max_params/max_params.md @@ -24,6 +24,19 @@ class Foo { } ``` +## Options + +- `max` (`number`, default: `3`): Maximum number of parameters. +- `maximum` (`number`, deprecated): Alias of `max`. +- `countVoidThis` (`boolean`, default: `false`): When `false`, a leading `this: void` parameter is not counted. + +The numeric shorthand is also supported: + +```typescript +// equivalent to { max: 4 } +['@typescript-eslint/max-params', 2, 4]; +``` + ## Original Documentation https://typescript-eslint.io/rules/max-params diff --git a/internal/plugins/typescript/rules/max_params/max_params_test.go b/internal/plugins/typescript/rules/max_params/max_params_test.go index 2029e85fe..0a250e97e 100644 --- a/internal/plugins/typescript/rules/max_params/max_params_test.go +++ b/internal/plugins/typescript/rules/max_params/max_params_test.go @@ -42,6 +42,10 @@ class Foo { Code: `function foo(a, b, c, d) {}`, Options: []interface{}{map[string]interface{}{"maximum": 4}}, }, + { + Code: `function foo(a, b, c, d) {}`, + Options: []interface{}{4}, + }, { Code: ` class Foo { @@ -166,11 +170,18 @@ declare function makeDate(m: number, d: number, y: number): Date; { Code: ` type sum = (a: number, b: number) => number; - `, + `, Options: []interface{}{map[string]interface{}{"max": 1}}, Errors: []rule_tester.InvalidTestCaseError{ {MessageId: "exceed", Line: 2, Column: 12, EndLine: 2, EndColumn: 44}, }, }, + { + Code: `function foo(a, b) {}`, + Options: []interface{}{1}, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "exceed", Line: 1, Column: 1, EndLine: 1, EndColumn: 22}, + }, + }, }) } From 06ebc85cce626e29e10652d11c9a6fdeb1f3cc7d Mon Sep 17 00:00:00 2001 From: ScriptedAlchemy Date: Wed, 11 Mar 2026 21:29:35 -0700 Subject: [PATCH 4/5] fix: cover ts signature nodes in max-params --- .../typescript/rules/max_params/max_params.go | 4 + .../rules/max_params/max_params_test.go | 47 ++++++---- .../__snapshots__/max-params.test.ts.snap | 88 +++++++++++++++++++ .../rules/max-params.test.ts | 22 +++++ 4 files changed, 142 insertions(+), 19 deletions(-) diff --git a/internal/plugins/typescript/rules/max_params/max_params.go b/internal/plugins/typescript/rules/max_params/max_params.go index 3a55976d0..733299c75 100644 --- a/internal/plugins/typescript/rules/max_params/max_params.go +++ b/internal/plugins/typescript/rules/max_params/max_params.go @@ -138,6 +138,10 @@ var MaxParamsRule = rule.CreateRule(rule.Rule{ ast.KindFunctionExpression: checkParameters, ast.KindArrowFunction: checkParameters, ast.KindMethodDeclaration: checkParameters, + ast.KindMethodSignature: checkParameters, + ast.KindCallSignature: checkParameters, + ast.KindConstructSignature: checkParameters, + ast.KindConstructorType: checkParameters, ast.KindConstructor: checkParameters, ast.KindGetAccessor: checkParameters, ast.KindSetAccessor: checkParameters, diff --git a/internal/plugins/typescript/rules/max_params/max_params_test.go b/internal/plugins/typescript/rules/max_params/max_params_test.go index 0a250e97e..28ace2122 100644 --- a/internal/plugins/typescript/rules/max_params/max_params_test.go +++ b/internal/plugins/typescript/rules/max_params/max_params_test.go @@ -82,25 +82,6 @@ type sum = (a: number, b: number) => number; `, Options: []interface{}{map[string]interface{}{"max": 2}}, }, - { - Code: ` -interface Foo { - method(a: number, b: number, c: number, d: number): void; -} - `, - }, - { - Code: ` -type CallSig = { - (a: number, b: number, c: number, d: number): void; -}; - `, - }, - { - Code: ` -type Ctor = new (a: number, b: number, c: number, d: number) => Foo; - `, - }, }, []rule_tester.InvalidTestCase{ { Code: `function foo(a, b, c, d) {}`, @@ -183,5 +164,33 @@ type sum = (a: number, b: number) => number; {MessageId: "exceed", Line: 1, Column: 1, EndLine: 1, EndColumn: 22}, }, }, + { + Code: ` +interface Foo { + method(a: number, b: number, c: number, d: number): void; +} + `, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "exceed", Line: 3, Column: 3, EndLine: 3, EndColumn: 60}, + }, + }, + { + Code: ` +type CallSig = { + (a: number, b: number, c: number, d: number): void; +}; + `, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "exceed", Line: 3, Column: 3, EndLine: 3, EndColumn: 54}, + }, + }, + { + Code: ` +type Ctor = new (a: number, b: number, c: number, d: number) => Foo; + `, + Errors: []rule_tester.InvalidTestCaseError{ + {MessageId: "exceed", Line: 2, Column: 13, EndLine: 2, EndColumn: 68}, + }, + }, }) } diff --git a/packages/rslint-test-tools/tests/typescript-eslint/rules/__snapshots__/max-params.test.ts.snap b/packages/rslint-test-tools/tests/typescript-eslint/rules/__snapshots__/max-params.test.ts.snap index 189363f06..e7020d453 100644 --- a/packages/rslint-test-tools/tests/typescript-eslint/rules/__snapshots__/max-params.test.ts.snap +++ b/packages/rslint-test-tools/tests/typescript-eslint/rules/__snapshots__/max-params.test.ts.snap @@ -249,3 +249,91 @@ type sum = (a: number, b: number) => number; "ruleCount": 1, } `; + +exports[`max-params > invalid 10`] = ` +{ + "code": " +interface Foo { + method(a: number, b: number, c: number, d: number): void; +} + ", + "diagnostics": [ + { + "message": "Function has too many parameters (4). Maximum allowed is 3.", + "messageId": "exceed", + "range": { + "end": { + "column": 60, + "line": 3, + }, + "start": { + "column": 3, + "line": 3, + }, + }, + "ruleName": "@typescript-eslint/max-params", + }, + ], + "errorCount": 1, + "fileCount": 1, + "ruleCount": 1, +} +`; + +exports[`max-params > invalid 11`] = ` +{ + "code": " +type CallSig = { + (a: number, b: number, c: number, d: number): void; +}; + ", + "diagnostics": [ + { + "message": "Function has too many parameters (4). Maximum allowed is 3.", + "messageId": "exceed", + "range": { + "end": { + "column": 54, + "line": 3, + }, + "start": { + "column": 3, + "line": 3, + }, + }, + "ruleName": "@typescript-eslint/max-params", + }, + ], + "errorCount": 1, + "fileCount": 1, + "ruleCount": 1, +} +`; + +exports[`max-params > invalid 12`] = ` +{ + "code": " +type Ctor = new (a: number, b: number, c: number, d: number) => Foo; + ", + "diagnostics": [ + { + "message": "Function has too many parameters (4). Maximum allowed is 3.", + "messageId": "exceed", + "range": { + "end": { + "column": 68, + "line": 2, + }, + "start": { + "column": 13, + "line": 2, + }, + }, + "ruleName": "@typescript-eslint/max-params", + }, + ], + "errorCount": 1, + "fileCount": 1, + "ruleCount": 1, +} +`; diff --git a/packages/rslint-test-tools/tests/typescript-eslint/rules/max-params.test.ts b/packages/rslint-test-tools/tests/typescript-eslint/rules/max-params.test.ts index ffa8231cc..fff51e4a7 100644 --- a/packages/rslint-test-tools/tests/typescript-eslint/rules/max-params.test.ts +++ b/packages/rslint-test-tools/tests/typescript-eslint/rules/max-params.test.ts @@ -124,5 +124,27 @@ type sum = (a: number, b: number) => number; errors: [{ messageId: 'exceed' }], options: [{ max: 1 }], }, + { + code: ` +interface Foo { + method(a: number, b: number, c: number, d: number): void; +} + `, + errors: [{ messageId: 'exceed' }], + }, + { + code: ` +type CallSig = { + (a: number, b: number, c: number, d: number): void; +}; + `, + errors: [{ messageId: 'exceed' }], + }, + { + code: ` +type Ctor = new (a: number, b: number, c: number, d: number) => Foo; + `, + errors: [{ messageId: 'exceed' }], + }, ], }); From 066aabd5ac7ae1e1fe244253466334fd907288d3 Mon Sep 17 00:00:00 2001 From: ScriptedAlchemy Date: Fri, 13 Mar 2026 00:42:09 -0700 Subject: [PATCH 5/5] fix: align max-params diagnostics with upstream --- .../typescript/rules/max_params/max_params.go | 62 ++++++++++++++++++- .../__snapshots__/max-params.test.ts.snap | 22 +++---- 2 files changed, 70 insertions(+), 14 deletions(-) diff --git a/internal/plugins/typescript/rules/max_params/max_params.go b/internal/plugins/typescript/rules/max_params/max_params.go index 733299c75..a4fda5499 100644 --- a/internal/plugins/typescript/rules/max_params/max_params.go +++ b/internal/plugins/typescript/rules/max_params/max_params.go @@ -105,10 +105,66 @@ func isVoidThisParameter(param *ast.Node) bool { return decl.Type != nil && decl.Type.Kind == ast.KindVoidKeyword } -func buildExceedMessage(count int, maxCount int) rule.RuleMessage { +func getNamedFunctionLabel(prefix string, nameNode *ast.Node) string { + if nameNode != nil && nameNode.Kind == ast.KindIdentifier { + return fmt.Sprintf("%s '%s'", prefix, nameNode.AsIdentifier().Text) + } + return prefix +} + +func getFunctionLabel(node *ast.Node) string { + switch node.Kind { + case ast.KindFunctionDeclaration: + if decl := node.AsFunctionDeclaration(); decl != nil { + return getNamedFunctionLabel("Function", decl.Name()) + } + return "Function" + case ast.KindFunctionExpression: + if expr := node.AsFunctionExpression(); expr != nil { + return getNamedFunctionLabel("Function", expr.Name()) + } + return "Function" + case ast.KindArrowFunction: + return "Arrow function" + case ast.KindMethodDeclaration: + if decl := node.AsMethodDeclaration(); decl != nil { + return getNamedFunctionLabel("Method", decl.Name()) + } + return "Method" + case ast.KindMethodSignature: + if sig := node.AsMethodSignatureDeclaration(); sig != nil { + return getNamedFunctionLabel("Method", sig.Name()) + } + return "Method" + case ast.KindConstructor: + return "Constructor" + case ast.KindGetAccessor: + if accessor := node.AsGetAccessorDeclaration(); accessor != nil { + return getNamedFunctionLabel("Getter", accessor.Name()) + } + return "Getter" + case ast.KindSetAccessor: + if accessor := node.AsSetAccessorDeclaration(); accessor != nil { + return getNamedFunctionLabel("Setter", accessor.Name()) + } + return "Setter" + case ast.KindFunctionType: + return "Function type" + case ast.KindCallSignature: + return "Call signature" + case ast.KindConstructSignature: + return "Constructor signature" + case ast.KindConstructorType: + return "Constructor type" + default: + return "Function" + } +} + +func buildExceedMessage(node *ast.Node, count int, maxCount int) rule.RuleMessage { return rule.RuleMessage{ Id: "exceed", - Description: fmt.Sprintf("Function has too many parameters (%d). Maximum allowed is %d.", count, maxCount), + Description: fmt.Sprintf("%s has too many parameters (%d). Maximum allowed is %d.", getFunctionLabel(node), count, maxCount), } } @@ -129,7 +185,7 @@ var MaxParamsRule = rule.CreateRule(rule.Rule{ } if count > opts.Max { - ctx.ReportNode(node, buildExceedMessage(count, opts.Max)) + ctx.ReportNode(node, buildExceedMessage(node, count, opts.Max)) } } diff --git a/packages/rslint-test-tools/tests/typescript-eslint/rules/__snapshots__/max-params.test.ts.snap b/packages/rslint-test-tools/tests/typescript-eslint/rules/__snapshots__/max-params.test.ts.snap index e7020d453..0b003dcba 100644 --- a/packages/rslint-test-tools/tests/typescript-eslint/rules/__snapshots__/max-params.test.ts.snap +++ b/packages/rslint-test-tools/tests/typescript-eslint/rules/__snapshots__/max-params.test.ts.snap @@ -5,7 +5,7 @@ exports[`max-params > invalid 1`] = ` "code": "function foo(a, b, c, d) {}", "diagnostics": [ { - "message": "Function has too many parameters (4). Maximum allowed is 3.", + "message": "Function 'foo' has too many parameters (4). Maximum allowed is 3.", "messageId": "exceed", "range": { "end": { @@ -57,7 +57,7 @@ exports[`max-params > invalid 3`] = ` "code": "const foo = (a, b, c, d) => {};", "diagnostics": [ { - "message": "Function has too many parameters (4). Maximum allowed is 3.", + "message": "Arrow function has too many parameters (4). Maximum allowed is 3.", "messageId": "exceed", "range": { "end": { @@ -83,7 +83,7 @@ exports[`max-params > invalid 4`] = ` "code": "const foo = a => {};", "diagnostics": [ { - "message": "Function has too many parameters (1). Maximum allowed is 0.", + "message": "Arrow function has too many parameters (1). Maximum allowed is 0.", "messageId": "exceed", "range": { "end": { @@ -113,7 +113,7 @@ class Foo { ", "diagnostics": [ { - "message": "Function has too many parameters (4). Maximum allowed is 3.", + "message": "Method 'method' has too many parameters (4). Maximum allowed is 3.", "messageId": "exceed", "range": { "end": { @@ -143,7 +143,7 @@ class Foo { ", "diagnostics": [ { - "message": "Function has too many parameters (2). Maximum allowed is 1.", + "message": "Method 'method' has too many parameters (2). Maximum allowed is 1.", "messageId": "exceed", "range": { "end": { @@ -173,7 +173,7 @@ class Foo { ", "diagnostics": [ { - "message": "Function has too many parameters (4). Maximum allowed is 3.", + "message": "Method 'method' has too many parameters (4). Maximum allowed is 3.", "messageId": "exceed", "range": { "end": { @@ -201,7 +201,7 @@ declare function makeDate(m: number, d: number, y: number): Date; ", "diagnostics": [ { - "message": "Function has too many parameters (3). Maximum allowed is 1.", + "message": "Function 'makeDate' has too many parameters (3). Maximum allowed is 1.", "messageId": "exceed", "range": { "end": { @@ -229,7 +229,7 @@ type sum = (a: number, b: number) => number; ", "diagnostics": [ { - "message": "Function has too many parameters (2). Maximum allowed is 1.", + "message": "Function type has too many parameters (2). Maximum allowed is 1.", "messageId": "exceed", "range": { "end": { @@ -259,7 +259,7 @@ interface Foo { ", "diagnostics": [ { - "message": "Function has too many parameters (4). Maximum allowed is 3.", + "message": "Method 'method' has too many parameters (4). Maximum allowed is 3.", "messageId": "exceed", "range": { "end": { @@ -289,7 +289,7 @@ type CallSig = { ", "diagnostics": [ { - "message": "Function has too many parameters (4). Maximum allowed is 3.", + "message": "Call signature has too many parameters (4). Maximum allowed is 3.", "messageId": "exceed", "range": { "end": { @@ -317,7 +317,7 @@ type Ctor = new (a: number, b: number, c: number, d: number) => Foo; ", "diagnostics": [ { - "message": "Function has too many parameters (4). Maximum allowed is 3.", + "message": "Constructor type has too many parameters (4). Maximum allowed is 3.", "messageId": "exceed", "range": { "end": {