@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
package/linthost/ast_helpers.go
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
package linthost
|
|
2
2
|
|
|
3
3
|
import (
|
|
4
|
+
"regexp"
|
|
4
5
|
"strings"
|
|
5
6
|
|
|
6
7
|
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
@@ -26,6 +27,36 @@ func nodeText(file *shimast.SourceFile, node *shimast.Node) string {
|
|
|
26
27
|
return strings.TrimRight(src[pos:end], " \t\r\n")
|
|
27
28
|
}
|
|
28
29
|
|
|
30
|
+
// templateRawNewlinePattern is acorn's TemplateElement raw normalization
|
|
31
|
+
// (`raw.replace(/\r\n?/g, "\n")` in parseTemplateElement).
|
|
32
|
+
var templateRawNewlinePattern = regexp.MustCompile(`\r\n?`)
|
|
33
|
+
|
|
34
|
+
// normalizeTemplateRaw reproduces the raw value ESLint sees for a template
|
|
35
|
+
// quasi: acorn materializes `TemplateElement.value.raw` with CR / CRLF
|
|
36
|
+
// collapsed to LF, so upstream rules both match patterns against and rewrite
|
|
37
|
+
// from the LF-normalized text. TypeScript's scanner performs the same
|
|
38
|
+
// collapse while cooking a template token (ECMA-262 normalizes <CR> and
|
|
39
|
+
// <CRLF> to <LF> in template values), so a raw payload sliced straight out of
|
|
40
|
+
// a CRLF source file must be normalized before it is compared with, or
|
|
41
|
+
// substituted back into, a cooked value.
|
|
42
|
+
func normalizeTemplateRaw(raw string) string {
|
|
43
|
+
return templateRawNewlinePattern.ReplaceAllString(raw, "\n")
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// isTaggedTemplateQuasi reports whether `node` is the template argument of a
|
|
47
|
+
// TaggedTemplateExpression — the position whose RAW text the tag function
|
|
48
|
+
// observes, and therefore the one where escape spelling is meaningful.
|
|
49
|
+
// A literal that merely appears somewhere inside a tagged template (e.g.
|
|
50
|
+
// within a substitution) or that sits in the tag slot is not the quasi and
|
|
51
|
+
// stays checked, mirroring upstream's isTaggedTemplateLiteral.
|
|
52
|
+
func isTaggedTemplateQuasi(node *shimast.Node) bool {
|
|
53
|
+
if node == nil || node.Parent == nil || node.Parent.Kind != shimast.KindTaggedTemplateExpression {
|
|
54
|
+
return false
|
|
55
|
+
}
|
|
56
|
+
tagged := node.Parent.AsTaggedTemplateExpression()
|
|
57
|
+
return tagged != nil && tagged.Template == node
|
|
58
|
+
}
|
|
59
|
+
|
|
29
60
|
// keywordStart returns the source offset of a declaration keyword such as
|
|
30
61
|
// `var` or `let` at the start of a node after leading trivia.
|
|
31
62
|
func keywordStart(file *shimast.SourceFile, node *shimast.Node, keyword string) int {
|
|
@@ -113,6 +144,103 @@ func tokenRange(file *shimast.SourceFile, node *shimast.Node) (int, int) {
|
|
|
113
144
|
return pos, end
|
|
114
145
|
}
|
|
115
146
|
|
|
147
|
+
// isTaggedTemplateElement reports whether a template token — a
|
|
148
|
+
// `KindNoSubstitutionTemplateLiteral` or one of the
|
|
149
|
+
// `KindTemplateHead`/`Middle`/`Tail` elements of a `KindTemplateExpression`
|
|
150
|
+
// — belongs to the quasi of a TaggedTemplateExpression. The tag function
|
|
151
|
+
// observes the raw text (`String.raw`, `dedent`, `gql`, `css`, …), so rules
|
|
152
|
+
// that police escape spelling must leave tagged templates alone; this is
|
|
153
|
+
// upstream's `isTaggedTemplateLiteral(node.parent)` guard on its
|
|
154
|
+
// `TemplateElement` handlers.
|
|
155
|
+
//
|
|
156
|
+
// A template that merely appears inside a tagged template — nested in one of
|
|
157
|
+
// its substitutions — is not tagged itself and stays checked, so the walk
|
|
158
|
+
// stops at the element's own enclosing template instead of searching upward
|
|
159
|
+
// for any tag. Template literal *types* can never be tagged and always
|
|
160
|
+
// answer false.
|
|
161
|
+
func isTaggedTemplateElement(node *shimast.Node) bool {
|
|
162
|
+
if node == nil {
|
|
163
|
+
return false
|
|
164
|
+
}
|
|
165
|
+
template := node
|
|
166
|
+
switch node.Kind {
|
|
167
|
+
case shimast.KindNoSubstitutionTemplateLiteral:
|
|
168
|
+
// The literal is the whole template; the tag, if any, is its parent.
|
|
169
|
+
case shimast.KindTemplateHead:
|
|
170
|
+
template = node.Parent
|
|
171
|
+
case shimast.KindTemplateMiddle, shimast.KindTemplateTail:
|
|
172
|
+
if node.Parent == nil {
|
|
173
|
+
return false
|
|
174
|
+
}
|
|
175
|
+
template = node.Parent.Parent
|
|
176
|
+
default:
|
|
177
|
+
return false
|
|
178
|
+
}
|
|
179
|
+
if template == nil || template.Parent == nil ||
|
|
180
|
+
template.Parent.Kind != shimast.KindTaggedTemplateExpression {
|
|
181
|
+
return false
|
|
182
|
+
}
|
|
183
|
+
tagged := template.Parent.AsTaggedTemplateExpression()
|
|
184
|
+
return tagged != nil && tagged.Template == template
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// isInsideTaggedTemplate reports whether a whole-template node — a
|
|
188
|
+
// `KindNoSubstitutionTemplateLiteral` or a `KindTemplateExpression` — sits
|
|
189
|
+
// anywhere inside a TaggedTemplateExpression, walking up to three ancestors so
|
|
190
|
+
// a template reached through a substitution span also answers true. Unlike the
|
|
191
|
+
// precise `isTaggedTemplateElement`, which asks only whether a template token's
|
|
192
|
+
// OWN enclosing template is tagged, this treats any enclosing tag as tainting.
|
|
193
|
+
// Used by `typescript/no-unnecessary-template-expression`.
|
|
194
|
+
func isInsideTaggedTemplate(node *shimast.Node) bool {
|
|
195
|
+
if node == nil || node.Parent == nil {
|
|
196
|
+
return false
|
|
197
|
+
}
|
|
198
|
+
parent := node.Parent
|
|
199
|
+
if parent.Kind == shimast.KindTaggedTemplateExpression {
|
|
200
|
+
return true
|
|
201
|
+
}
|
|
202
|
+
// Walk up at most two more hops to reach the TaggedTemplateExpression
|
|
203
|
+
// for the spans family.
|
|
204
|
+
for i := 0; i < 2 && parent.Parent != nil; i++ {
|
|
205
|
+
parent = parent.Parent
|
|
206
|
+
if parent.Kind == shimast.KindTaggedTemplateExpression {
|
|
207
|
+
return true
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return false
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// hasCommentBetween reports whether a comment begins anywhere in the source
|
|
214
|
+
// range [from, to). Fixers whose TextEdit keeps only part of the replaced
|
|
215
|
+
// span use it on the discarded sub-ranges: a comment there would be silently
|
|
216
|
+
// deleted by the edit, so the fix must be declined.
|
|
217
|
+
//
|
|
218
|
+
// The scan alternates SkipTriviaEx (whitespace, with StopAtComments so a
|
|
219
|
+
// comment opener is not consumed) with a two-byte opener check that
|
|
220
|
+
// distinguishes `//` and `/*` from a bare slash token. Non-trivia token
|
|
221
|
+
// bytes are stepped over one at a time, so an opener lookalike inside a
|
|
222
|
+
// string literal within the range would over-detect; callers only ever
|
|
223
|
+
// decline an autofix on a hit, which is the safe direction.
|
|
224
|
+
func hasCommentBetween(src string, from, to int) bool {
|
|
225
|
+
if from < 0 {
|
|
226
|
+
return false
|
|
227
|
+
}
|
|
228
|
+
if to > len(src) {
|
|
229
|
+
to = len(src)
|
|
230
|
+
}
|
|
231
|
+
for pos := from; pos < to; {
|
|
232
|
+
next := shimscanner.SkipTriviaEx(src, pos, &shimscanner.SkipTriviaOptions{StopAtComments: true})
|
|
233
|
+
if next < pos || next >= to {
|
|
234
|
+
return false
|
|
235
|
+
}
|
|
236
|
+
if src[next] == '/' && next+1 < len(src) && (src[next+1] == '/' || src[next+1] == '*') {
|
|
237
|
+
return true
|
|
238
|
+
}
|
|
239
|
+
pos = next + 1
|
|
240
|
+
}
|
|
241
|
+
return false
|
|
242
|
+
}
|
|
243
|
+
|
|
116
244
|
// isIdentifierPart reports whether `ch` can appear inside a JavaScript
|
|
117
245
|
// identifier — used as a word-boundary guard by keyword search helpers.
|
|
118
246
|
// Handles only ASCII; multibyte Unicode identifier parts are treated as
|
|
@@ -152,6 +280,184 @@ func stripParens(node *shimast.Node) *shimast.Node {
|
|
|
152
280
|
return node
|
|
153
281
|
}
|
|
154
282
|
|
|
283
|
+
// sameReferenceExpression reports whether two expressions denote the same
|
|
284
|
+
// repeatable runtime reference. Parentheses and TypeScript's type-only
|
|
285
|
+
// expression wrappers are transparent; identifiers, primitive literals,
|
|
286
|
+
// `this` / `super`, and property or element-access chains are compared
|
|
287
|
+
// recursively. Calls and other effectful expressions are deliberately rejected
|
|
288
|
+
// even when their source text is identical because evaluating them twice need
|
|
289
|
+
// not produce the same value.
|
|
290
|
+
//
|
|
291
|
+
// Static member keys are normalized to their runtime property key, so `x.y`
|
|
292
|
+
// and `x["y"]` compare equal. Optional and non-optional member accesses remain
|
|
293
|
+
// distinct because optional chaining changes evaluation semantics.
|
|
294
|
+
func sameReferenceExpression(left, right *shimast.Node) bool {
|
|
295
|
+
left = unwrapReferenceExpression(left)
|
|
296
|
+
right = unwrapReferenceExpression(right)
|
|
297
|
+
if left == nil || right == nil {
|
|
298
|
+
return false
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
switch left.Kind {
|
|
302
|
+
case shimast.KindIdentifier, shimast.KindPrivateIdentifier:
|
|
303
|
+
return left.Kind == right.Kind && shimast.NodeText(left) == shimast.NodeText(right)
|
|
304
|
+
case shimast.KindThisKeyword, shimast.KindSuperKeyword:
|
|
305
|
+
return left.Kind == right.Kind
|
|
306
|
+
case shimast.KindPropertyAccessExpression, shimast.KindElementAccessExpression:
|
|
307
|
+
leftMember, ok := referenceMemberParts(left)
|
|
308
|
+
if !ok {
|
|
309
|
+
return false
|
|
310
|
+
}
|
|
311
|
+
rightMember, ok := referenceMemberParts(right)
|
|
312
|
+
if !ok || leftMember.optional != rightMember.optional ||
|
|
313
|
+
!sameReferenceExpression(leftMember.object, rightMember.object) {
|
|
314
|
+
return false
|
|
315
|
+
}
|
|
316
|
+
if leftMember.staticKey != nil || rightMember.staticKey != nil {
|
|
317
|
+
return leftMember.staticKey != nil && rightMember.staticKey != nil &&
|
|
318
|
+
*leftMember.staticKey == *rightMember.staticKey &&
|
|
319
|
+
leftMember.private == rightMember.private
|
|
320
|
+
}
|
|
321
|
+
return sameReferenceExpression(leftMember.dynamicKey, rightMember.dynamicKey)
|
|
322
|
+
default:
|
|
323
|
+
leftValue, leftKind, leftOK := referencePrimitiveValue(left)
|
|
324
|
+
rightValue, rightKind, rightOK := referencePrimitiveValue(right)
|
|
325
|
+
return leftOK && rightOK && leftKind == rightKind && leftValue == rightValue
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// unwrapReferenceExpression removes syntax that has no runtime evaluation of
|
|
330
|
+
// its own. Keep this list explicit: optional chains, calls, and instantiation
|
|
331
|
+
// expressions are not harmless wrappers and must remain visible to callers.
|
|
332
|
+
func unwrapReferenceExpression(node *shimast.Node) *shimast.Node {
|
|
333
|
+
for node != nil {
|
|
334
|
+
switch node.Kind {
|
|
335
|
+
case shimast.KindParenthesizedExpression,
|
|
336
|
+
shimast.KindAsExpression,
|
|
337
|
+
shimast.KindSatisfiesExpression,
|
|
338
|
+
shimast.KindNonNullExpression,
|
|
339
|
+
shimast.KindTypeAssertionExpression:
|
|
340
|
+
next := node.Expression()
|
|
341
|
+
if next == nil {
|
|
342
|
+
return node
|
|
343
|
+
}
|
|
344
|
+
node = next
|
|
345
|
+
default:
|
|
346
|
+
return node
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
return nil
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
type referenceMember struct {
|
|
353
|
+
object *shimast.Node
|
|
354
|
+
staticKey *string
|
|
355
|
+
dynamicKey *shimast.Node
|
|
356
|
+
optional bool
|
|
357
|
+
private bool
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
func referenceMemberParts(node *shimast.Node) (referenceMember, bool) {
|
|
361
|
+
switch node.Kind {
|
|
362
|
+
case shimast.KindPropertyAccessExpression:
|
|
363
|
+
access := node.AsPropertyAccessExpression()
|
|
364
|
+
if access == nil || access.Expression == nil || access.Name() == nil {
|
|
365
|
+
return referenceMember{}, false
|
|
366
|
+
}
|
|
367
|
+
name := access.Name()
|
|
368
|
+
if name.Kind != shimast.KindIdentifier && name.Kind != shimast.KindPrivateIdentifier {
|
|
369
|
+
return referenceMember{}, false
|
|
370
|
+
}
|
|
371
|
+
key := shimast.NodeText(name)
|
|
372
|
+
return referenceMember{
|
|
373
|
+
object: access.Expression,
|
|
374
|
+
staticKey: &key,
|
|
375
|
+
optional: access.QuestionDotToken != nil,
|
|
376
|
+
private: name.Kind == shimast.KindPrivateIdentifier,
|
|
377
|
+
}, true
|
|
378
|
+
case shimast.KindElementAccessExpression:
|
|
379
|
+
access := node.AsElementAccessExpression()
|
|
380
|
+
if access == nil || access.Expression == nil || access.ArgumentExpression == nil {
|
|
381
|
+
return referenceMember{}, false
|
|
382
|
+
}
|
|
383
|
+
member := referenceMember{
|
|
384
|
+
object: access.Expression,
|
|
385
|
+
dynamicKey: access.ArgumentExpression,
|
|
386
|
+
optional: access.QuestionDotToken != nil,
|
|
387
|
+
}
|
|
388
|
+
if key, ok := referenceStaticPropertyKey(access.ArgumentExpression); ok {
|
|
389
|
+
member.staticKey = &key
|
|
390
|
+
member.dynamicKey = nil
|
|
391
|
+
}
|
|
392
|
+
return member, true
|
|
393
|
+
default:
|
|
394
|
+
return referenceMember{}, false
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
func referenceStaticPropertyKey(node *shimast.Node) (string, bool) {
|
|
399
|
+
node = unwrapReferenceExpression(node)
|
|
400
|
+
if node == nil {
|
|
401
|
+
return "", false
|
|
402
|
+
}
|
|
403
|
+
value, kind, ok := referencePrimitiveValue(node)
|
|
404
|
+
if ok {
|
|
405
|
+
if kind == "bigint" {
|
|
406
|
+
value = strings.TrimSuffix(value, "n")
|
|
407
|
+
}
|
|
408
|
+
return value, true
|
|
409
|
+
}
|
|
410
|
+
if node.Kind != shimast.KindPrefixUnaryExpression {
|
|
411
|
+
return "", false
|
|
412
|
+
}
|
|
413
|
+
prefix := node.AsPrefixUnaryExpression()
|
|
414
|
+
if prefix == nil || prefix.Operand == nil {
|
|
415
|
+
return "", false
|
|
416
|
+
}
|
|
417
|
+
value, kind, ok = referencePrimitiveValue(prefix.Operand)
|
|
418
|
+
if !ok || (kind != "number" && kind != "bigint") {
|
|
419
|
+
return "", false
|
|
420
|
+
}
|
|
421
|
+
value = strings.TrimSuffix(value, "n")
|
|
422
|
+
switch prefix.Operator {
|
|
423
|
+
case shimast.KindPlusToken:
|
|
424
|
+
return value, kind == "number"
|
|
425
|
+
case shimast.KindMinusToken:
|
|
426
|
+
if strings.Trim(value, "0.") == "" {
|
|
427
|
+
return "0", true
|
|
428
|
+
}
|
|
429
|
+
return "-" + value, true
|
|
430
|
+
default:
|
|
431
|
+
return "", false
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// referencePrimitiveValue returns a normalized value plus a runtime-kind tag.
|
|
436
|
+
// The tag keeps numerically similar values such as `1` and `1n` distinct when
|
|
437
|
+
// they are receivers; referenceStaticPropertyKey intentionally collapses them
|
|
438
|
+
// later because JavaScript converts both to the same string property key.
|
|
439
|
+
func referencePrimitiveValue(node *shimast.Node) (value, kind string, ok bool) {
|
|
440
|
+
if node == nil {
|
|
441
|
+
return "", "", false
|
|
442
|
+
}
|
|
443
|
+
switch node.Kind {
|
|
444
|
+
case shimast.KindStringLiteral, shimast.KindNoSubstitutionTemplateLiteral:
|
|
445
|
+
return stringLiteralText(node), "string", true
|
|
446
|
+
case shimast.KindNumericLiteral:
|
|
447
|
+
return numericLiteralText(node), "number", true
|
|
448
|
+
case shimast.KindBigIntLiteral:
|
|
449
|
+
return numericLiteralText(node), "bigint", true
|
|
450
|
+
case shimast.KindTrueKeyword:
|
|
451
|
+
return "true", "boolean", true
|
|
452
|
+
case shimast.KindFalseKeyword:
|
|
453
|
+
return "false", "boolean", true
|
|
454
|
+
case shimast.KindNullKeyword:
|
|
455
|
+
return "null", "null", true
|
|
456
|
+
default:
|
|
457
|
+
return "", "", false
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
155
461
|
// isMatchingPropertyAccess reports whether `node` reads the chain
|
|
156
462
|
// `head.tail[0].tail[1]…`. Useful for detecting `obj.__proto__` or
|
|
157
463
|
// `console.log` shapes regardless of nesting.
|
|
@@ -305,83 +611,275 @@ func (w *descendantsWalker) visitChild(child *shimast.Node) bool {
|
|
|
305
611
|
return false
|
|
306
612
|
}
|
|
307
613
|
|
|
308
|
-
//
|
|
309
|
-
// assignment's left-hand side. A bare Identifier yields one
|
|
614
|
+
// assignmentTargetIdentifiers collects the Identifier nodes written by an
|
|
615
|
+
// assignment's left-hand side. A bare Identifier yields one node. A
|
|
310
616
|
// destructuring-assignment target — parsed as an ArrayLiteralExpression
|
|
311
617
|
// (`[a, b] = …`) or ObjectLiteralExpression (`({a} = …)`) rather than a
|
|
312
618
|
// binding pattern — is walked so every nested write position is counted:
|
|
313
619
|
// array elements, object property values, shorthand properties, defaults
|
|
314
620
|
// (`[a = 1]`, `{a = 1}`), nested patterns, and rest elements.
|
|
315
621
|
//
|
|
316
|
-
//
|
|
317
|
-
//
|
|
318
|
-
//
|
|
319
|
-
|
|
622
|
+
// TypeScript assertion wrappers are transparent in reference position, so
|
|
623
|
+
// `as`, angle-bracket assertions, `satisfies`, non-null assertions, and
|
|
624
|
+
// parentheses are unwrapped recursively wherever they appear in a target.
|
|
625
|
+
// Property names in `{key: target}` are read positions, not writes, so only
|
|
626
|
+
// the property value contributes; member-access targets (`obj.x`) declare no
|
|
627
|
+
// local binding and are skipped. Returns nil for other shapes.
|
|
628
|
+
func assignmentTargetIdentifiers(node *shimast.Node) []*shimast.Node {
|
|
320
629
|
if node == nil {
|
|
321
630
|
return nil
|
|
322
631
|
}
|
|
323
|
-
if
|
|
324
|
-
return []
|
|
632
|
+
if node.Kind == shimast.KindIdentifier {
|
|
633
|
+
return []*shimast.Node{node}
|
|
634
|
+
}
|
|
635
|
+
var identifiers []*shimast.Node
|
|
636
|
+
collectAssignmentTargetIdentifiers(node, &identifiers)
|
|
637
|
+
return identifiers
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
// valueSymbolAtIdentifier returns the checker symbol for an identifier in a
|
|
641
|
+
// value position. A shorthand property assignment needs the checker's
|
|
642
|
+
// dedicated value-symbol query because GetSymbolAtLocation resolves the
|
|
643
|
+
// syntactic property name rather than the binding written by `({name} = value)`.
|
|
644
|
+
func valueSymbolAtIdentifier(ctx *Context, identifier *shimast.Node) *shimast.Symbol {
|
|
645
|
+
if ctx == nil || ctx.Checker == nil || identifier == nil || identifier.Kind != shimast.KindIdentifier {
|
|
646
|
+
return nil
|
|
647
|
+
}
|
|
648
|
+
if parent := identifier.Parent; parent != nil && parent.Kind == shimast.KindShorthandPropertyAssignment {
|
|
649
|
+
if shorthand := parent.AsShorthandPropertyAssignment(); shorthand != nil && shorthand.Name() == identifier {
|
|
650
|
+
return ctx.Checker.GetShorthandAssignmentValueSymbol(parent)
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
return ctx.Checker.GetSymbolAtLocation(identifier)
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
// canonicalValueSymbol resolves an identifier in value position and
|
|
657
|
+
// normalizes exported local/export pairs and declaration merges to one binding
|
|
658
|
+
// identity. TypeScript binds an exported declaration to both a local symbol
|
|
659
|
+
// (used by references in the declaring module) and its ExportSymbol (returned
|
|
660
|
+
// for the declaration name), so both sides must converge before comparison.
|
|
661
|
+
// Rules that protect declarations from writes use this for both the
|
|
662
|
+
// declaration name and every modifying reference, including shorthand
|
|
663
|
+
// destructuring targets.
|
|
664
|
+
func canonicalValueSymbol(ctx *Context, identifier *shimast.Node) *shimast.Symbol {
|
|
665
|
+
symbol := valueSymbolAtIdentifier(ctx, identifier)
|
|
666
|
+
if symbol == nil {
|
|
667
|
+
return nil
|
|
668
|
+
}
|
|
669
|
+
if symbol.Flags&shimast.SymbolFlagsExportValue != 0 && symbol.ExportSymbol != nil {
|
|
670
|
+
symbol = symbol.ExportSymbol
|
|
671
|
+
}
|
|
672
|
+
return ctx.Checker.GetMergedSymbol(symbol)
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
// writeTargetIdentifiers returns every identifier written by one reference-
|
|
676
|
+
// modifying syntax node. It recognizes assignment operators, update
|
|
677
|
+
// expressions, and expression initializers of for-in/of statements; nested
|
|
678
|
+
// destructuring and TypeScript assertion wrappers are delegated to the shared
|
|
679
|
+
// assignment-target walker.
|
|
680
|
+
//
|
|
681
|
+
// Walking all nodes can encounter a destructuring default both through its
|
|
682
|
+
// outer pattern and as a nested binary expression. Callers that report a full
|
|
683
|
+
// tree must therefore deduplicate the returned identifier nodes.
|
|
684
|
+
func writeTargetIdentifiers(node *shimast.Node) []*shimast.Node {
|
|
685
|
+
if node == nil {
|
|
686
|
+
return nil
|
|
687
|
+
}
|
|
688
|
+
switch node.Kind {
|
|
689
|
+
case shimast.KindBinaryExpression:
|
|
690
|
+
expression := node.AsBinaryExpression()
|
|
691
|
+
if expression != nil && expression.OperatorToken != nil &&
|
|
692
|
+
isAssignmentOperator(expression.OperatorToken.Kind) {
|
|
693
|
+
return assignmentTargetIdentifiers(expression.Left)
|
|
694
|
+
}
|
|
695
|
+
case shimast.KindPrefixUnaryExpression:
|
|
696
|
+
expression := node.AsPrefixUnaryExpression()
|
|
697
|
+
if expression != nil &&
|
|
698
|
+
(expression.Operator == shimast.KindPlusPlusToken || expression.Operator == shimast.KindMinusMinusToken) {
|
|
699
|
+
return assignmentTargetIdentifiers(expression.Operand)
|
|
700
|
+
}
|
|
701
|
+
case shimast.KindPostfixUnaryExpression:
|
|
702
|
+
expression := node.AsPostfixUnaryExpression()
|
|
703
|
+
if expression != nil &&
|
|
704
|
+
(expression.Operator == shimast.KindPlusPlusToken || expression.Operator == shimast.KindMinusMinusToken) {
|
|
705
|
+
return assignmentTargetIdentifiers(expression.Operand)
|
|
706
|
+
}
|
|
707
|
+
case shimast.KindForInStatement, shimast.KindForOfStatement:
|
|
708
|
+
statement := node.AsForInOrOfStatement()
|
|
709
|
+
if statement != nil && statement.Initializer != nil &&
|
|
710
|
+
statement.Initializer.Kind != shimast.KindVariableDeclarationList {
|
|
711
|
+
return assignmentTargetIdentifiers(statement.Initializer)
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
return nil
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
// assignmentTargetNames is the text-only projection used by rules that do not
|
|
718
|
+
// need binding identity.
|
|
719
|
+
func assignmentTargetNames(node *shimast.Node) []string {
|
|
720
|
+
identifiers := assignmentTargetIdentifiers(node)
|
|
721
|
+
if len(identifiers) == 0 {
|
|
722
|
+
return nil
|
|
723
|
+
}
|
|
724
|
+
names := make([]string, 0, len(identifiers))
|
|
725
|
+
for _, identifier := range identifiers {
|
|
726
|
+
if name := identifierText(identifier); name != "" {
|
|
727
|
+
names = append(names, name)
|
|
728
|
+
}
|
|
325
729
|
}
|
|
326
|
-
var names []string
|
|
327
|
-
collectAssignmentTargetNames(node, &names)
|
|
328
730
|
return names
|
|
329
731
|
}
|
|
330
732
|
|
|
331
|
-
//
|
|
733
|
+
// isDestructuringAssignmentTarget reports whether node belongs to the left
|
|
734
|
+
// side of a destructuring assignment. TypeScript-Go represents those targets
|
|
735
|
+
// as array/object literal expressions rather than binding-pattern nodes.
|
|
736
|
+
// Property keys, computed names, and default-value expressions are reads and
|
|
737
|
+
// therefore stop the target walk.
|
|
738
|
+
func isDestructuringAssignmentTarget(node *shimast.Node) bool {
|
|
739
|
+
child := node
|
|
740
|
+
for child != nil && child.Parent != nil {
|
|
741
|
+
parent := child.Parent
|
|
742
|
+
switch parent.Kind {
|
|
743
|
+
case shimast.KindBindingElement:
|
|
744
|
+
binding := parent.AsBindingElement()
|
|
745
|
+
if binding != nil &&
|
|
746
|
+
(binding.PropertyName == child || binding.Initializer == child) {
|
|
747
|
+
return false
|
|
748
|
+
}
|
|
749
|
+
case shimast.KindPropertyAssignment:
|
|
750
|
+
property := parent.AsPropertyAssignment()
|
|
751
|
+
if property != nil && property.Name() == child {
|
|
752
|
+
return false
|
|
753
|
+
}
|
|
754
|
+
case shimast.KindShorthandPropertyAssignment:
|
|
755
|
+
property := parent.AsShorthandPropertyAssignment()
|
|
756
|
+
if property != nil && property.ObjectAssignmentInitializer == child {
|
|
757
|
+
return false
|
|
758
|
+
}
|
|
759
|
+
case shimast.KindComputedPropertyName:
|
|
760
|
+
return false
|
|
761
|
+
case shimast.KindForInStatement, shimast.KindForOfStatement:
|
|
762
|
+
statement := parent.AsForInOrOfStatement()
|
|
763
|
+
return statement != nil && statement.Initializer == child
|
|
764
|
+
}
|
|
765
|
+
if parent.Kind == shimast.KindBinaryExpression {
|
|
766
|
+
expression := parent.AsBinaryExpression()
|
|
767
|
+
if expression != nil && expression.OperatorToken != nil &&
|
|
768
|
+
expression.OperatorToken.Kind == shimast.KindEqualsToken {
|
|
769
|
+
if expression.Left == child {
|
|
770
|
+
return true
|
|
771
|
+
}
|
|
772
|
+
if expression.Right == child {
|
|
773
|
+
return false
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
child = parent
|
|
778
|
+
}
|
|
779
|
+
return false
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
// collectAssignmentTargetIdentifiers appends to `identifiers` every identifier in a
|
|
332
783
|
// destructuring-assignment target. It descends only through write-target
|
|
333
784
|
// positions so reads (object property keys, computed-member expressions)
|
|
334
785
|
// never count as reassignments.
|
|
335
|
-
func
|
|
786
|
+
func collectAssignmentTargetIdentifiers(node *shimast.Node, identifiers *[]*shimast.Node) {
|
|
336
787
|
if node == nil {
|
|
337
788
|
return
|
|
338
789
|
}
|
|
339
790
|
switch node.Kind {
|
|
340
791
|
case shimast.KindIdentifier:
|
|
341
|
-
|
|
342
|
-
*names = append(*names, name)
|
|
343
|
-
}
|
|
792
|
+
*identifiers = append(*identifiers, node)
|
|
344
793
|
case shimast.KindParenthesizedExpression:
|
|
345
|
-
|
|
794
|
+
collectAssignmentTargetIdentifiers(stripParens(node), identifiers)
|
|
795
|
+
case shimast.KindNonNullExpression:
|
|
796
|
+
if expression := node.AsNonNullExpression(); expression != nil {
|
|
797
|
+
collectAssignmentTargetIdentifiers(expression.Expression, identifiers)
|
|
798
|
+
}
|
|
799
|
+
case shimast.KindAsExpression:
|
|
800
|
+
if expression := node.AsAsExpression(); expression != nil {
|
|
801
|
+
collectAssignmentTargetIdentifiers(expression.Expression, identifiers)
|
|
802
|
+
}
|
|
803
|
+
case shimast.KindTypeAssertionExpression:
|
|
804
|
+
if expression := node.AsTypeAssertion(); expression != nil {
|
|
805
|
+
collectAssignmentTargetIdentifiers(expression.Expression, identifiers)
|
|
806
|
+
}
|
|
807
|
+
case shimast.KindSatisfiesExpression:
|
|
808
|
+
if expression := node.AsSatisfiesExpression(); expression != nil {
|
|
809
|
+
collectAssignmentTargetIdentifiers(expression.Expression, identifiers)
|
|
810
|
+
}
|
|
346
811
|
case shimast.KindArrayLiteralExpression:
|
|
347
812
|
if arr := node.AsArrayLiteralExpression(); arr != nil && arr.Elements != nil {
|
|
348
813
|
for _, el := range arr.Elements.Nodes {
|
|
349
|
-
|
|
814
|
+
collectAssignmentTargetIdentifiers(el, identifiers)
|
|
350
815
|
}
|
|
351
816
|
}
|
|
352
817
|
case shimast.KindObjectLiteralExpression:
|
|
353
818
|
if obj := node.AsObjectLiteralExpression(); obj != nil && obj.Properties != nil {
|
|
354
819
|
for _, prop := range obj.Properties.Nodes {
|
|
355
|
-
|
|
820
|
+
collectAssignmentTargetIdentifiers(prop, identifiers)
|
|
356
821
|
}
|
|
357
822
|
}
|
|
358
823
|
case shimast.KindSpreadElement:
|
|
359
824
|
if spread := node.AsSpreadElement(); spread != nil {
|
|
360
|
-
|
|
825
|
+
collectAssignmentTargetIdentifiers(spread.Expression, identifiers)
|
|
361
826
|
}
|
|
362
827
|
case shimast.KindSpreadAssignment:
|
|
363
828
|
if spread := node.AsSpreadAssignment(); spread != nil {
|
|
364
|
-
|
|
829
|
+
collectAssignmentTargetIdentifiers(spread.Expression, identifiers)
|
|
365
830
|
}
|
|
366
831
|
case shimast.KindShorthandPropertyAssignment:
|
|
367
832
|
// `{a}` and `{a = 1}` — the property name is the write target; any
|
|
368
833
|
// ObjectAssignmentInitializer is a default value, not a target.
|
|
369
834
|
if short := node.AsShorthandPropertyAssignment(); short != nil {
|
|
370
|
-
|
|
835
|
+
collectAssignmentTargetIdentifiers(short.Name(), identifiers)
|
|
371
836
|
}
|
|
372
837
|
case shimast.KindPropertyAssignment:
|
|
373
838
|
// `{key: target}` — only the value (initializer) is written to.
|
|
374
839
|
if assignment := node.AsPropertyAssignment(); assignment != nil {
|
|
375
|
-
|
|
840
|
+
collectAssignmentTargetIdentifiers(assignment.Initializer, identifiers)
|
|
376
841
|
}
|
|
377
842
|
case shimast.KindBinaryExpression:
|
|
378
843
|
// A default inside a pattern (`[a = 1]`, `{key: a = 1}`) parses as an
|
|
379
844
|
// `=` BinaryExpression; only its left side is the write target.
|
|
380
845
|
if expr := node.AsBinaryExpression(); expr != nil &&
|
|
381
846
|
expr.OperatorToken != nil && expr.OperatorToken.Kind == shimast.KindEqualsToken {
|
|
382
|
-
|
|
847
|
+
collectAssignmentTargetIdentifiers(expr.Left, identifiers)
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
// isDestructuringDefaultAssignment distinguishes the `a = 1` syntax inside
|
|
853
|
+
// `[a = 1]` or `{a = 1}` from a second assignment. A real assignment nested
|
|
854
|
+
// in the default expression's right side leaves the outer target path and is
|
|
855
|
+
// therefore not classified as a default.
|
|
856
|
+
func isDestructuringDefaultAssignment(node *shimast.Node) bool {
|
|
857
|
+
if node == nil {
|
|
858
|
+
return false
|
|
859
|
+
}
|
|
860
|
+
current := node
|
|
861
|
+
for parent := node.Parent; parent != nil; parent = parent.Parent {
|
|
862
|
+
switch parent.Kind {
|
|
863
|
+
case shimast.KindParenthesizedExpression,
|
|
864
|
+
shimast.KindArrayLiteralExpression,
|
|
865
|
+
shimast.KindObjectLiteralExpression,
|
|
866
|
+
shimast.KindPropertyAssignment,
|
|
867
|
+
shimast.KindSpreadElement,
|
|
868
|
+
shimast.KindSpreadAssignment:
|
|
869
|
+
current = parent
|
|
870
|
+
continue
|
|
871
|
+
case shimast.KindShorthandPropertyAssignment:
|
|
872
|
+
return false
|
|
873
|
+
case shimast.KindBinaryExpression:
|
|
874
|
+
expression := parent.AsBinaryExpression()
|
|
875
|
+
return expression != nil && expression.OperatorToken != nil &&
|
|
876
|
+
isAssignmentOperator(expression.OperatorToken.Kind) &&
|
|
877
|
+
current.Pos() >= expression.Left.Pos() && current.End() <= expression.Left.End()
|
|
878
|
+
default:
|
|
879
|
+
return false
|
|
383
880
|
}
|
|
384
881
|
}
|
|
882
|
+
return false
|
|
385
883
|
}
|
|
386
884
|
|
|
387
885
|
// isLiteralLike reports whether `node` (after stripping parentheses) is a
|