@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.
- package/README.md +62 -24
- package/lib/index.js.map +1 -1
- package/package.json +3 -3
- package/plugin/ast_helpers.go +158 -158
- package/plugin/compile.go +303 -303
- package/plugin/config.go +322 -322
- package/plugin/engine.go +152 -153
- package/plugin/host.go +128 -128
- package/plugin/main.go +21 -21
- package/plugin/rules_arrays.go +51 -51
- package/plugin/rules_console.go +22 -23
- package/plugin/rules_debugger.go +4 -4
- package/plugin/rules_dupes.go +134 -134
- package/plugin/rules_empty.go +68 -68
- package/plugin/rules_eval.go +28 -28
- package/plugin/rules_finally.go +91 -91
- package/plugin/rules_gap.go +188 -188
- package/plugin/rules_logic.go +268 -268
- package/plugin/rules_loops.go +89 -89
- package/plugin/rules_misc.go +43 -43
- package/plugin/rules_problems.go +396 -396
- package/plugin/rules_protos.go +17 -17
- package/plugin/rules_self.go +56 -56
- package/plugin/rules_strings.go +65 -65
- package/plugin/rules_suggestions.go +863 -863
- package/plugin/rules_throw.go +16 -16
- package/plugin/rules_ts.go +161 -161
- package/plugin/rules_ts_extra.go +467 -467
- package/plugin/rules_var.go +86 -86
- package/src/index.ts +1 -4
package/plugin/rules_throw.go
CHANGED
|
@@ -9,23 +9,23 @@ type noThrowLiteral struct{}
|
|
|
9
9
|
func (noThrowLiteral) Name() string { return "no-throw-literal" }
|
|
10
10
|
func (noThrowLiteral) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindThrowStatement} }
|
|
11
11
|
func (noThrowLiteral) Check(ctx *Context, node *shimast.Node) {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
12
|
+
throw := node.AsThrowStatement()
|
|
13
|
+
if throw == nil {
|
|
14
|
+
return
|
|
15
|
+
}
|
|
16
|
+
expr := stripParens(throw.Expression)
|
|
17
|
+
if expr == nil {
|
|
18
|
+
return
|
|
19
|
+
}
|
|
20
|
+
if isLiteralExpression(expr) || expr.Kind == shimast.KindUndefinedKeyword {
|
|
21
|
+
ctx.Report(throw.Expression, "Expected an error object to be thrown.")
|
|
22
|
+
return
|
|
23
|
+
}
|
|
24
|
+
if id := identifierText(expr); id == "undefined" {
|
|
25
|
+
ctx.Report(throw.Expression, "Expected an error object to be thrown.")
|
|
26
|
+
}
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
func init() {
|
|
30
|
-
|
|
30
|
+
Register(noThrowLiteral{})
|
|
31
31
|
}
|
package/plugin/rules_ts.go
CHANGED
|
@@ -9,7 +9,7 @@ type noExplicitAny struct{}
|
|
|
9
9
|
func (noExplicitAny) Name() string { return "no-explicit-any" }
|
|
10
10
|
func (noExplicitAny) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindAnyKeyword} }
|
|
11
11
|
func (noExplicitAny) Check(ctx *Context, node *shimast.Node) {
|
|
12
|
-
|
|
12
|
+
ctx.Report(node, "Unexpected any. Specify a different type.")
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
// no-non-null-assertion: ban the postfix `!` non-null assertion.
|
|
@@ -17,10 +17,10 @@ type noNonNullAssertion struct{}
|
|
|
17
17
|
|
|
18
18
|
func (noNonNullAssertion) Name() string { return "no-non-null-assertion" }
|
|
19
19
|
func (noNonNullAssertion) Visits() []shimast.Kind {
|
|
20
|
-
|
|
20
|
+
return []shimast.Kind{shimast.KindNonNullExpression}
|
|
21
21
|
}
|
|
22
22
|
func (noNonNullAssertion) Check(ctx *Context, node *shimast.Node) {
|
|
23
|
-
|
|
23
|
+
ctx.Report(node, "Forbidden non-null assertion.")
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
// no-empty-interface: empty `interface { }` declarations are an alias
|
|
@@ -29,16 +29,16 @@ type noEmptyInterface struct{}
|
|
|
29
29
|
|
|
30
30
|
func (noEmptyInterface) Name() string { return "no-empty-interface" }
|
|
31
31
|
func (noEmptyInterface) Visits() []shimast.Kind {
|
|
32
|
-
|
|
32
|
+
return []shimast.Kind{shimast.KindInterfaceDeclaration}
|
|
33
33
|
}
|
|
34
34
|
func (noEmptyInterface) Check(ctx *Context, node *shimast.Node) {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
35
|
+
decl := node.AsInterfaceDeclaration()
|
|
36
|
+
if decl == nil || decl.Members == nil {
|
|
37
|
+
return
|
|
38
|
+
}
|
|
39
|
+
if len(decl.Members.Nodes) == 0 {
|
|
40
|
+
ctx.Report(node, "An empty interface is equivalent to '{}'.")
|
|
41
|
+
}
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
// no-inferrable-types: `let x: number = 0` — the annotation is what TS
|
|
@@ -47,73 +47,73 @@ type noInferrableTypes struct{}
|
|
|
47
47
|
|
|
48
48
|
func (noInferrableTypes) Name() string { return "no-inferrable-types" }
|
|
49
49
|
func (noInferrableTypes) Visits() []shimast.Kind {
|
|
50
|
-
|
|
50
|
+
return []shimast.Kind{shimast.KindVariableDeclaration, shimast.KindParameter, shimast.KindPropertyDeclaration}
|
|
51
51
|
}
|
|
52
52
|
func (noInferrableTypes) Check(ctx *Context, node *shimast.Node) {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
53
|
+
var typeNode, init *shimast.Node
|
|
54
|
+
switch node.Kind {
|
|
55
|
+
case shimast.KindVariableDeclaration:
|
|
56
|
+
decl := node.AsVariableDeclaration()
|
|
57
|
+
if decl == nil {
|
|
58
|
+
return
|
|
59
|
+
}
|
|
60
|
+
typeNode = decl.Type
|
|
61
|
+
init = decl.Initializer
|
|
62
|
+
case shimast.KindParameter:
|
|
63
|
+
decl := node.AsParameterDeclaration()
|
|
64
|
+
if decl == nil {
|
|
65
|
+
return
|
|
66
|
+
}
|
|
67
|
+
typeNode = decl.Type
|
|
68
|
+
init = decl.Initializer
|
|
69
|
+
case shimast.KindPropertyDeclaration:
|
|
70
|
+
decl := node.AsPropertyDeclaration()
|
|
71
|
+
if decl == nil {
|
|
72
|
+
return
|
|
73
|
+
}
|
|
74
|
+
typeNode = decl.Type
|
|
75
|
+
init = decl.Initializer
|
|
76
|
+
}
|
|
77
|
+
if typeNode == nil || init == nil {
|
|
78
|
+
return
|
|
79
|
+
}
|
|
80
|
+
if !isInferrablePair(typeNode, init) {
|
|
81
|
+
return
|
|
82
|
+
}
|
|
83
|
+
ctx.Report(typeNode, "Type annotation here is unnecessary.")
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
func isInferrablePair(typeNode, init *shimast.Node) bool {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
87
|
+
switch typeNode.Kind {
|
|
88
|
+
case shimast.KindStringKeyword:
|
|
89
|
+
return init.Kind == shimast.KindStringLiteral || init.Kind == shimast.KindNoSubstitutionTemplateLiteral || init.Kind == shimast.KindTemplateExpression
|
|
90
|
+
case shimast.KindNumberKeyword:
|
|
91
|
+
return init.Kind == shimast.KindNumericLiteral || isUnaryNumeric(init)
|
|
92
|
+
case shimast.KindBooleanKeyword:
|
|
93
|
+
return init.Kind == shimast.KindTrueKeyword || init.Kind == shimast.KindFalseKeyword
|
|
94
|
+
case shimast.KindBigIntKeyword:
|
|
95
|
+
return init.Kind == shimast.KindBigIntLiteral
|
|
96
|
+
case shimast.KindNullKeyword:
|
|
97
|
+
return init.Kind == shimast.KindNullKeyword
|
|
98
|
+
case shimast.KindUndefinedKeyword:
|
|
99
|
+
return identifierText(init) == "undefined" || init.Kind == shimast.KindVoidExpression
|
|
100
|
+
}
|
|
101
|
+
return false
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
func isUnaryNumeric(node *shimast.Node) bool {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
105
|
+
if node == nil || node.Kind != shimast.KindPrefixUnaryExpression {
|
|
106
|
+
return false
|
|
107
|
+
}
|
|
108
|
+
prefix := node.AsPrefixUnaryExpression()
|
|
109
|
+
if prefix == nil {
|
|
110
|
+
return false
|
|
111
|
+
}
|
|
112
|
+
switch prefix.Operator {
|
|
113
|
+
case shimast.KindPlusToken, shimast.KindMinusToken:
|
|
114
|
+
return prefix.Operand != nil && prefix.Operand.Kind == shimast.KindNumericLiteral
|
|
115
|
+
}
|
|
116
|
+
return false
|
|
117
117
|
}
|
|
118
118
|
|
|
119
119
|
// no-namespace: TypeScript-only `namespace`/`module` declarations. They
|
|
@@ -123,16 +123,16 @@ type noNamespace struct{}
|
|
|
123
123
|
func (noNamespace) Name() string { return "no-namespace" }
|
|
124
124
|
func (noNamespace) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindModuleDeclaration} }
|
|
125
125
|
func (noNamespace) Check(ctx *Context, node *shimast.Node) {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
126
|
+
decl := node.AsModuleDeclaration()
|
|
127
|
+
if decl == nil || decl.Name() == nil {
|
|
128
|
+
return
|
|
129
|
+
}
|
|
130
|
+
// Skip `declare module "fs"` ambient module declarations — those are
|
|
131
|
+
// the legitimate use case.
|
|
132
|
+
if decl.Name().Kind == shimast.KindStringLiteral {
|
|
133
|
+
return
|
|
134
|
+
}
|
|
135
|
+
ctx.Report(node, "ES2015 module syntax is preferred over namespaces.")
|
|
136
136
|
}
|
|
137
137
|
|
|
138
138
|
// no-this-alias: `const self = this;` reassigns `this` to a local. Use
|
|
@@ -142,13 +142,13 @@ type noThisAlias struct{}
|
|
|
142
142
|
func (noThisAlias) Name() string { return "no-this-alias" }
|
|
143
143
|
func (noThisAlias) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindVariableDeclaration} }
|
|
144
144
|
func (noThisAlias) Check(ctx *Context, node *shimast.Node) {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
145
|
+
decl := node.AsVariableDeclaration()
|
|
146
|
+
if decl == nil || decl.Initializer == nil {
|
|
147
|
+
return
|
|
148
|
+
}
|
|
149
|
+
if decl.Initializer.Kind == shimast.KindThisKeyword {
|
|
150
|
+
ctx.Report(node, "Unexpected aliasing of 'this' to local variable.")
|
|
151
|
+
}
|
|
152
152
|
}
|
|
153
153
|
|
|
154
154
|
// prefer-as-const: `as 'foo'` / `as 1` should be `as const`.
|
|
@@ -156,50 +156,50 @@ type preferAsConst struct{}
|
|
|
156
156
|
|
|
157
157
|
func (preferAsConst) Name() string { return "prefer-as-const" }
|
|
158
158
|
func (preferAsConst) Visits() []shimast.Kind {
|
|
159
|
-
|
|
159
|
+
return []shimast.Kind{shimast.KindAsExpression, shimast.KindTypeAssertionExpression}
|
|
160
160
|
}
|
|
161
161
|
func (preferAsConst) Check(ctx *Context, node *shimast.Node) {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
162
|
+
var expr, typeNode *shimast.Node
|
|
163
|
+
switch node.Kind {
|
|
164
|
+
case shimast.KindAsExpression:
|
|
165
|
+
as := node.AsAsExpression()
|
|
166
|
+
if as == nil {
|
|
167
|
+
return
|
|
168
|
+
}
|
|
169
|
+
expr = as.Expression
|
|
170
|
+
typeNode = as.Type
|
|
171
|
+
case shimast.KindTypeAssertionExpression:
|
|
172
|
+
ta := node.AsTypeAssertion()
|
|
173
|
+
if ta == nil {
|
|
174
|
+
return
|
|
175
|
+
}
|
|
176
|
+
expr = ta.Expression
|
|
177
|
+
typeNode = ta.Type
|
|
178
|
+
}
|
|
179
|
+
if expr == nil || typeNode == nil {
|
|
180
|
+
return
|
|
181
|
+
}
|
|
182
|
+
if typeNode.Kind != shimast.KindLiteralType {
|
|
183
|
+
return
|
|
184
|
+
}
|
|
185
|
+
literalType := typeNode.AsLiteralTypeNode()
|
|
186
|
+
if literalType == nil || literalType.Literal == nil {
|
|
187
|
+
return
|
|
188
|
+
}
|
|
189
|
+
if !literalsMatchSourceText(ctx.File, expr, literalType.Literal) {
|
|
190
|
+
return
|
|
191
|
+
}
|
|
192
|
+
ctx.Report(node, "Expected `as const` instead of `as` literal type.")
|
|
193
193
|
}
|
|
194
194
|
|
|
195
195
|
func literalsMatchSourceText(file *shimast.SourceFile, lhs, rhs *shimast.Node) bool {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
196
|
+
if lhs == nil || rhs == nil {
|
|
197
|
+
return false
|
|
198
|
+
}
|
|
199
|
+
if !isLiteralExpression(lhs) {
|
|
200
|
+
return false
|
|
201
|
+
}
|
|
202
|
+
return nodeText(file, lhs) == nodeText(file, rhs)
|
|
203
203
|
}
|
|
204
204
|
|
|
205
205
|
// no-require-imports: ban `require(...)` calls in TS source. Use
|
|
@@ -208,23 +208,23 @@ type noRequireImports struct{}
|
|
|
208
208
|
|
|
209
209
|
func (noRequireImports) Name() string { return "no-require-imports" }
|
|
210
210
|
func (noRequireImports) Visits() []shimast.Kind {
|
|
211
|
-
|
|
211
|
+
return []shimast.Kind{shimast.KindCallExpression, shimast.KindImportEqualsDeclaration}
|
|
212
212
|
}
|
|
213
213
|
func (noRequireImports) Check(ctx *Context, node *shimast.Node) {
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
214
|
+
switch node.Kind {
|
|
215
|
+
case shimast.KindCallExpression:
|
|
216
|
+
call := node.AsCallExpression()
|
|
217
|
+
if call == nil {
|
|
218
|
+
return
|
|
219
|
+
}
|
|
220
|
+
if callCalleeName(call) != "require" {
|
|
221
|
+
return
|
|
222
|
+
}
|
|
223
|
+
// Ignore `import("...")` — that's a different node kind.
|
|
224
|
+
ctx.Report(node, "A `require()` style import is forbidden.")
|
|
225
|
+
case shimast.KindImportEqualsDeclaration:
|
|
226
|
+
ctx.Report(node, "An `import = require()` style import is forbidden.")
|
|
227
|
+
}
|
|
228
228
|
}
|
|
229
229
|
|
|
230
230
|
// ban-ts-comment: `// @ts-ignore` / `// @ts-nocheck` / `// @ts-expect-error`
|
|
@@ -234,27 +234,27 @@ type banTsComment struct{}
|
|
|
234
234
|
func (banTsComment) Name() string { return "ban-ts-comment" }
|
|
235
235
|
func (banTsComment) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindSourceFile} }
|
|
236
236
|
func (banTsComment) Check(ctx *Context, node *shimast.Node) {
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
237
|
+
if ctx.File == nil {
|
|
238
|
+
return
|
|
239
|
+
}
|
|
240
|
+
for _, directive := range ctx.File.CommentDirectives {
|
|
241
|
+
switch directive.Kind {
|
|
242
|
+
case shimast.CommentDirectiveKindIgnore:
|
|
243
|
+
ctx.ReportRange(directive.Loc.Pos(), directive.Loc.End(), "Do not use `@ts-ignore` because it alters compilation errors.")
|
|
244
|
+
case shimast.CommentDirectiveKindExpectError:
|
|
245
|
+
ctx.ReportRange(directive.Loc.Pos(), directive.Loc.End(), "Do not use `@ts-expect-error` because it alters compilation errors.")
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
248
|
}
|
|
249
249
|
|
|
250
250
|
func init() {
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
251
|
+
Register(noExplicitAny{})
|
|
252
|
+
Register(noNonNullAssertion{})
|
|
253
|
+
Register(noEmptyInterface{})
|
|
254
|
+
Register(noInferrableTypes{})
|
|
255
|
+
Register(noNamespace{})
|
|
256
|
+
Register(noThisAlias{})
|
|
257
|
+
Register(preferAsConst{})
|
|
258
|
+
Register(noRequireImports{})
|
|
259
|
+
Register(banTsComment{})
|
|
260
260
|
}
|