@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,9 +8,9 @@
8
8
  package main
9
9
 
10
10
  import (
11
- "strings"
11
+ "strings"
12
12
 
13
- shimast "github.com/microsoft/typescript-go/shim/ast"
13
+ shimast "github.com/microsoft/typescript-go/shim/ast"
14
14
  )
15
15
 
16
16
  // no-dupe-else-if: `if (a) {} else if (a) {}` — the second branch is
@@ -20,33 +20,33 @@ type noDupeElseIf struct{}
20
20
  func (noDupeElseIf) Name() string { return "no-dupe-else-if" }
21
21
  func (noDupeElseIf) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindIfStatement} }
22
22
  func (noDupeElseIf) Check(ctx *Context, node *shimast.Node) {
23
- // Only fire on the *outermost* if; the recursion below scans the
24
- // chain once.
25
- if parent := node.Parent; parent != nil {
26
- if parent.Kind == shimast.KindIfStatement {
27
- outer := parent.AsIfStatement()
28
- if outer != nil && outer.ElseStatement == node {
29
- return
30
- }
31
- }
32
- }
33
- seen := map[string]bool{}
34
- cur := node
35
- for cur != nil && cur.Kind == shimast.KindIfStatement {
36
- stmt := cur.AsIfStatement()
37
- if stmt == nil || stmt.Expression == nil {
38
- break
39
- }
40
- key := nodeText(ctx.File, stmt.Expression)
41
- if key != "" {
42
- if seen[key] {
43
- ctx.Report(stmt.Expression, "This branch can never execute. Its condition is a duplicate of an earlier branch.")
44
- } else {
45
- seen[key] = true
46
- }
47
- }
48
- cur = stmt.ElseStatement
49
- }
23
+ // Only fire on the *outermost* if; the recursion below scans the
24
+ // chain once.
25
+ if parent := node.Parent; parent != nil {
26
+ if parent.Kind == shimast.KindIfStatement {
27
+ outer := parent.AsIfStatement()
28
+ if outer != nil && outer.ElseStatement == node {
29
+ return
30
+ }
31
+ }
32
+ }
33
+ seen := map[string]bool{}
34
+ cur := node
35
+ for cur != nil && cur.Kind == shimast.KindIfStatement {
36
+ stmt := cur.AsIfStatement()
37
+ if stmt == nil || stmt.Expression == nil {
38
+ break
39
+ }
40
+ key := nodeText(ctx.File, stmt.Expression)
41
+ if key != "" {
42
+ if seen[key] {
43
+ ctx.Report(stmt.Expression, "This branch can never execute. Its condition is a duplicate of an earlier branch.")
44
+ } else {
45
+ seen[key] = true
46
+ }
47
+ }
48
+ cur = stmt.ElseStatement
49
+ }
50
50
  }
51
51
 
52
52
  // no-ex-assign: `try { } catch (e) { e = 1; }` — reassigning the catch
@@ -56,44 +56,44 @@ type noExAssign struct{}
56
56
  func (noExAssign) Name() string { return "no-ex-assign" }
57
57
  func (noExAssign) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindCatchClause} }
58
58
  func (noExAssign) Check(ctx *Context, node *shimast.Node) {
59
- clause := node.AsCatchClause()
60
- if clause == nil || clause.VariableDeclaration == nil || clause.Block == nil {
61
- return
62
- }
63
- binding := clause.VariableDeclaration.AsVariableDeclaration()
64
- if binding == nil {
65
- return
66
- }
67
- name := identifierText(binding.Name())
68
- if name == "" {
69
- return
70
- }
71
- walkAssignments(clause.Block, name, func(target *shimast.Node) {
72
- ctx.Report(target, "Do not assign to the exception parameter.")
73
- })
59
+ clause := node.AsCatchClause()
60
+ if clause == nil || clause.VariableDeclaration == nil || clause.Block == nil {
61
+ return
62
+ }
63
+ binding := clause.VariableDeclaration.AsVariableDeclaration()
64
+ if binding == nil {
65
+ return
66
+ }
67
+ name := identifierText(binding.Name())
68
+ if name == "" {
69
+ return
70
+ }
71
+ walkAssignments(clause.Block, name, func(target *shimast.Node) {
72
+ ctx.Report(target, "Do not assign to the exception parameter.")
73
+ })
74
74
  }
75
75
 
76
76
  // walkAssignments invokes `report` on every `<name> = ...` shape inside
77
77
  // `root`. Used by no-ex-assign and friends.
78
78
  func walkAssignments(root *shimast.Node, name string, report func(*shimast.Node)) {
79
- if root == nil {
80
- return
81
- }
82
- root.ForEachChild(func(child *shimast.Node) bool {
83
- if child == nil {
84
- return false
85
- }
86
- if child.Kind == shimast.KindBinaryExpression {
87
- expr := child.AsBinaryExpression()
88
- if expr != nil && expr.OperatorToken != nil && isAssignmentOperator(expr.OperatorToken.Kind) {
89
- if identifierText(expr.Left) == name {
90
- report(expr.Left)
91
- }
92
- }
93
- }
94
- walkAssignments(child, name, report)
95
- return false
96
- })
79
+ if root == nil {
80
+ return
81
+ }
82
+ root.ForEachChild(func(child *shimast.Node) bool {
83
+ if child == nil {
84
+ return false
85
+ }
86
+ if child.Kind == shimast.KindBinaryExpression {
87
+ expr := child.AsBinaryExpression()
88
+ if expr != nil && expr.OperatorToken != nil && isAssignmentOperator(expr.OperatorToken.Kind) {
89
+ if identifierText(expr.Left) == name {
90
+ report(expr.Left)
91
+ }
92
+ }
93
+ }
94
+ walkAssignments(child, name, report)
95
+ return false
96
+ })
97
97
  }
98
98
 
99
99
  // no-empty-character-class: `/[]/` matches nothing.
@@ -101,32 +101,32 @@ type noEmptyCharacterClass struct{}
101
101
 
102
102
  func (noEmptyCharacterClass) Name() string { return "no-empty-character-class" }
103
103
  func (noEmptyCharacterClass) Visits() []shimast.Kind {
104
- return []shimast.Kind{shimast.KindRegularExpressionLiteral}
104
+ return []shimast.Kind{shimast.KindRegularExpressionLiteral}
105
105
  }
106
106
  func (noEmptyCharacterClass) Check(ctx *Context, node *shimast.Node) {
107
- src := nodeText(ctx.File, node)
108
- if hasEmptyCharClass(src) {
109
- ctx.Report(node, "Empty class.")
110
- }
107
+ src := nodeText(ctx.File, node)
108
+ if hasEmptyCharClass(src) {
109
+ ctx.Report(node, "Empty class.")
110
+ }
111
111
  }
112
112
 
113
113
  func hasEmptyCharClass(src string) bool {
114
- // Walk the regex literal source manually, respecting escapes.
115
- for i := 0; i < len(src); i++ {
116
- switch src[i] {
117
- case '\\':
118
- i++ // skip escape
119
- case '[':
120
- j := i + 1
121
- if j < len(src) && src[j] == '^' {
122
- j++
123
- }
124
- if j < len(src) && src[j] == ']' {
125
- return true
126
- }
127
- }
128
- }
129
- return false
114
+ // Walk the regex literal source manually, respecting escapes.
115
+ for i := 0; i < len(src); i++ {
116
+ switch src[i] {
117
+ case '\\':
118
+ i++ // skip escape
119
+ case '[':
120
+ j := i + 1
121
+ if j < len(src) && src[j] == '^' {
122
+ j++
123
+ }
124
+ if j < len(src) && src[j] == ']' {
125
+ return true
126
+ }
127
+ }
128
+ }
129
+ return false
130
130
  }
131
131
 
132
132
  // no-misleading-character-class: `/[👍]/` — surrogate pairs in regex.
@@ -134,40 +134,40 @@ type noMisleadingCharacterClass struct{}
134
134
 
135
135
  func (noMisleadingCharacterClass) Name() string { return "no-misleading-character-class" }
136
136
  func (noMisleadingCharacterClass) Visits() []shimast.Kind {
137
- return []shimast.Kind{shimast.KindRegularExpressionLiteral}
137
+ return []shimast.Kind{shimast.KindRegularExpressionLiteral}
138
138
  }
139
139
  func (noMisleadingCharacterClass) Check(ctx *Context, node *shimast.Node) {
140
- src := nodeText(ctx.File, node)
141
- if regexHasSurrogatePair(src) {
142
- ctx.Report(node, "Unexpected surrogate pair in character class. Use the 'u' flag.")
143
- }
140
+ src := nodeText(ctx.File, node)
141
+ if regexHasSurrogatePair(src) {
142
+ ctx.Report(node, "Unexpected surrogate pair in character class. Use the 'u' flag.")
143
+ }
144
144
  }
145
145
 
146
146
  func regexHasSurrogatePair(src string) bool {
147
- // Strip the trailing flags so we don't misread the `u` flag — it
148
- // suppresses this rule.
149
- end := strings.LastIndex(src, "/")
150
- if end < 0 {
151
- return false
152
- }
153
- flags := src[end+1:]
154
- if strings.ContainsRune(flags, 'u') {
155
- return false
156
- }
157
- body := src[:end]
158
- in := false
159
- for _, r := range body {
160
- switch r {
161
- case '[':
162
- in = true
163
- case ']':
164
- in = false
165
- }
166
- if in && r >= 0x10000 {
167
- return true
168
- }
169
- }
170
- return false
147
+ // Strip the trailing flags so we don't misread the `u` flag — it
148
+ // suppresses this rule.
149
+ end := strings.LastIndex(src, "/")
150
+ if end < 0 {
151
+ return false
152
+ }
153
+ flags := src[end+1:]
154
+ if strings.ContainsRune(flags, 'u') {
155
+ return false
156
+ }
157
+ body := src[:end]
158
+ in := false
159
+ for _, r := range body {
160
+ switch r {
161
+ case '[':
162
+ in = true
163
+ case ']':
164
+ in = false
165
+ }
166
+ if in && r >= 0x10000 {
167
+ return true
168
+ }
169
+ }
170
+ return false
171
171
  }
172
172
 
173
173
  // no-loss-of-precision: `9007199254740993` — integer literal larger than
@@ -179,40 +179,40 @@ type noLossOfPrecision struct{}
179
179
  func (noLossOfPrecision) Name() string { return "no-loss-of-precision" }
180
180
  func (noLossOfPrecision) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindNumericLiteral} }
181
181
  func (noLossOfPrecision) Check(ctx *Context, node *shimast.Node) {
182
- source := strings.TrimSpace(nodeText(ctx.File, node))
183
- if source == "" {
184
- return
185
- }
186
- if numericLiteralLosesPrecision(source) {
187
- ctx.Report(node, "This number literal will lose precision at runtime.")
188
- }
182
+ source := strings.TrimSpace(nodeText(ctx.File, node))
183
+ if source == "" {
184
+ return
185
+ }
186
+ if numericLiteralLosesPrecision(source) {
187
+ ctx.Report(node, "This number literal will lose precision at runtime.")
188
+ }
189
189
  }
190
190
 
191
191
  func numericLiteralLosesPrecision(text string) bool {
192
- // Strip underscore separators, exponents, decimal/hex/oct/binary
193
- // markers — for the simple-base-10 integer case the round-trip
194
- // parse → format check is sufficient.
195
- clean := strings.ReplaceAll(text, "_", "")
196
- if strings.ContainsAny(clean, "eE.xXoObB") {
197
- return false
198
- }
199
- if len(clean) < 16 {
200
- return false
201
- }
202
- // Trim leading zeros for comparison.
203
- trimmed := strings.TrimLeft(clean, "0")
204
- if trimmed == "" {
205
- return false
206
- }
207
- // 2^53 = 9007199254740992; anything larger as an integer literal loses precision.
208
- const maxSafe = "9007199254740992"
209
- if len(trimmed) < len(maxSafe) {
210
- return false
211
- }
212
- if len(trimmed) > len(maxSafe) {
213
- return true
214
- }
215
- return trimmed > maxSafe
192
+ // Strip underscore separators, exponents, decimal/hex/oct/binary
193
+ // markers — for the simple-base-10 integer case the round-trip
194
+ // parse → format check is sufficient.
195
+ clean := strings.ReplaceAll(text, "_", "")
196
+ if strings.ContainsAny(clean, "eE.xXoObB") {
197
+ return false
198
+ }
199
+ if len(clean) < 16 {
200
+ return false
201
+ }
202
+ // Trim leading zeros for comparison.
203
+ trimmed := strings.TrimLeft(clean, "0")
204
+ if trimmed == "" {
205
+ return false
206
+ }
207
+ // 2^53 = 9007199254740992; anything larger as an integer literal loses precision.
208
+ const maxSafe = "9007199254740992"
209
+ if len(trimmed) < len(maxSafe) {
210
+ return false
211
+ }
212
+ if len(trimmed) > len(maxSafe) {
213
+ return true
214
+ }
215
+ return trimmed > maxSafe
216
216
  }
217
217
 
218
218
  // no-class-assign: assigning to a class declaration's name.
@@ -221,17 +221,17 @@ type noClassAssign struct{}
221
221
  func (noClassAssign) Name() string { return "no-class-assign" }
222
222
  func (noClassAssign) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindClassDeclaration} }
223
223
  func (noClassAssign) Check(ctx *Context, node *shimast.Node) {
224
- decl := node.AsClassDeclaration()
225
- if decl == nil || decl.Name() == nil {
226
- return
227
- }
228
- name := identifierText(decl.Name())
229
- if name == "" {
230
- return
231
- }
232
- walkAssignments(ctx.File.AsNode(), name, func(target *shimast.Node) {
233
- ctx.Report(target, "'"+name+"' is a class.")
234
- })
224
+ decl := node.AsClassDeclaration()
225
+ if decl == nil || decl.Name() == nil {
226
+ return
227
+ }
228
+ name := identifierText(decl.Name())
229
+ if name == "" {
230
+ return
231
+ }
232
+ walkAssignments(ctx.File.AsNode(), name, func(target *shimast.Node) {
233
+ ctx.Report(target, "'"+name+"' is a class.")
234
+ })
235
235
  }
236
236
 
237
237
  // no-func-assign: same idea, but for function declarations.
@@ -240,17 +240,17 @@ type noFuncAssign struct{}
240
240
  func (noFuncAssign) Name() string { return "no-func-assign" }
241
241
  func (noFuncAssign) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindFunctionDeclaration} }
242
242
  func (noFuncAssign) Check(ctx *Context, node *shimast.Node) {
243
- decl := node.AsFunctionDeclaration()
244
- if decl == nil || decl.Name() == nil {
245
- return
246
- }
247
- name := identifierText(decl.Name())
248
- if name == "" {
249
- return
250
- }
251
- walkAssignments(ctx.File.AsNode(), name, func(target *shimast.Node) {
252
- ctx.Report(target, "'"+name+"' is a function.")
253
- })
243
+ decl := node.AsFunctionDeclaration()
244
+ if decl == nil || decl.Name() == nil {
245
+ return
246
+ }
247
+ name := identifierText(decl.Name())
248
+ if name == "" {
249
+ return
250
+ }
251
+ walkAssignments(ctx.File.AsNode(), name, func(target *shimast.Node) {
252
+ ctx.Report(target, "'"+name+"' is a function.")
253
+ })
254
254
  }
255
255
 
256
256
  // no-prototype-builtins: `obj.hasOwnProperty(x)` — should be
@@ -260,22 +260,22 @@ type noPrototypeBuiltins struct{}
260
260
  func (noPrototypeBuiltins) Name() string { return "no-prototype-builtins" }
261
261
  func (noPrototypeBuiltins) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindCallExpression} }
262
262
  func (noPrototypeBuiltins) Check(ctx *Context, node *shimast.Node) {
263
- call := node.AsCallExpression()
264
- if call == nil || call.Expression == nil {
265
- return
266
- }
267
- if call.Expression.Kind != shimast.KindPropertyAccessExpression {
268
- return
269
- }
270
- access := call.Expression.AsPropertyAccessExpression()
271
- if access == nil {
272
- return
273
- }
274
- method := identifierText(access.Name())
275
- switch method {
276
- case "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable":
277
- ctx.Report(node, "Do not access Object.prototype method '"+method+"' from target object.")
278
- }
263
+ call := node.AsCallExpression()
264
+ if call == nil || call.Expression == nil {
265
+ return
266
+ }
267
+ if call.Expression.Kind != shimast.KindPropertyAccessExpression {
268
+ return
269
+ }
270
+ access := call.Expression.AsPropertyAccessExpression()
271
+ if access == nil {
272
+ return
273
+ }
274
+ method := identifierText(access.Name())
275
+ switch method {
276
+ case "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable":
277
+ ctx.Report(node, "Do not access Object.prototype method '"+method+"' from target object.")
278
+ }
279
279
  }
280
280
 
281
281
  // no-async-promise-executor: `new Promise(async (resolve) => {...})`.
@@ -283,26 +283,26 @@ type noAsyncPromiseExecutor struct{}
283
283
 
284
284
  func (noAsyncPromiseExecutor) Name() string { return "no-async-promise-executor" }
285
285
  func (noAsyncPromiseExecutor) Visits() []shimast.Kind {
286
- return []shimast.Kind{shimast.KindNewExpression}
286
+ return []shimast.Kind{shimast.KindNewExpression}
287
287
  }
288
288
  func (noAsyncPromiseExecutor) Check(ctx *Context, node *shimast.Node) {
289
- ne := node.AsNewExpression()
290
- if ne == nil || identifierText(ne.Expression) != "Promise" {
291
- return
292
- }
293
- if ne.Arguments == nil || len(ne.Arguments.Nodes) == 0 {
294
- return
295
- }
296
- executor := ne.Arguments.Nodes[0]
297
- if executor == nil {
298
- return
299
- }
300
- if !isFunctionLikeKind(executor) {
301
- return
302
- }
303
- if hasAsyncModifier(executor) {
304
- ctx.Report(executor, "Promise executor functions should not be async.")
305
- }
289
+ ne := node.AsNewExpression()
290
+ if ne == nil || identifierText(ne.Expression) != "Promise" {
291
+ return
292
+ }
293
+ if ne.Arguments == nil || len(ne.Arguments.Nodes) == 0 {
294
+ return
295
+ }
296
+ executor := ne.Arguments.Nodes[0]
297
+ if executor == nil {
298
+ return
299
+ }
300
+ if !isFunctionLikeKind(executor) {
301
+ return
302
+ }
303
+ if hasAsyncModifier(executor) {
304
+ ctx.Report(executor, "Promise executor functions should not be async.")
305
+ }
306
306
  }
307
307
 
308
308
  // no-promise-executor-return: `new Promise(() => 1)` — the value is
@@ -311,28 +311,28 @@ type noPromiseExecutorReturn struct{}
311
311
 
312
312
  func (noPromiseExecutorReturn) Name() string { return "no-promise-executor-return" }
313
313
  func (noPromiseExecutorReturn) Visits() []shimast.Kind {
314
- return []shimast.Kind{shimast.KindNewExpression}
314
+ return []shimast.Kind{shimast.KindNewExpression}
315
315
  }
316
316
  func (noPromiseExecutorReturn) Check(ctx *Context, node *shimast.Node) {
317
- ne := node.AsNewExpression()
318
- if ne == nil || identifierText(ne.Expression) != "Promise" {
319
- return
320
- }
321
- if ne.Arguments == nil || len(ne.Arguments.Nodes) == 0 {
322
- return
323
- }
324
- executor := ne.Arguments.Nodes[0]
325
- if executor == nil || executor.Kind != shimast.KindArrowFunction {
326
- return
327
- }
328
- arrow := executor.AsArrowFunction()
329
- if arrow == nil || arrow.Body == nil {
330
- return
331
- }
332
- // Concise arrow body returns the value implicitly.
333
- if arrow.Body.Kind != shimast.KindBlock {
334
- ctx.Report(arrow.Body, "Return values from promise executor functions cannot be read.")
335
- }
317
+ ne := node.AsNewExpression()
318
+ if ne == nil || identifierText(ne.Expression) != "Promise" {
319
+ return
320
+ }
321
+ if ne.Arguments == nil || len(ne.Arguments.Nodes) == 0 {
322
+ return
323
+ }
324
+ executor := ne.Arguments.Nodes[0]
325
+ if executor == nil || executor.Kind != shimast.KindArrowFunction {
326
+ return
327
+ }
328
+ arrow := executor.AsArrowFunction()
329
+ if arrow == nil || arrow.Body == nil {
330
+ return
331
+ }
332
+ // Concise arrow body returns the value implicitly.
333
+ if arrow.Body.Kind != shimast.KindBlock {
334
+ ctx.Report(arrow.Body, "Return values from promise executor functions cannot be read.")
335
+ }
336
336
  }
337
337
 
338
338
  // no-control-regex: `/\x00/` — control characters in regex are usually
@@ -342,56 +342,56 @@ type noControlRegex struct{}
342
342
 
343
343
  func (noControlRegex) Name() string { return "no-control-regex" }
344
344
  func (noControlRegex) Visits() []shimast.Kind {
345
- return []shimast.Kind{shimast.KindRegularExpressionLiteral}
345
+ return []shimast.Kind{shimast.KindRegularExpressionLiteral}
346
346
  }
347
347
  func (noControlRegex) Check(ctx *Context, node *shimast.Node) {
348
- src := nodeText(ctx.File, node)
349
- if regexContainsControl(src) {
350
- ctx.Report(node, "Unexpected control character(s) in regular expression.")
351
- }
348
+ src := nodeText(ctx.File, node)
349
+ if regexContainsControl(src) {
350
+ ctx.Report(node, "Unexpected control character(s) in regular expression.")
351
+ }
352
352
  }
353
353
 
354
354
  func regexContainsControl(src string) bool {
355
- for i := 0; i < len(src); i++ {
356
- c := src[i]
357
- if c == '\\' && i+1 < len(src) {
358
- next := src[i+1]
359
- if next == 'x' && i+3 < len(src) {
360
- value := hexDigit(src[i+2])*16 + hexDigit(src[i+3])
361
- if value >= 0 && value < 0x20 {
362
- return true
363
- }
364
- i += 3
365
- continue
366
- }
367
- if next == 'u' && i+5 < len(src) {
368
- value := hexDigit(src[i+2])*4096 + hexDigit(src[i+3])*256 + hexDigit(src[i+4])*16 + hexDigit(src[i+5])
369
- if value >= 0 && value < 0x20 {
370
- return true
371
- }
372
- i += 5
373
- continue
374
- }
375
- i++
376
- continue
377
- }
378
- if c < 0x20 && c != '\t' && c != '\n' && c != '\r' {
379
- return true
380
- }
381
- }
382
- return false
355
+ for i := 0; i < len(src); i++ {
356
+ c := src[i]
357
+ if c == '\\' && i+1 < len(src) {
358
+ next := src[i+1]
359
+ if next == 'x' && i+3 < len(src) {
360
+ value := hexDigit(src[i+2])*16 + hexDigit(src[i+3])
361
+ if value >= 0 && value < 0x20 {
362
+ return true
363
+ }
364
+ i += 3
365
+ continue
366
+ }
367
+ if next == 'u' && i+5 < len(src) {
368
+ value := hexDigit(src[i+2])*4096 + hexDigit(src[i+3])*256 + hexDigit(src[i+4])*16 + hexDigit(src[i+5])
369
+ if value >= 0 && value < 0x20 {
370
+ return true
371
+ }
372
+ i += 5
373
+ continue
374
+ }
375
+ i++
376
+ continue
377
+ }
378
+ if c < 0x20 && c != '\t' && c != '\n' && c != '\r' {
379
+ return true
380
+ }
381
+ }
382
+ return false
383
383
  }
384
384
 
385
385
  func hexDigit(b byte) int {
386
- switch {
387
- case b >= '0' && b <= '9':
388
- return int(b - '0')
389
- case b >= 'a' && b <= 'f':
390
- return int(b-'a') + 10
391
- case b >= 'A' && b <= 'F':
392
- return int(b-'A') + 10
393
- }
394
- return -1
386
+ switch {
387
+ case b >= '0' && b <= '9':
388
+ return int(b - '0')
389
+ case b >= 'a' && b <= 'f':
390
+ return int(b-'a') + 10
391
+ case b >= 'A' && b <= 'F':
392
+ return int(b-'A') + 10
393
+ }
394
+ return -1
395
395
  }
396
396
 
397
397
  // no-irregular-whitespace: zero-width spaces, NBSP, etc. The TS parser
@@ -401,30 +401,30 @@ type noIrregularWhitespace struct{}
401
401
  func (noIrregularWhitespace) Name() string { return "no-irregular-whitespace" }
402
402
  func (noIrregularWhitespace) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindSourceFile} }
403
403
  func (noIrregularWhitespace) Check(ctx *Context, node *shimast.Node) {
404
- if ctx.File == nil {
405
- return
406
- }
407
- text := ctx.File.Text()
408
- for i, r := range text {
409
- if isIrregularWhitespace(r) {
410
- ctx.ReportRange(i, i+len(string(r)), "Irregular whitespace not allowed.")
411
- }
412
- }
404
+ if ctx.File == nil {
405
+ return
406
+ }
407
+ text := ctx.File.Text()
408
+ for i, r := range text {
409
+ if isIrregularWhitespace(r) {
410
+ ctx.ReportRange(i, i+len(string(r)), "Irregular whitespace not allowed.")
411
+ }
412
+ }
413
413
  }
414
414
 
415
415
  func isIrregularWhitespace(r rune) bool {
416
- switch r {
417
- case '\v', '\f',
418
- 0x00A0, 0x1680,
419
- 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005,
420
- 0x2006, 0x2007, 0x2008, 0x2009, 0x200A,
421
- 0x200B, 0x202F, 0x205F,
422
- 0x3000,
423
- 0x2028, 0x2029,
424
- 0xFEFF:
425
- return true
426
- }
427
- return false
416
+ switch r {
417
+ case '\v', '\f',
418
+ 0x00A0, 0x1680,
419
+ 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005,
420
+ 0x2006, 0x2007, 0x2008, 0x2009, 0x200A,
421
+ 0x200B, 0x202F, 0x205F,
422
+ 0x3000,
423
+ 0x2028, 0x2029,
424
+ 0xFEFF:
425
+ return true
426
+ }
427
+ return false
428
428
  }
429
429
 
430
430
  // no-fallthrough: `switch` cases that fall through to the next label
@@ -434,52 +434,52 @@ type noFallthrough struct{}
434
434
  func (noFallthrough) Name() string { return "no-fallthrough" }
435
435
  func (noFallthrough) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindSwitchStatement} }
436
436
  func (noFallthrough) Check(ctx *Context, node *shimast.Node) {
437
- sw := node.AsSwitchStatement()
438
- if sw == nil || sw.CaseBlock == nil {
439
- return
440
- }
441
- block := sw.CaseBlock.AsCaseBlock()
442
- if block == nil || block.Clauses == nil {
443
- return
444
- }
445
- clauses := block.Clauses.Nodes
446
- for i := 0; i+1 < len(clauses); i++ {
447
- clause := clauses[i].AsCaseOrDefaultClause()
448
- if clause == nil || clause.Statements == nil {
449
- continue
450
- }
451
- stmts := clause.Statements.Nodes
452
- if len(stmts) == 0 {
453
- continue // empty case is intentional, never a fallthrough.
454
- }
455
- if !isTerminating(stmts[len(stmts)-1]) {
456
- ctx.Report(clauses[i+1], "Expected a 'break' statement before this case.")
457
- }
458
- }
437
+ sw := node.AsSwitchStatement()
438
+ if sw == nil || sw.CaseBlock == nil {
439
+ return
440
+ }
441
+ block := sw.CaseBlock.AsCaseBlock()
442
+ if block == nil || block.Clauses == nil {
443
+ return
444
+ }
445
+ clauses := block.Clauses.Nodes
446
+ for i := 0; i+1 < len(clauses); i++ {
447
+ clause := clauses[i].AsCaseOrDefaultClause()
448
+ if clause == nil || clause.Statements == nil {
449
+ continue
450
+ }
451
+ stmts := clause.Statements.Nodes
452
+ if len(stmts) == 0 {
453
+ continue // empty case is intentional, never a fallthrough.
454
+ }
455
+ if !isTerminating(stmts[len(stmts)-1]) {
456
+ ctx.Report(clauses[i+1], "Expected a 'break' statement before this case.")
457
+ }
458
+ }
459
459
  }
460
460
 
461
461
  func isTerminating(stmt *shimast.Node) bool {
462
- if stmt == nil {
463
- return false
464
- }
465
- switch stmt.Kind {
466
- case shimast.KindBreakStatement,
467
- shimast.KindContinueStatement,
468
- shimast.KindReturnStatement,
469
- shimast.KindThrowStatement:
470
- return true
471
- case shimast.KindBlock:
472
- block := stmt.AsBlock()
473
- if block == nil || block.Statements == nil {
474
- return false
475
- }
476
- nodes := block.Statements.Nodes
477
- if len(nodes) == 0 {
478
- return false
479
- }
480
- return isTerminating(nodes[len(nodes)-1])
481
- }
482
- return false
462
+ if stmt == nil {
463
+ return false
464
+ }
465
+ switch stmt.Kind {
466
+ case shimast.KindBreakStatement,
467
+ shimast.KindContinueStatement,
468
+ shimast.KindReturnStatement,
469
+ shimast.KindThrowStatement:
470
+ return true
471
+ case shimast.KindBlock:
472
+ block := stmt.AsBlock()
473
+ if block == nil || block.Statements == nil {
474
+ return false
475
+ }
476
+ nodes := block.Statements.Nodes
477
+ if len(nodes) == 0 {
478
+ return false
479
+ }
480
+ return isTerminating(nodes[len(nodes)-1])
481
+ }
482
+ return false
483
483
  }
484
484
 
485
485
  // no-inner-declarations: `function foo() { if (x) { function bar() {} } }`
@@ -489,40 +489,40 @@ type noInnerDeclarations struct{}
489
489
 
490
490
  func (noInnerDeclarations) Name() string { return "no-inner-declarations" }
491
491
  func (noInnerDeclarations) Visits() []shimast.Kind {
492
- return []shimast.Kind{shimast.KindFunctionDeclaration, shimast.KindVariableStatement}
492
+ return []shimast.Kind{shimast.KindFunctionDeclaration, shimast.KindVariableStatement}
493
493
  }
494
494
  func (noInnerDeclarations) Check(ctx *Context, node *shimast.Node) {
495
- if node.Kind == shimast.KindVariableStatement {
496
- stmt := node.AsVariableStatement()
497
- if stmt == nil || stmt.DeclarationList == nil {
498
- return
499
- }
500
- // Only `var` is hoisted oddly.
501
- if !shimast.IsVar(stmt.DeclarationList) {
502
- return
503
- }
504
- }
505
- parent := node.Parent
506
- if parent == nil {
507
- return
508
- }
509
- switch parent.Kind {
510
- case shimast.KindSourceFile, shimast.KindModuleBlock:
511
- return
512
- case shimast.KindBlock:
513
- grand := parent.Parent
514
- if grand == nil {
515
- return
516
- }
517
- if isFunctionLikeKind(grand) {
518
- return
519
- }
520
- }
521
- what := "function"
522
- if node.Kind == shimast.KindVariableStatement {
523
- what = "variable"
524
- }
525
- ctx.Report(node, "Move "+what+" declaration to the function scope.")
495
+ if node.Kind == shimast.KindVariableStatement {
496
+ stmt := node.AsVariableStatement()
497
+ if stmt == nil || stmt.DeclarationList == nil {
498
+ return
499
+ }
500
+ // Only `var` is hoisted oddly.
501
+ if !shimast.IsVar(stmt.DeclarationList) {
502
+ return
503
+ }
504
+ }
505
+ parent := node.Parent
506
+ if parent == nil {
507
+ return
508
+ }
509
+ switch parent.Kind {
510
+ case shimast.KindSourceFile, shimast.KindModuleBlock:
511
+ return
512
+ case shimast.KindBlock:
513
+ grand := parent.Parent
514
+ if grand == nil {
515
+ return
516
+ }
517
+ if isFunctionLikeKind(grand) {
518
+ return
519
+ }
520
+ }
521
+ what := "function"
522
+ if node.Kind == shimast.KindVariableStatement {
523
+ what = "variable"
524
+ }
525
+ ctx.Report(node, "Move "+what+" declaration to the function scope.")
526
526
  }
527
527
 
528
528
  // no-obj-calls: `Math()`, `JSON()` — these globals are objects, not
@@ -531,53 +531,53 @@ type noObjCalls struct{}
531
531
 
532
532
  func (noObjCalls) Name() string { return "no-obj-calls" }
533
533
  func (noObjCalls) Visits() []shimast.Kind {
534
- return []shimast.Kind{shimast.KindCallExpression, shimast.KindNewExpression}
534
+ return []shimast.Kind{shimast.KindCallExpression, shimast.KindNewExpression}
535
535
  }
536
536
  func (noObjCalls) Check(ctx *Context, node *shimast.Node) {
537
- var callee *shimast.Node
538
- if node.Kind == shimast.KindCallExpression {
539
- callee = node.AsCallExpression().Expression
540
- } else {
541
- callee = node.AsNewExpression().Expression
542
- }
543
- switch identifierText(callee) {
544
- case "Math", "JSON", "Reflect", "Atomics", "Intl":
545
- ctx.Report(node, "'"+identifierText(callee)+"' is not a function.")
546
- }
537
+ var callee *shimast.Node
538
+ if node.Kind == shimast.KindCallExpression {
539
+ callee = node.AsCallExpression().Expression
540
+ } else {
541
+ callee = node.AsNewExpression().Expression
542
+ }
543
+ switch identifierText(callee) {
544
+ case "Math", "JSON", "Reflect", "Atomics", "Intl":
545
+ ctx.Report(node, "'"+identifierText(callee)+"' is not a function.")
546
+ }
547
547
  }
548
548
 
549
549
  // hasAsyncModifier returns whether a function-like node carries the
550
550
  // `async` keyword. Used by no-async-promise-executor.
551
551
  func hasAsyncModifier(node *shimast.Node) bool {
552
- if node == nil {
553
- return false
554
- }
555
- mods := node.Modifiers()
556
- if mods == nil {
557
- return false
558
- }
559
- for _, m := range mods.Nodes {
560
- if m != nil && m.Kind == shimast.KindAsyncKeyword {
561
- return true
562
- }
563
- }
564
- return false
552
+ if node == nil {
553
+ return false
554
+ }
555
+ mods := node.Modifiers()
556
+ if mods == nil {
557
+ return false
558
+ }
559
+ for _, m := range mods.Nodes {
560
+ if m != nil && m.Kind == shimast.KindAsyncKeyword {
561
+ return true
562
+ }
563
+ }
564
+ return false
565
565
  }
566
566
 
567
567
  func init() {
568
- Register(noDupeElseIf{})
569
- Register(noExAssign{})
570
- Register(noEmptyCharacterClass{})
571
- Register(noMisleadingCharacterClass{})
572
- Register(noLossOfPrecision{})
573
- Register(noClassAssign{})
574
- Register(noFuncAssign{})
575
- Register(noPrototypeBuiltins{})
576
- Register(noAsyncPromiseExecutor{})
577
- Register(noPromiseExecutorReturn{})
578
- Register(noControlRegex{})
579
- Register(noIrregularWhitespace{})
580
- Register(noFallthrough{})
581
- Register(noInnerDeclarations{})
582
- Register(noObjCalls{})
568
+ Register(noDupeElseIf{})
569
+ Register(noExAssign{})
570
+ Register(noEmptyCharacterClass{})
571
+ Register(noMisleadingCharacterClass{})
572
+ Register(noLossOfPrecision{})
573
+ Register(noClassAssign{})
574
+ Register(noFuncAssign{})
575
+ Register(noPrototypeBuiltins{})
576
+ Register(noAsyncPromiseExecutor{})
577
+ Register(noPromiseExecutorReturn{})
578
+ Register(noControlRegex{})
579
+ Register(noIrregularWhitespace{})
580
+ Register(noFallthrough{})
581
+ Register(noInnerDeclarations{})
582
+ Register(noObjCalls{})
583
583
  }