@ttsc/lint 0.18.4 → 0.19.1
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 +125 -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 +47 -8
- 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 +159 -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 +231 -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 +206 -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,23 +1,33 @@
|
|
|
1
|
-
// unicorn/consistent-existence-index-check:
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
1
|
+
// unicorn/consistent-existence-index-check: `indexOf`, `lastIndexOf`,
|
|
2
|
+
// `findIndex`, and `findLastIndex` answer "not found" with the sentinel `-1`,
|
|
3
|
+
// so an existence test reads clearest when it compares against that sentinel.
|
|
4
|
+
// Upstream targets one shape only — a `const` bound to such a call — and
|
|
5
|
+
// rewrites the magnitude comparisons on that binding: `index < 0` becomes
|
|
6
|
+
// `index === -1`, while `index >= 0` and `index > -1` become `index !== -1`.
|
|
7
|
+
// A bare `arr.indexOf(x) < 0` binds no index variable and is not reported,
|
|
8
|
+
// exactly as upstream leaves it alone.
|
|
7
9
|
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
10
|
+
// Checker-backed, because the rule is scope analysis rather than syntax.
|
|
11
|
+
// Upstream walks the declared variable's eslint-scope references and inspects
|
|
12
|
+
// the comparisons among them; the port asks the same question from the other
|
|
13
|
+
// end, resolving the compared identifier to its checker binding and inspecting
|
|
14
|
+
// that binding's declaration. Both directions enumerate the same
|
|
15
|
+
// (const-index-declaration, comparison) pairs, and the binding identity is what
|
|
16
|
+
// keeps a shadowing `let index`, a same-named binding in a sibling scope, and a
|
|
17
|
+
// parameter named `index` out of the report set.
|
|
18
|
+
//
|
|
19
|
+
// The reversed orientations (`0 > index`, `-1 < index`) are references whose
|
|
20
|
+
// binary-expression parent holds them on the right, which upstream skips, so
|
|
21
|
+
// they are not recognized here either.
|
|
17
22
|
// https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/consistent-existence-index-check.md
|
|
18
23
|
package linthost
|
|
19
24
|
|
|
20
|
-
import
|
|
25
|
+
import (
|
|
26
|
+
"fmt"
|
|
27
|
+
|
|
28
|
+
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
29
|
+
shimscanner "github.com/microsoft/typescript-go/shim/scanner"
|
|
30
|
+
)
|
|
21
31
|
|
|
22
32
|
var unicornConsistentExistenceIndexCheckMethods = map[string]struct{}{
|
|
23
33
|
"indexOf": {},
|
|
@@ -26,6 +36,35 @@ var unicornConsistentExistenceIndexCheckMethods = map[string]struct{}{
|
|
|
26
36
|
"findLastIndex": {},
|
|
27
37
|
}
|
|
28
38
|
|
|
39
|
+
// unicornConsistentExistenceIndexCheckReplacement is one rewrite of upstream's
|
|
40
|
+
// getReplacement: the operator and right operand to write, paired with the ones
|
|
41
|
+
// being replaced. The originals are message data, and the original value also
|
|
42
|
+
// decides whether the right operand is edited at all — `index > -1` already
|
|
43
|
+
// spells `-1`, so only its operator moves.
|
|
44
|
+
type unicornConsistentExistenceIndexCheckReplacement struct {
|
|
45
|
+
operator string
|
|
46
|
+
value string
|
|
47
|
+
originalOperator string
|
|
48
|
+
originalValue string
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// message renders upstream's message template. `===` is the non-existence
|
|
52
|
+
// check ("the element is absent"); `!==` is the existence check.
|
|
53
|
+
func (r unicornConsistentExistenceIndexCheckReplacement) message() string {
|
|
54
|
+
existence := "existence"
|
|
55
|
+
if r.operator == "===" {
|
|
56
|
+
existence = "non-existence"
|
|
57
|
+
}
|
|
58
|
+
return fmt.Sprintf(
|
|
59
|
+
"Prefer `%s %s` over `%s %s` to check %s.",
|
|
60
|
+
r.operator,
|
|
61
|
+
r.value,
|
|
62
|
+
r.originalOperator,
|
|
63
|
+
r.originalValue,
|
|
64
|
+
existence,
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
|
|
29
68
|
type unicornConsistentExistenceIndexCheck struct{}
|
|
30
69
|
|
|
31
70
|
func (unicornConsistentExistenceIndexCheck) Name() string {
|
|
@@ -34,35 +73,171 @@ func (unicornConsistentExistenceIndexCheck) Name() string {
|
|
|
34
73
|
func (unicornConsistentExistenceIndexCheck) Visits() []shimast.Kind {
|
|
35
74
|
return []shimast.Kind{shimast.KindBinaryExpression}
|
|
36
75
|
}
|
|
76
|
+
|
|
77
|
+
// The checker supplies binding identity: resolving the compared identifier to
|
|
78
|
+
// the `const` it was declared by is exactly the lookup eslint-scope performs
|
|
79
|
+
// upstream, and a name-only scan would collude shadowed and unrelated indexes.
|
|
80
|
+
func (unicornConsistentExistenceIndexCheck) NeedsTypeChecker() bool { return true }
|
|
81
|
+
|
|
37
82
|
func (unicornConsistentExistenceIndexCheck) Check(ctx *Context, node *shimast.Node) {
|
|
83
|
+
if ctx == nil || ctx.File == nil || ctx.Checker == nil || node == nil {
|
|
84
|
+
return
|
|
85
|
+
}
|
|
38
86
|
bin := node.AsBinaryExpression()
|
|
39
87
|
if bin == nil || bin.OperatorToken == nil || bin.Left == nil || bin.Right == nil {
|
|
40
88
|
return
|
|
41
89
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
90
|
+
// Parentheses are the only wrapper ESTree elides, so `(index) < 0` is a
|
|
91
|
+
// comparison of the binding while `(index as number) < 0` is not.
|
|
92
|
+
left := stripParens(bin.Left)
|
|
93
|
+
if left == nil || left.Kind != shimast.KindIdentifier {
|
|
45
94
|
return
|
|
46
95
|
}
|
|
47
|
-
left := stripParens(bin.Left)
|
|
48
96
|
right := stripParens(bin.Right)
|
|
49
|
-
|
|
50
|
-
|
|
97
|
+
replacement, ok := unicornConsistentExistenceIndexCheckReplacementFor(
|
|
98
|
+
bin.OperatorToken.Kind,
|
|
99
|
+
right,
|
|
100
|
+
)
|
|
101
|
+
if !ok || !unicornConsistentExistenceIndexCheckIsIndexBinding(ctx, left) {
|
|
102
|
+
return
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
text := ctx.File.Text()
|
|
106
|
+
operatorStart := shimscanner.SkipTrivia(text, bin.OperatorToken.Pos())
|
|
107
|
+
edits := []TextEdit{{
|
|
108
|
+
Pos: operatorStart,
|
|
109
|
+
End: bin.OperatorToken.End(),
|
|
110
|
+
Text: replacement.operator,
|
|
111
|
+
}}
|
|
112
|
+
if replacement.value != replacement.originalValue {
|
|
113
|
+
edits = append(edits, TextEdit{
|
|
114
|
+
Pos: shimscanner.SkipTrivia(text, right.Pos()),
|
|
115
|
+
End: right.End(),
|
|
116
|
+
Text: replacement.value,
|
|
117
|
+
})
|
|
51
118
|
}
|
|
119
|
+
// Upstream narrows the diagnostic to `<operator> <right>` rather than the
|
|
120
|
+
// whole comparison, so the caret lands on the part that has to change.
|
|
121
|
+
ctx.ReportRangeFix(operatorStart, right.End(), replacement.message(), edits...)
|
|
52
122
|
}
|
|
53
123
|
|
|
54
|
-
|
|
55
|
-
|
|
124
|
+
// unicornConsistentExistenceIndexCheckReplacementFor ports upstream's
|
|
125
|
+
// getReplacement. Only these three operator/operand pairs are magnitude
|
|
126
|
+
// spellings of the sentinel test; `index <= -1` and the equality forms are not
|
|
127
|
+
// recognized upstream and stay untouched here.
|
|
128
|
+
func unicornConsistentExistenceIndexCheckReplacementFor(
|
|
129
|
+
operator shimast.Kind,
|
|
130
|
+
right *shimast.Node,
|
|
131
|
+
) (unicornConsistentExistenceIndexCheckReplacement, bool) {
|
|
132
|
+
switch operator {
|
|
133
|
+
case shimast.KindLessThanToken:
|
|
134
|
+
if unicornConsistentExistenceIndexCheckIsZero(right) {
|
|
135
|
+
return unicornConsistentExistenceIndexCheckReplacement{
|
|
136
|
+
operator: "===",
|
|
137
|
+
value: "-1",
|
|
138
|
+
originalOperator: "<",
|
|
139
|
+
originalValue: "0",
|
|
140
|
+
}, true
|
|
141
|
+
}
|
|
142
|
+
case shimast.KindGreaterThanToken:
|
|
143
|
+
if unicornConsistentExistenceIndexCheckIsNegativeOne(right) {
|
|
144
|
+
return unicornConsistentExistenceIndexCheckReplacement{
|
|
145
|
+
operator: "!==",
|
|
146
|
+
value: "-1",
|
|
147
|
+
originalOperator: ">",
|
|
148
|
+
originalValue: "-1",
|
|
149
|
+
}, true
|
|
150
|
+
}
|
|
151
|
+
case shimast.KindGreaterThanEqualsToken:
|
|
152
|
+
if unicornConsistentExistenceIndexCheckIsZero(right) {
|
|
153
|
+
return unicornConsistentExistenceIndexCheckReplacement{
|
|
154
|
+
operator: "!==",
|
|
155
|
+
value: "-1",
|
|
156
|
+
originalOperator: ">=",
|
|
157
|
+
originalValue: "0",
|
|
158
|
+
}, true
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return unicornConsistentExistenceIndexCheckReplacement{}, false
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// unicornConsistentExistenceIndexCheckIsZero mirrors upstream's numeric-value
|
|
165
|
+
// test. TypeScript normalizes a numeric literal's text to the JavaScript
|
|
166
|
+
// number-to-string form, so `0`, `0.0`, `0x0`, and `0e0` all spell "0" here,
|
|
167
|
+
// while `0n` is a BigInt literal and `-0` is a unary expression — neither is a
|
|
168
|
+
// numeric-literal zero.
|
|
169
|
+
func unicornConsistentExistenceIndexCheckIsZero(node *shimast.Node) bool {
|
|
170
|
+
return node != nil && node.Kind == shimast.KindNumericLiteral &&
|
|
171
|
+
numericLiteralText(node) == "0"
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// unicornConsistentExistenceIndexCheckIsNegativeOne mirrors upstream's
|
|
175
|
+
// isNegativeOne: unary minus applied to the numeric literal one.
|
|
176
|
+
func unicornConsistentExistenceIndexCheckIsNegativeOne(node *shimast.Node) bool {
|
|
177
|
+
if node == nil || node.Kind != shimast.KindPrefixUnaryExpression {
|
|
178
|
+
return false
|
|
179
|
+
}
|
|
180
|
+
unary := node.AsPrefixUnaryExpression()
|
|
181
|
+
if unary == nil || unary.Operator != shimast.KindMinusToken {
|
|
182
|
+
return false
|
|
183
|
+
}
|
|
184
|
+
operand := stripParens(unary.Operand)
|
|
185
|
+
return operand != nil && operand.Kind == shimast.KindNumericLiteral &&
|
|
186
|
+
numericLiteralText(operand) == "1"
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// unicornConsistentExistenceIndexCheckIsIndexBinding reports whether the
|
|
190
|
+
// identifier resolves to a `const` declared from an index-returning call — the
|
|
191
|
+
// checker equivalent of upstream reaching a comparison through the declared
|
|
192
|
+
// variable's references. `let`, `var`, `using`, destructured bindings,
|
|
193
|
+
// parameters, imports, and consts initialized from anything else all fail here,
|
|
194
|
+
// so their comparisons are left alone.
|
|
195
|
+
func unicornConsistentExistenceIndexCheckIsIndexBinding(
|
|
196
|
+
ctx *Context,
|
|
197
|
+
identifier *shimast.Node,
|
|
198
|
+
) bool {
|
|
199
|
+
symbol := canonicalValueSymbol(ctx, identifier)
|
|
200
|
+
if symbol == nil || symbol.ValueDeclaration == nil {
|
|
56
201
|
return false
|
|
57
202
|
}
|
|
58
|
-
|
|
59
|
-
|
|
203
|
+
declaration := symbol.ValueDeclaration
|
|
204
|
+
if declaration.Kind != shimast.KindVariableDeclaration ||
|
|
205
|
+
declaration.Parent == nil ||
|
|
206
|
+
declaration.Parent.Kind != shimast.KindVariableDeclarationList {
|
|
60
207
|
return false
|
|
61
208
|
}
|
|
62
|
-
|
|
209
|
+
// `await using` carries the const flag too, so the block-scoped bits are
|
|
210
|
+
// compared as a whole against `const` alone.
|
|
211
|
+
if shimast.GetCombinedNodeFlags(declaration)&shimast.NodeFlagsBlockScoped !=
|
|
212
|
+
shimast.NodeFlagsConst {
|
|
63
213
|
return false
|
|
64
214
|
}
|
|
65
|
-
|
|
215
|
+
variable := declaration.AsVariableDeclaration()
|
|
216
|
+
if variable == nil || variable.Initializer == nil {
|
|
217
|
+
return false
|
|
218
|
+
}
|
|
219
|
+
if name := variable.Name(); name == nil || name.Kind != shimast.KindIdentifier {
|
|
220
|
+
return false
|
|
221
|
+
}
|
|
222
|
+
return unicornConsistentExistenceIndexCheckIsIndexCall(stripParens(variable.Initializer))
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// unicornConsistentExistenceIndexCheckIsIndexCall ports upstream's
|
|
226
|
+
// `isMethodCall(init, {methods: [...]})`: a plain call of a non-computed member
|
|
227
|
+
// whose property names one of the index-returning methods. The receiver is
|
|
228
|
+
// unconstrained — `_.indexOf(…)` counts just as much as `array.indexOf(…)`.
|
|
229
|
+
func unicornConsistentExistenceIndexCheckIsIndexCall(node *shimast.Node) bool {
|
|
230
|
+
if node == nil || node.Kind != shimast.KindCallExpression {
|
|
231
|
+
return false
|
|
232
|
+
}
|
|
233
|
+
// An optional chain (`foo?.indexOf(bar)`, `foo.indexOf?.(bar)`, or any `?.`
|
|
234
|
+
// earlier in the same chain) is a ChainExpression upstream, which is not a
|
|
235
|
+
// CallExpression and therefore never matches. Parenthesizing breaks the
|
|
236
|
+
// chain on both sides, so `(foo?.bar).indexOf(baz)` still counts.
|
|
237
|
+
if node.Flags&shimast.NodeFlagsOptionalChain != 0 {
|
|
238
|
+
return false
|
|
239
|
+
}
|
|
240
|
+
call := node.AsCallExpression()
|
|
66
241
|
if call == nil || call.Expression == nil ||
|
|
67
242
|
call.Expression.Kind != shimast.KindPropertyAccessExpression {
|
|
68
243
|
return false
|
|
@@ -71,7 +246,11 @@ func unicornConsistentExistenceIndexCheckMatch(callSide, literalSide *shimast.No
|
|
|
71
246
|
if access == nil {
|
|
72
247
|
return false
|
|
73
248
|
}
|
|
74
|
-
|
|
249
|
+
name := access.Name()
|
|
250
|
+
if name == nil || name.Kind != shimast.KindIdentifier {
|
|
251
|
+
return false
|
|
252
|
+
}
|
|
253
|
+
_, ok := unicornConsistentExistenceIndexCheckMethods[identifierText(name)]
|
|
75
254
|
return ok
|
|
76
255
|
}
|
|
77
256
|
|