@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.
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 +129 -25
  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
@@ -0,0 +1,817 @@
1
+ // typescript/no-misused-promises rejects thenables used as booleans and
2
+ // Promise-returning functions supplied to contracts that discard their
3
+ // return value. Every void-return check is driven by the Checker's resolved
4
+ // types; callback syntax and callee spelling are never used as substitutes
5
+ // for the destination contract.
6
+ package linthost
7
+
8
+ import (
9
+ "encoding/json"
10
+
11
+ shimast "github.com/microsoft/typescript-go/shim/ast"
12
+ shimchecker "github.com/microsoft/typescript-go/shim/checker"
13
+ )
14
+
15
+ type noMisusedPromises struct{ optionsRule }
16
+
17
+ type noMisusedPromisesRawOptions struct {
18
+ ChecksConditionals *bool `json:"checksConditionals"`
19
+ ChecksSpreads *bool `json:"checksSpreads"`
20
+ ChecksVoidReturn json.RawMessage `json:"checksVoidReturn"`
21
+ }
22
+
23
+ type noMisusedPromisesVoidOptions struct {
24
+ Arguments *bool `json:"arguments"`
25
+ Attributes *bool `json:"attributes"`
26
+ InheritedMethods *bool `json:"inheritedMethods"`
27
+ Properties *bool `json:"properties"`
28
+ Returns *bool `json:"returns"`
29
+ Variables *bool `json:"variables"`
30
+ }
31
+
32
+ type noMisusedPromisesOptions struct {
33
+ checksConditionals bool
34
+ checksSpreads bool
35
+ arguments bool
36
+ attributes bool
37
+ inheritedMethods bool
38
+ properties bool
39
+ returns bool
40
+ variables bool
41
+ }
42
+
43
+ func init() { Register(noMisusedPromises{}) }
44
+
45
+ func (noMisusedPromises) Name() string { return "typescript/no-misused-promises" }
46
+ func (noMisusedPromises) NeedsTypeChecker() bool {
47
+ return true
48
+ }
49
+ func (noMisusedPromises) Visits() []shimast.Kind {
50
+ return []shimast.Kind{
51
+ shimast.KindIfStatement,
52
+ shimast.KindWhileStatement,
53
+ shimast.KindDoStatement,
54
+ shimast.KindForStatement,
55
+ shimast.KindConditionalExpression,
56
+ shimast.KindPrefixUnaryExpression,
57
+ shimast.KindBinaryExpression,
58
+ shimast.KindCallExpression,
59
+ shimast.KindNewExpression,
60
+ shimast.KindSpreadAssignment,
61
+ shimast.KindVariableDeclaration,
62
+ shimast.KindPropertyAssignment,
63
+ shimast.KindShorthandPropertyAssignment,
64
+ shimast.KindMethodDeclaration,
65
+ shimast.KindReturnStatement,
66
+ shimast.KindJsxAttribute,
67
+ shimast.KindClassDeclaration,
68
+ shimast.KindClassExpression,
69
+ shimast.KindInterfaceDeclaration,
70
+ }
71
+ }
72
+
73
+ func (noMisusedPromises) Check(ctx *Context, node *shimast.Node) {
74
+ if ctx == nil || ctx.Checker == nil || node == nil {
75
+ return
76
+ }
77
+ options := noMisusedPromisesResolveOptions(ctx)
78
+ switch node.Kind {
79
+ case shimast.KindIfStatement:
80
+ if options.checksConditionals {
81
+ if statement := node.AsIfStatement(); statement != nil {
82
+ noMisusedPromisesCheckConditional(ctx, statement.Expression, true)
83
+ }
84
+ }
85
+ case shimast.KindWhileStatement:
86
+ if options.checksConditionals {
87
+ if statement := node.AsWhileStatement(); statement != nil {
88
+ noMisusedPromisesCheckConditional(ctx, statement.Expression, true)
89
+ }
90
+ }
91
+ case shimast.KindDoStatement:
92
+ if options.checksConditionals {
93
+ if statement := node.AsDoStatement(); statement != nil {
94
+ noMisusedPromisesCheckConditional(ctx, statement.Expression, true)
95
+ }
96
+ }
97
+ case shimast.KindForStatement:
98
+ if options.checksConditionals {
99
+ if statement := node.AsForStatement(); statement != nil {
100
+ noMisusedPromisesCheckConditional(ctx, statement.Condition, true)
101
+ }
102
+ }
103
+ case shimast.KindConditionalExpression:
104
+ if options.checksConditionals {
105
+ if expression := node.AsConditionalExpression(); expression != nil {
106
+ noMisusedPromisesCheckConditional(ctx, expression.Condition, true)
107
+ }
108
+ }
109
+ case shimast.KindPrefixUnaryExpression:
110
+ if options.checksConditionals && !noMisusedPromisesConditionalOwnedByAncestor(node) {
111
+ if expression := node.AsPrefixUnaryExpression(); expression != nil && expression.Operator == shimast.KindExclamationToken {
112
+ noMisusedPromisesCheckConditional(ctx, expression.Operand, true)
113
+ }
114
+ }
115
+ case shimast.KindBinaryExpression:
116
+ noMisusedPromisesCheckBinary(ctx, node, options)
117
+ case shimast.KindCallExpression, shimast.KindNewExpression:
118
+ var predicateArgument *shimast.Node
119
+ if options.checksConditionals {
120
+ predicateArgument = noMisusedPromisesCheckPredicate(ctx, node)
121
+ }
122
+ if options.arguments {
123
+ noMisusedPromisesCheckArguments(ctx, node, predicateArgument)
124
+ }
125
+ case shimast.KindSpreadAssignment:
126
+ if options.checksSpreads {
127
+ noMisusedPromisesCheckSpread(ctx, node)
128
+ }
129
+ case shimast.KindVariableDeclaration:
130
+ if options.variables {
131
+ noMisusedPromisesCheckVariable(ctx, node)
132
+ }
133
+ case shimast.KindPropertyAssignment, shimast.KindShorthandPropertyAssignment, shimast.KindMethodDeclaration:
134
+ if options.properties {
135
+ noMisusedPromisesCheckProperty(ctx, node)
136
+ }
137
+ case shimast.KindReturnStatement:
138
+ if options.returns {
139
+ noMisusedPromisesCheckReturn(ctx, node)
140
+ }
141
+ case shimast.KindJsxAttribute:
142
+ if options.attributes {
143
+ noMisusedPromisesCheckAttribute(ctx, node)
144
+ }
145
+ case shimast.KindClassDeclaration, shimast.KindClassExpression, shimast.KindInterfaceDeclaration:
146
+ if options.inheritedMethods {
147
+ noMisusedPromisesCheckInheritedMethods(ctx, node)
148
+ }
149
+ }
150
+ }
151
+
152
+ func noMisusedPromisesResolveOptions(ctx *Context) noMisusedPromisesOptions {
153
+ resolved := noMisusedPromisesOptions{
154
+ checksConditionals: true,
155
+ checksSpreads: true,
156
+ arguments: true,
157
+ attributes: true,
158
+ inheritedMethods: true,
159
+ properties: true,
160
+ returns: true,
161
+ variables: true,
162
+ }
163
+ var raw noMisusedPromisesRawOptions
164
+ if ctx.DecodeOptions(&raw) != nil {
165
+ return resolved
166
+ }
167
+ if raw.ChecksConditionals != nil {
168
+ resolved.checksConditionals = *raw.ChecksConditionals
169
+ }
170
+ if raw.ChecksSpreads != nil {
171
+ resolved.checksSpreads = *raw.ChecksSpreads
172
+ }
173
+ if len(raw.ChecksVoidReturn) == 0 {
174
+ return resolved
175
+ }
176
+ var enabled bool
177
+ if json.Unmarshal(raw.ChecksVoidReturn, &enabled) == nil {
178
+ resolved.arguments = enabled
179
+ resolved.attributes = enabled
180
+ resolved.inheritedMethods = enabled
181
+ resolved.properties = enabled
182
+ resolved.returns = enabled
183
+ resolved.variables = enabled
184
+ return resolved
185
+ }
186
+ var positions noMisusedPromisesVoidOptions
187
+ if json.Unmarshal(raw.ChecksVoidReturn, &positions) != nil {
188
+ return resolved
189
+ }
190
+ noMisusedPromisesSetOption(&resolved.arguments, positions.Arguments)
191
+ noMisusedPromisesSetOption(&resolved.attributes, positions.Attributes)
192
+ noMisusedPromisesSetOption(&resolved.inheritedMethods, positions.InheritedMethods)
193
+ noMisusedPromisesSetOption(&resolved.properties, positions.Properties)
194
+ noMisusedPromisesSetOption(&resolved.returns, positions.Returns)
195
+ noMisusedPromisesSetOption(&resolved.variables, positions.Variables)
196
+ return resolved
197
+ }
198
+
199
+ func noMisusedPromisesSetOption(target *bool, configured *bool) {
200
+ if configured != nil {
201
+ *target = *configured
202
+ }
203
+ }
204
+
205
+ func noMisusedPromisesCheckBinary(ctx *Context, node *shimast.Node, options noMisusedPromisesOptions) {
206
+ expression := node.AsBinaryExpression()
207
+ if expression == nil || expression.OperatorToken == nil {
208
+ return
209
+ }
210
+ switch expression.OperatorToken.Kind {
211
+ case shimast.KindAmpersandAmpersandToken, shimast.KindBarBarToken, shimast.KindQuestionQuestionToken:
212
+ if options.checksConditionals && !noMisusedPromisesConditionalOwnedByAncestor(node) {
213
+ noMisusedPromisesCheckConditional(ctx, node, false)
214
+ }
215
+ case shimast.KindEqualsToken:
216
+ if options.variables &&
217
+ noMisusedPromisesIsVoidReturningFunctionType(ctx.Checker, expression.Left, ctx.Checker.GetTypeAtLocation(expression.Left)) &&
218
+ noMisusedPromisesReturnsThenable(ctx.Checker, expression.Right) {
219
+ ctx.Report(expression.Right, "Promise-returning function assigned where a void return was expected.")
220
+ }
221
+ }
222
+ }
223
+
224
+ func noMisusedPromisesConditionalOwnedByAncestor(node *shimast.Node) bool {
225
+ owned, _ := noMisusedPromisesConditionalContextFromAncestor(node)
226
+ return owned
227
+ }
228
+
229
+ // noMisusedPromisesConditionalContextFromAncestor mirrors the upstream
230
+ // checked-node set without retaining state between Check calls. It reports
231
+ // whether an ancestor's recursive conditional walk reaches node and whether
232
+ // that walk is inside a test expression.
233
+ func noMisusedPromisesConditionalContextFromAncestor(node *shimast.Node) (bool, bool) {
234
+ child := node
235
+ for parent := node.Parent; parent != nil; parent = parent.Parent {
236
+ if parent.Kind == shimast.KindParenthesizedExpression {
237
+ child = parent
238
+ continue
239
+ }
240
+ switch parent.Kind {
241
+ case shimast.KindPrefixUnaryExpression:
242
+ expression := parent.AsPrefixUnaryExpression()
243
+ if expression != nil && expression.Operator == shimast.KindExclamationToken && expression.Operand == child {
244
+ return true, true
245
+ }
246
+ return false, false
247
+ case shimast.KindBinaryExpression:
248
+ expression := parent.AsBinaryExpression()
249
+ if expression == nil || expression.OperatorToken == nil {
250
+ return false, false
251
+ }
252
+ switch expression.OperatorToken.Kind {
253
+ case shimast.KindAmpersandAmpersandToken, shimast.KindBarBarToken, shimast.KindQuestionQuestionToken:
254
+ parentOwned, testExpression := noMisusedPromisesConditionalContextFromAncestor(parent)
255
+ if !parentOwned {
256
+ testExpression = false
257
+ }
258
+ if expression.Left == child {
259
+ return expression.OperatorToken.Kind != shimast.KindQuestionQuestionToken || testExpression, testExpression
260
+ }
261
+ if expression.Right == child {
262
+ return testExpression, testExpression
263
+ }
264
+ return false, false
265
+ }
266
+ return false, false
267
+ case shimast.KindConditionalExpression:
268
+ expression := parent.AsConditionalExpression()
269
+ return expression != nil && expression.Condition == child, true
270
+ case shimast.KindIfStatement:
271
+ statement := parent.AsIfStatement()
272
+ return statement != nil && statement.Expression == child, true
273
+ case shimast.KindWhileStatement:
274
+ statement := parent.AsWhileStatement()
275
+ return statement != nil && statement.Expression == child, true
276
+ case shimast.KindDoStatement:
277
+ statement := parent.AsDoStatement()
278
+ return statement != nil && statement.Expression == child, true
279
+ case shimast.KindForStatement:
280
+ statement := parent.AsForStatement()
281
+ return statement != nil && statement.Condition == child, true
282
+ }
283
+ return false, false
284
+ }
285
+ return false, false
286
+ }
287
+
288
+ func noMisusedPromisesCheckConditional(ctx *Context, expression *shimast.Node, testExpression bool) {
289
+ expression = stripParens(expression)
290
+ if expression == nil {
291
+ return
292
+ }
293
+ if expression.Kind == shimast.KindBinaryExpression {
294
+ binary := expression.AsBinaryExpression()
295
+ if binary == nil || binary.OperatorToken == nil {
296
+ return
297
+ }
298
+ switch binary.OperatorToken.Kind {
299
+ case shimast.KindAmpersandAmpersandToken, shimast.KindBarBarToken, shimast.KindQuestionQuestionToken:
300
+ if binary.OperatorToken.Kind != shimast.KindQuestionQuestionToken || testExpression {
301
+ noMisusedPromisesCheckConditional(ctx, binary.Left, testExpression)
302
+ }
303
+ if testExpression {
304
+ noMisusedPromisesCheckConditional(ctx, binary.Right, true)
305
+ }
306
+ return
307
+ }
308
+ }
309
+ if expression.Kind == shimast.KindPrefixUnaryExpression {
310
+ if prefix := expression.AsPrefixUnaryExpression(); prefix != nil && prefix.Operator == shimast.KindExclamationToken {
311
+ noMisusedPromisesCheckConditional(ctx, prefix.Operand, true)
312
+ return
313
+ }
314
+ }
315
+ if noMisusedPromisesIsAlwaysThenable(ctx.Checker, expression) {
316
+ ctx.Report(expression, "Expected a non-Promise value in a boolean conditional.")
317
+ }
318
+ }
319
+
320
+ func noMisusedPromisesIsAlwaysThenable(checker *shimchecker.Checker, node *shimast.Node) bool {
321
+ if checker == nil || node == nil {
322
+ return false
323
+ }
324
+ valueType := constrainedPromiseType(checker, checker.GetTypeAtLocation(node))
325
+ apparent := checker.GetApparentType(valueType)
326
+ parts := promiseUnionParts(apparent)
327
+ if len(parts) == 0 {
328
+ return false
329
+ }
330
+ for _, part := range parts {
331
+ if !isThenableAtLocation(checker, node, part) {
332
+ return false
333
+ }
334
+ }
335
+ return true
336
+ }
337
+
338
+ func noMisusedPromisesCheckPredicate(ctx *Context, node *shimast.Node) *shimast.Node {
339
+ if node.Kind != shimast.KindCallExpression {
340
+ return nil
341
+ }
342
+ call := node.AsCallExpression()
343
+ if call == nil || call.Arguments == nil || len(call.Arguments.Nodes) == 0 {
344
+ return nil
345
+ }
346
+ receiver, method, ok := noMisusedPromisesStaticMemberParts(call.Expression)
347
+ if !ok || !noMisusedPromisesIsArrayPredicate(method) || !noMisusedPromisesHasNativeArrayReceiver(ctx.Checker, receiver) {
348
+ return nil
349
+ }
350
+ callback := call.Arguments.Nodes[0]
351
+ if noMisusedPromisesReturnsThenable(ctx.Checker, callback) {
352
+ ctx.Report(callback, "Expected a non-Promise value to be returned from this predicate.")
353
+ return callback
354
+ }
355
+ return nil
356
+ }
357
+
358
+ func noMisusedPromisesIsArrayPredicate(method string) bool {
359
+ switch method {
360
+ case "every", "filter", "find", "findIndex", "findLast", "findLastIndex", "some":
361
+ return true
362
+ }
363
+ return false
364
+ }
365
+
366
+ func noMisusedPromisesStaticMemberParts(node *shimast.Node) (*shimast.Node, string, bool) {
367
+ node = stripParens(node)
368
+ if node == nil {
369
+ return nil, "", false
370
+ }
371
+ switch node.Kind {
372
+ case shimast.KindPropertyAccessExpression:
373
+ access := node.AsPropertyAccessExpression()
374
+ if access == nil {
375
+ return nil, "", false
376
+ }
377
+ method := identifierText(access.Name())
378
+ return access.Expression, method, method != ""
379
+ case shimast.KindElementAccessExpression:
380
+ access := node.AsElementAccessExpression()
381
+ if access == nil || access.ArgumentExpression == nil {
382
+ return nil, "", false
383
+ }
384
+ argument := stripParens(access.ArgumentExpression)
385
+ if argument == nil {
386
+ return nil, "", false
387
+ }
388
+ switch argument.Kind {
389
+ case shimast.KindStringLiteral, shimast.KindNoSubstitutionTemplateLiteral:
390
+ method := stringLiteralText(argument)
391
+ return access.Expression, method, method != ""
392
+ }
393
+ }
394
+ return nil, "", false
395
+ }
396
+
397
+ func noMisusedPromisesHasNativeArrayReceiver(checker *shimchecker.Checker, node *shimast.Node) bool {
398
+ if checker == nil || node == nil {
399
+ return false
400
+ }
401
+ return noMisusedPromisesTypeHasNativeArray(
402
+ checker,
403
+ checker.GetTypeAtLocation(node),
404
+ map[*shimchecker.Type]struct{}{},
405
+ )
406
+ }
407
+
408
+ func noMisusedPromisesTypeHasNativeArray(
409
+ checker *shimchecker.Checker,
410
+ valueType *shimchecker.Type,
411
+ seen map[*shimchecker.Type]struct{},
412
+ ) bool {
413
+ if checker == nil || valueType == nil {
414
+ return false
415
+ }
416
+ if _, ok := seen[valueType]; ok {
417
+ return false
418
+ }
419
+ seen[valueType] = struct{}{}
420
+ defer delete(seen, valueType)
421
+
422
+ flags := valueType.Flags()
423
+ if flags&shimchecker.TypeFlagsTypeParameter != 0 {
424
+ constraint := checker.GetBaseConstraintOfType(valueType)
425
+ return constraint != nil && constraint != valueType &&
426
+ noMisusedPromisesTypeHasNativeArray(checker, constraint, seen)
427
+ }
428
+ if flags&(shimchecker.TypeFlagsUnion|shimchecker.TypeFlagsIntersection) != 0 {
429
+ for _, part := range valueType.Types() {
430
+ if noMisusedPromisesTypeHasNativeArray(checker, part, seen) {
431
+ return true
432
+ }
433
+ }
434
+ return false
435
+ }
436
+ return shimchecker.Checker_isArrayType(checker, valueType) || shimchecker.IsTupleType(valueType)
437
+ }
438
+
439
+ func noMisusedPromisesCheckArguments(ctx *Context, node, predicateArgument *shimast.Node) {
440
+ expression, arguments, signatureKind := noMisusedPromisesCallParts(node)
441
+ if expression == nil || len(arguments) == 0 || noMisusedPromisesIsPromiseFinally(ctx.Checker, node, expression) {
442
+ return
443
+ }
444
+ calleeType := ctx.Checker.GetApparentType(ctx.Checker.GetTypeAtLocation(expression))
445
+ for index, argument := range arguments {
446
+ if argument == predicateArgument {
447
+ continue
448
+ }
449
+ acceptsThenable := false
450
+ acceptsVoid := false
451
+ classify := func(target *shimchecker.Type) {
452
+ if target == nil {
453
+ return
454
+ }
455
+ if noMisusedPromisesIsThenableReturningFunctionType(ctx.Checker, node, target) {
456
+ acceptsThenable = true
457
+ } else if noMisusedPromisesIsVoidReturningFunctionType(ctx.Checker, node, target) {
458
+ acceptsVoid = true
459
+ }
460
+ }
461
+ for _, part := range promiseUnionParts(calleeType) {
462
+ for _, signature := range ctx.Checker.GetSignaturesOfType(part, signatureKind) {
463
+ classify(noMisusedPromisesParameterTypeAt(ctx.Checker, expression, signature, index))
464
+ }
465
+ }
466
+ // The declared signatures are authoritative. Contextual typing is only a
467
+ // fallback for checker-instantiated positions that are not recoverable
468
+ // from those signatures. Classifying it eagerly would mistake an inferred
469
+ // `T = () => Promise<void>` for permission when the declaration says
470
+ // `T extends () => void`.
471
+ if !acceptsThenable && !acceptsVoid {
472
+ classify(ctx.Checker.GetContextualTypeForArgumentAtIndex(node, index))
473
+ }
474
+ if acceptsVoid && !acceptsThenable && noMisusedPromisesReturnsThenable(ctx.Checker, argument) {
475
+ ctx.Report(argument, "Promise returned in a function argument where a void return was expected.")
476
+ }
477
+ }
478
+ }
479
+
480
+ func noMisusedPromisesCallParts(node *shimast.Node) (*shimast.Node, []*shimast.Node, shimchecker.SignatureKind) {
481
+ if node.Kind == shimast.KindCallExpression {
482
+ call := node.AsCallExpression()
483
+ if call == nil {
484
+ return nil, nil, shimchecker.SignatureKindCall
485
+ }
486
+ if call.Arguments == nil {
487
+ return call.Expression, nil, shimchecker.SignatureKindCall
488
+ }
489
+ return call.Expression, call.Arguments.Nodes, shimchecker.SignatureKindCall
490
+ }
491
+ if node.Kind == shimast.KindNewExpression {
492
+ construct := node.AsNewExpression()
493
+ if construct == nil {
494
+ return nil, nil, shimchecker.SignatureKindConstruct
495
+ }
496
+ if construct.Arguments == nil {
497
+ return construct.Expression, nil, shimchecker.SignatureKindConstruct
498
+ }
499
+ return construct.Expression, construct.Arguments.Nodes, shimchecker.SignatureKindConstruct
500
+ }
501
+ return nil, nil, shimchecker.SignatureKindCall
502
+ }
503
+
504
+ func noMisusedPromisesParameterTypeAt(
505
+ checker *shimchecker.Checker,
506
+ location *shimast.Node,
507
+ signature *shimchecker.Signature,
508
+ index int,
509
+ ) *shimchecker.Type {
510
+ parameters := shimchecker.Signature_parameters(signature)
511
+ if len(parameters) == 0 {
512
+ return nil
513
+ }
514
+ last := len(parameters) - 1
515
+ if shimchecker.Signature_hasRestParameter(signature) && index >= last {
516
+ restType := constrainedPromiseType(
517
+ checker,
518
+ shimchecker.Checker_getTypeOfSymbolAtLocation(checker, parameters[last], location),
519
+ )
520
+ if restType != nil && shimchecker.IsTupleType(restType) {
521
+ elements := checker.GetTypeArguments(restType)
522
+ offset := index - last
523
+ if offset >= len(elements) {
524
+ return nil
525
+ }
526
+ return elements[offset]
527
+ }
528
+ return shimchecker.Checker_getRestTypeOfSignature(checker, signature)
529
+ }
530
+ if index >= len(parameters) {
531
+ return nil
532
+ }
533
+ return shimchecker.Checker_getTypeOfSymbolAtLocation(checker, parameters[index], location)
534
+ }
535
+
536
+ func noMisusedPromisesIsPromiseFinally(checker *shimchecker.Checker, node, expression *shimast.Node) bool {
537
+ if node.Kind != shimast.KindCallExpression {
538
+ return false
539
+ }
540
+ receiver, method, ok := promisePropertyAccessParts(expression)
541
+ if !ok || method != "finally" || receiver == nil {
542
+ return false
543
+ }
544
+ return isPromiseTypedExpression(checker, checker.GetTypeAtLocation(receiver))
545
+ }
546
+
547
+ func noMisusedPromisesCheckSpread(ctx *Context, node *shimast.Node) {
548
+ spread := node.AsSpreadAssignment()
549
+ if spread != nil && spread.Expression != nil && noMisusedPromisesIsSometimesThenable(ctx.Checker, spread.Expression) {
550
+ ctx.Report(spread.Expression, "Expected a non-Promise value to be spread in an object.")
551
+ }
552
+ }
553
+
554
+ func noMisusedPromisesIsSometimesThenable(checker *shimchecker.Checker, node *shimast.Node) bool {
555
+ if checker == nil || node == nil {
556
+ return false
557
+ }
558
+ valueType := checker.GetApparentType(constrainedPromiseType(checker, checker.GetTypeAtLocation(node)))
559
+ for _, part := range promiseUnionParts(valueType) {
560
+ if isThenableAtLocation(checker, node, part) {
561
+ return true
562
+ }
563
+ }
564
+ return false
565
+ }
566
+
567
+ func noMisusedPromisesCheckVariable(ctx *Context, node *shimast.Node) {
568
+ declaration := node.AsVariableDeclaration()
569
+ if declaration == nil || declaration.Initializer == nil {
570
+ return
571
+ }
572
+ if noMisusedPromisesIsSyncUsingDeclaration(node) && noMisusedPromisesHasThenableDispose(ctx.Checker, declaration.Initializer) {
573
+ ctx.Report(declaration.Initializer, "Synchronous disposal cannot observe a Promise returned by `Symbol.dispose`.")
574
+ return
575
+ }
576
+ if declaration.Type == nil || declaration.Name() == nil {
577
+ return
578
+ }
579
+ targetType := ctx.Checker.GetTypeAtLocation(declaration.Name())
580
+ if noMisusedPromisesHasVoidDispose(ctx.Checker, declaration.Name(), targetType) &&
581
+ noMisusedPromisesHasThenableDispose(ctx.Checker, declaration.Initializer) {
582
+ ctx.Report(declaration.Initializer, "Promise-returning `Symbol.dispose` assigned where synchronous disposal was expected.")
583
+ return
584
+ }
585
+ if noMisusedPromisesIsVoidReturningFunctionType(ctx.Checker, declaration.Initializer, targetType) &&
586
+ noMisusedPromisesReturnsThenable(ctx.Checker, declaration.Initializer) {
587
+ ctx.Report(declaration.Initializer, "Promise-returning function assigned to a variable where a void return was expected.")
588
+ }
589
+ }
590
+
591
+ func noMisusedPromisesIsSyncUsingDeclaration(node *shimast.Node) bool {
592
+ if node == nil || node.Parent == nil || node.Parent.Kind != shimast.KindVariableDeclarationList {
593
+ return false
594
+ }
595
+ flags := shimast.GetCombinedNodeFlags(node.Parent) & shimast.NodeFlagsBlockScoped
596
+ return flags == shimast.NodeFlagsUsing
597
+ }
598
+
599
+ func noMisusedPromisesHasThenableDispose(checker *shimchecker.Checker, initializer *shimast.Node) bool {
600
+ if checker == nil || initializer == nil {
601
+ return false
602
+ }
603
+ propertyName := shimchecker.Checker_getPropertyNameForKnownSymbolName(checker, "dispose")
604
+ if propertyName == "" {
605
+ return false
606
+ }
607
+ valueType := checker.GetApparentType(checker.GetTypeAtLocation(initializer))
608
+ for _, part := range promiseUnionParts(valueType) {
609
+ property := checker.GetPropertyOfType(part, propertyName)
610
+ if property == nil {
611
+ continue
612
+ }
613
+ propertyType := shimchecker.Checker_getTypeOfSymbolAtLocation(checker, property, initializer)
614
+ if noMisusedPromisesIsThenableReturningFunctionType(checker, initializer, propertyType) {
615
+ return true
616
+ }
617
+ }
618
+ return false
619
+ }
620
+
621
+ func noMisusedPromisesHasVoidDispose(
622
+ checker *shimchecker.Checker,
623
+ location *shimast.Node,
624
+ valueType *shimchecker.Type,
625
+ ) bool {
626
+ if checker == nil || location == nil || valueType == nil {
627
+ return false
628
+ }
629
+ propertyName := shimchecker.Checker_getPropertyNameForKnownSymbolName(checker, "dispose")
630
+ if propertyName == "" {
631
+ return false
632
+ }
633
+ valueType = checker.GetApparentType(constrainedPromiseType(checker, valueType))
634
+ for _, part := range promiseUnionParts(valueType) {
635
+ property := checker.GetPropertyOfType(part, propertyName)
636
+ if property == nil {
637
+ continue
638
+ }
639
+ propertyType := shimchecker.Checker_getTypeOfSymbolAtLocation(checker, property, location)
640
+ if noMisusedPromisesIsVoidReturningFunctionType(checker, location, propertyType) {
641
+ return true
642
+ }
643
+ }
644
+ return false
645
+ }
646
+
647
+ func noMisusedPromisesCheckProperty(ctx *Context, node *shimast.Node) {
648
+ if node.Parent == nil || node.Parent.Kind != shimast.KindObjectLiteralExpression {
649
+ return
650
+ }
651
+ var value *shimast.Node
652
+ switch node.Kind {
653
+ case shimast.KindPropertyAssignment:
654
+ if property := node.AsPropertyAssignment(); property != nil {
655
+ value = property.Initializer
656
+ }
657
+ case shimast.KindShorthandPropertyAssignment:
658
+ if property := node.AsShorthandPropertyAssignment(); property != nil {
659
+ value = property.Name()
660
+ }
661
+ case shimast.KindMethodDeclaration:
662
+ if name := node.Name(); name != nil && name.Kind == shimast.KindComputedPropertyName {
663
+ return
664
+ }
665
+ value = node
666
+ }
667
+ if value == nil {
668
+ return
669
+ }
670
+ targetType := ctx.Checker.GetContextualTypeForObjectLiteralElement(node, 0)
671
+ if noMisusedPromisesIsVoidReturningFunctionType(ctx.Checker, value, targetType) &&
672
+ noMisusedPromisesReturnsThenable(ctx.Checker, value) {
673
+ ctx.Report(value, "Promise-returning function provided to a property where a void return was expected.")
674
+ }
675
+ }
676
+
677
+ func noMisusedPromisesCheckReturn(ctx *Context, node *shimast.Node) {
678
+ statement := node.AsReturnStatement()
679
+ if statement == nil || statement.Expression == nil {
680
+ return
681
+ }
682
+ targetType := ctx.Checker.GetContextualType(statement.Expression, 0)
683
+ if noMisusedPromisesIsVoidReturningFunctionType(ctx.Checker, statement.Expression, targetType) &&
684
+ noMisusedPromisesReturnsThenable(ctx.Checker, statement.Expression) {
685
+ ctx.Report(statement.Expression, "Promise-returning function returned where a void return was expected.")
686
+ }
687
+ }
688
+
689
+ func noMisusedPromisesCheckAttribute(ctx *Context, node *shimast.Node) {
690
+ attribute := node.AsJsxAttribute()
691
+ if attribute == nil || attribute.Initializer == nil || attribute.Initializer.Kind != shimast.KindJsxExpression {
692
+ return
693
+ }
694
+ container := attribute.Initializer.AsJsxExpression()
695
+ if container == nil || container.Expression == nil {
696
+ return
697
+ }
698
+ targetType := ctx.Checker.GetContextualTypeForJsxAttribute(node)
699
+ if noMisusedPromisesIsVoidReturningFunctionType(ctx.Checker, container.Expression, targetType) &&
700
+ noMisusedPromisesReturnsThenable(ctx.Checker, container.Expression) {
701
+ ctx.Report(container.Expression, "Promise-returning function provided to an attribute where a void return was expected.")
702
+ }
703
+ }
704
+
705
+ func noMisusedPromisesCheckInheritedMethods(ctx *Context, node *shimast.Node) {
706
+ members, heritage := noMisusedPromisesClassParts(node)
707
+ for _, member := range members {
708
+ if member == nil || member.Name() == nil || hasModifier(member, shimast.KindStaticKeyword) ||
709
+ !noMisusedPromisesReturnsThenable(ctx.Checker, member) {
710
+ continue
711
+ }
712
+ name, ok := noUnsafeAssignmentPropertyName(member.Name())
713
+ if !ok {
714
+ continue
715
+ }
716
+ for _, typeNode := range heritage {
717
+ baseType := ctx.Checker.GetTypeAtLocation(typeNode)
718
+ property := ctx.Checker.GetPropertyOfType(baseType, name)
719
+ if property == nil {
720
+ continue
721
+ }
722
+ targetType := shimchecker.Checker_getTypeOfSymbolAtLocation(ctx.Checker, property, member)
723
+ if noMisusedPromisesIsVoidReturningFunctionType(ctx.Checker, member, targetType) {
724
+ ctx.Report(member, "Promise-returning method overrides an inherited method where a void return was expected.")
725
+ break
726
+ }
727
+ }
728
+ }
729
+ }
730
+
731
+ func noMisusedPromisesClassParts(node *shimast.Node) ([]*shimast.Node, []*shimast.Node) {
732
+ var members *shimast.NodeList
733
+ var clauses *shimast.NodeList
734
+ switch node.Kind {
735
+ case shimast.KindClassDeclaration:
736
+ if declaration := node.AsClassDeclaration(); declaration != nil {
737
+ members = declaration.Members
738
+ clauses = declaration.HeritageClauses
739
+ }
740
+ case shimast.KindClassExpression:
741
+ if declaration := node.AsClassExpression(); declaration != nil {
742
+ members = declaration.Members
743
+ clauses = declaration.HeritageClauses
744
+ }
745
+ case shimast.KindInterfaceDeclaration:
746
+ if declaration := node.AsInterfaceDeclaration(); declaration != nil {
747
+ members = declaration.Members
748
+ clauses = declaration.HeritageClauses
749
+ }
750
+ }
751
+ var memberNodes []*shimast.Node
752
+ if members != nil {
753
+ memberNodes = members.Nodes
754
+ }
755
+ heritage := make([]*shimast.Node, 0)
756
+ if clauses != nil {
757
+ for _, clauseNode := range clauses.Nodes {
758
+ clause := clauseNode.AsHeritageClause()
759
+ if clause != nil && clause.Types != nil {
760
+ heritage = append(heritage, clause.Types.Nodes...)
761
+ }
762
+ }
763
+ }
764
+ return memberNodes, heritage
765
+ }
766
+
767
+ func noMisusedPromisesReturnsThenable(checker *shimchecker.Checker, node *shimast.Node) bool {
768
+ if checker == nil || node == nil {
769
+ return false
770
+ }
771
+ valueType := checker.GetApparentType(checker.GetTypeAtLocation(stripParens(node)))
772
+ return noMisusedPromisesIsThenableReturningFunctionType(checker, node, valueType)
773
+ }
774
+
775
+ func noMisusedPromisesIsThenableReturningFunctionType(
776
+ checker *shimchecker.Checker,
777
+ location *shimast.Node,
778
+ valueType *shimchecker.Type,
779
+ ) bool {
780
+ if checker == nil || valueType == nil {
781
+ return false
782
+ }
783
+ valueType = constrainedPromiseType(checker, valueType)
784
+ for _, part := range promiseUnionParts(valueType) {
785
+ for _, signature := range checker.GetSignaturesOfType(part, shimchecker.SignatureKindCall) {
786
+ returnType := constrainedPromiseType(checker, checker.GetReturnTypeOfSignature(signature))
787
+ if isThenableAtLocation(checker, location, returnType) {
788
+ return true
789
+ }
790
+ }
791
+ }
792
+ return false
793
+ }
794
+
795
+ func noMisusedPromisesIsVoidReturningFunctionType(
796
+ checker *shimchecker.Checker,
797
+ location *shimast.Node,
798
+ valueType *shimchecker.Type,
799
+ ) bool {
800
+ if checker == nil || valueType == nil {
801
+ return false
802
+ }
803
+ valueType = constrainedPromiseType(checker, valueType)
804
+ hadVoid := false
805
+ for _, part := range promiseUnionParts(valueType) {
806
+ for _, signature := range checker.GetSignaturesOfType(part, shimchecker.SignatureKindCall) {
807
+ returnType := constrainedPromiseType(checker, checker.GetReturnTypeOfSignature(signature))
808
+ if isThenableAtLocation(checker, location, returnType) {
809
+ return false
810
+ }
811
+ if returnType != nil && returnType.Flags()&shimchecker.TypeFlagsVoid != 0 {
812
+ hadVoid = true
813
+ }
814
+ }
815
+ }
816
+ return hadVoid
817
+ }