@ttsc/lint 0.7.2 → 0.8.0-dev.20260505

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.
@@ -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
- 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
- }
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
- 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
- }
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
- 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
- }
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
- 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
- }
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
- 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 ""
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
- 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 ""
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
- Register(noDuplicateCase{})
177
- Register(noDupeKeys{})
178
- Register(noDupeArgs{})
176
+ Register(noDuplicateCase{})
177
+ Register(noDupeKeys{})
178
+ Register(noDupeArgs{})
179
179
  }
@@ -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
- 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.")
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
- 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
- }
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
- 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
- }
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
- return []shimast.Kind{shimast.KindObjectBindingPattern, shimast.KindArrayBindingPattern}
70
+ return []shimast.Kind{shimast.KindObjectBindingPattern, shimast.KindArrayBindingPattern}
71
71
  }
72
72
  func (noEmptyPattern) Check(ctx *Context, node *shimast.Node) {
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
- }
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
- 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
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
- Register(noEmpty{})
108
- Register(noEmptyFunction{})
109
- Register(noEmptyPattern{})
107
+ Register(noEmpty{})
108
+ Register(noEmptyFunction{})
109
+ Register(noEmptyPattern{})
110
110
  }
@@ -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
- call := node.AsCallExpression()
15
- if call == nil {
16
- return
17
- }
18
- if callCalleeName(call) == "eval" {
19
- ctx.Report(node, "eval can be harmful.")
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
- return []shimast.Kind{shimast.KindStringLiteral, shimast.KindNoSubstitutionTemplateLiteral}
30
+ return []shimast.Kind{shimast.KindStringLiteral, shimast.KindNoSubstitutionTemplateLiteral}
31
31
  }
32
32
  func (noScriptURL) Check(ctx *Context, node *shimast.Node) {
33
- text := stringLiteralText(node)
34
- if isJavaScriptURL(text) {
35
- ctx.Report(node, "Script URL is a form of eval.")
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
- 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
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
- Register(noEval{})
58
- Register(noScriptURL{})
57
+ Register(noEval{})
58
+ Register(noScriptURL{})
59
59
  }