@ttsc/lint 0.18.3 → 0.19.0
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 +95 -43
- package/go.mod +5 -4
- package/internal/rulecode/rulecode.go +88 -0
- package/lib/index.d.ts +10 -1
- package/lib/index.js +45 -17
- package/lib/index.js.map +1 -1
- package/lib/structures/ITtscLintConfig.d.ts +3 -0
- package/lib/structures/TtscLintRuleSetting.d.ts +3 -2
- package/lib/structures/format/ITtscLintFormat.d.ts +3 -2
- package/lib/structures/format/ITtscLintFormatSortImports.d.ts +20 -4
- package/lib/structures/rules/ITtscLintBoundariesRuleOptions.d.ts +80 -8
- package/lib/structures/rules/ITtscLintBoundariesRules.d.ts +6 -8
- package/lib/structures/rules/ITtscLintContributorRules.d.ts +18 -2
- package/lib/structures/rules/ITtscLintCoreRuleOptions.d.ts +347 -0
- package/lib/structures/rules/ITtscLintCoreRuleOptions.js +3 -0
- package/lib/structures/rules/ITtscLintCoreRuleOptions.js.map +1 -0
- package/lib/structures/rules/ITtscLintCoreRules.d.ts +101 -67
- package/lib/structures/rules/ITtscLintRegexpRules.d.ts +7 -2
- package/lib/structures/rules/ITtscLintRuleOptionsMap.d.ts +53 -7
- package/lib/structures/rules/ITtscLintRules.d.ts +6 -2
- package/lib/structures/rules/ITtscLintSolidRules.d.ts +0 -8
- package/lib/structures/rules/ITtscLintTypeScriptRuleOptions.d.ts +206 -0
- package/lib/structures/rules/ITtscLintTypeScriptRuleOptions.js +8 -0
- package/lib/structures/rules/ITtscLintTypeScriptRuleOptions.js.map +1 -0
- package/lib/structures/rules/ITtscLintTypeScriptRules.d.ts +55 -42
- package/lib/structures/rules/ITtscLintUnicornRuleOptions.d.ts +276 -0
- package/lib/structures/rules/ITtscLintUnicornRuleOptions.js +3 -0
- package/lib/structures/rules/ITtscLintUnicornRuleOptions.js.map +1 -0
- package/lib/structures/rules/ITtscLintUnicornRules.d.ts +44 -36
- package/lib/structures/rules/index.d.ts +3 -0
- package/lib/structures/rules/index.js +3 -0
- package/lib/structures/rules/index.js.map +1 -1
- package/linthost/ast_helpers.go +521 -23
- package/linthost/ast_selector_matcher.go +1628 -0
- package/linthost/ast_selector_parser.go +628 -0
- package/linthost/comment_scan.go +154 -0
- package/linthost/compile.go +129 -79
- package/linthost/config.go +352 -88
- package/linthost/config_format.go +15 -1
- package/linthost/contrib_adapter.go +129 -25
- package/linthost/declaration_rules.go +12 -11
- package/linthost/directives.go +9 -67
- package/linthost/dispatch.go +37 -3
- package/linthost/engine.go +368 -53
- package/linthost/escape_string.go +77 -0
- package/linthost/fix.go +133 -23
- package/linthost/flags_gen.go +18 -17
- package/linthost/format.go +73 -167
- package/linthost/format_editor_settings.go +134 -26
- package/linthost/grapheme_tables_gen.go +1920 -0
- package/linthost/host.go +160 -61
- package/linthost/literal_escape_scan.go +117 -0
- package/linthost/lsp.go +545 -98
- package/linthost/numeric_literal_precision.go +228 -0
- package/linthost/polyfill_browserslist.go +1439 -0
- package/linthost/polyfill_browserslist_config.go +603 -0
- package/linthost/polyfill_corejs.go +750 -0
- package/linthost/polyfill_data.go +113 -0
- package/linthost/polyfill_data_gen.json +39868 -0
- package/linthost/print_nodes_array.go +7 -1
- package/linthost/print_nodes_object.go +7 -1
- package/linthost/project_engine.go +151 -0
- package/linthost/project_rules.go +78 -0
- package/linthost/regex_clean.go +191 -0
- package/linthost/regex_tree.go +1544 -0
- package/linthost/regex_tree_optimizer.go +1685 -0
- package/linthost/regexp_cache.go +44 -0
- package/linthost/rule_codes.go +105 -0
- package/linthost/rule_codes.json +745 -0
- package/linthost/rules_ban_ts_comment.go +312 -0
- package/linthost/rules_boundaries.go +128 -43
- package/linthost/rules_boundaries_dependencies.go +1013 -0
- package/linthost/rules_core_extra.go +62 -92
- package/linthost/rules_cypress.go +1 -1
- package/linthost/rules_default_case.go +87 -11
- package/linthost/rules_dupes.go +38 -13
- package/linthost/rules_empty.go +188 -20
- package/linthost/rules_escape.go +62 -48
- package/linthost/rules_format_arrow_parens.go +19 -5
- package/linthost/rules_format_bracket_spacing.go +1 -1
- package/linthost/rules_format_clause_join.go +1 -1
- package/linthost/rules_format_declaration_header.go +31 -19
- package/linthost/rules_format_indent.go +1 -1
- package/linthost/rules_format_jsdoc.go +1 -1
- package/linthost/rules_format_orphan_semi.go +1 -1
- package/linthost/rules_format_parameter_properties.go +17 -7
- package/linthost/rules_format_print_width.go +1 -1
- package/linthost/rules_format_quote_props.go +1 -1
- package/linthost/rules_format_quotes.go +1 -1
- package/linthost/rules_format_semi.go +106 -79
- package/linthost/rules_format_sort_imports.go +79 -45
- package/linthost/rules_format_statement_split.go +1 -1
- package/linthost/rules_format_ternary_nullish_parens.go +1 -1
- package/linthost/rules_format_trailing_comma.go +64 -1
- package/linthost/rules_format_whitespace.go +1 -1
- package/linthost/rules_functional.go +21 -22
- package/linthost/rules_gap.go +80 -3
- package/linthost/rules_grouped_accessor_pairs.go +84 -25
- package/linthost/rules_guard_for_in.go +64 -147
- package/linthost/rules_jsx_a11y.go +232 -22
- package/linthost/rules_logic.go +84 -38
- package/linthost/rules_nextjs.go +105 -16
- package/linthost/rules_no_class_assign.go +70 -0
- package/linthost/rules_no_duplicate_imports.go +266 -0
- package/linthost/rules_no_else_return.go +156 -38
- package/linthost/rules_no_extend_native.go +113 -17
- package/linthost/rules_no_fallthrough.go +1013 -0
- package/linthost/rules_no_func_assign.go +70 -0
- package/linthost/rules_no_import_assign.go +278 -86
- package/linthost/rules_no_inner_declarations.go +186 -0
- package/linthost/rules_no_loop_func.go +438 -53
- package/linthost/rules_no_mixed_operators.go +208 -52
- package/linthost/rules_no_param_reassign.go +376 -61
- package/linthost/rules_no_promise_executor_return.go +156 -0
- package/linthost/rules_no_restricted_imports.go +1013 -57
- package/linthost/rules_no_restricted_syntax.go +190 -26
- package/linthost/rules_no_useless_constructor.go +6 -36
- package/linthost/rules_problems.go +154 -311
- package/linthost/rules_promise.go +2578 -109
- package/linthost/rules_react_extras.go +71 -45
- package/linthost/rules_react_hooks.go +18 -2
- package/linthost/rules_react_perf.go +1 -0
- package/linthost/rules_react_refresh.go +1 -1
- package/linthost/rules_regexp.go +230 -14
- package/linthost/rules_security.go +45 -19
- package/linthost/rules_self.go +21 -1
- package/linthost/rules_solid.go +13 -7
- package/linthost/rules_storybook.go +1 -1
- package/linthost/rules_suggestions.go +490 -84
- package/linthost/rules_testing_library.go +12 -1
- package/linthost/rules_ts.go +227 -54
- package/linthost/rules_ts_async.go +0 -131
- package/linthost/rules_ts_no_base_to_string.go +3 -4
- package/linthost/rules_ts_no_misused_promises.go +817 -0
- package/linthost/rules_ts_no_restricted_types.go +320 -34
- package/linthost/rules_ts_no_unnecessary_qualifier.go +132 -55
- package/linthost/rules_ts_no_unsafe_assignment.go +608 -60
- package/linthost/rules_ts_require_array_sort_compare.go +5 -5
- package/linthost/rules_ts_switch_exhaustiveness_check.go +556 -86
- package/linthost/rules_unicorn_better_regex.go +224 -16
- package/linthost/rules_unicorn_consistent_destructuring.go +1002 -7
- package/linthost/rules_unicorn_consistent_existence_index_check.go +208 -29
- package/linthost/rules_unicorn_consistent_function_scoping.go +1041 -8
- package/linthost/rules_unicorn_consistent_template_literal_escape.go +178 -7
- package/linthost/rules_unicorn_escape_case.go +30 -19
- package/linthost/rules_unicorn_filename_case.go +763 -16
- package/linthost/rules_unicorn_import_style.go +783 -17
- package/linthost/rules_unicorn_isolated_functions.go +934 -14
- package/linthost/rules_unicorn_no_hex_escape.go +26 -11
- package/linthost/rules_unicorn_no_typeof_undefined.go +234 -25
- package/linthost/rules_unicorn_no_unnecessary_polyfills.go +376 -21
- package/linthost/rules_unicorn_no_unused_properties.go +756 -19
- package/linthost/rules_unicorn_number_literal_case.go +58 -39
- package/linthost/rules_unicorn_prefer_at.go +7 -3
- package/linthost/rules_unicorn_prefer_number_properties.go +381 -48
- package/linthost/rules_unicorn_prefer_optional_catch_binding.go +24 -24
- package/linthost/rules_unicorn_prefer_simple_condition_first.go +597 -19
- package/linthost/rules_unicorn_prefer_string_raw.go +147 -16
- package/linthost/rules_unicorn_prevent_abbreviations.go +1963 -95
- package/linthost/rules_unicorn_string_content.go +611 -15
- package/linthost/rules_unicorn_switch_case_break_position.go +172 -15
- package/linthost/rules_unicorn_template_indent.go +923 -18
- package/linthost/rules_unicorn_text_encoding_identifier_case.go +202 -42
- package/linthost/rules_unicorn_throw_new_error.go +218 -26
- package/linthost/rules_var.go +829 -142
- package/linthost/string_length.go +222 -0
- package/package.json +4 -3
- package/rule/project.go +145 -0
- package/rule/rule.go +58 -1
- package/src/index.ts +58 -17
- package/src/structures/ITtscLintConfig.ts +3 -0
- package/src/structures/TtscLintRuleSetting.ts +3 -2
- package/src/structures/format/ITtscLintFormat.ts +3 -2
- package/src/structures/format/ITtscLintFormatSortImports.ts +21 -4
- package/src/structures/rules/ITtscLintBoundariesRuleOptions.ts +122 -8
- package/src/structures/rules/ITtscLintBoundariesRules.ts +6 -8
- package/src/structures/rules/ITtscLintContributorRules.ts +18 -2
- package/src/structures/rules/ITtscLintCoreRuleOptions.ts +465 -0
- package/src/structures/rules/ITtscLintCoreRules.ts +122 -67
- package/src/structures/rules/ITtscLintRegexpRules.ts +7 -2
- package/src/structures/rules/ITtscLintRuleOptionsMap.ts +88 -7
- package/src/structures/rules/ITtscLintRules.ts +7 -2
- package/src/structures/rules/ITtscLintSolidRules.ts +0 -9
- package/src/structures/rules/ITtscLintTypeScriptRuleOptions.ts +248 -0
- package/src/structures/rules/ITtscLintTypeScriptRules.ts +64 -42
- package/src/structures/rules/ITtscLintUnicornRuleOptions.ts +350 -0
- package/src/structures/rules/ITtscLintUnicornRules.ts +60 -36
- package/src/structures/rules/index.ts +3 -0
|
@@ -1,85 +1,470 @@
|
|
|
1
|
-
// noLoopFunc reports
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
// (timer, microtask, event handler) observes whatever the binding holds
|
|
6
|
-
// at call time — usually the loop's terminal value, never the value the
|
|
7
|
-
// developer reasoned about at the textual position.
|
|
1
|
+
// noLoopFunc reports closures created in loops only when they capture a
|
|
2
|
+
// binding whose value can change between iterations. The analysis mirrors
|
|
3
|
+
// ESLint's reference semantics while using TypeScript-Go symbols to preserve
|
|
4
|
+
// lexical identity through shadowing, destructuring, and TypeScript syntax.
|
|
8
5
|
// https://eslint.org/docs/latest/rules/no-loop-func
|
|
9
|
-
//
|
|
10
|
-
// Conservative baseline: every function-like syntactically inside a loop
|
|
11
|
-
// body is flagged. Capture analysis (the rule's full upstream
|
|
12
|
-
// implementation only fires when the function actually closes over a
|
|
13
|
-
// `let` / `var` / `const` declared outside) is deferred; the false-
|
|
14
|
-
// positive rate is acceptable because the fix — hoist the function out
|
|
15
|
-
// of the loop — is almost always the cleaner code in either case.
|
|
16
|
-
//
|
|
17
|
-
// The walk stops at nested loop boundaries — each enclosing loop visits
|
|
18
|
-
// itself once, so an inner loop reports its own nested function-likes
|
|
19
|
-
// and the outer loop only owns the function-likes directly inside it.
|
|
20
|
-
// Inner function-likes are also boundaries: a closure inside an outer
|
|
21
|
-
// function-likes is that inner scope's problem.
|
|
22
6
|
package linthost
|
|
23
7
|
|
|
24
8
|
import (
|
|
9
|
+
"strings"
|
|
10
|
+
|
|
25
11
|
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
26
12
|
)
|
|
27
13
|
|
|
28
14
|
type noLoopFunc struct{}
|
|
29
15
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
16
|
+
type noLoopFuncReference struct {
|
|
17
|
+
node *shimast.Node
|
|
18
|
+
symbol *shimast.Symbol
|
|
19
|
+
scope *shimast.Node
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
type noLoopFuncBindingKind uint8
|
|
23
|
+
|
|
24
|
+
const (
|
|
25
|
+
noLoopFuncOtherBinding noLoopFuncBindingKind = iota
|
|
26
|
+
noLoopFuncLetBinding
|
|
27
|
+
noLoopFuncConstantBinding
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
type noLoopFuncBinding struct {
|
|
31
|
+
rangeNode *shimast.Node
|
|
32
|
+
scope *shimast.Node
|
|
33
|
+
kind noLoopFuncBindingKind
|
|
39
34
|
}
|
|
35
|
+
|
|
36
|
+
type noLoopFuncAnalysis struct {
|
|
37
|
+
ctx *Context
|
|
38
|
+
declarationIdentifiers map[*shimast.Node]struct{}
|
|
39
|
+
writeIdentifiers map[*shimast.Node]struct{}
|
|
40
|
+
functionReferences map[*shimast.Node][]noLoopFuncReference
|
|
41
|
+
writesBySymbol map[*shimast.Symbol][]noLoopFuncReference
|
|
42
|
+
bindings map[*shimast.Symbol]noLoopFuncBinding
|
|
43
|
+
skippedIIFEs map[*shimast.Node]struct{}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
func (noLoopFunc) Name() string { return "no-loop-func" }
|
|
47
|
+
func (noLoopFunc) NeedsTypeChecker() bool { return true }
|
|
48
|
+
func (noLoopFunc) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindSourceFile} }
|
|
40
49
|
func (noLoopFunc) Check(ctx *Context, node *shimast.Node) {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
50
|
+
if ctx.Checker == nil || node == nil {
|
|
51
|
+
return
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
analysis := &noLoopFuncAnalysis{
|
|
55
|
+
ctx: ctx,
|
|
56
|
+
declarationIdentifiers: make(map[*shimast.Node]struct{}),
|
|
57
|
+
writeIdentifiers: make(map[*shimast.Node]struct{}),
|
|
58
|
+
functionReferences: make(map[*shimast.Node][]noLoopFuncReference),
|
|
59
|
+
writesBySymbol: make(map[*shimast.Symbol][]noLoopFuncReference),
|
|
60
|
+
bindings: make(map[*shimast.Symbol]noLoopFuncBinding),
|
|
61
|
+
skippedIIFEs: make(map[*shimast.Node]struct{}),
|
|
62
|
+
}
|
|
63
|
+
analysis.collectSyntax(node)
|
|
64
|
+
analysis.collectReferences(node)
|
|
65
|
+
analysis.checkFunctions(node)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
func (a *noLoopFuncAnalysis) collectSyntax(source *shimast.Node) {
|
|
69
|
+
walkDescendants(source, func(node *shimast.Node) {
|
|
70
|
+
if node.Kind != shimast.KindShorthandPropertyAssignment {
|
|
71
|
+
for _, identifier := range noLoopFuncNamedIdentifiers(node.Name()) {
|
|
72
|
+
a.declarationIdentifiers[identifier] = struct{}{}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
for _, identifier := range writeTargetIdentifiers(node) {
|
|
77
|
+
a.writeIdentifiers[identifier] = struct{}{}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if node.Kind != shimast.KindVariableDeclaration {
|
|
44
81
|
return
|
|
45
82
|
}
|
|
46
|
-
|
|
47
|
-
|
|
83
|
+
declaration := node.AsVariableDeclaration()
|
|
84
|
+
if declaration == nil {
|
|
48
85
|
return
|
|
49
86
|
}
|
|
50
|
-
|
|
51
|
-
|
|
87
|
+
initialized := declaration.Initializer != nil
|
|
88
|
+
if !initialized && node.Parent != nil && node.Parent.Kind == shimast.KindVariableDeclarationList &&
|
|
89
|
+
node.Parent.Parent != nil &&
|
|
90
|
+
(node.Parent.Parent.Kind == shimast.KindForInStatement || node.Parent.Parent.Kind == shimast.KindForOfStatement) {
|
|
91
|
+
statement := node.Parent.Parent.AsForInOrOfStatement()
|
|
92
|
+
initialized = statement != nil && statement.Initializer == node.Parent
|
|
93
|
+
}
|
|
94
|
+
if initialized {
|
|
95
|
+
for _, identifier := range bindingIdentifierNodes(declaration.Name()) {
|
|
96
|
+
a.writeIdentifiers[identifier] = struct{}{}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
})
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// noLoopFuncNamedIdentifiers returns identifier-shaped names without asking
|
|
103
|
+
// the AST to reinterpret arbitrary declaration names as binding patterns.
|
|
104
|
+
// Computed names contain evaluated expressions and must remain available to
|
|
105
|
+
// reference analysis, while ordinary named declarations and members are not
|
|
106
|
+
// runtime references to their own spelling.
|
|
107
|
+
func noLoopFuncNamedIdentifiers(name *shimast.Node) []*shimast.Node {
|
|
108
|
+
if name == nil {
|
|
109
|
+
return nil
|
|
110
|
+
}
|
|
111
|
+
switch name.Kind {
|
|
112
|
+
case shimast.KindIdentifier:
|
|
113
|
+
return []*shimast.Node{name}
|
|
114
|
+
case shimast.KindObjectBindingPattern, shimast.KindArrayBindingPattern:
|
|
115
|
+
return bindingIdentifierNodes(name)
|
|
116
|
+
}
|
|
117
|
+
return nil
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
func (a *noLoopFuncAnalysis) collectReferences(source *shimast.Node) {
|
|
121
|
+
walkDescendants(source, func(node *shimast.Node) {
|
|
122
|
+
if node.Kind != shimast.KindIdentifier {
|
|
52
123
|
return
|
|
53
124
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
125
|
+
|
|
126
|
+
if _, written := a.writeIdentifiers[node]; written {
|
|
127
|
+
if symbol := canonicalValueSymbol(a.ctx, node); symbol != nil {
|
|
128
|
+
a.writesBySymbol[symbol] = append(a.writesBySymbol[symbol], noLoopFuncReference{
|
|
129
|
+
node: node,
|
|
130
|
+
symbol: symbol,
|
|
131
|
+
scope: noLoopFuncVariableScope(node),
|
|
132
|
+
})
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if !a.isRuntimeReference(node) {
|
|
137
|
+
return
|
|
138
|
+
}
|
|
139
|
+
symbol := canonicalValueSymbol(a.ctx, node)
|
|
140
|
+
if symbol == nil {
|
|
141
|
+
return
|
|
142
|
+
}
|
|
143
|
+
reference := noLoopFuncReference{
|
|
144
|
+
node: node,
|
|
145
|
+
symbol: symbol,
|
|
146
|
+
scope: noLoopFuncVariableScope(node),
|
|
147
|
+
}
|
|
148
|
+
for ancestor := node.Parent; ancestor != nil; ancestor = ancestor.Parent {
|
|
149
|
+
if noLoopFuncIsCheckedFunction(ancestor) && noLoopFuncReferenceBelongsToFunction(node, ancestor) {
|
|
150
|
+
a.functionReferences[ancestor] = append(a.functionReferences[ancestor], reference)
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
})
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
func (a *noLoopFuncAnalysis) isRuntimeReference(node *shimast.Node) bool {
|
|
157
|
+
if _, declaration := a.declarationIdentifiers[node]; declaration {
|
|
158
|
+
return false
|
|
159
|
+
}
|
|
160
|
+
if shimast.IsPartOfTypeNode(node) || noLoopFuncInsideTypeQuery(node) {
|
|
161
|
+
return false
|
|
58
162
|
}
|
|
59
|
-
node
|
|
60
|
-
walk(child)
|
|
163
|
+
if !isValueReferenceIdentifier(node) {
|
|
61
164
|
return false
|
|
165
|
+
}
|
|
166
|
+
parent := node.Parent
|
|
167
|
+
if parent != nil && parent.Kind == shimast.KindBindingElement {
|
|
168
|
+
binding := parent.AsBindingElement()
|
|
169
|
+
if binding != nil && binding.PropertyName == node {
|
|
170
|
+
return false
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return true
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
func (a *noLoopFuncAnalysis) checkFunctions(source *shimast.Node) {
|
|
177
|
+
walkDescendants(source, func(function *shimast.Node) {
|
|
178
|
+
if !noLoopFuncIsCheckedFunction(function) {
|
|
179
|
+
return
|
|
180
|
+
}
|
|
181
|
+
loop := noLoopFuncContainingLoop(function, a.skippedIIFEs)
|
|
182
|
+
if loop == nil {
|
|
183
|
+
return
|
|
184
|
+
}
|
|
185
|
+
if noLoopFuncIsEligibleIIFE(function) && !a.isReferencedNamedIIFE(function) {
|
|
186
|
+
a.skippedIIFEs[function] = struct{}{}
|
|
187
|
+
return
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
unsafeNames := make([]string, 0)
|
|
191
|
+
seenNames := make(map[string]struct{})
|
|
192
|
+
safety := make(map[*shimast.Symbol]bool)
|
|
193
|
+
for _, reference := range a.functionReferences[function] {
|
|
194
|
+
if a.symbolDeclaredInside(reference.symbol, function) {
|
|
195
|
+
continue
|
|
196
|
+
}
|
|
197
|
+
safe, exists := safety[reference.symbol]
|
|
198
|
+
if !exists {
|
|
199
|
+
safe = a.isSafe(loop, reference.symbol)
|
|
200
|
+
safety[reference.symbol] = safe
|
|
201
|
+
}
|
|
202
|
+
if safe {
|
|
203
|
+
continue
|
|
204
|
+
}
|
|
205
|
+
name := identifierText(reference.node)
|
|
206
|
+
if name == "" {
|
|
207
|
+
continue
|
|
208
|
+
}
|
|
209
|
+
if _, exists := seenNames[name]; exists {
|
|
210
|
+
continue
|
|
211
|
+
}
|
|
212
|
+
seenNames[name] = struct{}{}
|
|
213
|
+
unsafeNames = append(unsafeNames, name)
|
|
214
|
+
}
|
|
215
|
+
if len(unsafeNames) == 0 {
|
|
216
|
+
return
|
|
217
|
+
}
|
|
218
|
+
quoted := make([]string, len(unsafeNames))
|
|
219
|
+
for index, name := range unsafeNames {
|
|
220
|
+
quoted[index] = "'" + name + "'"
|
|
221
|
+
}
|
|
222
|
+
a.ctx.Report(function, "Function declared in a loop contains unsafe references to variable(s) "+strings.Join(quoted, ", ")+".")
|
|
62
223
|
})
|
|
63
224
|
}
|
|
64
225
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
// loop reports only the function-likes directly inside its own body.
|
|
68
|
-
func isLoopKind(n *shimast.Node) bool {
|
|
69
|
-
if n == nil {
|
|
226
|
+
func (a *noLoopFuncAnalysis) isReferencedNamedIIFE(function *shimast.Node) bool {
|
|
227
|
+
if function.Kind != shimast.KindFunctionExpression {
|
|
70
228
|
return false
|
|
71
229
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
230
|
+
name := function.Name()
|
|
231
|
+
if name == nil || name.Kind != shimast.KindIdentifier {
|
|
232
|
+
return false
|
|
233
|
+
}
|
|
234
|
+
symbol := canonicalValueSymbol(a.ctx, name)
|
|
235
|
+
if symbol == nil {
|
|
236
|
+
return false
|
|
237
|
+
}
|
|
238
|
+
for _, reference := range a.functionReferences[function] {
|
|
239
|
+
if reference.symbol == symbol {
|
|
240
|
+
return true
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
return false
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
func (a *noLoopFuncAnalysis) symbolDeclaredInside(symbol *shimast.Symbol, function *shimast.Node) bool {
|
|
247
|
+
for _, declaration := range symbol.Declarations {
|
|
248
|
+
if declaration != nil && declaration.Pos() >= function.Pos() && declaration.End() <= function.End() {
|
|
249
|
+
return true
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
return false
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
func (a *noLoopFuncAnalysis) isSafe(loop *shimast.Node, symbol *shimast.Symbol) bool {
|
|
256
|
+
binding := a.binding(symbol)
|
|
257
|
+
if binding.kind == noLoopFuncConstantBinding {
|
|
78
258
|
return true
|
|
79
259
|
}
|
|
260
|
+
if binding.kind == noLoopFuncLetBinding && binding.rangeNode != nil &&
|
|
261
|
+
binding.rangeNode.Pos() > loop.Pos() && binding.rangeNode.End() < loop.End() {
|
|
262
|
+
return true
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
var excluded *shimast.Node
|
|
266
|
+
if binding.kind == noLoopFuncLetBinding {
|
|
267
|
+
excluded = binding.rangeNode
|
|
268
|
+
}
|
|
269
|
+
border := noLoopFuncTopLoop(loop, excluded, a.skippedIIFEs)
|
|
270
|
+
for _, write := range a.writesBySymbol[symbol] {
|
|
271
|
+
if write.scope != binding.scope || write.node.Pos() >= border.Pos() {
|
|
272
|
+
return false
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
return true
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
func (a *noLoopFuncAnalysis) binding(symbol *shimast.Symbol) noLoopFuncBinding {
|
|
279
|
+
if binding, exists := a.bindings[symbol]; exists {
|
|
280
|
+
return binding
|
|
281
|
+
}
|
|
282
|
+
binding := noLoopFuncBinding{kind: noLoopFuncOtherBinding}
|
|
283
|
+
declarations := symbol.Declarations
|
|
284
|
+
if symbol.ValueDeclaration != nil {
|
|
285
|
+
declarations = append([]*shimast.Node{symbol.ValueDeclaration}, declarations...)
|
|
286
|
+
}
|
|
287
|
+
for _, declaration := range declarations {
|
|
288
|
+
root := noLoopFuncRootVariableDeclaration(declaration)
|
|
289
|
+
if root == nil || root.Kind != shimast.KindVariableDeclaration {
|
|
290
|
+
if binding.rangeNode == nil && declaration != nil {
|
|
291
|
+
binding.rangeNode = declaration
|
|
292
|
+
binding.scope = noLoopFuncVariableScope(declaration)
|
|
293
|
+
}
|
|
294
|
+
continue
|
|
295
|
+
}
|
|
296
|
+
binding.rangeNode = root
|
|
297
|
+
if root.Parent != nil && root.Parent.Kind == shimast.KindVariableDeclarationList {
|
|
298
|
+
binding.rangeNode = root.Parent
|
|
299
|
+
}
|
|
300
|
+
binding.scope = noLoopFuncVariableScope(root)
|
|
301
|
+
flags := shimast.GetCombinedNodeFlags(root)
|
|
302
|
+
switch {
|
|
303
|
+
case flags&shimast.NodeFlagsConstant != 0:
|
|
304
|
+
binding.kind = noLoopFuncConstantBinding
|
|
305
|
+
case flags&shimast.NodeFlagsLet != 0:
|
|
306
|
+
binding.kind = noLoopFuncLetBinding
|
|
307
|
+
}
|
|
308
|
+
break
|
|
309
|
+
}
|
|
310
|
+
a.bindings[symbol] = binding
|
|
311
|
+
return binding
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
func noLoopFuncRootVariableDeclaration(node *shimast.Node) *shimast.Node {
|
|
315
|
+
for node != nil && node.Kind == shimast.KindBindingElement {
|
|
316
|
+
if node.Parent == nil {
|
|
317
|
+
return nil
|
|
318
|
+
}
|
|
319
|
+
node = node.Parent.Parent
|
|
320
|
+
}
|
|
321
|
+
return node
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
func noLoopFuncVariableScope(node *shimast.Node) *shimast.Node {
|
|
325
|
+
for ancestor := node; ancestor != nil; ancestor = ancestor.Parent {
|
|
326
|
+
if noLoopFuncIsAnyFunction(ancestor) || ancestor.Kind == shimast.KindSourceFile ||
|
|
327
|
+
ancestor.Kind == shimast.KindClassStaticBlockDeclaration {
|
|
328
|
+
return ancestor
|
|
329
|
+
}
|
|
330
|
+
if ancestor.Kind == shimast.KindPropertyDeclaration {
|
|
331
|
+
property := ancestor.AsPropertyDeclaration()
|
|
332
|
+
if property != nil && noLoopFuncNodeContains(property.Initializer, node) {
|
|
333
|
+
return ancestor
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
return nil
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
func noLoopFuncContainingLoop(node *shimast.Node, skippedIIFEs map[*shimast.Node]struct{}) *shimast.Node {
|
|
341
|
+
child := node
|
|
342
|
+
for parent := node.Parent; parent != nil; parent = parent.Parent {
|
|
343
|
+
switch parent.Kind {
|
|
344
|
+
case shimast.KindWhileStatement, shimast.KindDoStatement:
|
|
345
|
+
return parent
|
|
346
|
+
case shimast.KindForStatement:
|
|
347
|
+
statement := parent.AsForStatement()
|
|
348
|
+
if statement == nil || statement.Initializer != child {
|
|
349
|
+
return parent
|
|
350
|
+
}
|
|
351
|
+
case shimast.KindForInStatement, shimast.KindForOfStatement:
|
|
352
|
+
statement := parent.AsForInOrOfStatement()
|
|
353
|
+
if statement == nil || statement.Expression != child {
|
|
354
|
+
return parent
|
|
355
|
+
}
|
|
356
|
+
default:
|
|
357
|
+
if noLoopFuncIsAnyFunction(parent) {
|
|
358
|
+
if _, skipped := skippedIIFEs[parent]; !skipped {
|
|
359
|
+
return nil
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
child = parent
|
|
364
|
+
}
|
|
365
|
+
return nil
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
func noLoopFuncTopLoop(loop, excluded *shimast.Node, skippedIIFEs map[*shimast.Node]struct{}) *shimast.Node {
|
|
369
|
+
top := loop
|
|
370
|
+
for {
|
|
371
|
+
outer := noLoopFuncContainingLoop(top, skippedIIFEs)
|
|
372
|
+
if outer == nil || (excluded != nil && outer.Pos() < excluded.End()) {
|
|
373
|
+
return top
|
|
374
|
+
}
|
|
375
|
+
top = outer
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
func noLoopFuncIsEligibleIIFE(function *shimast.Node) bool {
|
|
380
|
+
if function.Kind != shimast.KindFunctionExpression && function.Kind != shimast.KindArrowFunction {
|
|
381
|
+
return false
|
|
382
|
+
}
|
|
383
|
+
if function.ModifierFlags()&shimast.ModifierFlagsAsync != 0 || noLoopFuncIsGenerator(function) {
|
|
384
|
+
return false
|
|
385
|
+
}
|
|
386
|
+
expression := function
|
|
387
|
+
parent := expression.Parent
|
|
388
|
+
for parent != nil && parent.Kind == shimast.KindParenthesizedExpression && parent.Expression() == expression {
|
|
389
|
+
expression = parent
|
|
390
|
+
parent = parent.Parent
|
|
391
|
+
}
|
|
392
|
+
if parent == nil || parent.Kind != shimast.KindCallExpression {
|
|
393
|
+
return false
|
|
394
|
+
}
|
|
395
|
+
call := parent.AsCallExpression()
|
|
396
|
+
return call != nil && stripParens(call.Expression) == function
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
func noLoopFuncIsGenerator(function *shimast.Node) bool {
|
|
400
|
+
if function.Kind == shimast.KindFunctionExpression {
|
|
401
|
+
expression := function.AsFunctionExpression()
|
|
402
|
+
return expression != nil && expression.AsteriskToken != nil
|
|
403
|
+
}
|
|
80
404
|
return false
|
|
81
405
|
}
|
|
82
406
|
|
|
407
|
+
func noLoopFuncInsideTypeQuery(node *shimast.Node) bool {
|
|
408
|
+
for ancestor := node.Parent; ancestor != nil; ancestor = ancestor.Parent {
|
|
409
|
+
if ancestor.Kind == shimast.KindTypeQuery {
|
|
410
|
+
return true
|
|
411
|
+
}
|
|
412
|
+
if noLoopFuncIsAnyFunction(ancestor) || ancestor.Kind == shimast.KindSourceFile {
|
|
413
|
+
return false
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
return false
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
func noLoopFuncReferenceBelongsToFunction(reference, function *shimast.Node) bool {
|
|
420
|
+
child := reference
|
|
421
|
+
for child != nil && child.Parent != function {
|
|
422
|
+
child = child.Parent
|
|
423
|
+
}
|
|
424
|
+
if child == nil {
|
|
425
|
+
return false
|
|
426
|
+
}
|
|
427
|
+
if child == function.Body() {
|
|
428
|
+
return true
|
|
429
|
+
}
|
|
430
|
+
if child.Kind != shimast.KindParameter {
|
|
431
|
+
return false
|
|
432
|
+
}
|
|
433
|
+
parameter := child.AsParameterDeclaration()
|
|
434
|
+
return parameter != nil &&
|
|
435
|
+
(noLoopFuncNodeContains(parameter.Name(), reference) || noLoopFuncNodeContains(parameter.Initializer, reference))
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
func noLoopFuncNodeContains(container, node *shimast.Node) bool {
|
|
439
|
+
for current := node; current != nil; current = current.Parent {
|
|
440
|
+
if current == container {
|
|
441
|
+
return true
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
return false
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
func noLoopFuncIsCheckedFunction(node *shimast.Node) bool {
|
|
448
|
+
if node == nil {
|
|
449
|
+
return false
|
|
450
|
+
}
|
|
451
|
+
switch node.Kind {
|
|
452
|
+
case shimast.KindArrowFunction,
|
|
453
|
+
shimast.KindFunctionExpression,
|
|
454
|
+
shimast.KindFunctionDeclaration,
|
|
455
|
+
shimast.KindMethodDeclaration,
|
|
456
|
+
shimast.KindGetAccessor,
|
|
457
|
+
shimast.KindSetAccessor,
|
|
458
|
+
shimast.KindConstructor:
|
|
459
|
+
return true
|
|
460
|
+
}
|
|
461
|
+
return false
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
func noLoopFuncIsAnyFunction(node *shimast.Node) bool {
|
|
465
|
+
return node != nil && isFunctionLikeKind(node)
|
|
466
|
+
}
|
|
467
|
+
|
|
83
468
|
func init() {
|
|
84
469
|
Register(noLoopFunc{})
|
|
85
470
|
}
|