@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/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA,0DAA6B;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA,0DAA6B;AAK7B,qDAAmC;AAEnC,0BACE,QAA0D;IAE1D,OAAO;QACL,IAAI,EAAE,YAAY;QAClB,MAAM,EAAE,mBAAI,CAAC,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,QAAQ,CAAC;QAC/C,KAAK,EAAE,OAAO;KACf,CAAC;AACJ,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ttsc/lint",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.3",
|
|
4
4
|
"description": "Reference ttsc plugin: ESLint-style lint rules hosted in the same Program/Checker as the type-check pass.",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "lib/index.d.ts",
|
|
@@ -27,10 +27,10 @@
|
|
|
27
27
|
"plugin"
|
|
28
28
|
],
|
|
29
29
|
"devDependencies": {
|
|
30
|
-
"@typescript/native-preview": "7.0.0-dev.
|
|
30
|
+
"@typescript/native-preview": "7.0.0-dev.20260501.1",
|
|
31
31
|
"@types/node": "^25.3.0",
|
|
32
32
|
"rimraf": "^6.1.2",
|
|
33
|
-
"ttsc": "0.7.
|
|
33
|
+
"ttsc": "0.7.3"
|
|
34
34
|
},
|
|
35
35
|
"repository": {
|
|
36
36
|
"type": "git",
|
package/plugin/ast_helpers.go
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
package main
|
|
2
2
|
|
|
3
3
|
import (
|
|
4
|
-
|
|
4
|
+
"strings"
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
7
|
+
shimscanner "github.com/microsoft/typescript-go/shim/scanner"
|
|
8
8
|
)
|
|
9
9
|
|
|
10
10
|
// nodeText returns the source text under a node with all leading
|
|
@@ -14,213 +14,213 @@ import (
|
|
|
14
14
|
// would include any preceding comment because tsgo's Pos points at
|
|
15
15
|
// the start of leading trivia, not the actual token.
|
|
16
16
|
func nodeText(file *shimast.SourceFile, node *shimast.Node) string {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
17
|
+
if file == nil || node == nil {
|
|
18
|
+
return ""
|
|
19
|
+
}
|
|
20
|
+
src := file.Text()
|
|
21
|
+
end := node.End()
|
|
22
|
+
pos := shimscanner.SkipTrivia(src, node.Pos())
|
|
23
|
+
if pos < 0 || end > len(src) || pos >= end {
|
|
24
|
+
return ""
|
|
25
|
+
}
|
|
26
|
+
return strings.TrimRight(src[pos:end], " \t\r\n")
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
// identifierText returns the lexical name of an Identifier node, or "" if
|
|
30
30
|
// the node isn't an Identifier.
|
|
31
31
|
func identifierText(node *shimast.Node) string {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
32
|
+
if node == nil || node.Kind != shimast.KindIdentifier {
|
|
33
|
+
return ""
|
|
34
|
+
}
|
|
35
|
+
id := node.AsIdentifier()
|
|
36
|
+
if id == nil {
|
|
37
|
+
return ""
|
|
38
|
+
}
|
|
39
|
+
return id.Text
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
// stripParens descends through ParenthesizedExpression nodes and returns
|
|
43
43
|
// the first non-parenthesized child. ESLint rules typically operate on
|
|
44
44
|
// the canonical form.
|
|
45
45
|
func stripParens(node *shimast.Node) *shimast.Node {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
46
|
+
for node != nil && node.Kind == shimast.KindParenthesizedExpression {
|
|
47
|
+
next := node.AsParenthesizedExpression()
|
|
48
|
+
if next == nil || next.Expression == nil {
|
|
49
|
+
return node
|
|
50
|
+
}
|
|
51
|
+
node = next.Expression
|
|
52
|
+
}
|
|
53
|
+
return node
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
// isMatchingPropertyAccess reports whether `node` reads the chain
|
|
57
57
|
// `head.tail[0].tail[1]…`. Useful for detecting `obj.__proto__` or
|
|
58
58
|
// `console.log` shapes regardless of nesting.
|
|
59
59
|
func isMatchingPropertyAccess(node *shimast.Node, head string, tail ...string) bool {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
60
|
+
if node == nil || node.Kind != shimast.KindPropertyAccessExpression {
|
|
61
|
+
return false
|
|
62
|
+
}
|
|
63
|
+
chain := []*shimast.Node{}
|
|
64
|
+
cur := node
|
|
65
|
+
for cur != nil && cur.Kind == shimast.KindPropertyAccessExpression {
|
|
66
|
+
access := cur.AsPropertyAccessExpression()
|
|
67
|
+
if access == nil {
|
|
68
|
+
return false
|
|
69
|
+
}
|
|
70
|
+
chain = append([]*shimast.Node{access.Name()}, chain...)
|
|
71
|
+
cur = access.Expression
|
|
72
|
+
}
|
|
73
|
+
if cur == nil || identifierText(cur) != head {
|
|
74
|
+
return false
|
|
75
|
+
}
|
|
76
|
+
if len(chain) != len(tail) {
|
|
77
|
+
return false
|
|
78
|
+
}
|
|
79
|
+
for i, want := range tail {
|
|
80
|
+
if identifierText(chain[i]) != want {
|
|
81
|
+
return false
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return true
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
// isLiteralBoolean returns the boolean value (and ok=true) for a
|
|
88
88
|
// `KindTrueKeyword` / `KindFalseKeyword` literal. Other nodes return
|
|
89
89
|
// (false, false).
|
|
90
90
|
func isLiteralBoolean(node *shimast.Node) (bool, bool) {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
91
|
+
if node == nil {
|
|
92
|
+
return false, false
|
|
93
|
+
}
|
|
94
|
+
switch node.Kind {
|
|
95
|
+
case shimast.KindTrueKeyword:
|
|
96
|
+
return true, true
|
|
97
|
+
case shimast.KindFalseKeyword:
|
|
98
|
+
return false, true
|
|
99
|
+
}
|
|
100
|
+
return false, false
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
// isLiteralExpression returns true for nodes whose value is intrinsically
|
|
104
104
|
// truthy / falsy at parse time — these flag `no-constant-condition` etc.
|
|
105
105
|
func isLiteralExpression(node *shimast.Node) bool {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
106
|
+
if node == nil {
|
|
107
|
+
return false
|
|
108
|
+
}
|
|
109
|
+
switch node.Kind {
|
|
110
|
+
case
|
|
111
|
+
shimast.KindStringLiteral,
|
|
112
|
+
shimast.KindNumericLiteral,
|
|
113
|
+
shimast.KindBigIntLiteral,
|
|
114
|
+
shimast.KindNoSubstitutionTemplateLiteral,
|
|
115
|
+
shimast.KindRegularExpressionLiteral,
|
|
116
|
+
shimast.KindTrueKeyword,
|
|
117
|
+
shimast.KindFalseKeyword,
|
|
118
|
+
shimast.KindNullKeyword:
|
|
119
|
+
return true
|
|
120
|
+
}
|
|
121
|
+
return false
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
// callCalleeName returns the simple-identifier callee of a CallExpression
|
|
125
125
|
// (e.g. `eval` from `eval("...")`). Returns "" when the callee is more
|
|
126
126
|
// complex than a bare identifier.
|
|
127
127
|
func callCalleeName(call *shimast.CallExpression) string {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
128
|
+
if call == nil || call.Expression == nil {
|
|
129
|
+
return ""
|
|
130
|
+
}
|
|
131
|
+
return identifierText(call.Expression)
|
|
132
132
|
}
|
|
133
133
|
|
|
134
134
|
// numericLiteralText returns the literal text of a numeric / bigint
|
|
135
135
|
// literal, normalized for the comparisons rules need (`-0`, `0xFF`).
|
|
136
136
|
func numericLiteralText(node *shimast.Node) string {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
137
|
+
if node == nil {
|
|
138
|
+
return ""
|
|
139
|
+
}
|
|
140
|
+
switch node.Kind {
|
|
141
|
+
case shimast.KindNumericLiteral:
|
|
142
|
+
if lit := node.AsNumericLiteral(); lit != nil {
|
|
143
|
+
return lit.Text
|
|
144
|
+
}
|
|
145
|
+
case shimast.KindBigIntLiteral:
|
|
146
|
+
if lit := node.AsBigIntLiteral(); lit != nil {
|
|
147
|
+
return lit.Text
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return ""
|
|
151
151
|
}
|
|
152
152
|
|
|
153
153
|
// stringLiteralText returns the value of a string-shaped literal:
|
|
154
154
|
// StringLiteral or NoSubstitutionTemplateLiteral.
|
|
155
155
|
func stringLiteralText(node *shimast.Node) string {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
156
|
+
if node == nil {
|
|
157
|
+
return ""
|
|
158
|
+
}
|
|
159
|
+
switch node.Kind {
|
|
160
|
+
case shimast.KindStringLiteral:
|
|
161
|
+
if lit := node.AsStringLiteral(); lit != nil {
|
|
162
|
+
return lit.Text
|
|
163
|
+
}
|
|
164
|
+
case shimast.KindNoSubstitutionTemplateLiteral:
|
|
165
|
+
if lit := node.AsNoSubstitutionTemplateLiteral(); lit != nil {
|
|
166
|
+
return lit.Text
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return ""
|
|
170
170
|
}
|
|
171
171
|
|
|
172
172
|
// walkDescendants visits node and every child below it, depth-first.
|
|
173
173
|
func walkDescendants(node *shimast.Node, visit func(*shimast.Node)) {
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
174
|
+
if node == nil {
|
|
175
|
+
return
|
|
176
|
+
}
|
|
177
|
+
visit(node)
|
|
178
|
+
node.ForEachChild(func(child *shimast.Node) bool {
|
|
179
|
+
walkDescendants(child, visit)
|
|
180
|
+
return false
|
|
181
|
+
})
|
|
182
182
|
}
|
|
183
183
|
|
|
184
184
|
func bindingIdentifierNames(node *shimast.Node) []string {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
185
|
+
if node == nil {
|
|
186
|
+
return nil
|
|
187
|
+
}
|
|
188
|
+
if name := identifierText(node); name != "" {
|
|
189
|
+
return []string{name}
|
|
190
|
+
}
|
|
191
|
+
if node.Kind != shimast.KindObjectBindingPattern && node.Kind != shimast.KindArrayBindingPattern {
|
|
192
|
+
return nil
|
|
193
|
+
}
|
|
194
|
+
var names []string
|
|
195
|
+
walkDescendants(node, func(child *shimast.Node) {
|
|
196
|
+
if child == node {
|
|
197
|
+
return
|
|
198
|
+
}
|
|
199
|
+
if name := identifierText(child); name != "" {
|
|
200
|
+
names = append(names, name)
|
|
201
|
+
}
|
|
202
|
+
})
|
|
203
|
+
return names
|
|
204
204
|
}
|
|
205
205
|
|
|
206
206
|
func isLiteralLike(node *shimast.Node) bool {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
207
|
+
node = stripParens(node)
|
|
208
|
+
if node == nil {
|
|
209
|
+
return false
|
|
210
|
+
}
|
|
211
|
+
if isLiteralExpression(node) {
|
|
212
|
+
return true
|
|
213
|
+
}
|
|
214
|
+
if node.Kind == shimast.KindPrefixUnaryExpression {
|
|
215
|
+
prefix := node.AsPrefixUnaryExpression()
|
|
216
|
+
if prefix == nil {
|
|
217
|
+
return false
|
|
218
|
+
}
|
|
219
|
+
switch prefix.Operator {
|
|
220
|
+
case shimast.KindPlusToken, shimast.KindMinusToken:
|
|
221
|
+
return prefix.Operand != nil &&
|
|
222
|
+
(prefix.Operand.Kind == shimast.KindNumericLiteral || prefix.Operand.Kind == shimast.KindBigIntLiteral)
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return false
|
|
226
226
|
}
|