@ttsc/lint 0.7.2 → 0.7.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,9 +6,9 @@
6
6
  package main
7
7
 
8
8
  import (
9
- "strings"
9
+ "strings"
10
10
 
11
- shimast "github.com/microsoft/typescript-go/shim/ast"
11
+ shimast "github.com/microsoft/typescript-go/shim/ast"
12
12
  )
13
13
 
14
14
  // no-confusing-non-null-assertion: `a! == b` reads ambiguously.
@@ -16,25 +16,25 @@ type noConfusingNonNullAssertion struct{}
16
16
 
17
17
  func (noConfusingNonNullAssertion) Name() string { return "no-confusing-non-null-assertion" }
18
18
  func (noConfusingNonNullAssertion) Visits() []shimast.Kind {
19
- return []shimast.Kind{shimast.KindBinaryExpression}
19
+ return []shimast.Kind{shimast.KindBinaryExpression}
20
20
  }
21
21
  func (noConfusingNonNullAssertion) Check(ctx *Context, node *shimast.Node) {
22
- expr := node.AsBinaryExpression()
23
- if expr == nil || expr.OperatorToken == nil || expr.Left == nil {
24
- return
25
- }
26
- switch expr.OperatorToken.Kind {
27
- case shimast.KindEqualsEqualsToken,
28
- shimast.KindEqualsEqualsEqualsToken,
29
- shimast.KindExclamationEqualsToken,
30
- shimast.KindExclamationEqualsEqualsToken,
31
- shimast.KindEqualsToken:
32
- default:
33
- return
34
- }
35
- if expr.Left.Kind == shimast.KindNonNullExpression {
36
- ctx.Report(node, "Confusing combination of non-null assertion and equality.")
37
- }
22
+ expr := node.AsBinaryExpression()
23
+ if expr == nil || expr.OperatorToken == nil || expr.Left == nil {
24
+ return
25
+ }
26
+ switch expr.OperatorToken.Kind {
27
+ case shimast.KindEqualsEqualsToken,
28
+ shimast.KindEqualsEqualsEqualsToken,
29
+ shimast.KindExclamationEqualsToken,
30
+ shimast.KindExclamationEqualsEqualsToken,
31
+ shimast.KindEqualsToken:
32
+ default:
33
+ return
34
+ }
35
+ if expr.Left.Kind == shimast.KindNonNullExpression {
36
+ ctx.Report(node, "Confusing combination of non-null assertion and equality.")
37
+ }
38
38
  }
39
39
 
40
40
  // no-duplicate-enum-values: `enum E { A = 1, B = 1 }` — duplicate values
@@ -43,38 +43,38 @@ type noDuplicateEnumValues struct{}
43
43
 
44
44
  func (noDuplicateEnumValues) Name() string { return "no-duplicate-enum-values" }
45
45
  func (noDuplicateEnumValues) Visits() []shimast.Kind {
46
- return []shimast.Kind{shimast.KindEnumDeclaration}
46
+ return []shimast.Kind{shimast.KindEnumDeclaration}
47
47
  }
48
48
  func (noDuplicateEnumValues) Check(ctx *Context, node *shimast.Node) {
49
- decl := node.AsEnumDeclaration()
50
- if decl == nil || decl.Members == nil {
51
- return
52
- }
53
- seen := map[string]bool{}
54
- for _, member := range decl.Members.Nodes {
55
- if member == nil {
56
- continue
57
- }
58
- em := member.AsEnumMember()
59
- if em == nil || em.Initializer == nil {
60
- continue
61
- }
62
- init := em.Initializer
63
- var key string
64
- switch init.Kind {
65
- case shimast.KindNumericLiteral, shimast.KindBigIntLiteral:
66
- key = "n:" + numericLiteralText(init)
67
- case shimast.KindStringLiteral, shimast.KindNoSubstitutionTemplateLiteral:
68
- key = "s:" + stringLiteralText(init)
69
- default:
70
- continue
71
- }
72
- if seen[key] {
73
- ctx.Report(member, "Duplicate enum member value.")
74
- continue
75
- }
76
- seen[key] = true
77
- }
49
+ decl := node.AsEnumDeclaration()
50
+ if decl == nil || decl.Members == nil {
51
+ return
52
+ }
53
+ seen := map[string]bool{}
54
+ for _, member := range decl.Members.Nodes {
55
+ if member == nil {
56
+ continue
57
+ }
58
+ em := member.AsEnumMember()
59
+ if em == nil || em.Initializer == nil {
60
+ continue
61
+ }
62
+ init := em.Initializer
63
+ var key string
64
+ switch init.Kind {
65
+ case shimast.KindNumericLiteral, shimast.KindBigIntLiteral:
66
+ key = "n:" + numericLiteralText(init)
67
+ case shimast.KindStringLiteral, shimast.KindNoSubstitutionTemplateLiteral:
68
+ key = "s:" + stringLiteralText(init)
69
+ default:
70
+ continue
71
+ }
72
+ if seen[key] {
73
+ ctx.Report(member, "Duplicate enum member value.")
74
+ continue
75
+ }
76
+ seen[key] = true
77
+ }
78
78
  }
79
79
 
80
80
  // no-extra-non-null-assertion: `a!!` / `a!?.b` collapses two assertions.
@@ -82,16 +82,16 @@ type noExtraNonNullAssertion struct{}
82
82
 
83
83
  func (noExtraNonNullAssertion) Name() string { return "no-extra-non-null-assertion" }
84
84
  func (noExtraNonNullAssertion) Visits() []shimast.Kind {
85
- return []shimast.Kind{shimast.KindNonNullExpression}
85
+ return []shimast.Kind{shimast.KindNonNullExpression}
86
86
  }
87
87
  func (noExtraNonNullAssertion) Check(ctx *Context, node *shimast.Node) {
88
- parent := node.Parent
89
- if parent == nil {
90
- return
91
- }
92
- if parent.Kind == shimast.KindNonNullExpression {
93
- ctx.Report(node, "Forbidden extra non-null assertion.")
94
- }
88
+ parent := node.Parent
89
+ if parent == nil {
90
+ return
91
+ }
92
+ if parent.Kind == shimast.KindNonNullExpression {
93
+ ctx.Report(node, "Forbidden extra non-null assertion.")
94
+ }
95
95
  }
96
96
 
97
97
  // no-non-null-asserted-optional-chain: `foo?.bar!` — the chain produces
@@ -100,49 +100,49 @@ type noNonNullAssertedOptionalChain struct{}
100
100
 
101
101
  func (noNonNullAssertedOptionalChain) Name() string { return "no-non-null-asserted-optional-chain" }
102
102
  func (noNonNullAssertedOptionalChain) Visits() []shimast.Kind {
103
- return []shimast.Kind{shimast.KindNonNullExpression}
103
+ return []shimast.Kind{shimast.KindNonNullExpression}
104
104
  }
105
105
  func (noNonNullAssertedOptionalChain) Check(ctx *Context, node *shimast.Node) {
106
- inner := node.AsNonNullExpression()
107
- if inner == nil || inner.Expression == nil {
108
- return
109
- }
110
- if containsOptionalChain(inner.Expression) {
111
- ctx.Report(node, "Optional chain expressions can return undefined; non-null assertion bypasses that check.")
112
- }
106
+ inner := node.AsNonNullExpression()
107
+ if inner == nil || inner.Expression == nil {
108
+ return
109
+ }
110
+ if containsOptionalChain(inner.Expression) {
111
+ ctx.Report(node, "Optional chain expressions can return undefined; non-null assertion bypasses that check.")
112
+ }
113
113
  }
114
114
 
115
115
  func containsOptionalChain(node *shimast.Node) bool {
116
- if node == nil {
117
- return false
118
- }
119
- switch node.Kind {
120
- case shimast.KindPropertyAccessExpression:
121
- access := node.AsPropertyAccessExpression()
122
- if access != nil && access.QuestionDotToken != nil {
123
- return true
124
- }
125
- if access != nil {
126
- return containsOptionalChain(access.Expression)
127
- }
128
- case shimast.KindElementAccessExpression:
129
- access := node.AsElementAccessExpression()
130
- if access != nil && access.QuestionDotToken != nil {
131
- return true
132
- }
133
- if access != nil {
134
- return containsOptionalChain(access.Expression)
135
- }
136
- case shimast.KindCallExpression:
137
- call := node.AsCallExpression()
138
- if call != nil && call.QuestionDotToken != nil {
139
- return true
140
- }
141
- if call != nil {
142
- return containsOptionalChain(call.Expression)
143
- }
144
- }
145
- return false
116
+ if node == nil {
117
+ return false
118
+ }
119
+ switch node.Kind {
120
+ case shimast.KindPropertyAccessExpression:
121
+ access := node.AsPropertyAccessExpression()
122
+ if access != nil && access.QuestionDotToken != nil {
123
+ return true
124
+ }
125
+ if access != nil {
126
+ return containsOptionalChain(access.Expression)
127
+ }
128
+ case shimast.KindElementAccessExpression:
129
+ access := node.AsElementAccessExpression()
130
+ if access != nil && access.QuestionDotToken != nil {
131
+ return true
132
+ }
133
+ if access != nil {
134
+ return containsOptionalChain(access.Expression)
135
+ }
136
+ case shimast.KindCallExpression:
137
+ call := node.AsCallExpression()
138
+ if call != nil && call.QuestionDotToken != nil {
139
+ return true
140
+ }
141
+ if call != nil {
142
+ return containsOptionalChain(call.Expression)
143
+ }
144
+ }
145
+ return false
146
146
  }
147
147
 
148
148
  // no-misused-new: declaring a `new` signature on a non-class interface
@@ -152,30 +152,30 @@ type noMisusedNew struct{}
152
152
 
153
153
  func (noMisusedNew) Name() string { return "no-misused-new" }
154
154
  func (noMisusedNew) Visits() []shimast.Kind {
155
- return []shimast.Kind{shimast.KindInterfaceDeclaration, shimast.KindTypeAliasDeclaration}
155
+ return []shimast.Kind{shimast.KindInterfaceDeclaration, shimast.KindTypeAliasDeclaration}
156
156
  }
157
157
  func (noMisusedNew) Check(ctx *Context, node *shimast.Node) {
158
- if node.Kind != shimast.KindInterfaceDeclaration {
159
- return
160
- }
161
- decl := node.AsInterfaceDeclaration()
162
- if decl == nil || decl.Members == nil {
163
- return
164
- }
165
- for _, member := range decl.Members.Nodes {
166
- if member == nil {
167
- continue
168
- }
169
- switch member.Kind {
170
- case shimast.KindConstructor:
171
- ctx.Report(member, "Interfaces cannot have constructors. Use a class instead.")
172
- case shimast.KindMethodSignature:
173
- ms := member.AsMethodSignatureDeclaration()
174
- if ms != nil && identifierText(ms.Name()) == "constructor" {
175
- ctx.Report(member, "Interfaces cannot have constructors. Use a class instead.")
176
- }
177
- }
178
- }
158
+ if node.Kind != shimast.KindInterfaceDeclaration {
159
+ return
160
+ }
161
+ decl := node.AsInterfaceDeclaration()
162
+ if decl == nil || decl.Members == nil {
163
+ return
164
+ }
165
+ for _, member := range decl.Members.Nodes {
166
+ if member == nil {
167
+ continue
168
+ }
169
+ switch member.Kind {
170
+ case shimast.KindConstructor:
171
+ ctx.Report(member, "Interfaces cannot have constructors. Use a class instead.")
172
+ case shimast.KindMethodSignature:
173
+ ms := member.AsMethodSignatureDeclaration()
174
+ if ms != nil && identifierText(ms.Name()) == "constructor" {
175
+ ctx.Report(member, "Interfaces cannot have constructors. Use a class instead.")
176
+ }
177
+ }
178
+ }
179
179
  }
180
180
 
181
181
  // prefer-enum-initializers: every enum member should have an explicit
@@ -184,22 +184,22 @@ type preferEnumInitializers struct{}
184
184
 
185
185
  func (preferEnumInitializers) Name() string { return "prefer-enum-initializers" }
186
186
  func (preferEnumInitializers) Visits() []shimast.Kind {
187
- return []shimast.Kind{shimast.KindEnumDeclaration}
187
+ return []shimast.Kind{shimast.KindEnumDeclaration}
188
188
  }
189
189
  func (preferEnumInitializers) Check(ctx *Context, node *shimast.Node) {
190
- decl := node.AsEnumDeclaration()
191
- if decl == nil || decl.Members == nil {
192
- return
193
- }
194
- for _, member := range decl.Members.Nodes {
195
- if member == nil {
196
- continue
197
- }
198
- em := member.AsEnumMember()
199
- if em != nil && em.Initializer == nil {
200
- ctx.Report(member, "Enum member should have an explicit initializer.")
201
- }
202
- }
190
+ decl := node.AsEnumDeclaration()
191
+ if decl == nil || decl.Members == nil {
192
+ return
193
+ }
194
+ for _, member := range decl.Members.Nodes {
195
+ if member == nil {
196
+ continue
197
+ }
198
+ em := member.AsEnumMember()
199
+ if em != nil && em.Initializer == nil {
200
+ ctx.Report(member, "Enum member should have an explicit initializer.")
201
+ }
202
+ }
203
203
  }
204
204
 
205
205
  // prefer-for-of: `for (let i = 0; i < arr.length; i++) { use(arr[i]) }`
@@ -209,90 +209,90 @@ type preferForOf struct{}
209
209
  func (preferForOf) Name() string { return "prefer-for-of" }
210
210
  func (preferForOf) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindForStatement} }
211
211
  func (preferForOf) Check(ctx *Context, node *shimast.Node) {
212
- loop := node.AsForStatement()
213
- if loop == nil || loop.Initializer == nil || loop.Condition == nil || loop.Incrementor == nil {
214
- return
215
- }
216
- // Initializer: `let i = 0` (single declarator with name `i`).
217
- init := loop.Initializer
218
- if init.Kind != shimast.KindVariableDeclarationList {
219
- return
220
- }
221
- list := init.AsVariableDeclarationList()
222
- if list == nil || list.Declarations == nil || len(list.Declarations.Nodes) != 1 {
223
- return
224
- }
225
- decl := list.Declarations.Nodes[0].AsVariableDeclaration()
226
- if decl == nil {
227
- return
228
- }
229
- counter := identifierText(decl.Name())
230
- if counter == "" {
231
- return
232
- }
233
- if numericLiteralText(decl.Initializer) != "0" {
234
- return
235
- }
236
- // Condition: `i < <something>.length`.
237
- cond := loop.Condition.AsBinaryExpression()
238
- if cond == nil || cond.OperatorToken == nil {
239
- return
240
- }
241
- if cond.OperatorToken.Kind != shimast.KindLessThanToken {
242
- return
243
- }
244
- if identifierText(cond.Left) != counter {
245
- return
246
- }
247
- if cond.Right == nil || cond.Right.Kind != shimast.KindPropertyAccessExpression {
248
- return
249
- }
250
- rightAccess := cond.Right.AsPropertyAccessExpression()
251
- if rightAccess == nil || identifierText(rightAccess.Name()) != "length" {
252
- return
253
- }
254
- // Incrementor: `i++` or `++i`.
255
- if !isCounterIncrement(loop.Incrementor, counter) {
256
- return
257
- }
258
- ctx.Report(node, "Prefer a 'for-of' loop instead of a 'for' loop with this simple iteration.")
212
+ loop := node.AsForStatement()
213
+ if loop == nil || loop.Initializer == nil || loop.Condition == nil || loop.Incrementor == nil {
214
+ return
215
+ }
216
+ // Initializer: `let i = 0` (single declarator with name `i`).
217
+ init := loop.Initializer
218
+ if init.Kind != shimast.KindVariableDeclarationList {
219
+ return
220
+ }
221
+ list := init.AsVariableDeclarationList()
222
+ if list == nil || list.Declarations == nil || len(list.Declarations.Nodes) != 1 {
223
+ return
224
+ }
225
+ decl := list.Declarations.Nodes[0].AsVariableDeclaration()
226
+ if decl == nil {
227
+ return
228
+ }
229
+ counter := identifierText(decl.Name())
230
+ if counter == "" {
231
+ return
232
+ }
233
+ if numericLiteralText(decl.Initializer) != "0" {
234
+ return
235
+ }
236
+ // Condition: `i < <something>.length`.
237
+ cond := loop.Condition.AsBinaryExpression()
238
+ if cond == nil || cond.OperatorToken == nil {
239
+ return
240
+ }
241
+ if cond.OperatorToken.Kind != shimast.KindLessThanToken {
242
+ return
243
+ }
244
+ if identifierText(cond.Left) != counter {
245
+ return
246
+ }
247
+ if cond.Right == nil || cond.Right.Kind != shimast.KindPropertyAccessExpression {
248
+ return
249
+ }
250
+ rightAccess := cond.Right.AsPropertyAccessExpression()
251
+ if rightAccess == nil || identifierText(rightAccess.Name()) != "length" {
252
+ return
253
+ }
254
+ // Incrementor: `i++` or `++i`.
255
+ if !isCounterIncrement(loop.Incrementor, counter) {
256
+ return
257
+ }
258
+ ctx.Report(node, "Prefer a 'for-of' loop instead of a 'for' loop with this simple iteration.")
259
259
  }
260
260
 
261
261
  func isCounterIncrement(node *shimast.Node, counter string) bool {
262
- switch node.Kind {
263
- case shimast.KindPostfixUnaryExpression:
264
- post := node.AsPostfixUnaryExpression()
265
- return post != nil && post.Operator == shimast.KindPlusPlusToken && identifierText(post.Operand) == counter
266
- case shimast.KindPrefixUnaryExpression:
267
- pre := node.AsPrefixUnaryExpression()
268
- return pre != nil && pre.Operator == shimast.KindPlusPlusToken && identifierText(pre.Operand) == counter
269
- }
270
- return false
262
+ switch node.Kind {
263
+ case shimast.KindPostfixUnaryExpression:
264
+ post := node.AsPostfixUnaryExpression()
265
+ return post != nil && post.Operator == shimast.KindPlusPlusToken && identifierText(post.Operand) == counter
266
+ case shimast.KindPrefixUnaryExpression:
267
+ pre := node.AsPrefixUnaryExpression()
268
+ return pre != nil && pre.Operator == shimast.KindPlusPlusToken && identifierText(pre.Operand) == counter
269
+ }
270
+ return false
271
271
  }
272
272
 
273
273
  // prefer-function-type: a single-call-signature interface or type alias
274
274
  // is more readably written as a function type.
275
275
  //
276
- // interface F { (x: number): string } -> type F = (x: number) => string
276
+ // interface F { (x: number): string } -> type F = (x: number) => string
277
277
  type preferFunctionType struct{}
278
278
 
279
279
  func (preferFunctionType) Name() string { return "prefer-function-type" }
280
280
  func (preferFunctionType) Visits() []shimast.Kind {
281
- return []shimast.Kind{shimast.KindInterfaceDeclaration}
281
+ return []shimast.Kind{shimast.KindInterfaceDeclaration}
282
282
  }
283
283
  func (preferFunctionType) Check(ctx *Context, node *shimast.Node) {
284
- decl := node.AsInterfaceDeclaration()
285
- if decl == nil || decl.Members == nil || len(decl.Members.Nodes) != 1 {
286
- return
287
- }
288
- if decl.HeritageClauses != nil && len(decl.HeritageClauses.Nodes) > 0 {
289
- return
290
- }
291
- member := decl.Members.Nodes[0]
292
- if member == nil || member.Kind != shimast.KindCallSignature {
293
- return
294
- }
295
- ctx.Report(node, "Interface only has a call signature; use 'type' alias and function type instead.")
284
+ decl := node.AsInterfaceDeclaration()
285
+ if decl == nil || decl.Members == nil || len(decl.Members.Nodes) != 1 {
286
+ return
287
+ }
288
+ if decl.HeritageClauses != nil && len(decl.HeritageClauses.Nodes) > 0 {
289
+ return
290
+ }
291
+ member := decl.Members.Nodes[0]
292
+ if member == nil || member.Kind != shimast.KindCallSignature {
293
+ return
294
+ }
295
+ ctx.Report(node, "Interface only has a call signature; use 'type' alias and function type instead.")
296
296
  }
297
297
 
298
298
  // prefer-namespace-keyword: `module Foo {}` (TS namespace via `module`
@@ -301,20 +301,20 @@ type preferNamespaceKeyword struct{}
301
301
 
302
302
  func (preferNamespaceKeyword) Name() string { return "prefer-namespace-keyword" }
303
303
  func (preferNamespaceKeyword) Visits() []shimast.Kind {
304
- return []shimast.Kind{shimast.KindModuleDeclaration}
304
+ return []shimast.Kind{shimast.KindModuleDeclaration}
305
305
  }
306
306
  func (preferNamespaceKeyword) Check(ctx *Context, node *shimast.Node) {
307
- decl := node.AsModuleDeclaration()
308
- if decl == nil || decl.Name() == nil {
309
- return
310
- }
311
- if decl.Name().Kind == shimast.KindStringLiteral {
312
- return // ambient module: `declare module "fs" {}` is fine
313
- }
314
- if decl.Keyword != shimast.KindModuleKeyword {
315
- return
316
- }
317
- ctx.Report(node, "Use 'namespace' instead of 'module' to declare custom TypeScript modules.")
307
+ decl := node.AsModuleDeclaration()
308
+ if decl == nil || decl.Name() == nil {
309
+ return
310
+ }
311
+ if decl.Name().Kind == shimast.KindStringLiteral {
312
+ return // ambient module: `declare module "fs" {}` is fine
313
+ }
314
+ if decl.Keyword != shimast.KindModuleKeyword {
315
+ return
316
+ }
317
+ ctx.Report(node, "Use 'namespace' instead of 'module' to declare custom TypeScript modules.")
318
318
  }
319
319
 
320
320
  // triple-slash-reference: `/// <reference path="..." />` directives.
@@ -324,15 +324,15 @@ type tripleSlashReference struct{}
324
324
  func (tripleSlashReference) Name() string { return "triple-slash-reference" }
325
325
  func (tripleSlashReference) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindSourceFile} }
326
326
  func (tripleSlashReference) Check(ctx *Context, node *shimast.Node) {
327
- if ctx.File == nil {
328
- return
329
- }
330
- for _, ref := range ctx.File.ReferencedFiles {
331
- ctx.ReportRange(ref.Pos(), ref.End(), "Do not use triple slash references for "+ref.FileName+".")
332
- }
333
- for _, ref := range ctx.File.TypeReferenceDirectives {
334
- ctx.ReportRange(ref.Pos(), ref.End(), "Do not use triple slash references for "+ref.FileName+".")
335
- }
327
+ if ctx.File == nil {
328
+ return
329
+ }
330
+ for _, ref := range ctx.File.ReferencedFiles {
331
+ ctx.ReportRange(ref.Pos(), ref.End(), "Do not use triple slash references for "+ref.FileName+".")
332
+ }
333
+ for _, ref := range ctx.File.TypeReferenceDirectives {
334
+ ctx.ReportRange(ref.Pos(), ref.End(), "Do not use triple slash references for "+ref.FileName+".")
335
+ }
336
336
  }
337
337
 
338
338
  // no-array-delete: `delete arr[0]` leaves a sparse hole. Use `splice`.
@@ -341,23 +341,23 @@ type noArrayDelete struct{}
341
341
  func (noArrayDelete) Name() string { return "no-array-delete" }
342
342
  func (noArrayDelete) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindDeleteExpression} }
343
343
  func (noArrayDelete) Check(ctx *Context, node *shimast.Node) {
344
- del := node.AsDeleteExpression()
345
- if del == nil || del.Expression == nil {
346
- return
347
- }
348
- if del.Expression.Kind != shimast.KindElementAccessExpression {
349
- return
350
- }
351
- access := del.Expression.AsElementAccessExpression()
352
- if access == nil || access.ArgumentExpression == nil {
353
- return
354
- }
355
- // Numeric subscript ⇒ likely-array delete. (Object delete via
356
- // numeric key is rare.)
357
- switch access.ArgumentExpression.Kind {
358
- case shimast.KindNumericLiteral, shimast.KindIdentifier:
359
- ctx.Report(node, "Using delete with an array expression is unsafe.")
360
- }
344
+ del := node.AsDeleteExpression()
345
+ if del == nil || del.Expression == nil {
346
+ return
347
+ }
348
+ if del.Expression.Kind != shimast.KindElementAccessExpression {
349
+ return
350
+ }
351
+ access := del.Expression.AsElementAccessExpression()
352
+ if access == nil || access.ArgumentExpression == nil {
353
+ return
354
+ }
355
+ // Numeric subscript ⇒ likely-array delete. (Object delete via
356
+ // numeric key is rare.)
357
+ switch access.ArgumentExpression.Kind {
358
+ case shimast.KindNumericLiteral, shimast.KindIdentifier:
359
+ ctx.Report(node, "Using delete with an array expression is unsafe.")
360
+ }
361
361
  }
362
362
 
363
363
  // consistent-type-imports: `import { Foo } from "./types"` where Foo is
@@ -373,84 +373,84 @@ type consistentTypeImports struct{}
373
373
 
374
374
  func (consistentTypeImports) Name() string { return "consistent-type-imports" }
375
375
  func (consistentTypeImports) Visits() []shimast.Kind {
376
- return []shimast.Kind{shimast.KindImportDeclaration}
376
+ return []shimast.Kind{shimast.KindImportDeclaration}
377
377
  }
378
378
  func (consistentTypeImports) Check(ctx *Context, node *shimast.Node) {
379
- decl := node.AsImportDeclaration()
380
- if decl == nil || decl.ImportClause == nil {
381
- return
382
- }
383
- clause := decl.ImportClause.AsImportClause()
384
- if clause == nil {
385
- return
386
- }
387
- if clause.PhaseModifier == shimast.KindTypeKeyword {
388
- return // already `import type`.
389
- }
390
- if clause.NamedBindings == nil || clause.NamedBindings.Kind != shimast.KindNamedImports {
391
- return
392
- }
393
- named := clause.NamedBindings.AsNamedImports()
394
- if named == nil || named.Elements == nil {
395
- return
396
- }
397
- names := []string{}
398
- for _, spec := range named.Elements.Nodes {
399
- if spec == nil {
400
- continue
401
- }
402
- s := spec.AsImportSpecifier()
403
- if s == nil || s.IsTypeOnly {
404
- continue
405
- }
406
- if name := identifierText(s.Name()); name != "" {
407
- names = append(names, name)
408
- }
409
- }
410
- if len(names) == 0 {
411
- return
412
- }
413
- if !allUsesAreTypeOnly(ctx.File.AsNode(), names) {
414
- return
415
- }
416
- ctx.Report(node, "All imports in the declaration are only used as types. Use `import type`.")
379
+ decl := node.AsImportDeclaration()
380
+ if decl == nil || decl.ImportClause == nil {
381
+ return
382
+ }
383
+ clause := decl.ImportClause.AsImportClause()
384
+ if clause == nil {
385
+ return
386
+ }
387
+ if clause.PhaseModifier == shimast.KindTypeKeyword {
388
+ return // already `import type`.
389
+ }
390
+ if clause.NamedBindings == nil || clause.NamedBindings.Kind != shimast.KindNamedImports {
391
+ return
392
+ }
393
+ named := clause.NamedBindings.AsNamedImports()
394
+ if named == nil || named.Elements == nil {
395
+ return
396
+ }
397
+ names := []string{}
398
+ for _, spec := range named.Elements.Nodes {
399
+ if spec == nil {
400
+ continue
401
+ }
402
+ s := spec.AsImportSpecifier()
403
+ if s == nil || s.IsTypeOnly {
404
+ continue
405
+ }
406
+ if name := identifierText(s.Name()); name != "" {
407
+ names = append(names, name)
408
+ }
409
+ }
410
+ if len(names) == 0 {
411
+ return
412
+ }
413
+ if !allUsesAreTypeOnly(ctx.File.AsNode(), names) {
414
+ return
415
+ }
416
+ ctx.Report(node, "All imports in the declaration are only used as types. Use `import type`.")
417
417
  }
418
418
 
419
419
  func allUsesAreTypeOnly(root *shimast.Node, names []string) bool {
420
- want := map[string]bool{}
421
- for _, n := range names {
422
- want[n] = true
423
- }
424
- allOk := true
425
- var visit func(n *shimast.Node, inType bool)
426
- visit = func(n *shimast.Node, inType bool) {
427
- if n == nil || !allOk {
428
- return
429
- }
430
- typeContext := inType
431
- switch n.Kind {
432
- case shimast.KindTypeReference,
433
- shimast.KindTypeAliasDeclaration,
434
- shimast.KindInterfaceDeclaration,
435
- shimast.KindTypeQuery,
436
- shimast.KindTypeOperator,
437
- shimast.KindTypeLiteral:
438
- typeContext = true
439
- case shimast.KindIdentifier:
440
- if !typeContext && want[identifierText(n)] {
441
- allOk = false
442
- return
443
- }
444
- case shimast.KindImportDeclaration:
445
- return // don't descend into other imports
446
- }
447
- n.ForEachChild(func(c *shimast.Node) bool {
448
- visit(c, typeContext)
449
- return false
450
- })
451
- }
452
- visit(root, false)
453
- return allOk
420
+ want := map[string]bool{}
421
+ for _, n := range names {
422
+ want[n] = true
423
+ }
424
+ allOk := true
425
+ var visit func(n *shimast.Node, inType bool)
426
+ visit = func(n *shimast.Node, inType bool) {
427
+ if n == nil || !allOk {
428
+ return
429
+ }
430
+ typeContext := inType
431
+ switch n.Kind {
432
+ case shimast.KindTypeReference,
433
+ shimast.KindTypeAliasDeclaration,
434
+ shimast.KindInterfaceDeclaration,
435
+ shimast.KindTypeQuery,
436
+ shimast.KindTypeOperator,
437
+ shimast.KindTypeLiteral:
438
+ typeContext = true
439
+ case shimast.KindIdentifier:
440
+ if !typeContext && want[identifierText(n)] {
441
+ allOk = false
442
+ return
443
+ }
444
+ case shimast.KindImportDeclaration:
445
+ return // don't descend into other imports
446
+ }
447
+ n.ForEachChild(func(c *shimast.Node) bool {
448
+ visit(c, typeContext)
449
+ return false
450
+ })
451
+ }
452
+ visit(root, false)
453
+ return allOk
454
454
  }
455
455
 
456
456
  // no-empty-object-type: `interface Foo {}` / `type Foo = {}`. ESLint
@@ -461,13 +461,13 @@ type noEmptyObjectType struct{}
461
461
  func (noEmptyObjectType) Name() string { return "no-empty-object-type" }
462
462
  func (noEmptyObjectType) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindTypeLiteral} }
463
463
  func (noEmptyObjectType) Check(ctx *Context, node *shimast.Node) {
464
- lit := node.AsTypeLiteralNode()
465
- if lit == nil || lit.Members == nil {
466
- return
467
- }
468
- if len(lit.Members.Nodes) == 0 {
469
- ctx.Report(node, "The `{}` type is generally not what's intended; consider `Record<string, unknown>` or `unknown`.")
470
- }
464
+ lit := node.AsTypeLiteralNode()
465
+ if lit == nil || lit.Members == nil {
466
+ return
467
+ }
468
+ if len(lit.Members.Nodes) == 0 {
469
+ ctx.Report(node, "The `{}` type is generally not what's intended; consider `Record<string, unknown>` or `unknown`.")
470
+ }
471
471
  }
472
472
 
473
473
  // array-type: `Array<T>` vs `T[]`. ESLint default mode prefers `T[]`.
@@ -476,22 +476,22 @@ type arrayType struct{}
476
476
  func (arrayType) Name() string { return "array-type" }
477
477
  func (arrayType) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindTypeReference} }
478
478
  func (arrayType) Check(ctx *Context, node *shimast.Node) {
479
- ref := node.AsTypeReferenceNode()
480
- if ref == nil || ref.TypeName == nil {
481
- return
482
- }
483
- name := identifierText(ref.TypeName)
484
- if name != "Array" && name != "ReadonlyArray" {
485
- return
486
- }
487
- if ref.TypeArguments == nil || len(ref.TypeArguments.Nodes) != 1 {
488
- return
489
- }
490
- if name == "Array" {
491
- ctx.Report(node, "Use 'T[]' instead of 'Array<T>'.")
492
- } else {
493
- ctx.Report(node, "Use 'readonly T[]' instead of 'ReadonlyArray<T>'.")
494
- }
479
+ ref := node.AsTypeReferenceNode()
480
+ if ref == nil || ref.TypeName == nil {
481
+ return
482
+ }
483
+ name := identifierText(ref.TypeName)
484
+ if name != "Array" && name != "ReadonlyArray" {
485
+ return
486
+ }
487
+ if ref.TypeArguments == nil || len(ref.TypeArguments.Nodes) != 1 {
488
+ return
489
+ }
490
+ if name == "Array" {
491
+ ctx.Report(node, "Use 'T[]' instead of 'Array<T>'.")
492
+ } else {
493
+ ctx.Report(node, "Use 'readonly T[]' instead of 'ReadonlyArray<T>'.")
494
+ }
495
495
  }
496
496
 
497
497
  // consistent-indexed-object-style: `{ [key: string]: T }` vs
@@ -500,18 +500,18 @@ type consistentIndexedObjectStyle struct{}
500
500
 
501
501
  func (consistentIndexedObjectStyle) Name() string { return "consistent-indexed-object-style" }
502
502
  func (consistentIndexedObjectStyle) Visits() []shimast.Kind {
503
- return []shimast.Kind{shimast.KindTypeLiteral}
503
+ return []shimast.Kind{shimast.KindTypeLiteral}
504
504
  }
505
505
  func (consistentIndexedObjectStyle) Check(ctx *Context, node *shimast.Node) {
506
- lit := node.AsTypeLiteralNode()
507
- if lit == nil || lit.Members == nil || len(lit.Members.Nodes) != 1 {
508
- return
509
- }
510
- member := lit.Members.Nodes[0]
511
- if member == nil || member.Kind != shimast.KindIndexSignature {
512
- return
513
- }
514
- ctx.Report(node, "An index signature is preferred to be a Record type.")
506
+ lit := node.AsTypeLiteralNode()
507
+ if lit == nil || lit.Members == nil || len(lit.Members.Nodes) != 1 {
508
+ return
509
+ }
510
+ member := lit.Members.Nodes[0]
511
+ if member == nil || member.Kind != shimast.KindIndexSignature {
512
+ return
513
+ }
514
+ ctx.Report(node, "An index signature is preferred to be a Record type.")
515
515
  }
516
516
 
517
517
  // no-explicit-any-rest-parameter — keeping this distinct from
@@ -528,24 +528,24 @@ type banTslintComment struct{}
528
528
  func (banTslintComment) Name() string { return "ban-tslint-comment" }
529
529
  func (banTslintComment) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindSourceFile} }
530
530
  func (banTslintComment) Check(ctx *Context, node *shimast.Node) {
531
- if ctx.File == nil {
532
- return
533
- }
534
- text := ctx.File.Text()
535
- for i := 0; i < len(text)-2; i++ {
536
- if text[i] == '/' && text[i+1] == '/' {
537
- // Find end of line.
538
- end := i
539
- for end < len(text) && text[end] != '\n' {
540
- end++
541
- }
542
- line := text[i:end]
543
- if strings.Contains(line, "tslint:") {
544
- ctx.ReportRange(i, end, "tslint comment detected.")
545
- }
546
- i = end
547
- }
548
- }
531
+ if ctx.File == nil {
532
+ return
533
+ }
534
+ text := ctx.File.Text()
535
+ for i := 0; i < len(text)-2; i++ {
536
+ if text[i] == '/' && text[i+1] == '/' {
537
+ // Find end of line.
538
+ end := i
539
+ for end < len(text) && text[end] != '\n' {
540
+ end++
541
+ }
542
+ line := text[i:end]
543
+ if strings.Contains(line, "tslint:") {
544
+ ctx.ReportRange(i, end, "tslint comment detected.")
545
+ }
546
+ i = end
547
+ }
548
+ }
549
549
  }
550
550
 
551
551
  // adjacent-overload-signatures: function/method overloads must be
@@ -555,122 +555,122 @@ type adjacentOverloadSignatures struct{}
555
555
 
556
556
  func (adjacentOverloadSignatures) Name() string { return "adjacent-overload-signatures" }
557
557
  func (adjacentOverloadSignatures) Visits() []shimast.Kind {
558
- return []shimast.Kind{shimast.KindInterfaceDeclaration, shimast.KindTypeLiteral, shimast.KindClassDeclaration, shimast.KindClassExpression, shimast.KindModuleBlock, shimast.KindSourceFile}
558
+ return []shimast.Kind{shimast.KindInterfaceDeclaration, shimast.KindTypeLiteral, shimast.KindClassDeclaration, shimast.KindClassExpression, shimast.KindModuleBlock, shimast.KindSourceFile}
559
559
  }
560
560
  func (adjacentOverloadSignatures) Check(ctx *Context, node *shimast.Node) {
561
- members := containerMembers(node)
562
- if len(members) == 0 {
563
- return
564
- }
565
- type entry struct {
566
- index int
567
- name string
568
- kind shimast.Kind
569
- }
570
- seen := []entry{}
571
- for i, m := range members {
572
- name, kind, ok := overloadName(m)
573
- if !ok {
574
- continue
575
- }
576
- for _, prev := range seen {
577
- if prev.name == name && prev.kind == kind && prev.index < i-1 {
578
- // Check there isn't already a same-name entry adjacent.
579
- if i > 0 {
580
- prevName, prevKind, _ := overloadName(members[i-1])
581
- if prevName == name && prevKind == kind {
582
- break
583
- }
584
- }
585
- ctx.Report(m, "All "+name+" signatures should be adjacent.")
586
- break
587
- }
588
- }
589
- seen = append(seen, entry{index: i, name: name, kind: kind})
590
- }
561
+ members := containerMembers(node)
562
+ if len(members) == 0 {
563
+ return
564
+ }
565
+ type entry struct {
566
+ index int
567
+ name string
568
+ kind shimast.Kind
569
+ }
570
+ seen := []entry{}
571
+ for i, m := range members {
572
+ name, kind, ok := overloadName(m)
573
+ if !ok {
574
+ continue
575
+ }
576
+ for _, prev := range seen {
577
+ if prev.name == name && prev.kind == kind && prev.index < i-1 {
578
+ // Check there isn't already a same-name entry adjacent.
579
+ if i > 0 {
580
+ prevName, prevKind, _ := overloadName(members[i-1])
581
+ if prevName == name && prevKind == kind {
582
+ break
583
+ }
584
+ }
585
+ ctx.Report(m, "All "+name+" signatures should be adjacent.")
586
+ break
587
+ }
588
+ }
589
+ seen = append(seen, entry{index: i, name: name, kind: kind})
590
+ }
591
591
  }
592
592
 
593
593
  func containerMembers(node *shimast.Node) []*shimast.Node {
594
- switch node.Kind {
595
- case shimast.KindInterfaceDeclaration:
596
- decl := node.AsInterfaceDeclaration()
597
- if decl != nil && decl.Members != nil {
598
- return decl.Members.Nodes
599
- }
600
- case shimast.KindTypeLiteral:
601
- lit := node.AsTypeLiteralNode()
602
- if lit != nil && lit.Members != nil {
603
- return lit.Members.Nodes
604
- }
605
- case shimast.KindClassDeclaration:
606
- decl := node.AsClassDeclaration()
607
- if decl != nil && decl.Members != nil {
608
- return decl.Members.Nodes
609
- }
610
- case shimast.KindClassExpression:
611
- decl := node.AsClassExpression()
612
- if decl != nil && decl.Members != nil {
613
- return decl.Members.Nodes
614
- }
615
- case shimast.KindModuleBlock:
616
- mb := node.AsModuleBlock()
617
- if mb != nil && mb.Statements != nil {
618
- return mb.Statements.Nodes
619
- }
620
- case shimast.KindSourceFile:
621
- f := node.AsSourceFile()
622
- if f != nil && f.Statements != nil {
623
- return f.Statements.Nodes
624
- }
625
- }
626
- return nil
594
+ switch node.Kind {
595
+ case shimast.KindInterfaceDeclaration:
596
+ decl := node.AsInterfaceDeclaration()
597
+ if decl != nil && decl.Members != nil {
598
+ return decl.Members.Nodes
599
+ }
600
+ case shimast.KindTypeLiteral:
601
+ lit := node.AsTypeLiteralNode()
602
+ if lit != nil && lit.Members != nil {
603
+ return lit.Members.Nodes
604
+ }
605
+ case shimast.KindClassDeclaration:
606
+ decl := node.AsClassDeclaration()
607
+ if decl != nil && decl.Members != nil {
608
+ return decl.Members.Nodes
609
+ }
610
+ case shimast.KindClassExpression:
611
+ decl := node.AsClassExpression()
612
+ if decl != nil && decl.Members != nil {
613
+ return decl.Members.Nodes
614
+ }
615
+ case shimast.KindModuleBlock:
616
+ mb := node.AsModuleBlock()
617
+ if mb != nil && mb.Statements != nil {
618
+ return mb.Statements.Nodes
619
+ }
620
+ case shimast.KindSourceFile:
621
+ f := node.AsSourceFile()
622
+ if f != nil && f.Statements != nil {
623
+ return f.Statements.Nodes
624
+ }
625
+ }
626
+ return nil
627
627
  }
628
628
 
629
629
  func overloadName(m *shimast.Node) (string, shimast.Kind, bool) {
630
- if m == nil {
631
- return "", 0, false
632
- }
633
- switch m.Kind {
634
- case shimast.KindMethodSignature:
635
- ms := m.AsMethodSignatureDeclaration()
636
- if ms != nil {
637
- return identifierText(ms.Name()), m.Kind, true
638
- }
639
- case shimast.KindMethodDeclaration:
640
- md := m.AsMethodDeclaration()
641
- if md != nil {
642
- return identifierText(md.Name()), m.Kind, true
643
- }
644
- case shimast.KindFunctionDeclaration:
645
- fd := m.AsFunctionDeclaration()
646
- if fd != nil {
647
- return identifierText(fd.Name()), m.Kind, true
648
- }
649
- case shimast.KindCallSignature, shimast.KindConstructSignature:
650
- return "(" + m.Kind.String() + ")", m.Kind, true
651
- }
652
- return "", 0, false
630
+ if m == nil {
631
+ return "", 0, false
632
+ }
633
+ switch m.Kind {
634
+ case shimast.KindMethodSignature:
635
+ ms := m.AsMethodSignatureDeclaration()
636
+ if ms != nil {
637
+ return identifierText(ms.Name()), m.Kind, true
638
+ }
639
+ case shimast.KindMethodDeclaration:
640
+ md := m.AsMethodDeclaration()
641
+ if md != nil {
642
+ return identifierText(md.Name()), m.Kind, true
643
+ }
644
+ case shimast.KindFunctionDeclaration:
645
+ fd := m.AsFunctionDeclaration()
646
+ if fd != nil {
647
+ return identifierText(fd.Name()), m.Kind, true
648
+ }
649
+ case shimast.KindCallSignature, shimast.KindConstructSignature:
650
+ return "(" + m.Kind.String() + ")", m.Kind, true
651
+ }
652
+ return "", 0, false
653
653
  }
654
654
 
655
655
  // no-this-alias-helper: shared helpers for ts rules above.
656
656
  var _ = struct{}{}
657
657
 
658
658
  func init() {
659
- Register(noConfusingNonNullAssertion{})
660
- Register(noDuplicateEnumValues{})
661
- Register(noExtraNonNullAssertion{})
662
- Register(noNonNullAssertedOptionalChain{})
663
- Register(noMisusedNew{})
664
- Register(preferEnumInitializers{})
665
- Register(preferForOf{})
666
- Register(preferFunctionType{})
667
- Register(preferNamespaceKeyword{})
668
- Register(tripleSlashReference{})
669
- Register(noArrayDelete{})
670
- Register(consistentTypeImports{})
671
- Register(noEmptyObjectType{})
672
- Register(arrayType{})
673
- Register(consistentIndexedObjectStyle{})
674
- Register(banTslintComment{})
675
- Register(adjacentOverloadSignatures{})
659
+ Register(noConfusingNonNullAssertion{})
660
+ Register(noDuplicateEnumValues{})
661
+ Register(noExtraNonNullAssertion{})
662
+ Register(noNonNullAssertedOptionalChain{})
663
+ Register(noMisusedNew{})
664
+ Register(preferEnumInitializers{})
665
+ Register(preferForOf{})
666
+ Register(preferFunctionType{})
667
+ Register(preferNamespaceKeyword{})
668
+ Register(tripleSlashReference{})
669
+ Register(noArrayDelete{})
670
+ Register(consistentTypeImports{})
671
+ Register(noEmptyObjectType{})
672
+ Register(arrayType{})
673
+ Register(consistentIndexedObjectStyle{})
674
+ Register(banTslintComment{})
675
+ Register(adjacentOverloadSignatures{})
676
676
  }