@ttsc/lint 0.18.4 → 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.
Files changed (188) hide show
  1. package/README.md +95 -43
  2. package/go.mod +5 -4
  3. package/internal/rulecode/rulecode.go +88 -0
  4. package/lib/index.d.ts +10 -1
  5. package/lib/index.js +45 -17
  6. package/lib/index.js.map +1 -1
  7. package/lib/structures/ITtscLintConfig.d.ts +3 -0
  8. package/lib/structures/TtscLintRuleSetting.d.ts +3 -2
  9. package/lib/structures/format/ITtscLintFormat.d.ts +3 -2
  10. package/lib/structures/format/ITtscLintFormatSortImports.d.ts +20 -4
  11. package/lib/structures/rules/ITtscLintBoundariesRuleOptions.d.ts +80 -8
  12. package/lib/structures/rules/ITtscLintBoundariesRules.d.ts +6 -8
  13. package/lib/structures/rules/ITtscLintContributorRules.d.ts +18 -2
  14. package/lib/structures/rules/ITtscLintCoreRuleOptions.d.ts +347 -0
  15. package/lib/structures/rules/ITtscLintCoreRuleOptions.js +3 -0
  16. package/lib/structures/rules/ITtscLintCoreRuleOptions.js.map +1 -0
  17. package/lib/structures/rules/ITtscLintCoreRules.d.ts +101 -67
  18. package/lib/structures/rules/ITtscLintRegexpRules.d.ts +7 -2
  19. package/lib/structures/rules/ITtscLintRuleOptionsMap.d.ts +53 -7
  20. package/lib/structures/rules/ITtscLintRules.d.ts +6 -2
  21. package/lib/structures/rules/ITtscLintSolidRules.d.ts +0 -8
  22. package/lib/structures/rules/ITtscLintTypeScriptRuleOptions.d.ts +206 -0
  23. package/lib/structures/rules/ITtscLintTypeScriptRuleOptions.js +8 -0
  24. package/lib/structures/rules/ITtscLintTypeScriptRuleOptions.js.map +1 -0
  25. package/lib/structures/rules/ITtscLintTypeScriptRules.d.ts +55 -42
  26. package/lib/structures/rules/ITtscLintUnicornRuleOptions.d.ts +276 -0
  27. package/lib/structures/rules/ITtscLintUnicornRuleOptions.js +3 -0
  28. package/lib/structures/rules/ITtscLintUnicornRuleOptions.js.map +1 -0
  29. package/lib/structures/rules/ITtscLintUnicornRules.d.ts +44 -36
  30. package/lib/structures/rules/index.d.ts +3 -0
  31. package/lib/structures/rules/index.js +3 -0
  32. package/lib/structures/rules/index.js.map +1 -1
  33. package/linthost/ast_helpers.go +521 -23
  34. package/linthost/ast_selector_matcher.go +1628 -0
  35. package/linthost/ast_selector_parser.go +628 -0
  36. package/linthost/comment_scan.go +154 -0
  37. package/linthost/compile.go +129 -79
  38. package/linthost/config.go +352 -88
  39. package/linthost/config_format.go +15 -1
  40. package/linthost/contrib_adapter.go +47 -8
  41. package/linthost/declaration_rules.go +12 -11
  42. package/linthost/directives.go +9 -67
  43. package/linthost/dispatch.go +37 -3
  44. package/linthost/engine.go +368 -53
  45. package/linthost/escape_string.go +77 -0
  46. package/linthost/fix.go +133 -23
  47. package/linthost/flags_gen.go +18 -17
  48. package/linthost/format.go +73 -167
  49. package/linthost/format_editor_settings.go +134 -26
  50. package/linthost/grapheme_tables_gen.go +1920 -0
  51. package/linthost/host.go +160 -61
  52. package/linthost/literal_escape_scan.go +117 -0
  53. package/linthost/lsp.go +545 -98
  54. package/linthost/numeric_literal_precision.go +228 -0
  55. package/linthost/polyfill_browserslist.go +1439 -0
  56. package/linthost/polyfill_browserslist_config.go +603 -0
  57. package/linthost/polyfill_corejs.go +750 -0
  58. package/linthost/polyfill_data.go +113 -0
  59. package/linthost/polyfill_data_gen.json +39868 -0
  60. package/linthost/print_nodes_array.go +7 -1
  61. package/linthost/print_nodes_object.go +7 -1
  62. package/linthost/project_engine.go +151 -0
  63. package/linthost/project_rules.go +78 -0
  64. package/linthost/regex_clean.go +191 -0
  65. package/linthost/regex_tree.go +1544 -0
  66. package/linthost/regex_tree_optimizer.go +1685 -0
  67. package/linthost/regexp_cache.go +44 -0
  68. package/linthost/rule_codes.go +105 -0
  69. package/linthost/rule_codes.json +745 -0
  70. package/linthost/rules_ban_ts_comment.go +312 -0
  71. package/linthost/rules_boundaries.go +128 -43
  72. package/linthost/rules_boundaries_dependencies.go +1013 -0
  73. package/linthost/rules_core_extra.go +62 -92
  74. package/linthost/rules_cypress.go +1 -1
  75. package/linthost/rules_default_case.go +87 -11
  76. package/linthost/rules_dupes.go +38 -13
  77. package/linthost/rules_empty.go +188 -20
  78. package/linthost/rules_escape.go +62 -48
  79. package/linthost/rules_format_arrow_parens.go +19 -5
  80. package/linthost/rules_format_bracket_spacing.go +1 -1
  81. package/linthost/rules_format_clause_join.go +1 -1
  82. package/linthost/rules_format_declaration_header.go +31 -19
  83. package/linthost/rules_format_indent.go +1 -1
  84. package/linthost/rules_format_jsdoc.go +1 -1
  85. package/linthost/rules_format_orphan_semi.go +1 -1
  86. package/linthost/rules_format_parameter_properties.go +17 -7
  87. package/linthost/rules_format_print_width.go +1 -1
  88. package/linthost/rules_format_quote_props.go +1 -1
  89. package/linthost/rules_format_quotes.go +1 -1
  90. package/linthost/rules_format_semi.go +106 -79
  91. package/linthost/rules_format_sort_imports.go +79 -45
  92. package/linthost/rules_format_statement_split.go +1 -1
  93. package/linthost/rules_format_ternary_nullish_parens.go +1 -1
  94. package/linthost/rules_format_trailing_comma.go +64 -1
  95. package/linthost/rules_format_whitespace.go +1 -1
  96. package/linthost/rules_functional.go +21 -22
  97. package/linthost/rules_gap.go +80 -3
  98. package/linthost/rules_grouped_accessor_pairs.go +84 -25
  99. package/linthost/rules_guard_for_in.go +64 -147
  100. package/linthost/rules_jsx_a11y.go +232 -22
  101. package/linthost/rules_logic.go +84 -38
  102. package/linthost/rules_nextjs.go +105 -16
  103. package/linthost/rules_no_class_assign.go +70 -0
  104. package/linthost/rules_no_duplicate_imports.go +266 -0
  105. package/linthost/rules_no_else_return.go +156 -38
  106. package/linthost/rules_no_extend_native.go +113 -17
  107. package/linthost/rules_no_fallthrough.go +1013 -0
  108. package/linthost/rules_no_func_assign.go +70 -0
  109. package/linthost/rules_no_import_assign.go +278 -86
  110. package/linthost/rules_no_inner_declarations.go +186 -0
  111. package/linthost/rules_no_loop_func.go +438 -53
  112. package/linthost/rules_no_mixed_operators.go +208 -52
  113. package/linthost/rules_no_param_reassign.go +376 -61
  114. package/linthost/rules_no_promise_executor_return.go +156 -0
  115. package/linthost/rules_no_restricted_imports.go +1013 -57
  116. package/linthost/rules_no_restricted_syntax.go +190 -26
  117. package/linthost/rules_no_useless_constructor.go +6 -36
  118. package/linthost/rules_problems.go +154 -311
  119. package/linthost/rules_promise.go +2578 -109
  120. package/linthost/rules_react_extras.go +71 -45
  121. package/linthost/rules_react_hooks.go +18 -2
  122. package/linthost/rules_react_perf.go +1 -0
  123. package/linthost/rules_react_refresh.go +1 -1
  124. package/linthost/rules_regexp.go +230 -14
  125. package/linthost/rules_security.go +45 -19
  126. package/linthost/rules_self.go +21 -1
  127. package/linthost/rules_solid.go +13 -7
  128. package/linthost/rules_storybook.go +1 -1
  129. package/linthost/rules_suggestions.go +490 -84
  130. package/linthost/rules_testing_library.go +12 -1
  131. package/linthost/rules_ts.go +227 -54
  132. package/linthost/rules_ts_async.go +0 -131
  133. package/linthost/rules_ts_no_base_to_string.go +3 -4
  134. package/linthost/rules_ts_no_misused_promises.go +817 -0
  135. package/linthost/rules_ts_no_restricted_types.go +320 -34
  136. package/linthost/rules_ts_no_unnecessary_qualifier.go +132 -55
  137. package/linthost/rules_ts_no_unsafe_assignment.go +608 -60
  138. package/linthost/rules_ts_require_array_sort_compare.go +5 -5
  139. package/linthost/rules_ts_switch_exhaustiveness_check.go +556 -86
  140. package/linthost/rules_unicorn_better_regex.go +224 -16
  141. package/linthost/rules_unicorn_consistent_destructuring.go +1002 -7
  142. package/linthost/rules_unicorn_consistent_existence_index_check.go +208 -29
  143. package/linthost/rules_unicorn_consistent_function_scoping.go +1041 -8
  144. package/linthost/rules_unicorn_consistent_template_literal_escape.go +178 -7
  145. package/linthost/rules_unicorn_escape_case.go +30 -19
  146. package/linthost/rules_unicorn_filename_case.go +763 -16
  147. package/linthost/rules_unicorn_import_style.go +783 -17
  148. package/linthost/rules_unicorn_isolated_functions.go +934 -14
  149. package/linthost/rules_unicorn_no_hex_escape.go +26 -11
  150. package/linthost/rules_unicorn_no_typeof_undefined.go +234 -25
  151. package/linthost/rules_unicorn_no_unnecessary_polyfills.go +376 -21
  152. package/linthost/rules_unicorn_no_unused_properties.go +756 -19
  153. package/linthost/rules_unicorn_number_literal_case.go +58 -39
  154. package/linthost/rules_unicorn_prefer_at.go +7 -3
  155. package/linthost/rules_unicorn_prefer_number_properties.go +381 -48
  156. package/linthost/rules_unicorn_prefer_optional_catch_binding.go +24 -24
  157. package/linthost/rules_unicorn_prefer_simple_condition_first.go +597 -19
  158. package/linthost/rules_unicorn_prefer_string_raw.go +147 -16
  159. package/linthost/rules_unicorn_prevent_abbreviations.go +1963 -95
  160. package/linthost/rules_unicorn_string_content.go +611 -15
  161. package/linthost/rules_unicorn_switch_case_break_position.go +172 -15
  162. package/linthost/rules_unicorn_template_indent.go +923 -18
  163. package/linthost/rules_unicorn_text_encoding_identifier_case.go +202 -42
  164. package/linthost/rules_unicorn_throw_new_error.go +218 -26
  165. package/linthost/rules_var.go +829 -142
  166. package/linthost/string_length.go +222 -0
  167. package/package.json +4 -3
  168. package/rule/project.go +145 -0
  169. package/rule/rule.go +58 -1
  170. package/src/index.ts +58 -17
  171. package/src/structures/ITtscLintConfig.ts +3 -0
  172. package/src/structures/TtscLintRuleSetting.ts +3 -2
  173. package/src/structures/format/ITtscLintFormat.ts +3 -2
  174. package/src/structures/format/ITtscLintFormatSortImports.ts +21 -4
  175. package/src/structures/rules/ITtscLintBoundariesRuleOptions.ts +122 -8
  176. package/src/structures/rules/ITtscLintBoundariesRules.ts +6 -8
  177. package/src/structures/rules/ITtscLintContributorRules.ts +18 -2
  178. package/src/structures/rules/ITtscLintCoreRuleOptions.ts +465 -0
  179. package/src/structures/rules/ITtscLintCoreRules.ts +122 -67
  180. package/src/structures/rules/ITtscLintRegexpRules.ts +7 -2
  181. package/src/structures/rules/ITtscLintRuleOptionsMap.ts +88 -7
  182. package/src/structures/rules/ITtscLintRules.ts +7 -2
  183. package/src/structures/rules/ITtscLintSolidRules.ts +0 -9
  184. package/src/structures/rules/ITtscLintTypeScriptRuleOptions.ts +248 -0
  185. package/src/structures/rules/ITtscLintTypeScriptRules.ts +64 -42
  186. package/src/structures/rules/ITtscLintUnicornRuleOptions.ts +350 -0
  187. package/src/structures/rules/ITtscLintUnicornRules.ts +60 -36
  188. package/src/structures/rules/index.ts +3 -0
@@ -1,34 +1,28 @@
1
- // typescript/no-unsafe-assignment: assigning an `any`-typed value into
2
- // a concretely typed location erases the static guarantee the target
3
- // declaration carries. `const num: number = anyValue;` compiles, but
4
- // every later use of `num` operates on whatever `anyValue` actually
5
- // holds at runtime. typescript-eslint:
6
- // https://typescript-eslint.io/rules/no-unsafe-assignment/
7
- //
8
- // Type-aware. The rule visits two shapes:
9
- //
10
- // - `VariableDeclaration` — the initializer of `let x: T = ...` or
11
- // `const x: T = ...`. Without a typed target the assignment is
12
- // trivially safe (the local becomes `any` too), so the rule
13
- // requires an explicit type annotation OR a target that resolves
14
- // to a non-`any` type.
15
- // - `BinaryExpression` with `=` operator — a plain reassignment like
16
- // `x = anyValue`. The LHS already has a static type from its
17
- // declaration; the rule fires when the RHS is `any` and the LHS is
18
- // not.
19
- //
20
- // As with the rest of the unsafe-* family `unknown` is intentionally
21
- // NOT flagged: it forces a narrowing assertion before the value can be
22
- // stored in a typed slot, which is the explicit ergonomic the upstream
23
- // rule pushes toward.
1
+ // typescript/no-unsafe-assignment rejects `any` values that escape through an
2
+ // assignment boundary. It follows @typescript-eslint/no-unsafe-assignment's
3
+ // assignment sites and generic comparison boundary: direct `any` is unsafe
4
+ // unless the receiver is `unknown`, while nested type arguments are compared
5
+ // only when both sides are references to the same generic target.
24
6
  package linthost
25
7
 
26
8
  import (
9
+ "fmt"
10
+ "strconv"
11
+ "strings"
12
+
27
13
  shimast "github.com/microsoft/typescript-go/shim/ast"
14
+ shimchecker "github.com/microsoft/typescript-go/shim/checker"
28
15
  )
29
16
 
30
17
  type noUnsafeAssignment struct{}
31
18
 
19
+ type noUnsafeAssignmentComparison uint8
20
+
21
+ const (
22
+ noUnsafeAssignmentComparisonNone noUnsafeAssignmentComparison = iota
23
+ noUnsafeAssignmentComparisonBasic
24
+ )
25
+
32
26
  func (noUnsafeAssignment) Name() string { return "typescript/no-unsafe-assignment" }
33
27
  func (noUnsafeAssignment) NeedsTypeChecker() bool {
34
28
  return true
@@ -36,66 +30,620 @@ func (noUnsafeAssignment) NeedsTypeChecker() bool {
36
30
  func (noUnsafeAssignment) Visits() []shimast.Kind {
37
31
  return []shimast.Kind{
38
32
  shimast.KindVariableDeclaration,
33
+ shimast.KindParameter,
34
+ shimast.KindPropertyDeclaration,
35
+ shimast.KindBindingElement,
39
36
  shimast.KindBinaryExpression,
37
+ shimast.KindPropertyAssignment,
38
+ shimast.KindShorthandPropertyAssignment,
39
+ shimast.KindSpreadElement,
40
+ shimast.KindJsxAttribute,
40
41
  }
41
42
  }
43
+
42
44
  func (noUnsafeAssignment) Check(ctx *Context, node *shimast.Node) {
43
- if ctx.Checker == nil {
45
+ if ctx.Checker == nil || node == nil {
44
46
  return
45
47
  }
48
+
46
49
  switch node.Kind {
47
50
  case shimast.KindVariableDeclaration:
48
- decl := node.AsVariableDeclaration()
49
- if decl == nil || decl.Initializer == nil {
50
- return
51
+ noUnsafeAssignmentCheckVariable(ctx, node)
52
+ case shimast.KindParameter:
53
+ noUnsafeAssignmentCheckParameter(ctx, node)
54
+ case shimast.KindPropertyDeclaration:
55
+ noUnsafeAssignmentCheckPropertyDeclaration(ctx, node)
56
+ case shimast.KindBindingElement:
57
+ noUnsafeAssignmentCheckBindingDefault(ctx, node)
58
+ case shimast.KindBinaryExpression:
59
+ noUnsafeAssignmentCheckBinary(ctx, node)
60
+ case shimast.KindPropertyAssignment:
61
+ noUnsafeAssignmentCheckObjectProperty(ctx, node)
62
+ case shimast.KindShorthandPropertyAssignment:
63
+ noUnsafeAssignmentCheckShorthandProperty(ctx, node)
64
+ case shimast.KindSpreadElement:
65
+ noUnsafeAssignmentCheckArraySpread(ctx, node)
66
+ case shimast.KindJsxAttribute:
67
+ noUnsafeAssignmentCheckJsxAttribute(ctx, node)
68
+ }
69
+ }
70
+
71
+ func noUnsafeAssignmentCheckVariable(ctx *Context, node *shimast.Node) {
72
+ decl := node.AsVariableDeclaration()
73
+ if decl == nil || decl.Name() == nil || decl.Initializer == nil {
74
+ return
75
+ }
76
+ comparison := noUnsafeAssignmentComparisonNone
77
+ if decl.Type != nil {
78
+ comparison = noUnsafeAssignmentComparisonBasic
79
+ }
80
+ noUnsafeAssignmentCheckSite(ctx, decl.Name(), decl.Initializer, node, comparison)
81
+ }
82
+
83
+ func noUnsafeAssignmentCheckParameter(ctx *Context, node *shimast.Node) {
84
+ decl := node.AsParameterDeclaration()
85
+ if decl == nil || decl.Name() == nil || decl.Initializer == nil {
86
+ return
87
+ }
88
+ // A default initializer is an AssignmentPattern upstream. The parameter has
89
+ // an established receiver type even when it has no written annotation.
90
+ noUnsafeAssignmentCheckSite(
91
+ ctx,
92
+ decl.Name(),
93
+ decl.Initializer,
94
+ node,
95
+ noUnsafeAssignmentComparisonBasic,
96
+ )
97
+ }
98
+
99
+ func noUnsafeAssignmentCheckPropertyDeclaration(ctx *Context, node *shimast.Node) {
100
+ decl := node.AsPropertyDeclaration()
101
+ if decl == nil || decl.Name() == nil || decl.Initializer == nil {
102
+ return
103
+ }
104
+ comparison := noUnsafeAssignmentComparisonNone
105
+ if decl.Type != nil {
106
+ comparison = noUnsafeAssignmentComparisonBasic
107
+ }
108
+ noUnsafeAssignmentCheck(
109
+ ctx,
110
+ decl.Name(),
111
+ decl.Initializer,
112
+ node,
113
+ comparison,
114
+ nil,
115
+ )
116
+ }
117
+
118
+ func noUnsafeAssignmentCheckBindingDefault(ctx *Context, node *shimast.Node) {
119
+ binding := node.AsBindingElement()
120
+ if binding == nil || binding.Name() == nil || binding.Initializer == nil {
121
+ return
122
+ }
123
+ noUnsafeAssignmentCheckSite(
124
+ ctx,
125
+ binding.Name(),
126
+ binding.Initializer,
127
+ node,
128
+ noUnsafeAssignmentComparisonBasic,
129
+ )
130
+ }
131
+
132
+ func noUnsafeAssignmentCheckBinary(ctx *Context, node *shimast.Node) {
133
+ expression := node.AsBinaryExpression()
134
+ if expression == nil || expression.OperatorToken == nil ||
135
+ expression.OperatorToken.Kind != shimast.KindEqualsToken ||
136
+ expression.Left == nil || expression.Right == nil {
137
+ return
138
+ }
139
+ noUnsafeAssignmentCheckSite(
140
+ ctx,
141
+ expression.Left,
142
+ expression.Right,
143
+ node,
144
+ noUnsafeAssignmentComparisonBasic,
145
+ )
146
+ }
147
+
148
+ func noUnsafeAssignmentCheckObjectProperty(ctx *Context, node *shimast.Node) {
149
+ property := node.AsPropertyAssignment()
150
+ if property == nil || property.Name() == nil || property.Initializer == nil ||
151
+ node.Parent == nil || node.Parent.Kind != shimast.KindObjectLiteralExpression ||
152
+ isDestructuringAssignmentTarget(node) {
153
+ return
154
+ }
155
+ receiverType := ctx.Checker.GetContextualTypeForObjectLiteralElement(node, 0)
156
+ noUnsafeAssignmentCheck(
157
+ ctx,
158
+ property.Name(),
159
+ property.Initializer,
160
+ node,
161
+ noUnsafeAssignmentComparisonBasic,
162
+ receiverType,
163
+ )
164
+ }
165
+
166
+ func noUnsafeAssignmentCheckShorthandProperty(ctx *Context, node *shimast.Node) {
167
+ property := node.AsShorthandPropertyAssignment()
168
+ if property == nil || property.Name() == nil {
169
+ return
170
+ }
171
+
172
+ // `{ value = fallback }` in a destructuring assignment is represented by a
173
+ // shorthand property with an object-assignment initializer in the tsgo AST.
174
+ if property.ObjectAssignmentInitializer != nil {
175
+ noUnsafeAssignmentCheckSite(
176
+ ctx,
177
+ property.Name(),
178
+ property.ObjectAssignmentInitializer,
179
+ node,
180
+ noUnsafeAssignmentComparisonBasic,
181
+ )
182
+ return
183
+ }
184
+
185
+ if node.Parent == nil || node.Parent.Kind != shimast.KindObjectLiteralExpression ||
186
+ isDestructuringAssignmentTarget(node) {
187
+ return
188
+ }
189
+ receiverType := ctx.Checker.GetContextualTypeForObjectLiteralElement(node, 0)
190
+ noUnsafeAssignmentCheck(
191
+ ctx,
192
+ property.Name(),
193
+ property.Name(),
194
+ node,
195
+ noUnsafeAssignmentComparisonBasic,
196
+ receiverType,
197
+ )
198
+ }
199
+
200
+ func noUnsafeAssignmentCheckArraySpread(ctx *Context, node *shimast.Node) {
201
+ spread := node.AsSpreadElement()
202
+ if spread == nil || spread.Expression == nil || node.Parent == nil ||
203
+ node.Parent.Kind != shimast.KindArrayLiteralExpression ||
204
+ isDestructuringAssignmentTarget(node) {
205
+ return
206
+ }
207
+ senderType := ctx.Checker.GetTypeAtLocation(spread.Expression)
208
+ if typeIsUnsafeAny(senderType) || noUnsafeAssignmentIsAnyArray(ctx.Checker, senderType) {
209
+ ctx.Report(node, "Unsafe spread of an `any` value in an array.")
210
+ }
211
+ }
212
+
213
+ func noUnsafeAssignmentCheckJsxAttribute(ctx *Context, node *shimast.Node) {
214
+ attribute := node.AsJsxAttribute()
215
+ if attribute == nil || attribute.Initializer == nil ||
216
+ attribute.Initializer.Kind != shimast.KindJsxExpression {
217
+ return
218
+ }
219
+ container := attribute.Initializer.AsJsxExpression()
220
+ if container == nil || container.Expression == nil {
221
+ return
222
+ }
223
+ receiverType := ctx.Checker.GetContextualTypeForJsxAttribute(node)
224
+ noUnsafeAssignmentCheck(
225
+ ctx,
226
+ attribute.Name(),
227
+ container.Expression,
228
+ container.Expression,
229
+ noUnsafeAssignmentComparisonBasic,
230
+ receiverType,
231
+ )
232
+ }
233
+
234
+ // noUnsafeAssignmentCheckSite checks the main assignment and then, only when
235
+ // that boundary did not report, checks unsafe leaves exposed by destructuring.
236
+ func noUnsafeAssignmentCheckSite(
237
+ ctx *Context,
238
+ receiverNode *shimast.Node,
239
+ senderNode *shimast.Node,
240
+ reportingNode *shimast.Node,
241
+ comparison noUnsafeAssignmentComparison,
242
+ ) {
243
+ if noUnsafeAssignmentCheck(
244
+ ctx,
245
+ receiverNode,
246
+ senderNode,
247
+ reportingNode,
248
+ comparison,
249
+ nil,
250
+ ) {
251
+ return
252
+ }
253
+ senderType := ctx.Checker.GetTypeAtLocation(stripParens(senderNode))
254
+ if noUnsafeAssignmentCheckArrayDestructure(ctx, stripParens(receiverNode), senderType, senderNode) {
255
+ return
256
+ }
257
+ noUnsafeAssignmentCheckObjectDestructure(ctx, stripParens(receiverNode), senderType, senderNode)
258
+ }
259
+
260
+ // noUnsafeAssignmentCheck returns true when it reports this assignment.
261
+ func noUnsafeAssignmentCheck(
262
+ ctx *Context,
263
+ receiverNode *shimast.Node,
264
+ senderNode *shimast.Node,
265
+ reportingNode *shimast.Node,
266
+ comparison noUnsafeAssignmentComparison,
267
+ contextualReceiver *shimchecker.Type,
268
+ ) bool {
269
+ senderNode = stripParens(senderNode)
270
+ if receiverNode == nil || senderNode == nil {
271
+ return false
272
+ }
273
+
274
+ receiverType := contextualReceiver
275
+ if receiverType == nil {
276
+ receiverType = ctx.Checker.GetTypeAtLocation(stripParens(receiverNode))
277
+ }
278
+ senderType := ctx.Checker.GetTypeAtLocation(senderNode)
279
+ if receiverType == nil || senderType == nil {
280
+ return false
281
+ }
282
+
283
+ if typeIsUnsafeAny(senderType) {
284
+ if noUnsafeAssignmentIsUnknown(receiverType) {
285
+ return false
51
286
  }
52
- // Only flag when the target has an explicit type annotation —
53
- // `const x = anyValue;` widens `x` to `any` too, which is the
54
- // `no-explicit-any` family's concern, not this rule's.
55
- if decl.Type == nil {
56
- return
287
+ ctx.Report(reportingNode, "Unsafe assignment of an `any` value.")
288
+ return true
289
+ }
290
+
291
+ if comparison == noUnsafeAssignmentComparisonNone {
292
+ return false
293
+ }
294
+
295
+ result := noUnsafeAssignmentCompareTypes(
296
+ ctx.Checker,
297
+ senderType,
298
+ receiverType,
299
+ senderNode,
300
+ make(map[*shimchecker.Type]map[*shimchecker.Type]struct{}),
301
+ )
302
+ if result == nil {
303
+ return false
304
+ }
305
+ ctx.Report(
306
+ reportingNode,
307
+ fmt.Sprintf(
308
+ "Unsafe assignment of type `%s` to a variable of type `%s`.",
309
+ ctx.Checker.TypeToString(result.sender),
310
+ ctx.Checker.TypeToString(result.receiver),
311
+ ),
312
+ )
313
+ return true
314
+ }
315
+
316
+ type noUnsafeAssignmentTypePair struct {
317
+ sender *shimchecker.Type
318
+ receiver *shimchecker.Type
319
+ }
320
+
321
+ func noUnsafeAssignmentCompareTypes(
322
+ checker *shimchecker.Checker,
323
+ sender *shimchecker.Type,
324
+ receiver *shimchecker.Type,
325
+ senderNode *shimast.Node,
326
+ visited map[*shimchecker.Type]map[*shimchecker.Type]struct{},
327
+ ) *noUnsafeAssignmentTypePair {
328
+ if sender == nil || receiver == nil {
329
+ return nil
330
+ }
331
+ if typeIsUnsafeAny(sender) {
332
+ if noUnsafeAssignmentIsUnknown(receiver) || typeIsUnsafeAny(receiver) {
333
+ return nil
334
+ }
335
+ return &noUnsafeAssignmentTypePair{sender: sender, receiver: receiver}
336
+ }
337
+
338
+ receivers := visited[sender]
339
+ if receivers == nil {
340
+ receivers = make(map[*shimchecker.Type]struct{})
341
+ visited[sender] = receivers
342
+ } else if _, ok := receivers[receiver]; ok {
343
+ return nil
344
+ }
345
+ receivers[receiver] = struct{}{}
346
+
347
+ if !noUnsafeAssignmentIsTypeReference(sender) ||
348
+ !noUnsafeAssignmentIsTypeReference(receiver) ||
349
+ sender.Target() != receiver.Target() {
350
+ return nil
351
+ }
352
+ if noUnsafeAssignmentIsEmptyMapConstructor(senderNode) {
353
+ return nil
354
+ }
355
+
356
+ senderArguments := checker.GetTypeArguments(sender)
357
+ receiverArguments := checker.GetTypeArguments(receiver)
358
+ for index, senderArgument := range senderArguments {
359
+ if index >= len(receiverArguments) {
360
+ return nil
361
+ }
362
+ if noUnsafeAssignmentCompareTypes(
363
+ checker,
364
+ senderArgument,
365
+ receiverArguments[index],
366
+ senderNode,
367
+ visited,
368
+ ) != nil {
369
+ return &noUnsafeAssignmentTypePair{sender: sender, receiver: receiver}
370
+ }
371
+ }
372
+ return nil
373
+ }
374
+
375
+ func noUnsafeAssignmentIsTypeReference(value *shimchecker.Type) bool {
376
+ return value != nil &&
377
+ value.Flags()&shimchecker.TypeFlagsObject != 0 &&
378
+ value.ObjectFlags()&shimchecker.ObjectFlagsReference != 0
379
+ }
380
+
381
+ func noUnsafeAssignmentIsUnknown(value *shimchecker.Type) bool {
382
+ return value != nil && value.Flags()&shimchecker.TypeFlagsUnknown != 0
383
+ }
384
+
385
+ func noUnsafeAssignmentIsEmptyMapConstructor(node *shimast.Node) bool {
386
+ node = stripParens(node)
387
+ if node == nil || node.Kind != shimast.KindNewExpression {
388
+ return false
389
+ }
390
+ expression := node.AsNewExpression()
391
+ if expression == nil || identifierText(expression.Expression) != "Map" ||
392
+ expression.TypeArguments != nil {
393
+ return false
394
+ }
395
+ return expression.Arguments == nil || len(expression.Arguments.Nodes) == 0
396
+ }
397
+
398
+ func noUnsafeAssignmentIsAnyArray(checker *shimchecker.Checker, value *shimchecker.Type) bool {
399
+ if checker == nil || value == nil {
400
+ return false
401
+ }
402
+ return typeIsUnsafeAny(checker.GetElementTypeOfArrayType(value))
403
+ }
404
+
405
+ func noUnsafeAssignmentCheckArrayDestructure(
406
+ ctx *Context,
407
+ receiver *shimast.Node,
408
+ senderType *shimchecker.Type,
409
+ senderNode *shimast.Node,
410
+ ) bool {
411
+ if receiver == nil || senderType == nil ||
412
+ (receiver.Kind != shimast.KindArrayBindingPattern &&
413
+ receiver.Kind != shimast.KindArrayLiteralExpression) {
414
+ return false
415
+ }
416
+ if noUnsafeAssignmentIsAnyArray(ctx.Checker, senderType) {
417
+ ctx.Report(receiver, "Unsafe array destructuring of an `any` array value.")
418
+ return true
419
+ }
420
+ if !shimchecker.IsTupleType(senderType) {
421
+ return false
422
+ }
423
+
424
+ senderElements := ctx.Checker.GetTypeArguments(senderType)
425
+ receiverElements := noUnsafeAssignmentArrayPatternElements(receiver)
426
+ didReport := false
427
+ for index, element := range receiverElements {
428
+ if element == nil || index >= len(senderElements) {
429
+ continue
430
+ }
431
+ target, reportingNode, canRecurse := noUnsafeAssignmentArrayPatternTarget(element)
432
+ if target == nil {
433
+ continue
57
434
  }
58
- rhs := stripParens(decl.Initializer)
59
- if rhs == nil {
60
- return
435
+ elementType := senderElements[index]
436
+ if typeIsUnsafeAny(elementType) {
437
+ ctx.Report(reportingNode, "Unsafe array destructuring of a tuple element with an `any` value.")
438
+ didReport = true
439
+ continue
61
440
  }
62
- rhsType := ctx.Checker.GetTypeAtLocation(rhs)
63
- if !typeIsUnsafeAny(rhsType) {
64
- return
441
+ if !canRecurse {
442
+ continue
65
443
  }
66
- lhsType := ctx.Checker.GetTypeFromTypeNode(decl.Type)
67
- if typeIsUnsafeAny(lhsType) {
68
- return
444
+ if noUnsafeAssignmentCheckArrayDestructure(ctx, target, elementType, senderNode) ||
445
+ noUnsafeAssignmentCheckObjectDestructure(ctx, target, elementType, senderNode) {
446
+ didReport = true
69
447
  }
70
- ctx.Report(node, noUnsafeAssignmentMessage)
448
+ }
449
+ return didReport
450
+ }
451
+
452
+ func noUnsafeAssignmentArrayPatternElements(receiver *shimast.Node) []*shimast.Node {
453
+ switch receiver.Kind {
454
+ case shimast.KindArrayBindingPattern:
455
+ pattern := receiver.AsBindingPattern()
456
+ if pattern != nil && pattern.Elements != nil {
457
+ return pattern.Elements.Nodes
458
+ }
459
+ case shimast.KindArrayLiteralExpression:
460
+ pattern := receiver.AsArrayLiteralExpression()
461
+ if pattern != nil && pattern.Elements != nil {
462
+ return pattern.Elements.Nodes
463
+ }
464
+ }
465
+ return nil
466
+ }
467
+
468
+ func noUnsafeAssignmentArrayPatternTarget(element *shimast.Node) (
469
+ target *shimast.Node,
470
+ reportingNode *shimast.Node,
471
+ canRecurse bool,
472
+ ) {
473
+ switch element.Kind {
474
+ case shimast.KindOmittedExpression, shimast.KindSpreadElement:
475
+ return nil, nil, false
476
+ case shimast.KindBindingElement:
477
+ binding := element.AsBindingElement()
478
+ if binding == nil || binding.DotDotDotToken != nil || binding.Name() == nil {
479
+ return nil, nil, false
480
+ }
481
+ if binding.Initializer != nil {
482
+ return binding.Name(), element, false
483
+ }
484
+ return binding.Name(), binding.Name(), true
71
485
  case shimast.KindBinaryExpression:
72
- bin := node.AsBinaryExpression()
73
- if bin == nil || bin.OperatorToken == nil {
74
- return
486
+ expression := element.AsBinaryExpression()
487
+ if expression == nil || expression.OperatorToken == nil ||
488
+ expression.OperatorToken.Kind != shimast.KindEqualsToken {
489
+ return element, element, true
75
490
  }
76
- if bin.OperatorToken.Kind != shimast.KindEqualsToken {
77
- return
491
+ return expression.Left, element, false
492
+ default:
493
+ return element, element, true
494
+ }
495
+ }
496
+
497
+ func noUnsafeAssignmentCheckObjectDestructure(
498
+ ctx *Context,
499
+ receiver *shimast.Node,
500
+ senderType *shimchecker.Type,
501
+ senderNode *shimast.Node,
502
+ ) bool {
503
+ if receiver == nil || senderType == nil ||
504
+ (receiver.Kind != shimast.KindObjectBindingPattern &&
505
+ receiver.Kind != shimast.KindObjectLiteralExpression) {
506
+ return false
507
+ }
508
+
509
+ properties := make(map[string]*shimchecker.Type)
510
+ for _, symbol := range shimchecker.Checker_getPropertiesOfType(ctx.Checker, senderType) {
511
+ if symbol == nil {
512
+ continue
78
513
  }
79
- if bin.Left == nil || bin.Right == nil {
80
- return
514
+ properties[symbol.Name] = shimchecker.Checker_getTypeOfSymbolAtLocation(
515
+ ctx.Checker,
516
+ symbol,
517
+ senderNode,
518
+ )
519
+ }
520
+
521
+ didReport := false
522
+ for _, property := range noUnsafeAssignmentObjectPatternProperties(receiver) {
523
+ keyNode, target, reportingNode, canRecurse := noUnsafeAssignmentObjectPatternTarget(property)
524
+ key, ok := noUnsafeAssignmentPropertyName(keyNode)
525
+ if !ok || target == nil {
526
+ continue
527
+ }
528
+ propertyType := properties[key]
529
+ if propertyType == nil {
530
+ continue
81
531
  }
82
- rhs := stripParens(bin.Right)
83
- if rhs == nil {
84
- return
532
+ if typeIsUnsafeAny(propertyType) {
533
+ ctx.Report(reportingNode, "Unsafe object destructuring of a property with an `any` value.")
534
+ didReport = true
535
+ continue
85
536
  }
86
- rhsType := ctx.Checker.GetTypeAtLocation(rhs)
87
- if !typeIsUnsafeAny(rhsType) {
88
- return
537
+ if !canRecurse {
538
+ continue
89
539
  }
90
- lhsType := ctx.Checker.GetTypeAtLocation(bin.Left)
91
- if typeIsUnsafeAny(lhsType) {
92
- return
540
+ if noUnsafeAssignmentCheckArrayDestructure(ctx, target, propertyType, senderNode) ||
541
+ noUnsafeAssignmentCheckObjectDestructure(ctx, target, propertyType, senderNode) {
542
+ didReport = true
93
543
  }
94
- ctx.Report(node, noUnsafeAssignmentMessage)
95
544
  }
545
+ return didReport
96
546
  }
97
547
 
98
- const noUnsafeAssignmentMessage = "Unsafe assignment of a value typed as `any` to a typed target. Narrow the value before storing it."
548
+ func noUnsafeAssignmentObjectPatternProperties(receiver *shimast.Node) []*shimast.Node {
549
+ switch receiver.Kind {
550
+ case shimast.KindObjectBindingPattern:
551
+ pattern := receiver.AsBindingPattern()
552
+ if pattern != nil && pattern.Elements != nil {
553
+ return pattern.Elements.Nodes
554
+ }
555
+ case shimast.KindObjectLiteralExpression:
556
+ pattern := receiver.AsObjectLiteralExpression()
557
+ if pattern != nil && pattern.Properties != nil {
558
+ return pattern.Properties.Nodes
559
+ }
560
+ }
561
+ return nil
562
+ }
563
+
564
+ func noUnsafeAssignmentObjectPatternTarget(property *shimast.Node) (
565
+ key *shimast.Node,
566
+ target *shimast.Node,
567
+ reportingNode *shimast.Node,
568
+ canRecurse bool,
569
+ ) {
570
+ if property == nil {
571
+ return nil, nil, nil, false
572
+ }
573
+ switch property.Kind {
574
+ case shimast.KindBindingElement:
575
+ binding := property.AsBindingElement()
576
+ if binding == nil || binding.DotDotDotToken != nil || binding.Name() == nil {
577
+ return nil, nil, nil, false
578
+ }
579
+ key = binding.PropertyName
580
+ if key == nil {
581
+ key = binding.Name()
582
+ }
583
+ if binding.Initializer != nil {
584
+ return key, binding.Name(), property, false
585
+ }
586
+ return key, binding.Name(), binding.Name(), true
587
+ case shimast.KindPropertyAssignment:
588
+ assignment := property.AsPropertyAssignment()
589
+ if assignment == nil || assignment.Name() == nil || assignment.Initializer == nil {
590
+ return nil, nil, nil, false
591
+ }
592
+ canRecurse = assignment.Initializer.Kind != shimast.KindBinaryExpression
593
+ return assignment.Name(), assignment.Initializer, assignment.Initializer, canRecurse
594
+ case shimast.KindShorthandPropertyAssignment:
595
+ assignment := property.AsShorthandPropertyAssignment()
596
+ if assignment == nil || assignment.Name() == nil {
597
+ return nil, nil, nil, false
598
+ }
599
+ if assignment.ObjectAssignmentInitializer != nil {
600
+ return assignment.Name(), assignment.Name(), property, false
601
+ }
602
+ return assignment.Name(), assignment.Name(), assignment.Name(), true
603
+ }
604
+ return nil, nil, nil, false
605
+ }
606
+
607
+ func noUnsafeAssignmentPropertyName(node *shimast.Node) (string, bool) {
608
+ node = stripParens(node)
609
+ if node == nil {
610
+ return "", false
611
+ }
612
+ switch node.Kind {
613
+ case shimast.KindIdentifier:
614
+ return identifierText(node), true
615
+ case shimast.KindStringLiteral, shimast.KindNoSubstitutionTemplateLiteral:
616
+ return stringLiteralText(node), true
617
+ case shimast.KindNumericLiteral, shimast.KindBigIntLiteral:
618
+ return noUnsafeAssignmentNormalizeNumericKey(numericLiteralText(node)), true
619
+ case shimast.KindTrueKeyword:
620
+ return "true", true
621
+ case shimast.KindFalseKeyword:
622
+ return "false", true
623
+ case shimast.KindNullKeyword:
624
+ return "null", true
625
+ case shimast.KindComputedPropertyName:
626
+ computed := node.AsComputedPropertyName()
627
+ if computed != nil {
628
+ return noUnsafeAssignmentPropertyName(computed.Expression)
629
+ }
630
+ }
631
+ return "", false
632
+ }
633
+
634
+ func noUnsafeAssignmentNormalizeNumericKey(value string) string {
635
+ value = strings.ReplaceAll(strings.TrimSuffix(value, "n"), "_", "")
636
+ if integer, err := strconv.ParseInt(value, 0, 64); err == nil {
637
+ return strconv.FormatInt(integer, 10)
638
+ }
639
+ if integer, err := strconv.ParseUint(value, 0, 64); err == nil {
640
+ return strconv.FormatUint(integer, 10)
641
+ }
642
+ if number, err := strconv.ParseFloat(value, 64); err == nil {
643
+ return strconv.FormatFloat(number, 'g', -1, 64)
644
+ }
645
+ return value
646
+ }
99
647
 
100
648
  func init() {
101
649
  Register(noUnsafeAssignment{})