@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/plugin/engine.go CHANGED
@@ -18,50 +18,50 @@
18
18
  package main
19
19
 
20
20
  import (
21
- "sort"
21
+ "sort"
22
22
 
23
- shimast "github.com/microsoft/typescript-go/shim/ast"
24
- shimchecker "github.com/microsoft/typescript-go/shim/checker"
25
- shimscanner "github.com/microsoft/typescript-go/shim/scanner"
23
+ shimast "github.com/microsoft/typescript-go/shim/ast"
24
+ shimchecker "github.com/microsoft/typescript-go/shim/checker"
25
+ shimscanner "github.com/microsoft/typescript-go/shim/scanner"
26
26
  )
27
27
 
28
28
  // Rule is the contract every lint rule satisfies.
29
29
  type Rule interface {
30
- // Name is the identifier that users put in their `rules` map. Use
31
- // the same names as `eslint` / `@typescript-eslint` where possible —
32
- // this plugin is a host, not a renaming exercise.
33
- Name() string
30
+ // Name is the identifier that users put in their `rules` map. Use
31
+ // the same names as `eslint` / `@typescript-eslint` where possible —
32
+ // this plugin is a host, not a renaming exercise.
33
+ Name() string
34
34
 
35
- // Visits returns the AST kinds the rule cares about. The engine only
36
- // dispatches to rules that registered for the visited node's kind,
37
- // which keeps the per-node hot path linear in active rules rather
38
- // than total rules.
39
- Visits() []shimast.Kind
35
+ // Visits returns the AST kinds the rule cares about. The engine only
36
+ // dispatches to rules that registered for the visited node's kind,
37
+ // which keeps the per-node hot path linear in active rules rather
38
+ // than total rules.
39
+ Visits() []shimast.Kind
40
40
 
41
- // Check is invoked once per relevant node. Use `ctx.Report` to emit
42
- // findings.
43
- Check(ctx *Context, node *shimast.Node)
41
+ // Check is invoked once per relevant node. Use `ctx.Report` to emit
42
+ // findings.
43
+ Check(ctx *Context, node *shimast.Node)
44
44
  }
45
45
 
46
46
  // Context is the per-(file, rule) handle the engine passes to `Check`.
47
47
  type Context struct {
48
- File *shimast.SourceFile
49
- Checker *shimchecker.Checker
50
- Severity Severity
48
+ File *shimast.SourceFile
49
+ Checker *shimchecker.Checker
50
+ Severity Severity
51
51
 
52
- rule Rule
53
- collect func(*Finding)
52
+ rule Rule
53
+ collect func(*Finding)
54
54
  }
55
55
 
56
56
  // Finding is one rule-emitted diagnostic before it gets converted into a
57
57
  // driver Diagnostic.
58
58
  type Finding struct {
59
- Rule string
60
- Severity Severity
61
- File *shimast.SourceFile
62
- Pos int
63
- End int
64
- Message string
59
+ Rule string
60
+ Severity Severity
61
+ File *shimast.SourceFile
62
+ Pos int
63
+ End int
64
+ Message string
65
65
  }
66
66
 
67
67
  // Report records a finding at the given node's source range. The pos is
@@ -69,50 +69,49 @@ type Finding struct {
69
69
  // `path:line:col` banner points at the offending token, not the start of
70
70
  // the surrounding indentation. A finding is silently dropped if the
71
71
  // configured severity is `off` (defensive — the engine already filters
72
- // by severity before calling Check, but a rule might lazy-evaluate in
73
- // the future).
72
+ // by severity before calling Check, but Report is the final gate).
74
73
  func (c *Context) Report(node *shimast.Node, message string) {
75
- if c.Severity == SeverityOff || node == nil {
76
- return
77
- }
78
- pos := node.Pos()
79
- if c.File != nil {
80
- pos = shimscanner.SkipTrivia(c.File.Text(), pos)
81
- }
82
- c.collect(&Finding{
83
- Rule: c.rule.Name(),
84
- Severity: c.Severity,
85
- File: c.File,
86
- Pos: pos,
87
- End: node.End(),
88
- Message: message,
89
- })
74
+ if c.Severity == SeverityOff || node == nil {
75
+ return
76
+ }
77
+ pos := node.Pos()
78
+ if c.File != nil {
79
+ pos = shimscanner.SkipTrivia(c.File.Text(), pos)
80
+ }
81
+ c.collect(&Finding{
82
+ Rule: c.rule.Name(),
83
+ Severity: c.Severity,
84
+ File: c.File,
85
+ Pos: pos,
86
+ End: node.End(),
87
+ Message: message,
88
+ })
90
89
  }
91
90
 
92
91
  // ReportRange records a finding at an explicit byte range inside the
93
92
  // current file. Use this when the rule wants to highlight a sub-token of
94
93
  // a node (e.g. an operator inside a BinaryExpression).
95
94
  func (c *Context) ReportRange(pos, end int, message string) {
96
- if c.Severity == SeverityOff || c.File == nil {
97
- return
98
- }
99
- if end <= pos {
100
- end = pos + 1
101
- }
102
- c.collect(&Finding{
103
- Rule: c.rule.Name(),
104
- Severity: c.Severity,
105
- File: c.File,
106
- Pos: pos,
107
- End: end,
108
- Message: message,
109
- })
95
+ if c.Severity == SeverityOff || c.File == nil {
96
+ return
97
+ }
98
+ if end <= pos {
99
+ end = pos + 1
100
+ }
101
+ c.collect(&Finding{
102
+ Rule: c.rule.Name(),
103
+ Severity: c.Severity,
104
+ File: c.File,
105
+ Pos: pos,
106
+ End: end,
107
+ Message: message,
108
+ })
110
109
  }
111
110
 
112
111
  // registry stores the package-global rule list keyed by name. Tests can
113
112
  // also reach into it via `LookupRule`.
114
113
  type registry struct {
115
- rules map[string]Rule
114
+ rules map[string]Rule
116
115
  }
117
116
 
118
117
  var registered = &registry{rules: map[string]Rule{}}
@@ -120,13 +119,13 @@ var registered = &registry{rules: map[string]Rule{}}
120
119
  // Register adds a rule to the global registry. Called from each rule's
121
120
  // `init()`. Duplicate names are a programmer error and panic.
122
121
  func Register(rule Rule) {
123
- if rule == nil {
124
- panic("@ttsc/lint: Register called with nil rule")
125
- }
126
- if _, exists := registered.rules[rule.Name()]; exists {
127
- panic("@ttsc/lint: rule " + rule.Name() + " registered twice")
128
- }
129
- registered.rules[rule.Name()] = rule
122
+ if rule == nil {
123
+ panic("@ttsc/lint: Register called with nil rule")
124
+ }
125
+ if _, exists := registered.rules[rule.Name()]; exists {
126
+ panic("@ttsc/lint: rule " + rule.Name() + " registered twice")
127
+ }
128
+ registered.rules[rule.Name()] = rule
130
129
  }
131
130
 
132
131
  // LookupRule returns the registered rule by name, or nil if missing.
@@ -135,21 +134,21 @@ func LookupRule(name string) Rule { return registered.rules[name] }
135
134
  // AllRuleNames returns the registry sorted alphabetically. Useful for
136
135
  // `--list-rules` style introspection and stable test snapshots.
137
136
  func AllRuleNames() []string {
138
- names := make([]string, 0, len(registered.rules))
139
- for n := range registered.rules {
140
- names = append(names, n)
141
- }
142
- sort.Strings(names)
143
- return names
137
+ names := make([]string, 0, len(registered.rules))
138
+ for n := range registered.rules {
139
+ names = append(names, n)
140
+ }
141
+ sort.Strings(names)
142
+ return names
144
143
  }
145
144
 
146
145
  // Engine binds a rule configuration to a Program and walks the AST once
147
146
  // per source file, dispatching each visited node to its interested rules.
148
147
  type Engine struct {
149
- config RuleConfig
150
- rules map[shimast.Kind][]Rule
151
- enabled map[string]Severity
152
- unknown []string
148
+ config RuleConfig
149
+ rules map[shimast.Kind][]Rule
150
+ enabled map[string]Severity
151
+ unknown []string
153
152
  }
154
153
 
155
154
  // NewEngine returns an engine configured for `config`. Rules whose
@@ -157,27 +156,27 @@ type Engine struct {
157
156
  // an unknown rule are recorded so the caller can surface them as a
158
157
  // configuration warning rather than a silent typo.
159
158
  func NewEngine(config RuleConfig) *Engine {
160
- eng := &Engine{
161
- config: config,
162
- rules: make(map[shimast.Kind][]Rule),
163
- enabled: make(map[string]Severity),
164
- }
165
- for name, sev := range config {
166
- rule, ok := registered.rules[name]
167
- if !ok {
168
- eng.unknown = append(eng.unknown, name)
169
- continue
170
- }
171
- if sev == SeverityOff {
172
- continue
173
- }
174
- eng.enabled[name] = sev
175
- for _, kind := range rule.Visits() {
176
- eng.rules[kind] = append(eng.rules[kind], rule)
177
- }
178
- }
179
- sort.Strings(eng.unknown)
180
- return eng
159
+ eng := &Engine{
160
+ config: config,
161
+ rules: make(map[shimast.Kind][]Rule),
162
+ enabled: make(map[string]Severity),
163
+ }
164
+ for name, sev := range config {
165
+ rule, ok := registered.rules[name]
166
+ if !ok {
167
+ eng.unknown = append(eng.unknown, name)
168
+ continue
169
+ }
170
+ if sev == SeverityOff {
171
+ continue
172
+ }
173
+ eng.enabled[name] = sev
174
+ for _, kind := range rule.Visits() {
175
+ eng.rules[kind] = append(eng.rules[kind], rule)
176
+ }
177
+ }
178
+ sort.Strings(eng.unknown)
179
+ return eng
181
180
  }
182
181
 
183
182
  // UnknownRules returns the names of rules that appeared in the config but
@@ -191,69 +190,69 @@ func (e *Engine) EnabledRules() map[string]Severity { return e.enabled }
191
190
  // Run walks every non-declaration source file in the program and
192
191
  // returns the collected findings.
193
192
  func (e *Engine) Run(files []*shimast.SourceFile, checker *shimchecker.Checker) []*Finding {
194
- var findings []*Finding
195
- for _, file := range files {
196
- if file == nil || file.IsDeclarationFile {
197
- continue
198
- }
199
- findings = append(findings, e.runFile(file, checker)...)
200
- }
201
- return findings
193
+ var findings []*Finding
194
+ for _, file := range files {
195
+ if file == nil || file.IsDeclarationFile {
196
+ continue
197
+ }
198
+ findings = append(findings, e.runFile(file, checker)...)
199
+ }
200
+ return findings
202
201
  }
203
202
 
204
203
  // runFile is the per-file driver. The visitor is allocated once per file
205
204
  // to keep the per-node hot path branch-free; it visits children
206
205
  // post-order so parents see their already-checked subtrees.
207
206
  func (e *Engine) runFile(file *shimast.SourceFile, checker *shimchecker.Checker) []*Finding {
208
- var collected []*Finding
209
- collect := func(f *Finding) { collected = append(collected, f) }
207
+ var collected []*Finding
208
+ collect := func(f *Finding) { collected = append(collected, f) }
210
209
 
211
- var walk func(node *shimast.Node)
212
- walk = func(node *shimast.Node) {
213
- if node == nil {
214
- return
215
- }
216
- if rules, ok := e.rules[node.Kind]; ok {
217
- for _, rule := range rules {
218
- ctx := &Context{
219
- File: file,
220
- Checker: checker,
221
- Severity: e.enabled[rule.Name()],
222
- rule: rule,
223
- collect: collect,
224
- }
225
- rule.Check(ctx, node)
226
- }
227
- }
228
- node.ForEachChild(func(child *shimast.Node) bool {
229
- walk(child)
230
- return false // visit every child
231
- })
232
- }
210
+ var walk func(node *shimast.Node)
211
+ walk = func(node *shimast.Node) {
212
+ if node == nil {
213
+ return
214
+ }
215
+ if rules, ok := e.rules[node.Kind]; ok {
216
+ for _, rule := range rules {
217
+ ctx := &Context{
218
+ File: file,
219
+ Checker: checker,
220
+ Severity: e.enabled[rule.Name()],
221
+ rule: rule,
222
+ collect: collect,
223
+ }
224
+ rule.Check(ctx, node)
225
+ }
226
+ }
227
+ node.ForEachChild(func(child *shimast.Node) bool {
228
+ walk(child)
229
+ return false // visit every child
230
+ })
231
+ }
233
232
 
234
- // SourceFile dispatches into its statement list directly; we walk
235
- // statements explicitly so the file node itself can be inspected by
236
- // rules (e.g., `ban-ts-comment` reads CommentDirectives off the
237
- // SourceFile).
238
- if rules, ok := e.rules[shimast.KindSourceFile]; ok {
239
- for _, rule := range rules {
240
- ctx := &Context{
241
- File: file,
242
- Checker: checker,
243
- Severity: e.enabled[rule.Name()],
244
- rule: rule,
245
- collect: collect,
246
- }
247
- rule.Check(ctx, file.AsNode())
248
- }
249
- }
233
+ // SourceFile dispatches into its statement list directly; we walk
234
+ // statements explicitly so the file node itself can be inspected by
235
+ // rules (e.g., `ban-ts-comment` reads CommentDirectives off the
236
+ // SourceFile).
237
+ if rules, ok := e.rules[shimast.KindSourceFile]; ok {
238
+ for _, rule := range rules {
239
+ ctx := &Context{
240
+ File: file,
241
+ Checker: checker,
242
+ Severity: e.enabled[rule.Name()],
243
+ rule: rule,
244
+ collect: collect,
245
+ }
246
+ rule.Check(ctx, file.AsNode())
247
+ }
248
+ }
250
249
 
251
- statements := file.Statements
252
- if statements == nil {
253
- return collected
254
- }
255
- for _, stmt := range statements.Nodes {
256
- walk(stmt)
257
- }
258
- return collected
250
+ statements := file.Statements
251
+ if statements == nil {
252
+ return collected
253
+ }
254
+ for _, stmt := range statements.Nodes {
255
+ walk(stmt)
256
+ }
257
+ return collected
259
258
  }