@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_protos.go
CHANGED
|
@@ -9,16 +9,16 @@ type noIterator struct{}
|
|
|
9
9
|
|
|
10
10
|
func (noIterator) Name() string { return "no-iterator" }
|
|
11
11
|
func (noIterator) Visits() []shimast.Kind {
|
|
12
|
-
|
|
12
|
+
return []shimast.Kind{shimast.KindPropertyAccessExpression}
|
|
13
13
|
}
|
|
14
14
|
func (noIterator) Check(ctx *Context, node *shimast.Node) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
15
|
+
access := node.AsPropertyAccessExpression()
|
|
16
|
+
if access == nil {
|
|
17
|
+
return
|
|
18
|
+
}
|
|
19
|
+
if identifierText(access.Name()) == "__iterator__" {
|
|
20
|
+
ctx.Report(node, "Reserved name '__iterator__'.")
|
|
21
|
+
}
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
// no-proto: `obj.__proto__` access is legacy. Use `Object.getPrototypeOf`
|
|
@@ -29,16 +29,16 @@ type noProto struct{}
|
|
|
29
29
|
func (noProto) Name() string { return "no-proto" }
|
|
30
30
|
func (noProto) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindPropertyAccessExpression} }
|
|
31
31
|
func (noProto) Check(ctx *Context, node *shimast.Node) {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
32
|
+
access := node.AsPropertyAccessExpression()
|
|
33
|
+
if access == nil {
|
|
34
|
+
return
|
|
35
|
+
}
|
|
36
|
+
if identifierText(access.Name()) == "__proto__" {
|
|
37
|
+
ctx.Report(node, "The '__proto__' property is deprecated.")
|
|
38
|
+
}
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
func init() {
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
Register(noIterator{})
|
|
43
|
+
Register(noProto{})
|
|
44
44
|
}
|
package/plugin/rules_self.go
CHANGED
|
@@ -11,35 +11,35 @@ type noSelfAssign struct{}
|
|
|
11
11
|
func (noSelfAssign) Name() string { return "no-self-assign" }
|
|
12
12
|
func (noSelfAssign) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindBinaryExpression} }
|
|
13
13
|
func (noSelfAssign) Check(ctx *Context, node *shimast.Node) {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
14
|
+
expr := node.AsBinaryExpression()
|
|
15
|
+
if expr == nil || expr.OperatorToken == nil {
|
|
16
|
+
return
|
|
17
|
+
}
|
|
18
|
+
if expr.OperatorToken.Kind != shimast.KindEqualsToken {
|
|
19
|
+
return
|
|
20
|
+
}
|
|
21
|
+
left := stripParens(expr.Left)
|
|
22
|
+
right := stripParens(expr.Right)
|
|
23
|
+
if left == nil || right == nil {
|
|
24
|
+
return
|
|
25
|
+
}
|
|
26
|
+
if !isAssignableLeftHand(left) {
|
|
27
|
+
return
|
|
28
|
+
}
|
|
29
|
+
if nodeText(ctx.File, left) == nodeText(ctx.File, right) {
|
|
30
|
+
ctx.Report(node, "Self-assignment of a variable.")
|
|
31
|
+
}
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
func isAssignableLeftHand(node *shimast.Node) bool {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
35
|
+
if node == nil {
|
|
36
|
+
return false
|
|
37
|
+
}
|
|
38
|
+
switch node.Kind {
|
|
39
|
+
case shimast.KindIdentifier, shimast.KindPropertyAccessExpression, shimast.KindElementAccessExpression:
|
|
40
|
+
return true
|
|
41
|
+
}
|
|
42
|
+
return false
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
// no-self-compare: `x === x`, `x !== x`, etc. Useful for catching typos
|
|
@@ -50,40 +50,40 @@ type noSelfCompare struct{}
|
|
|
50
50
|
func (noSelfCompare) Name() string { return "no-self-compare" }
|
|
51
51
|
func (noSelfCompare) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindBinaryExpression} }
|
|
52
52
|
func (noSelfCompare) Check(ctx *Context, node *shimast.Node) {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
53
|
+
expr := node.AsBinaryExpression()
|
|
54
|
+
if expr == nil || expr.OperatorToken == nil {
|
|
55
|
+
return
|
|
56
|
+
}
|
|
57
|
+
if !isComparisonOperator(expr.OperatorToken.Kind) {
|
|
58
|
+
return
|
|
59
|
+
}
|
|
60
|
+
left := stripParens(expr.Left)
|
|
61
|
+
right := stripParens(expr.Right)
|
|
62
|
+
if left == nil || right == nil {
|
|
63
|
+
return
|
|
64
|
+
}
|
|
65
|
+
if nodeText(ctx.File, left) == nodeText(ctx.File, right) {
|
|
66
|
+
ctx.Report(node, "Comparing to itself is potentially pointless.")
|
|
67
|
+
}
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
func isComparisonOperator(kind shimast.Kind) bool {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
71
|
+
switch kind {
|
|
72
|
+
case
|
|
73
|
+
shimast.KindEqualsEqualsToken,
|
|
74
|
+
shimast.KindEqualsEqualsEqualsToken,
|
|
75
|
+
shimast.KindExclamationEqualsToken,
|
|
76
|
+
shimast.KindExclamationEqualsEqualsToken,
|
|
77
|
+
shimast.KindLessThanToken,
|
|
78
|
+
shimast.KindGreaterThanToken,
|
|
79
|
+
shimast.KindLessThanEqualsToken,
|
|
80
|
+
shimast.KindGreaterThanEqualsToken:
|
|
81
|
+
return true
|
|
82
|
+
}
|
|
83
|
+
return false
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
func init() {
|
|
87
|
-
|
|
88
|
-
|
|
87
|
+
Register(noSelfAssign{})
|
|
88
|
+
Register(noSelfCompare{})
|
|
89
89
|
}
|
package/plugin/rules_strings.go
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
package main
|
|
2
2
|
|
|
3
3
|
import (
|
|
4
|
-
|
|
4
|
+
"strings"
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
7
7
|
)
|
|
8
8
|
|
|
9
9
|
// no-template-curly-in-string: a regular string contains `${...}`. The
|
|
@@ -13,33 +13,33 @@ type noTemplateCurlyInString struct{}
|
|
|
13
13
|
|
|
14
14
|
func (noTemplateCurlyInString) Name() string { return "no-template-curly-in-string" }
|
|
15
15
|
func (noTemplateCurlyInString) Visits() []shimast.Kind {
|
|
16
|
-
|
|
16
|
+
return []shimast.Kind{shimast.KindStringLiteral}
|
|
17
17
|
}
|
|
18
18
|
func (noTemplateCurlyInString) Check(ctx *Context, node *shimast.Node) {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
text := stringLiteralText(node)
|
|
20
|
+
if hasTemplatePlaceholder(text) {
|
|
21
|
+
ctx.Report(node, "Unexpected template string expression in regular string.")
|
|
22
|
+
}
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
func hasTemplatePlaceholder(text string) bool {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
26
|
+
if !strings.Contains(text, "${") {
|
|
27
|
+
return false
|
|
28
|
+
}
|
|
29
|
+
// Match `${ ... }` style, not just literal "${" with nothing after.
|
|
30
|
+
idx := strings.Index(text, "${")
|
|
31
|
+
for idx >= 0 {
|
|
32
|
+
rest := text[idx+2:]
|
|
33
|
+
if strings.Contains(rest, "}") {
|
|
34
|
+
return true
|
|
35
|
+
}
|
|
36
|
+
next := strings.Index(text[idx+1:], "${")
|
|
37
|
+
if next < 0 {
|
|
38
|
+
return false
|
|
39
|
+
}
|
|
40
|
+
idx += next + 1
|
|
41
|
+
}
|
|
42
|
+
return false
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
// no-multi-str: `"line one \\n line two"` style backslash continuations.
|
|
@@ -50,23 +50,23 @@ type noMultiStr struct{}
|
|
|
50
50
|
func (noMultiStr) Name() string { return "no-multi-str" }
|
|
51
51
|
func (noMultiStr) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindStringLiteral} }
|
|
52
52
|
func (noMultiStr) Check(ctx *Context, node *shimast.Node) {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
53
|
+
src := nodeText(ctx.File, node)
|
|
54
|
+
// Look for `\` immediately before a newline in the *raw* source text.
|
|
55
|
+
if hasBackslashLineContinuation(src) {
|
|
56
|
+
ctx.Report(node, "Multiline support is limited to comments.")
|
|
57
|
+
}
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
func hasBackslashLineContinuation(src string) bool {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
61
|
+
for i := 0; i < len(src)-1; i++ {
|
|
62
|
+
if src[i] == '\\' {
|
|
63
|
+
next := src[i+1]
|
|
64
|
+
if next == '\n' || next == '\r' {
|
|
65
|
+
return true
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return false
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
// no-useless-concat: `"a" + "b"` of two literals. The compiler can't
|
|
@@ -77,27 +77,27 @@ type noUselessConcat struct{}
|
|
|
77
77
|
func (noUselessConcat) Name() string { return "no-useless-concat" }
|
|
78
78
|
func (noUselessConcat) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindBinaryExpression} }
|
|
79
79
|
func (noUselessConcat) Check(ctx *Context, node *shimast.Node) {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
80
|
+
expr := node.AsBinaryExpression()
|
|
81
|
+
if expr == nil || expr.OperatorToken == nil {
|
|
82
|
+
return
|
|
83
|
+
}
|
|
84
|
+
if expr.OperatorToken.Kind != shimast.KindPlusToken {
|
|
85
|
+
return
|
|
86
|
+
}
|
|
87
|
+
if isStringLikeLiteral(stripParens(expr.Left)) && isStringLikeLiteral(stripParens(expr.Right)) {
|
|
88
|
+
ctx.Report(node, "Unexpected string concatenation of literals.")
|
|
89
|
+
}
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
func isStringLikeLiteral(node *shimast.Node) bool {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
93
|
+
if node == nil {
|
|
94
|
+
return false
|
|
95
|
+
}
|
|
96
|
+
switch node.Kind {
|
|
97
|
+
case shimast.KindStringLiteral, shimast.KindNoSubstitutionTemplateLiteral:
|
|
98
|
+
return true
|
|
99
|
+
}
|
|
100
|
+
return false
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
// no-octal: `010` octal literal. Has to be opt-in via `0o10` in
|
|
@@ -108,18 +108,18 @@ type noOctal struct{}
|
|
|
108
108
|
func (noOctal) Name() string { return "no-octal" }
|
|
109
109
|
func (noOctal) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindNumericLiteral} }
|
|
110
110
|
func (noOctal) Check(ctx *Context, node *shimast.Node) {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
111
|
+
src := nodeText(ctx.File, node)
|
|
112
|
+
src = strings.TrimLeft(src, " \t")
|
|
113
|
+
if len(src) >= 2 && src[0] == '0' && isAsciiDigit(src[1]) {
|
|
114
|
+
ctx.Report(node, "Octal literals should not be used.")
|
|
115
|
+
}
|
|
116
116
|
}
|
|
117
117
|
|
|
118
118
|
func isAsciiDigit(b byte) bool { return b >= '0' && b <= '9' }
|
|
119
119
|
|
|
120
120
|
func init() {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
121
|
+
Register(noTemplateCurlyInString{})
|
|
122
|
+
Register(noMultiStr{})
|
|
123
|
+
Register(noUselessConcat{})
|
|
124
|
+
Register(noOctal{})
|
|
125
125
|
}
|