@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.
- 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_dupes.go
CHANGED
|
@@ -9,33 +9,33 @@ type noDuplicateCase struct{}
|
|
|
9
9
|
func (noDuplicateCase) Name() string { return "no-duplicate-case" }
|
|
10
10
|
func (noDuplicateCase) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindSwitchStatement} }
|
|
11
11
|
func (noDuplicateCase) Check(ctx *Context, node *shimast.Node) {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
12
|
+
sw := node.AsSwitchStatement()
|
|
13
|
+
if sw == nil || sw.CaseBlock == nil {
|
|
14
|
+
return
|
|
15
|
+
}
|
|
16
|
+
block := sw.CaseBlock.AsCaseBlock()
|
|
17
|
+
if block == nil || block.Clauses == nil {
|
|
18
|
+
return
|
|
19
|
+
}
|
|
20
|
+
seen := make(map[string]bool, len(block.Clauses.Nodes))
|
|
21
|
+
for _, clause := range block.Clauses.Nodes {
|
|
22
|
+
if clause == nil || clause.Kind != shimast.KindCaseClause {
|
|
23
|
+
continue
|
|
24
|
+
}
|
|
25
|
+
caseClause := clause.AsCaseOrDefaultClause()
|
|
26
|
+
if caseClause == nil || caseClause.Expression == nil {
|
|
27
|
+
continue
|
|
28
|
+
}
|
|
29
|
+
key := nodeText(ctx.File, caseClause.Expression)
|
|
30
|
+
if key == "" {
|
|
31
|
+
continue
|
|
32
|
+
}
|
|
33
|
+
if seen[key] {
|
|
34
|
+
ctx.Report(clause, "Duplicate case label.")
|
|
35
|
+
continue
|
|
36
|
+
}
|
|
37
|
+
seen[key] = true
|
|
38
|
+
}
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
// no-dupe-keys: duplicated property names in a single object literal.
|
|
@@ -45,23 +45,23 @@ type noDupeKeys struct{}
|
|
|
45
45
|
func (noDupeKeys) Name() string { return "no-dupe-keys" }
|
|
46
46
|
func (noDupeKeys) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindObjectLiteralExpression} }
|
|
47
47
|
func (noDupeKeys) Check(ctx *Context, node *shimast.Node) {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
48
|
+
obj := node.AsObjectLiteralExpression()
|
|
49
|
+
if obj == nil || obj.Properties == nil {
|
|
50
|
+
return
|
|
51
|
+
}
|
|
52
|
+
seen := make(map[string]*shimast.Node, len(obj.Properties.Nodes))
|
|
53
|
+
for _, prop := range obj.Properties.Nodes {
|
|
54
|
+
key := propertyKey(ctx.File, prop)
|
|
55
|
+
if key == "" {
|
|
56
|
+
continue
|
|
57
|
+
}
|
|
58
|
+
if first, ok := seen[key]; ok {
|
|
59
|
+
ctx.Report(prop, "Duplicate key '"+key+"'.")
|
|
60
|
+
_ = first
|
|
61
|
+
continue
|
|
62
|
+
}
|
|
63
|
+
seen[key] = prop
|
|
64
|
+
}
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
// no-dupe-args: duplicated parameter names in a single function head.
|
|
@@ -70,34 +70,34 @@ type noDupeArgs struct{}
|
|
|
70
70
|
|
|
71
71
|
func (noDupeArgs) Name() string { return "no-dupe-args" }
|
|
72
72
|
func (noDupeArgs) Visits() []shimast.Kind {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
73
|
+
return []shimast.Kind{
|
|
74
|
+
shimast.KindFunctionDeclaration,
|
|
75
|
+
shimast.KindFunctionExpression,
|
|
76
|
+
shimast.KindArrowFunction,
|
|
77
|
+
shimast.KindMethodDeclaration,
|
|
78
|
+
shimast.KindConstructor,
|
|
79
|
+
shimast.KindGetAccessor,
|
|
80
|
+
shimast.KindSetAccessor,
|
|
81
|
+
}
|
|
82
82
|
}
|
|
83
83
|
func (noDupeArgs) Check(ctx *Context, node *shimast.Node) {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
84
|
+
params := node.Parameters()
|
|
85
|
+
seen := make(map[string]bool, len(params))
|
|
86
|
+
for _, param := range params {
|
|
87
|
+
paramDecl := param.AsParameterDeclaration()
|
|
88
|
+
if paramDecl == nil {
|
|
89
|
+
continue
|
|
90
|
+
}
|
|
91
|
+
name := identifierText(paramDecl.Name())
|
|
92
|
+
if name == "" {
|
|
93
|
+
continue
|
|
94
|
+
}
|
|
95
|
+
if seen[name] {
|
|
96
|
+
ctx.Report(param, "Duplicate parameter name '"+name+"'.")
|
|
97
|
+
continue
|
|
98
|
+
}
|
|
99
|
+
seen[name] = true
|
|
100
|
+
}
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
// propertyKey returns a stable key for a property in an object literal.
|
|
@@ -106,74 +106,74 @@ func (noDupeArgs) Check(ctx *Context, node *shimast.Node) {
|
|
|
106
106
|
// expressions still won't cause false positives because the same expr
|
|
107
107
|
// produces the same source text).
|
|
108
108
|
func propertyKey(file *shimast.SourceFile, prop *shimast.Node) string {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
109
|
+
if prop == nil {
|
|
110
|
+
return ""
|
|
111
|
+
}
|
|
112
|
+
switch prop.Kind {
|
|
113
|
+
case shimast.KindPropertyAssignment:
|
|
114
|
+
assignment := prop.AsPropertyAssignment()
|
|
115
|
+
if assignment == nil {
|
|
116
|
+
return ""
|
|
117
|
+
}
|
|
118
|
+
return staticPropertyKey(file, assignment.Name())
|
|
119
|
+
case shimast.KindShorthandPropertyAssignment:
|
|
120
|
+
short := prop.AsShorthandPropertyAssignment()
|
|
121
|
+
if short == nil {
|
|
122
|
+
return ""
|
|
123
|
+
}
|
|
124
|
+
return staticPropertyKey(file, short.Name())
|
|
125
|
+
case shimast.KindMethodDeclaration:
|
|
126
|
+
method := prop.AsMethodDeclaration()
|
|
127
|
+
if method == nil {
|
|
128
|
+
return ""
|
|
129
|
+
}
|
|
130
|
+
return staticPropertyKey(file, method.Name())
|
|
131
|
+
case shimast.KindGetAccessor, shimast.KindSetAccessor:
|
|
132
|
+
// Getter and setter pairs share a name but are not duplicates.
|
|
133
|
+
// Add a kind suffix so the dedupe key separates them.
|
|
134
|
+
switch prop.Kind {
|
|
135
|
+
case shimast.KindGetAccessor:
|
|
136
|
+
get := prop.AsGetAccessorDeclaration()
|
|
137
|
+
if get == nil {
|
|
138
|
+
return ""
|
|
139
|
+
}
|
|
140
|
+
if k := staticPropertyKey(file, get.Name()); k != "" {
|
|
141
|
+
return "get:" + k
|
|
142
|
+
}
|
|
143
|
+
case shimast.KindSetAccessor:
|
|
144
|
+
set := prop.AsSetAccessorDeclaration()
|
|
145
|
+
if set == nil {
|
|
146
|
+
return ""
|
|
147
|
+
}
|
|
148
|
+
if k := staticPropertyKey(file, set.Name()); k != "" {
|
|
149
|
+
return "set:" + k
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return ""
|
|
154
154
|
}
|
|
155
155
|
|
|
156
156
|
func staticPropertyKey(file *shimast.SourceFile, name *shimast.Node) string {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
157
|
+
if name == nil {
|
|
158
|
+
return ""
|
|
159
|
+
}
|
|
160
|
+
switch name.Kind {
|
|
161
|
+
case shimast.KindIdentifier:
|
|
162
|
+
return identifierText(name)
|
|
163
|
+
case shimast.KindStringLiteral, shimast.KindNoSubstitutionTemplateLiteral:
|
|
164
|
+
return stringLiteralText(name)
|
|
165
|
+
case shimast.KindNumericLiteral, shimast.KindBigIntLiteral:
|
|
166
|
+
return numericLiteralText(name)
|
|
167
|
+
case shimast.KindComputedPropertyName:
|
|
168
|
+
// fall back to source text so a computed `[`foo`]` still compares
|
|
169
|
+
// against the literal `foo` form when both appear together.
|
|
170
|
+
return nodeText(file, name)
|
|
171
|
+
}
|
|
172
|
+
return ""
|
|
173
173
|
}
|
|
174
174
|
|
|
175
175
|
func init() {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
176
|
+
Register(noDuplicateCase{})
|
|
177
|
+
Register(noDupeKeys{})
|
|
178
|
+
Register(noDupeArgs{})
|
|
179
179
|
}
|
package/plugin/rules_empty.go
CHANGED
|
@@ -10,21 +10,21 @@ type noEmpty struct{}
|
|
|
10
10
|
func (noEmpty) Name() string { return "no-empty" }
|
|
11
11
|
func (noEmpty) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindBlock} }
|
|
12
12
|
func (noEmpty) Check(ctx *Context, node *shimast.Node) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
13
|
+
block := node.AsBlock()
|
|
14
|
+
if block == nil || block.Statements == nil {
|
|
15
|
+
return
|
|
16
|
+
}
|
|
17
|
+
if len(block.Statements.Nodes) > 0 {
|
|
18
|
+
return
|
|
19
|
+
}
|
|
20
|
+
parent := node.Parent
|
|
21
|
+
if parent != nil && parent.Kind == shimast.KindCatchClause {
|
|
22
|
+
return // tolerated — see ESLint default options
|
|
23
|
+
}
|
|
24
|
+
if isFunctionLikeKind(parent) {
|
|
25
|
+
return // empty function body is `no-empty-function`'s job
|
|
26
|
+
}
|
|
27
|
+
ctx.Report(node, "Empty block statement.")
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
// no-empty-function: empty function / method / arrow / accessor bodies.
|
|
@@ -33,31 +33,31 @@ type noEmptyFunction struct{}
|
|
|
33
33
|
|
|
34
34
|
func (noEmptyFunction) Name() string { return "no-empty-function" }
|
|
35
35
|
func (noEmptyFunction) Visits() []shimast.Kind {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
36
|
+
return []shimast.Kind{
|
|
37
|
+
shimast.KindFunctionDeclaration,
|
|
38
|
+
shimast.KindFunctionExpression,
|
|
39
|
+
shimast.KindArrowFunction,
|
|
40
|
+
shimast.KindMethodDeclaration,
|
|
41
|
+
shimast.KindGetAccessor,
|
|
42
|
+
shimast.KindSetAccessor,
|
|
43
|
+
shimast.KindConstructor,
|
|
44
|
+
}
|
|
45
45
|
}
|
|
46
46
|
func (noEmptyFunction) Check(ctx *Context, node *shimast.Node) {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
47
|
+
body := node.Body()
|
|
48
|
+
if body == nil {
|
|
49
|
+
return
|
|
50
|
+
}
|
|
51
|
+
if body.Kind != shimast.KindBlock {
|
|
52
|
+
return
|
|
53
|
+
}
|
|
54
|
+
block := body.AsBlock()
|
|
55
|
+
if block == nil || block.Statements == nil {
|
|
56
|
+
return
|
|
57
|
+
}
|
|
58
|
+
if len(block.Statements.Nodes) == 0 {
|
|
59
|
+
ctx.Report(node, "Unexpected empty function.")
|
|
60
|
+
}
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
// no-empty-pattern: `({}) => x` or `function ({}) {}` — destructuring
|
|
@@ -67,44 +67,44 @@ type noEmptyPattern struct{}
|
|
|
67
67
|
|
|
68
68
|
func (noEmptyPattern) Name() string { return "no-empty-pattern" }
|
|
69
69
|
func (noEmptyPattern) Visits() []shimast.Kind {
|
|
70
|
-
|
|
70
|
+
return []shimast.Kind{shimast.KindObjectBindingPattern, shimast.KindArrayBindingPattern}
|
|
71
71
|
}
|
|
72
72
|
func (noEmptyPattern) Check(ctx *Context, node *shimast.Node) {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
73
|
+
pattern := node.AsBindingPattern()
|
|
74
|
+
if pattern == nil || pattern.Elements == nil {
|
|
75
|
+
return
|
|
76
|
+
}
|
|
77
|
+
if len(pattern.Elements.Nodes) == 0 {
|
|
78
|
+
shape := "object"
|
|
79
|
+
if node.Kind == shimast.KindArrayBindingPattern {
|
|
80
|
+
shape = "array"
|
|
81
|
+
}
|
|
82
|
+
ctx.Report(node, "Unexpected empty "+shape+" pattern.")
|
|
83
|
+
}
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
// isFunctionLikeKind reports whether the node represents a function-like
|
|
87
87
|
// host whose body is the relevant scope for the empty check.
|
|
88
88
|
func isFunctionLikeKind(n *shimast.Node) bool {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
89
|
+
if n == nil {
|
|
90
|
+
return false
|
|
91
|
+
}
|
|
92
|
+
switch n.Kind {
|
|
93
|
+
case
|
|
94
|
+
shimast.KindFunctionDeclaration,
|
|
95
|
+
shimast.KindFunctionExpression,
|
|
96
|
+
shimast.KindArrowFunction,
|
|
97
|
+
shimast.KindMethodDeclaration,
|
|
98
|
+
shimast.KindGetAccessor,
|
|
99
|
+
shimast.KindSetAccessor,
|
|
100
|
+
shimast.KindConstructor:
|
|
101
|
+
return true
|
|
102
|
+
}
|
|
103
|
+
return false
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
func init() {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
107
|
+
Register(noEmpty{})
|
|
108
|
+
Register(noEmptyFunction{})
|
|
109
|
+
Register(noEmptyPattern{})
|
|
110
110
|
}
|
package/plugin/rules_eval.go
CHANGED
|
@@ -11,13 +11,13 @@ type noEval struct{}
|
|
|
11
11
|
func (noEval) Name() string { return "no-eval" }
|
|
12
12
|
func (noEval) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindCallExpression} }
|
|
13
13
|
func (noEval) Check(ctx *Context, node *shimast.Node) {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
14
|
+
call := node.AsCallExpression()
|
|
15
|
+
if call == nil {
|
|
16
|
+
return
|
|
17
|
+
}
|
|
18
|
+
if callCalleeName(call) == "eval" {
|
|
19
|
+
ctx.Report(node, "eval can be harmful.")
|
|
20
|
+
}
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
// no-script-url: forbid `"javascript:..."` literals. Often used to inject
|
|
@@ -27,33 +27,33 @@ type noScriptURL struct{}
|
|
|
27
27
|
|
|
28
28
|
func (noScriptURL) Name() string { return "no-script-url" }
|
|
29
29
|
func (noScriptURL) Visits() []shimast.Kind {
|
|
30
|
-
|
|
30
|
+
return []shimast.Kind{shimast.KindStringLiteral, shimast.KindNoSubstitutionTemplateLiteral}
|
|
31
31
|
}
|
|
32
32
|
func (noScriptURL) Check(ctx *Context, node *shimast.Node) {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
text := stringLiteralText(node)
|
|
34
|
+
if isJavaScriptURL(text) {
|
|
35
|
+
ctx.Report(node, "Script URL is a form of eval.")
|
|
36
|
+
}
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
func isJavaScriptURL(text string) bool {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
40
|
+
const prefix = "javascript:"
|
|
41
|
+
if len(text) < len(prefix) {
|
|
42
|
+
return false
|
|
43
|
+
}
|
|
44
|
+
for i := 0; i < len(prefix); i++ {
|
|
45
|
+
c := text[i]
|
|
46
|
+
if c >= 'A' && c <= 'Z' {
|
|
47
|
+
c += 'a' - 'A'
|
|
48
|
+
}
|
|
49
|
+
if c != prefix[i] {
|
|
50
|
+
return false
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return true
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
func init() {
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
Register(noEval{})
|
|
58
|
+
Register(noScriptURL{})
|
|
59
59
|
}
|