@ttsc/lint 0.7.1 → 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.
@@ -8,17 +8,17 @@ type noEmptyStaticBlock struct{}
8
8
 
9
9
  func (noEmptyStaticBlock) Name() string { return "no-empty-static-block" }
10
10
  func (noEmptyStaticBlock) Visits() []shimast.Kind {
11
- return []shimast.Kind{shimast.KindClassStaticBlockDeclaration}
11
+ return []shimast.Kind{shimast.KindClassStaticBlockDeclaration}
12
12
  }
13
13
  func (noEmptyStaticBlock) Check(ctx *Context, node *shimast.Node) {
14
- block := node.AsClassStaticBlockDeclaration()
15
- if block == nil || block.Body == nil {
16
- return
17
- }
18
- body := block.Body.AsBlock()
19
- if body == nil || body.Statements == nil || len(body.Statements.Nodes) == 0 {
20
- ctx.Report(node, "Unexpected empty static block.")
21
- }
14
+ block := node.AsClassStaticBlockDeclaration()
15
+ if block == nil || block.Body == nil {
16
+ return
17
+ }
18
+ body := block.Body.AsBlock()
19
+ if body == nil || body.Statements == nil || len(body.Statements.Nodes) == 0 {
20
+ ctx.Report(node, "Unexpected empty static block.")
21
+ }
22
22
  }
23
23
 
24
24
  // no-setter-return: setters must not return a value.
@@ -28,23 +28,23 @@ type noSetterReturn struct{}
28
28
  func (noSetterReturn) Name() string { return "no-setter-return" }
29
29
  func (noSetterReturn) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindReturnStatement} }
30
30
  func (noSetterReturn) Check(ctx *Context, node *shimast.Node) {
31
- ret := node.AsReturnStatement()
32
- if ret == nil || ret.Expression == nil || !isInsideDirectSetter(node) {
33
- return
34
- }
35
- ctx.Report(node, "Setter should not return a value.")
31
+ ret := node.AsReturnStatement()
32
+ if ret == nil || ret.Expression == nil || !isInsideDirectSetter(node) {
33
+ return
34
+ }
35
+ ctx.Report(node, "Setter should not return a value.")
36
36
  }
37
37
 
38
38
  func isInsideDirectSetter(node *shimast.Node) bool {
39
- for p := node.Parent; p != nil; p = p.Parent {
40
- if p.Kind == shimast.KindSetAccessor {
41
- return true
42
- }
43
- if isFunctionLikeKind(p) {
44
- return false
45
- }
46
- }
47
- return false
39
+ for p := node.Parent; p != nil; p = p.Parent {
40
+ if p.Kind == shimast.KindSetAccessor {
41
+ return true
42
+ }
43
+ if isFunctionLikeKind(p) {
44
+ return false
45
+ }
46
+ }
47
+ return false
48
48
  }
49
49
 
50
50
  // no-unused-labels: a label is useful only when a break/continue targets it.
@@ -54,31 +54,31 @@ type noUnusedLabels struct{}
54
54
  func (noUnusedLabels) Name() string { return "no-unused-labels" }
55
55
  func (noUnusedLabels) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindLabeledStatement} }
56
56
  func (noUnusedLabels) Check(ctx *Context, node *shimast.Node) {
57
- stmt := node.AsLabeledStatement()
58
- if stmt == nil || stmt.Label == nil {
59
- return
60
- }
61
- label := identifierText(stmt.Label)
62
- if label == "" {
63
- return
64
- }
65
- used := false
66
- walkDescendants(stmt.Statement, func(child *shimast.Node) {
67
- if used || child == nil {
68
- return
69
- }
70
- switch child.Kind {
71
- case shimast.KindBreakStatement:
72
- br := child.AsBreakStatement()
73
- used = br != nil && identifierText(br.Label) == label
74
- case shimast.KindContinueStatement:
75
- cont := child.AsContinueStatement()
76
- used = cont != nil && identifierText(cont.Label) == label
77
- }
78
- })
79
- if !used {
80
- ctx.Report(stmt.Label, "Label '"+label+"' is defined but never used.")
81
- }
57
+ stmt := node.AsLabeledStatement()
58
+ if stmt == nil || stmt.Label == nil {
59
+ return
60
+ }
61
+ label := identifierText(stmt.Label)
62
+ if label == "" {
63
+ return
64
+ }
65
+ used := false
66
+ walkDescendants(stmt.Statement, func(child *shimast.Node) {
67
+ if used || child == nil {
68
+ return
69
+ }
70
+ switch child.Kind {
71
+ case shimast.KindBreakStatement:
72
+ br := child.AsBreakStatement()
73
+ used = br != nil && identifierText(br.Label) == label
74
+ case shimast.KindContinueStatement:
75
+ cont := child.AsContinueStatement()
76
+ used = cont != nil && identifierText(cont.Label) == label
77
+ }
78
+ })
79
+ if !used {
80
+ ctx.Report(stmt.Label, "Label '"+label+"' is defined but never used.")
81
+ }
82
82
  }
83
83
 
84
84
  // no-dynamic-delete: avoid deleting properties through dynamic keys.
@@ -88,29 +88,29 @@ type noDynamicDelete struct{}
88
88
  func (noDynamicDelete) Name() string { return "no-dynamic-delete" }
89
89
  func (noDynamicDelete) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindDeleteExpression} }
90
90
  func (noDynamicDelete) Check(ctx *Context, node *shimast.Node) {
91
- del := node.AsDeleteExpression()
92
- if del == nil || del.Expression == nil || del.Expression.Kind != shimast.KindElementAccessExpression {
93
- return
94
- }
95
- access := del.Expression.AsElementAccessExpression()
96
- if access == nil || isStaticPropertyKey(access.ArgumentExpression) {
97
- return
98
- }
99
- ctx.Report(node, "Do not delete dynamically computed property keys.")
91
+ del := node.AsDeleteExpression()
92
+ if del == nil || del.Expression == nil || del.Expression.Kind != shimast.KindElementAccessExpression {
93
+ return
94
+ }
95
+ access := del.Expression.AsElementAccessExpression()
96
+ if access == nil || isStaticPropertyKey(access.ArgumentExpression) {
97
+ return
98
+ }
99
+ ctx.Report(node, "Do not delete dynamically computed property keys.")
100
100
  }
101
101
 
102
102
  func isStaticPropertyKey(node *shimast.Node) bool {
103
- node = stripParens(node)
104
- if node == nil {
105
- return false
106
- }
107
- switch node.Kind {
108
- case shimast.KindStringLiteral,
109
- shimast.KindNoSubstitutionTemplateLiteral,
110
- shimast.KindNumericLiteral:
111
- return true
112
- }
113
- return false
103
+ node = stripParens(node)
104
+ if node == nil {
105
+ return false
106
+ }
107
+ switch node.Kind {
108
+ case shimast.KindStringLiteral,
109
+ shimast.KindNoSubstitutionTemplateLiteral,
110
+ shimast.KindNumericLiteral:
111
+ return true
112
+ }
113
+ return false
114
114
  }
115
115
 
116
116
  // no-non-null-asserted-nullish-coalescing: `foo! ?? bar` is contradictory.
@@ -118,22 +118,22 @@ func isStaticPropertyKey(node *shimast.Node) bool {
118
118
  type noNonNullAssertedNullishCoalescing struct{}
119
119
 
120
120
  func (noNonNullAssertedNullishCoalescing) Name() string {
121
- return "no-non-null-asserted-nullish-coalescing"
121
+ return "no-non-null-asserted-nullish-coalescing"
122
122
  }
123
123
  func (noNonNullAssertedNullishCoalescing) Visits() []shimast.Kind {
124
- return []shimast.Kind{shimast.KindBinaryExpression}
124
+ return []shimast.Kind{shimast.KindBinaryExpression}
125
125
  }
126
126
  func (noNonNullAssertedNullishCoalescing) Check(ctx *Context, node *shimast.Node) {
127
- expr := node.AsBinaryExpression()
128
- if expr == nil || expr.OperatorToken == nil || expr.OperatorToken.Kind != shimast.KindQuestionQuestionToken {
129
- return
130
- }
131
- if left := stripParens(expr.Left); left != nil && left.Kind == shimast.KindNonNullExpression {
132
- ctx.Report(left, "Nullish coalescing is unnecessary after a non-null assertion.")
133
- }
134
- if right := stripParens(expr.Right); right != nil && right.Kind == shimast.KindNonNullExpression {
135
- ctx.Report(right, "Nullish coalescing is unnecessary before a non-null assertion.")
136
- }
127
+ expr := node.AsBinaryExpression()
128
+ if expr == nil || expr.OperatorToken == nil || expr.OperatorToken.Kind != shimast.KindQuestionQuestionToken {
129
+ return
130
+ }
131
+ if left := stripParens(expr.Left); left != nil && left.Kind == shimast.KindNonNullExpression {
132
+ ctx.Report(left, "Nullish coalescing is unnecessary after a non-null assertion.")
133
+ }
134
+ if right := stripParens(expr.Right); right != nil && right.Kind == shimast.KindNonNullExpression {
135
+ ctx.Report(right, "Nullish coalescing is unnecessary before a non-null assertion.")
136
+ }
137
137
  }
138
138
 
139
139
  // no-unnecessary-type-constraint: `<T extends any>` / `<T extends unknown>`.
@@ -142,16 +142,16 @@ type noUnnecessaryTypeConstraint struct{}
142
142
 
143
143
  func (noUnnecessaryTypeConstraint) Name() string { return "no-unnecessary-type-constraint" }
144
144
  func (noUnnecessaryTypeConstraint) Visits() []shimast.Kind {
145
- return []shimast.Kind{shimast.KindTypeParameter}
145
+ return []shimast.Kind{shimast.KindTypeParameter}
146
146
  }
147
147
  func (noUnnecessaryTypeConstraint) Check(ctx *Context, node *shimast.Node) {
148
- param := node.AsTypeParameterDeclaration()
149
- if param == nil || param.Constraint == nil {
150
- return
151
- }
152
- if param.Constraint.Kind == shimast.KindAnyKeyword || param.Constraint.Kind == shimast.KindUnknownKeyword {
153
- ctx.Report(param.Constraint, "Constraining a type parameter to any or unknown is unnecessary.")
154
- }
148
+ param := node.AsTypeParameterDeclaration()
149
+ if param == nil || param.Constraint == nil {
150
+ return
151
+ }
152
+ if param.Constraint.Kind == shimast.KindAnyKeyword || param.Constraint.Kind == shimast.KindUnknownKeyword {
153
+ ctx.Report(param.Constraint, "Constraining a type parameter to any or unknown is unnecessary.")
154
+ }
155
155
  }
156
156
 
157
157
  // no-unsafe-function-type: `Function` accepts any callable shape.
@@ -161,11 +161,11 @@ type noUnsafeFunctionType struct{}
161
161
  func (noUnsafeFunctionType) Name() string { return "no-unsafe-function-type" }
162
162
  func (noUnsafeFunctionType) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindTypeReference} }
163
163
  func (noUnsafeFunctionType) Check(ctx *Context, node *shimast.Node) {
164
- ref := node.AsTypeReferenceNode()
165
- if ref == nil || identifierText(ref.TypeName) != "Function" {
166
- return
167
- }
168
- ctx.Report(node, "The Function type is unsafe. Use a specific function type instead.")
164
+ ref := node.AsTypeReferenceNode()
165
+ if ref == nil || identifierText(ref.TypeName) != "Function" {
166
+ return
167
+ }
168
+ ctx.Report(node, "The Function type is unsafe. Use a specific function type instead.")
169
169
  }
170
170
 
171
171
  // no-wrapper-object-types: prefer primitive type keywords over boxed object
@@ -176,14 +176,14 @@ type noWrapperObjectTypes struct{}
176
176
  func (noWrapperObjectTypes) Name() string { return "no-wrapper-object-types" }
177
177
  func (noWrapperObjectTypes) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindTypeReference} }
178
178
  func (noWrapperObjectTypes) Check(ctx *Context, node *shimast.Node) {
179
- ref := node.AsTypeReferenceNode()
180
- if ref == nil {
181
- return
182
- }
183
- switch identifierText(ref.TypeName) {
184
- case "String", "Number", "Boolean", "Symbol", "BigInt", "Object":
185
- ctx.Report(node, "Use primitive type keywords instead of wrapper object types.")
186
- }
179
+ ref := node.AsTypeReferenceNode()
180
+ if ref == nil {
181
+ return
182
+ }
183
+ switch identifierText(ref.TypeName) {
184
+ case "String", "Number", "Boolean", "Symbol", "BigInt", "Object":
185
+ ctx.Report(node, "Use primitive type keywords instead of wrapper object types.")
186
+ }
187
187
  }
188
188
 
189
189
  // no-useless-constructor: an empty constructor with no parameters is noise.
@@ -193,17 +193,17 @@ type noUselessConstructor struct{}
193
193
  func (noUselessConstructor) Name() string { return "no-useless-constructor" }
194
194
  func (noUselessConstructor) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindConstructor} }
195
195
  func (noUselessConstructor) Check(ctx *Context, node *shimast.Node) {
196
- ctor := node.AsConstructorDeclaration()
197
- if ctor == nil || ctor.Body == nil {
198
- return
199
- }
200
- if len(node.Parameters()) != 0 {
201
- return
202
- }
203
- body := ctor.Body.AsBlock()
204
- if body == nil || body.Statements == nil || len(body.Statements.Nodes) == 0 {
205
- ctx.Report(node, "Useless empty constructor.")
206
- }
196
+ ctor := node.AsConstructorDeclaration()
197
+ if ctor == nil || ctor.Body == nil {
198
+ return
199
+ }
200
+ if len(node.Parameters()) != 0 {
201
+ return
202
+ }
203
+ body := ctor.Body.AsBlock()
204
+ if body == nil || body.Statements == nil || len(body.Statements.Nodes) == 0 {
205
+ ctx.Report(node, "Useless empty constructor.")
206
+ }
207
207
  }
208
208
 
209
209
  // prefer-literal-enum-member: computed enum members are harder to inspect.
@@ -213,11 +213,11 @@ type preferLiteralEnumMember struct{}
213
213
  func (preferLiteralEnumMember) Name() string { return "prefer-literal-enum-member" }
214
214
  func (preferLiteralEnumMember) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindEnumMember} }
215
215
  func (preferLiteralEnumMember) Check(ctx *Context, node *shimast.Node) {
216
- member := node.AsEnumMember()
217
- if member == nil || member.Initializer == nil || isLiteralLike(member.Initializer) {
218
- return
219
- }
220
- ctx.Report(member.Initializer, "Enum member initializer should be a literal value.")
216
+ member := node.AsEnumMember()
217
+ if member == nil || member.Initializer == nil || isLiteralLike(member.Initializer) {
218
+ return
219
+ }
220
+ ctx.Report(member.Initializer, "Enum member initializer should be a literal value.")
221
221
  }
222
222
 
223
223
  // consistent-type-assertions: prefer `value as Type` over `<Type>value`.
@@ -226,10 +226,10 @@ type consistentTypeAssertions struct{}
226
226
 
227
227
  func (consistentTypeAssertions) Name() string { return "consistent-type-assertions" }
228
228
  func (consistentTypeAssertions) Visits() []shimast.Kind {
229
- return []shimast.Kind{shimast.KindTypeAssertionExpression}
229
+ return []shimast.Kind{shimast.KindTypeAssertionExpression}
230
230
  }
231
231
  func (consistentTypeAssertions) Check(ctx *Context, node *shimast.Node) {
232
- ctx.Report(node, "Use `as` type assertions instead of angle-bracket assertions.")
232
+ ctx.Report(node, "Use `as` type assertions instead of angle-bracket assertions.")
233
233
  }
234
234
 
235
235
  // consistent-type-definitions: prefer interfaces for object-shaped public
@@ -239,14 +239,14 @@ type consistentTypeDefinitions struct{}
239
239
 
240
240
  func (consistentTypeDefinitions) Name() string { return "consistent-type-definitions" }
241
241
  func (consistentTypeDefinitions) Visits() []shimast.Kind {
242
- return []shimast.Kind{shimast.KindTypeAliasDeclaration}
242
+ return []shimast.Kind{shimast.KindTypeAliasDeclaration}
243
243
  }
244
244
  func (consistentTypeDefinitions) Check(ctx *Context, node *shimast.Node) {
245
- alias := node.AsTypeAliasDeclaration()
246
- if alias == nil || alias.Type == nil || alias.Type.Kind != shimast.KindTypeLiteral {
247
- return
248
- }
249
- ctx.Report(node, "Use an interface instead of a type literal alias.")
245
+ alias := node.AsTypeAliasDeclaration()
246
+ if alias == nil || alias.Type == nil || alias.Type.Kind != shimast.KindTypeLiteral {
247
+ return
248
+ }
249
+ ctx.Report(node, "Use an interface instead of a type literal alias.")
250
250
  }
251
251
 
252
252
  // dot-notation: `obj["prop"]` should be `obj.prop` when the key is a valid
@@ -256,34 +256,34 @@ type dotNotation struct{}
256
256
 
257
257
  func (dotNotation) Name() string { return "dot-notation" }
258
258
  func (dotNotation) Visits() []shimast.Kind {
259
- return []shimast.Kind{shimast.KindElementAccessExpression}
259
+ return []shimast.Kind{shimast.KindElementAccessExpression}
260
260
  }
261
261
  func (dotNotation) Check(ctx *Context, node *shimast.Node) {
262
- access := node.AsElementAccessExpression()
263
- if access == nil {
264
- return
265
- }
266
- key := stringLiteralText(access.ArgumentExpression)
267
- if key == "" || !isSimpleIdentifierName(key) {
268
- return
269
- }
270
- ctx.Report(node, "Use dot notation instead of a string literal property access.")
262
+ access := node.AsElementAccessExpression()
263
+ if access == nil {
264
+ return
265
+ }
266
+ key := stringLiteralText(access.ArgumentExpression)
267
+ if key == "" || !isSimpleIdentifierName(key) {
268
+ return
269
+ }
270
+ ctx.Report(node, "Use dot notation instead of a string literal property access.")
271
271
  }
272
272
 
273
273
  func isSimpleIdentifierName(value string) bool {
274
- if value == "" {
275
- return false
276
- }
277
- for i, r := range value {
278
- if r == '_' || r == '$' || r >= 'a' && r <= 'z' || r >= 'A' && r <= 'Z' {
279
- continue
280
- }
281
- if i > 0 && r >= '0' && r <= '9' {
282
- continue
283
- }
284
- return false
285
- }
286
- return true
274
+ if value == "" {
275
+ return false
276
+ }
277
+ for i, r := range value {
278
+ if r == '_' || r == '$' || r >= 'a' && r <= 'z' || r >= 'A' && r <= 'Z' {
279
+ continue
280
+ }
281
+ if i > 0 && r >= '0' && r <= '9' {
282
+ continue
283
+ }
284
+ return false
285
+ }
286
+ return true
287
287
  }
288
288
 
289
289
  // no-unsafe-declaration-merging: class/interface merging hides runtime vs type
@@ -293,49 +293,49 @@ type noUnsafeDeclarationMerging struct{}
293
293
 
294
294
  func (noUnsafeDeclarationMerging) Name() string { return "no-unsafe-declaration-merging" }
295
295
  func (noUnsafeDeclarationMerging) Visits() []shimast.Kind {
296
- return []shimast.Kind{shimast.KindSourceFile}
296
+ return []shimast.Kind{shimast.KindSourceFile}
297
297
  }
298
298
  func (noUnsafeDeclarationMerging) Check(ctx *Context, node *shimast.Node) {
299
- classes := map[string]bool{}
300
- var interfaces []*shimast.Node
301
- walkDescendants(node, func(child *shimast.Node) {
302
- switch child.Kind {
303
- case shimast.KindClassDeclaration:
304
- decl := child.AsClassDeclaration()
305
- if decl != nil {
306
- if name := identifierText(decl.Name()); name != "" {
307
- classes[name] = true
308
- }
309
- }
310
- case shimast.KindInterfaceDeclaration:
311
- interfaces = append(interfaces, child)
312
- }
313
- })
314
- for _, ifaceNode := range interfaces {
315
- decl := ifaceNode.AsInterfaceDeclaration()
316
- if decl == nil {
317
- continue
318
- }
319
- name := identifierText(decl.Name())
320
- if name != "" && classes[name] {
321
- ctx.Report(ifaceNode, "Unsafe declaration merging between class and interface '"+name+"'.")
322
- }
323
- }
299
+ classes := map[string]bool{}
300
+ var interfaces []*shimast.Node
301
+ walkDescendants(node, func(child *shimast.Node) {
302
+ switch child.Kind {
303
+ case shimast.KindClassDeclaration:
304
+ decl := child.AsClassDeclaration()
305
+ if decl != nil {
306
+ if name := identifierText(decl.Name()); name != "" {
307
+ classes[name] = true
308
+ }
309
+ }
310
+ case shimast.KindInterfaceDeclaration:
311
+ interfaces = append(interfaces, child)
312
+ }
313
+ })
314
+ for _, ifaceNode := range interfaces {
315
+ decl := ifaceNode.AsInterfaceDeclaration()
316
+ if decl == nil {
317
+ continue
318
+ }
319
+ name := identifierText(decl.Name())
320
+ if name != "" && classes[name] {
321
+ ctx.Report(ifaceNode, "Unsafe declaration merging between class and interface '"+name+"'.")
322
+ }
323
+ }
324
324
  }
325
325
 
326
326
  func init() {
327
- Register(noEmptyStaticBlock{})
328
- Register(noSetterReturn{})
329
- Register(noUnusedLabels{})
330
- Register(noDynamicDelete{})
331
- Register(noNonNullAssertedNullishCoalescing{})
332
- Register(noUnnecessaryTypeConstraint{})
333
- Register(noUnsafeDeclarationMerging{})
334
- Register(noUnsafeFunctionType{})
335
- Register(noWrapperObjectTypes{})
336
- Register(noUselessConstructor{})
337
- Register(preferLiteralEnumMember{})
338
- Register(consistentTypeAssertions{})
339
- Register(consistentTypeDefinitions{})
340
- Register(dotNotation{})
327
+ Register(noEmptyStaticBlock{})
328
+ Register(noSetterReturn{})
329
+ Register(noUnusedLabels{})
330
+ Register(noDynamicDelete{})
331
+ Register(noNonNullAssertedNullishCoalescing{})
332
+ Register(noUnnecessaryTypeConstraint{})
333
+ Register(noUnsafeDeclarationMerging{})
334
+ Register(noUnsafeFunctionType{})
335
+ Register(noWrapperObjectTypes{})
336
+ Register(noUselessConstructor{})
337
+ Register(preferLiteralEnumMember{})
338
+ Register(consistentTypeAssertions{})
339
+ Register(consistentTypeDefinitions{})
340
+ Register(dotNotation{})
341
341
  }