Skip to content

Commit 8826a0e

Browse files
authored
Merge branch 'main' into no-template-curly-in-string
2 parents 7af4c37 + c182e75 commit 8826a0e

File tree

7 files changed

+533
-1
lines changed

7 files changed

+533
-1
lines changed

internal/config/config.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import (
3939
"github.com/web-infra-dev/rslint/internal/plugins/typescript/rules/no_implied_eval"
4040
"github.com/web-infra-dev/rslint/internal/plugins/typescript/rules/no_invalid_void_type"
4141
"github.com/web-infra-dev/rslint/internal/plugins/typescript/rules/no_meaningless_void_operator"
42+
"github.com/web-infra-dev/rslint/internal/plugins/typescript/rules/no_misused_new"
4243
"github.com/web-infra-dev/rslint/internal/plugins/typescript/rules/no_misused_promises"
4344
"github.com/web-infra-dev/rslint/internal/plugins/typescript/rules/no_misused_spread"
4445
"github.com/web-infra-dev/rslint/internal/plugins/typescript/rules/no_mixed_enums"
@@ -96,6 +97,7 @@ import (
9697
"github.com/web-infra-dev/rslint/internal/rules/no_constructor_return"
9798
"github.com/web-infra-dev/rslint/internal/rules/no_debugger"
9899
"github.com/web-infra-dev/rslint/internal/rules/no_template_curly_in_string"
100+
"github.com/web-infra-dev/rslint/internal/rules/no_sparse_arrays"
99101
)
100102

101103
// RslintConfig represents the top-level configuration array
@@ -392,6 +394,7 @@ func registerAllTypeScriptEslintPluginRules() {
392394
GlobalRuleRegistry.Register("@typescript-eslint/no-for-in-array", no_for_in_array.NoForInArrayRule)
393395
GlobalRuleRegistry.Register("@typescript-eslint/no-implied-eval", no_implied_eval.NoImpliedEvalRule)
394396
GlobalRuleRegistry.Register("@typescript-eslint/no-meaningless-void-operator", no_meaningless_void_operator.NoMeaninglessVoidOperatorRule)
397+
GlobalRuleRegistry.Register("@typescript-eslint/no-misused-new", no_misused_new.NoMisusedNewRule)
395398
GlobalRuleRegistry.Register("@typescript-eslint/no-misused-promises", no_misused_promises.NoMisusedPromisesRule)
396399
GlobalRuleRegistry.Register("@typescript-eslint/no-misused-spread", no_misused_spread.NoMisusedSpreadRule)
397400
GlobalRuleRegistry.Register("@typescript-eslint/no-mixed-enums", no_mixed_enums.NoMixedEnumsRule)
@@ -461,6 +464,7 @@ func registerAllCoreEslintRules() {
461464
GlobalRuleRegistry.Register("no-constructor-return", no_constructor_return.NoConstructorReturnRule)
462465
GlobalRuleRegistry.Register("no-debugger", no_debugger.NoDebuggerRule)
463466
GlobalRuleRegistry.Register("no-template-curly-in-string", no_template_curly_in_string.NoTemplateCurlyInString)
467+
GlobalRuleRegistry.Register("no-sparse-arrays", no_sparse_arrays.NoSparseArraysRule)
464468
}
465469

466470
// getAllTypeScriptEslintPluginRules returns all registered rules (for backward compatibility when no config is provided)
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package no_misused_new
2+
3+
import (
4+
"github.com/microsoft/typescript-go/shim/ast"
5+
"github.com/web-infra-dev/rslint/internal/rule"
6+
)
7+
8+
/**
9+
* check whether the name of the return type of the node is the same as the name of the parent node.
10+
*/
11+
func check(node *ast.Node) bool {
12+
parentName := node.Parent.Name()
13+
if parentName == nil {
14+
return false
15+
}
16+
17+
nodeType := node.Type()
18+
if nodeType != nil && ast.IsTypeReferenceNode(nodeType) {
19+
typeName := nodeType.AsTypeReference().TypeName
20+
if ast.IsIdentifier(typeName) {
21+
return typeName.Text() == parentName.Text()
22+
}
23+
}
24+
return false
25+
}
26+
27+
var NoMisusedNewRule = rule.CreateRule(rule.Rule{
28+
Name: "no-misused-new",
29+
Run: func(ctx rule.RuleContext, options any) rule.RuleListeners {
30+
return rule.RuleListeners{
31+
ast.KindMethodDeclaration: func(node *ast.Node) {
32+
parentKind := node.Parent.Kind
33+
if parentKind != ast.KindClassDeclaration && parentKind != ast.KindClassExpression {
34+
return
35+
}
36+
37+
if node.Name().Text() != "new" {
38+
return
39+
}
40+
// If the function body exists, it's valid for this rule.
41+
body := node.Body()
42+
if body != nil {
43+
return
44+
}
45+
46+
if check(node) {
47+
ctx.ReportNode(node, rule.RuleMessage{
48+
Id: "errorMessageClass",
49+
Description: "Class cannot have method named `new`.",
50+
})
51+
}
52+
},
53+
ast.KindConstructSignature: func(node *ast.Node) {
54+
if node.Parent.Kind != ast.KindInterfaceDeclaration {
55+
return
56+
}
57+
if check(node) {
58+
ctx.ReportNode(node, rule.RuleMessage{
59+
Id: "errorMessageInterface",
60+
Description: "interfaces cannot be constructed, only classes.",
61+
})
62+
}
63+
},
64+
ast.KindMethodSignature: func(node *ast.Node) {
65+
if node.Name().Text() == "constructor" {
66+
ctx.ReportNode(node, rule.RuleMessage{
67+
Id: "errorMessageInterface",
68+
Description: "interfaces cannot be constructed, only classes.",
69+
})
70+
}
71+
},
72+
}
73+
},
74+
})
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
package no_misused_new
2+
3+
import (
4+
"testing"
5+
6+
"github.com/web-infra-dev/rslint/internal/plugins/typescript/rules/fixtures"
7+
"github.com/web-infra-dev/rslint/internal/rule_tester"
8+
)
9+
10+
func TestNoMisusedNewRule(t *testing.T) {
11+
rule_tester.RunRuleTester(fixtures.GetRootDir(), "tsconfig.json", t, &NoMisusedNewRule, []rule_tester.ValidTestCase{
12+
{
13+
Code: `
14+
declare abstract class C {
15+
foo() {}
16+
get new();
17+
bar();
18+
}
19+
`,
20+
},
21+
{
22+
Code: `
23+
class C {
24+
constructor();
25+
}
26+
`,
27+
},
28+
{
29+
Code: `
30+
const foo = class {
31+
constructor();
32+
};
33+
`,
34+
},
35+
{
36+
Code: `
37+
const foo = class {
38+
new(): X;
39+
};
40+
`,
41+
},
42+
{
43+
Code: `
44+
class C {
45+
constructor() {}
46+
}
47+
`,
48+
},
49+
{
50+
Code: `
51+
const foo = class {
52+
new() {}
53+
};
54+
`,
55+
},
56+
{
57+
Code: `
58+
const foo = class {
59+
constructor() {}
60+
};
61+
`,
62+
},
63+
{
64+
Code: `
65+
interface I {
66+
new (): {};
67+
}
68+
`,
69+
},
70+
{
71+
Code: `type T = { new (): T };`,
72+
},
73+
{
74+
Code: `
75+
export default class {
76+
constructor();
77+
}
78+
`,
79+
},
80+
{
81+
Code: `
82+
interface foo {
83+
new <T>(): bar<T>;
84+
}
85+
`,
86+
},
87+
{
88+
Code: `
89+
interface foo {
90+
new <T>(): 'x';
91+
}
92+
`,
93+
},
94+
}, []rule_tester.InvalidTestCase{
95+
{
96+
Code: `
97+
interface I {
98+
new (): I;
99+
constructor(): void;
100+
}
101+
`,
102+
Errors: []rule_tester.InvalidTestCaseError{
103+
{
104+
MessageId: "errorMessageInterface",
105+
Line: 3,
106+
},
107+
{
108+
MessageId: "errorMessageInterface",
109+
Line: 4,
110+
},
111+
},
112+
},
113+
{
114+
Code: `
115+
interface G {
116+
new <T>(): G<T>;
117+
}
118+
`,
119+
Errors: []rule_tester.InvalidTestCaseError{
120+
{
121+
MessageId: "errorMessageInterface",
122+
},
123+
},
124+
},
125+
{
126+
Code: `
127+
type T = {
128+
constructor(): void;
129+
};
130+
`,
131+
Errors: []rule_tester.InvalidTestCaseError{
132+
{
133+
MessageId: "errorMessageInterface",
134+
},
135+
},
136+
},
137+
{
138+
Code: `
139+
class C {
140+
new(): C;
141+
}
142+
`,
143+
Errors: []rule_tester.InvalidTestCaseError{
144+
{
145+
MessageId: "errorMessageClass",
146+
},
147+
},
148+
},
149+
{
150+
Code: `
151+
declare abstract class C {
152+
new(): C;
153+
}
154+
`,
155+
Errors: []rule_tester.InvalidTestCaseError{
156+
{
157+
MessageId: "errorMessageClass",
158+
},
159+
},
160+
},
161+
{
162+
Code: `
163+
interface I {
164+
constructor(): '';
165+
}
166+
`,
167+
Errors: []rule_tester.InvalidTestCaseError{
168+
{
169+
MessageId: "errorMessageInterface",
170+
},
171+
},
172+
},
173+
})
174+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package no_sparse_arrays
2+
3+
import (
4+
"github.com/microsoft/typescript-go/shim/ast"
5+
"github.com/web-infra-dev/rslint/internal/rule"
6+
)
7+
8+
// https://eslint.org/docs/latest/rules/no-sparse-arrays
9+
var NoSparseArraysRule = rule.Rule{
10+
Name: "no-sparse-arrays",
11+
Run: func(ctx rule.RuleContext, options any) rule.RuleListeners {
12+
return rule.RuleListeners{
13+
ast.KindArrayLiteralExpression: func(node *ast.Node) {
14+
for _, v := range node.AsArrayLiteralExpression().Elements.Nodes {
15+
if v.Kind == ast.KindOmittedExpression {
16+
ctx.ReportNode(node, rule.RuleMessage{
17+
Id: "unexpectedSparseArray",
18+
Description: "Unexpected comma in middle of array.",
19+
})
20+
}
21+
}
22+
},
23+
}
24+
},
25+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package no_sparse_arrays
2+
3+
import (
4+
"testing"
5+
6+
"github.com/web-infra-dev/rslint/internal/plugins/typescript/rules/fixtures"
7+
"github.com/web-infra-dev/rslint/internal/rule_tester"
8+
)
9+
10+
func TestNoSparseArraysRule(t *testing.T) {
11+
rule_tester.RunRuleTester(
12+
fixtures.GetRootDir(),
13+
"tsconfig.json",
14+
t,
15+
&NoSparseArraysRule,
16+
// Valid cases - ported from ESLint
17+
[]rule_tester.ValidTestCase{
18+
{Code: `var a = [ 1, 2, ]`},
19+
},
20+
// Invalid cases - ported from ESLint
21+
[]rule_tester.InvalidTestCase{
22+
{
23+
Code: `var a = [,];`,
24+
Errors: []rule_tester.InvalidTestCaseError{
25+
{MessageId: "unexpectedSparseArray", Line: 1, Column: 9},
26+
},
27+
},
28+
{
29+
Code: `var a = [ 1,, 2];`,
30+
Errors: []rule_tester.InvalidTestCaseError{
31+
{MessageId: "unexpectedSparseArray", Line: 1, Column: 9},
32+
},
33+
},
34+
// This test case is commented out because it produces a TypeScript compilation error:
35+
// error creating TS program for /tsconfig.json: found 5 syntactic errors. [Invalid character. [\r\n\t/* comment */,\n// comment\n ,]; Invalid character. [\r\n\t/* comment */,\n// comment\n ,]; Invalid character. [\r\n\t/* comment */,\n// comment\n ,]; Invalid character. [\r\n\t/* comment */,\n// comment\n ,]; ']' expected. [\r\n\t/* comment */,\n// comment\n ,];]
36+
//{
37+
// Code: `[\r\n\t/* comment */,\n// comment\n ,];`,
38+
// Errors: []rule_tester.InvalidTestCaseError{
39+
// {MessageId: "unexpectedSparseArray", Line: 1, Column: 9},
40+
// },
41+
//},
42+
{
43+
Code: `[(( [a,] )),,,];`,
44+
Errors: []rule_tester.InvalidTestCaseError{
45+
{MessageId: "unexpectedSparseArray", Line: 1, Column: 1},
46+
{MessageId: "unexpectedSparseArray", Line: 1, Column: 1},
47+
},
48+
},
49+
{
50+
Code: `[,(( [a,] )),,];`,
51+
Errors: []rule_tester.InvalidTestCaseError{
52+
{MessageId: "unexpectedSparseArray", Line: 1, Column: 1},
53+
{MessageId: "unexpectedSparseArray", Line: 1, Column: 1},
54+
},
55+
},
56+
},
57+
)
58+
}

packages/rslint-test-tools/rstest.config.mts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ export default defineConfig({
9393
// './tests/typescript-eslint/rules/no-loss-of-precision.test.ts',
9494
// './tests/typescript-eslint/rules/no-magic-numbers.test.ts',
9595
// './tests/typescript-eslint/rules/no-meaningless-void-operator.test.ts',
96-
// './tests/typescript-eslint/rules/no-misused-new.test.ts',
96+
'./tests/typescript-eslint/rules/no-misused-new.test.ts',
9797
// './tests/typescript-eslint/rules/no-misused-promises.test.ts',
9898
// './tests/typescript-eslint/rules/no-misused-spread.test.ts',
9999
// './tests/typescript-eslint/rules/no-mixed-enums.test.ts',

0 commit comments

Comments
 (0)