@ttsc/lint 0.18.4 → 0.19.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (188) hide show
  1. package/README.md +125 -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 +159 -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 +231 -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 +206 -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,6 +1,11 @@
1
1
  package linthost
2
2
 
3
3
  import (
4
+ "encoding/json"
5
+ "path/filepath"
6
+ "runtime"
7
+ "strings"
8
+
4
9
  shimast "github.com/microsoft/typescript-go/shim/ast"
5
10
  shimchecker "github.com/microsoft/typescript-go/shim/checker"
6
11
  )
@@ -13,11 +18,27 @@ const promiseRulePrefix = "promise/"
13
18
  // recommended-type-checked:
14
19
  // https://typescript-eslint.io/rules/await-thenable/
15
20
  //
21
+ // The upstream rule checks four `await`-related constructs, and this port
22
+ // mirrors all four:
23
+ //
24
+ // - `await expr` — the operand must be a Promise or a thenable
25
+ // (checkAwaitThenableExpression);
26
+ // - `for await...of expr` — the source must expose the async-iterator
27
+ // protocol (`[Symbol.asyncIterator]`), not merely the sync fallback
28
+ // JavaScript permits (checkAwaitThenableForAwaitOf);
29
+ // - `await using x = expr` — each initializer must expose the
30
+ // async-dispose protocol (`[Symbol.asyncDispose]`), not only
31
+ // `[Symbol.dispose]` (checkAwaitThenableAwaitUsing);
32
+ // - `Promise.all` / `allSettled` / `any` / `race` — the resolved receiver
33
+ // must be the native Promise constructor and iterable members must not be
34
+ // statically always non-awaitable (checkAwaitThenablePromiseAggregator).
35
+ //
16
36
  // This is the first rule in the corpus to consult `ctx.Checker`. The
17
37
  // shim's `Checker` is a type alias for tsgo's `*innerchecker.Checker`,
18
38
  // so every exported method (`GetTypeAtLocation`, `GetPromisedTypeOfPromise`,
19
39
  // `GetPropertyOfType`, `GetSignaturesOfType`) is callable directly with
20
- // no shim addition.
40
+ // no shim addition; the well-known-symbol lookups additionally go through
41
+ // the linknamed `Checker_getPropertyNameForKnownSymbolName`.
21
42
  type awaitThenable struct{}
22
43
 
23
44
  func (awaitThenable) Name() string { return "typescript/await-thenable" }
@@ -25,12 +46,36 @@ func (awaitThenable) NeedsTypeChecker() bool {
25
46
  return true
26
47
  }
27
48
  func (awaitThenable) Visits() []shimast.Kind {
28
- return []shimast.Kind{shimast.KindAwaitExpression}
49
+ return []shimast.Kind{
50
+ shimast.KindAwaitExpression,
51
+ shimast.KindCallExpression,
52
+ shimast.KindForOfStatement,
53
+ shimast.KindVariableDeclarationList,
54
+ }
29
55
  }
30
56
  func (awaitThenable) Check(ctx *Context, node *shimast.Node) {
31
57
  if ctx.Checker == nil {
32
58
  return
33
59
  }
60
+ switch node.Kind {
61
+ case shimast.KindAwaitExpression:
62
+ checkAwaitThenableExpression(ctx, node)
63
+ case shimast.KindCallExpression:
64
+ checkAwaitThenablePromiseAggregator(ctx, node)
65
+ case shimast.KindForOfStatement:
66
+ checkAwaitThenableForAwaitOf(ctx, node)
67
+ case shimast.KindVariableDeclarationList:
68
+ checkAwaitThenableAwaitUsing(ctx, node)
69
+ }
70
+ }
71
+
72
+ // checkAwaitThenableExpression handles the ordinary `await expr` arm. The
73
+ // operand must be definitely non-awaitable before the rule reports. `any`,
74
+ // `unknown`, unconstrained type parameters, and unions with a thenable member
75
+ // remain clean because their awaitability is uncertain. The finding offers an
76
+ // opt-in suggestion that drops only the `await` token. It is not an autofix:
77
+ // even a non-thenable await creates an observable microtask boundary.
78
+ func checkAwaitThenableExpression(ctx *Context, node *shimast.Node) {
34
79
  expr := node.AsAwaitExpression()
35
80
  if expr == nil || expr.Expression == nil {
36
81
  return
@@ -39,70 +84,510 @@ func (awaitThenable) Check(ctx *Context, node *shimast.Node) {
39
84
  if operandType == nil {
40
85
  return
41
86
  }
42
- if isAwaitable(ctx.Checker, operandType) {
87
+ if classifyPromiseAwaitability(ctx.Checker, expr.Expression, operandType) != promiseAwaitabilityNever {
43
88
  return
44
89
  }
45
- message := "Unexpected `await` of a non-Promise (non-thenable) value."
46
- // Fix: drop the `await ` keyword and the following whitespace by
47
- // replacing [node.Pos(), expr.Expression.Pos()) with empty text.
48
- // `node.Pos()` may include leading trivia; use tokenRange to anchor
49
- // the start at the actual `await` token.
90
+ message := "Unexpected `await` of a non-Promise (non-\"Thenable\") value."
91
+ // Match upstream's suggestion exactly: delete the keyword token while
92
+ // preserving all trivia between it and the operand.
50
93
  startPos, _ := tokenRange(ctx.File, node)
51
- operandStart := expr.Expression.Pos()
52
- if startPos < 0 || operandStart <= startPos {
94
+ awaitEnd := startPos + len("await")
95
+ if startPos < 0 || awaitEnd > expr.Expression.Pos() {
53
96
  ctx.Report(node, message)
54
97
  return
55
98
  }
56
- ctx.ReportFix(
99
+ ctx.ReportSuggestion(
57
100
  node,
58
101
  message,
59
- TextEdit{Pos: startPos, End: operandStart, Text: ""},
102
+ "Remove unnecessary `await`.",
103
+ TextEdit{Pos: startPos, End: awaitEnd, Text: ""},
60
104
  )
61
105
  }
62
106
 
63
- // isAwaitable reports whether `t` is safe to `await`. A type is awaitable
64
- // when it is `any` / `unknown` / `never` (out of scope for strictness),
65
- // when it is a Promise, when it is a thenable (has a callable `then`),
66
- // or for union/intersection types when ANY constituent satisfies one
67
- // of the above. The union case is the round-2 repair: `GetPromisedTypeOfPromise`
68
- // returns nil on `Promise<X> | number` because the outer type is not a
69
- // reference to globalPromise, and `GetPropertyOfType` filters `then` as
70
- // a partial member, so without iterating constituents the rule would
71
- // fire on legitimate code.
72
- // isAwaitable reports whether t is safe to await. A type is awaitable when:
73
- // - its flags include Any, Unknown, or Never (these escape static strictness);
74
- // - it is a Promise (GetPromisedTypeOfPromise returns non-nil); or
75
- // - it is thenable (has a callable `then` property).
107
+ // checkAwaitThenablePromiseAggregator handles the native Promise collection
108
+ // methods. An array literal reports each always-non-awaitable member on that
109
+ // member; a typed array, tuple, or Iterable reports once on the argument when
110
+ // any possible element type is definitely non-awaitable. The paths
111
+ // deliberately differ for a `Promise<T> | T` element: a literal member may be
112
+ // awaitable at runtime, while a typed container includes a non-awaitable
113
+ // possibility.
114
+ func checkAwaitThenablePromiseAggregator(ctx *Context, node *shimast.Node) {
115
+ call := node.AsCallExpression()
116
+ object, _, ok := promiseAggregatorCallParts(call)
117
+ if !ok || call.Arguments == nil || len(call.Arguments.Nodes) == 0 {
118
+ return
119
+ }
120
+ objectType := ctx.Checker.GetTypeAtLocation(object)
121
+ if !isNativePromiseConstructorLike(ctx.Checker, objectType) {
122
+ return
123
+ }
124
+
125
+ argument := stripParens(call.Arguments.Nodes[0])
126
+ if argument == nil {
127
+ return
128
+ }
129
+ if argument.Kind == shimast.KindArrayLiteralExpression {
130
+ array := argument.AsArrayLiteralExpression()
131
+ if array == nil || array.Elements == nil {
132
+ return
133
+ }
134
+ for _, element := range array.Elements.Nodes {
135
+ if element == nil || element.Kind == shimast.KindOmittedExpression {
136
+ continue
137
+ }
138
+ elementType := ctx.Checker.GetTypeAtLocation(element)
139
+ if promiseTypePartsAllHaveAwaitability(ctx.Checker, element, elementType, promiseAwaitabilityNever) {
140
+ reportInvalidPromiseAggregatorInput(ctx, element)
141
+ }
142
+ }
143
+ return
144
+ }
145
+
146
+ argumentType := constrainedPromiseType(ctx.Checker, ctx.Checker.GetTypeAtLocation(argument))
147
+ if !isIterablePromiseAggregatorInput(ctx.Checker, argument, argumentType) {
148
+ return
149
+ }
150
+ for _, part := range promiseUnionParts(argumentType) {
151
+ for _, elementType := range promiseIterableElementTypes(ctx.Checker, part) {
152
+ if promiseTypePartsHaveAwaitability(ctx.Checker, argument, elementType, promiseAwaitabilityNever) {
153
+ reportInvalidPromiseAggregatorInput(ctx, argument)
154
+ return
155
+ }
156
+ }
157
+ }
158
+ }
159
+
160
+ func reportInvalidPromiseAggregatorInput(ctx *Context, node *shimast.Node) {
161
+ ctx.Report(node, "Unexpected iterable of non-Promise (non-\"Thenable\") values passed to promise aggregator.")
162
+ }
163
+
164
+ // checkAwaitThenableForAwaitOf handles the `for await...of` arm. Iterating a
165
+ // merely sync iterable with `for await` is legal JavaScript (the runtime
166
+ // falls back to the sync iterator and awaits each yielded value), but it has
167
+ // different iterator-closing/error semantics than a plain `for...of` and can
168
+ // obscure serial Promise consumption, so this port rejects any source
169
+ // whose type does not expose a callable `[Symbol.asyncIterator]`. `any`
170
+ // escapes the check, mirroring typescript-eslint's `isTypeAnyType` gate. The
171
+ // diagnostic anchors on the iterable expression, not the whole statement, so
172
+ // the banner points at the offending value. No autofix: upstream ships only a
173
+ // manual suggestion here, because dropping the `await` changes runtime
174
+ // behavior (e.g. a sync iterable of Promises would stop resolving its
175
+ // elements).
176
+ func checkAwaitThenableForAwaitOf(ctx *Context, node *shimast.Node) {
177
+ stmt := node.AsForInOrOfStatement()
178
+ if stmt == nil || stmt.AwaitModifier == nil || stmt.Expression == nil {
179
+ return
180
+ }
181
+ t := ctx.Checker.GetTypeAtLocation(stmt.Expression)
182
+ if t == nil || t.Flags()&shimchecker.TypeFlagsAny != 0 {
183
+ return
184
+ }
185
+ if hasCallableWellKnownSymbolProperty(ctx.Checker, stmt.Expression, t, "asyncIterator") {
186
+ return
187
+ }
188
+ ctx.Report(stmt.Expression, "Unexpected `for await...of` of a value that is not async iterable.")
189
+ }
190
+
191
+ // checkAwaitThenableAwaitUsing handles the `await using` arm. Awaiting the
192
+ // disposal of a resource that only implements `[Symbol.dispose]` is legal
193
+ // (the runtime wraps the sync disposer), but the `await` adds a scheduling
194
+ // point with no value, so this port requires every initializer to
195
+ // expose a callable `[Symbol.asyncDispose]`. Declarations without an
196
+ // initializer (the `for (await using x of ...)` binding form) are skipped, as
197
+ // is `any`, mirroring typescript-eslint. Each offending declarator reports on
198
+ // its own initializer expression. No autofix: upstream offers only a manual
199
+ // suggestion, and only for single-declarator statements.
200
+ func checkAwaitThenableAwaitUsing(ctx *Context, node *shimast.Node) {
201
+ if shimast.GetCombinedNodeFlags(node)&shimast.NodeFlagsBlockScoped != shimast.NodeFlagsAwaitUsing {
202
+ return
203
+ }
204
+ list := node.AsVariableDeclarationList()
205
+ if list == nil || list.Declarations == nil {
206
+ return
207
+ }
208
+ for _, declNode := range list.Declarations.Nodes {
209
+ decl := declNode.AsVariableDeclaration()
210
+ if decl == nil || decl.Initializer == nil {
211
+ continue
212
+ }
213
+ t := ctx.Checker.GetTypeAtLocation(decl.Initializer)
214
+ if t == nil || t.Flags()&shimchecker.TypeFlagsAny != 0 {
215
+ continue
216
+ }
217
+ if hasCallableWellKnownSymbolProperty(ctx.Checker, decl.Initializer, t, "asyncDispose") {
218
+ continue
219
+ }
220
+ ctx.Report(decl.Initializer, "Unexpected `await using` of a value that is not async disposable.")
221
+ }
222
+ }
223
+
224
+ // hasCallableWellKnownSymbolProperty reports whether `t` — or, for a union,
225
+ // ANY constituent — exposes a callable property keyed by the global
226
+ // well-known symbol `Symbol.<symbolName>`:
227
+ //
228
+ // - the property NAME is resolved through the checker's own
229
+ // `getPropertyNameForKnownSymbolName`, i.e. through the unique-symbol
230
+ // type of the real global `SymbolConstructor` member, never by matching
231
+ // source text or declared type names;
232
+ // - the property LOOKUP goes through `GetPropertyOfType`, which resolves
233
+ // inherited members, intersections, and type aliases;
234
+ // - the property TYPE is resolved at the source location before checking
235
+ // call signatures, so a same-named non-callable property is not mistaken
236
+ // for an implemented protocol;
237
+ // - constrained type parameters recurse through their base constraint.
76
238
  //
77
- // For union and intersection types the function recurses into constituents: if
78
- // ANY constituent is awaitable the whole type is considered awaitable. This is
79
- // necessary because GetPromisedTypeOfPromise returns nil on composite types
80
- // like `Promise<X> | number` even though the expression can legally be awaited.
81
- func isAwaitable(checker *shimchecker.Checker, t *shimchecker.Type) bool {
239
+ // Union constituents are tested individually because a union type only
240
+ // surfaces properties present on every constituent, while upstream accepts a
241
+ // union when at least one constituent implements the protocol (e.g.
242
+ // `AsyncIterable<T> | Iterable<T>` stays valid under `for await`).
243
+ func hasCallableWellKnownSymbolProperty(
244
+ checker *shimchecker.Checker,
245
+ location *shimast.Node,
246
+ t *shimchecker.Type,
247
+ symbolName string,
248
+ ) bool {
82
249
  if checker == nil || t == nil {
83
250
  return false
84
251
  }
85
- flags := t.Flags()
86
- if flags&shimchecker.TypeFlagsAny != 0 ||
87
- flags&shimchecker.TypeFlagsUnknown != 0 ||
88
- flags&shimchecker.TypeFlagsNever != 0 {
89
- return true
252
+ if t.Flags()&shimchecker.TypeFlagsTypeParameter != 0 {
253
+ constraint := checker.GetBaseConstraintOfType(t)
254
+ if constraint == nil || constraint == t {
255
+ return false
256
+ }
257
+ return hasCallableWellKnownSymbolProperty(checker, location, constraint, symbolName)
90
258
  }
91
- if flags&(shimchecker.TypeFlagsUnion|shimchecker.TypeFlagsIntersection) != 0 {
259
+ if t.Flags()&shimchecker.TypeFlagsUnion != 0 {
92
260
  for _, part := range t.Types() {
93
261
  if part == nil {
94
262
  continue
95
263
  }
96
- if isAwaitable(checker, part) {
264
+ if hasCallableWellKnownSymbolProperty(checker, location, part, symbolName) {
97
265
  return true
98
266
  }
99
267
  }
100
268
  return false
101
269
  }
102
- if checker.GetPromisedTypeOfPromise(t) != nil {
270
+ name := shimchecker.Checker_getPropertyNameForKnownSymbolName(checker, symbolName)
271
+ if name == "" {
272
+ return false
273
+ }
274
+ property := checker.GetPropertyOfType(t, name)
275
+ if property == nil {
276
+ return false
277
+ }
278
+ propertyType := checker.GetTypeOfSymbolAtLocation(property, location)
279
+ if propertyType == nil {
280
+ return false
281
+ }
282
+ return len(checker.GetSignaturesOfType(propertyType, shimchecker.SignatureKindCall)) > 0
283
+ }
284
+
285
+ type promiseAwaitability uint8
286
+
287
+ const (
288
+ promiseAwaitabilityAlways promiseAwaitability = iota
289
+ promiseAwaitabilityNever
290
+ promiseAwaitabilityMay
291
+ )
292
+
293
+ // classifyPromiseAwaitability is the shared await-thenable policy boundary.
294
+ // It distinguishes definitely thenable, definitely non-thenable, and unknown
295
+ // values so callers can choose their own quantifier. Ordinary `await` reports
296
+ // only Never; literal aggregator members report when every union constituent
297
+ // is Never; typed container members report when any constituent is Never.
298
+ //
299
+ // This classifier intentionally does not replace isPromiseTypedExpression.
300
+ // no-floating-promises and no-misused-promises use a separate structural
301
+ // Promise policy, and adopting this tri-state policy there requires an
302
+ // explicit rule decision rather than an incidental helper refactor.
303
+ func classifyPromiseAwaitability(
304
+ checker *shimchecker.Checker,
305
+ location *shimast.Node,
306
+ t *shimchecker.Type,
307
+ ) promiseAwaitability {
308
+ if checker == nil || t == nil {
309
+ return promiseAwaitabilityMay
310
+ }
311
+ if t.Flags()&shimchecker.TypeFlagsTypeParameter != 0 {
312
+ constraint := checker.GetBaseConstraintOfType(t)
313
+ if constraint == nil || constraint == t {
314
+ return promiseAwaitabilityMay
315
+ }
316
+ return classifyPromiseAwaitability(checker, location, constraint)
317
+ }
318
+ if t.Flags()&(shimchecker.TypeFlagsAny|shimchecker.TypeFlagsUnknown) != 0 {
319
+ return promiseAwaitabilityMay
320
+ }
321
+ if t.Flags()&shimchecker.TypeFlagsUnion != 0 {
322
+ parts := t.Types()
323
+ if len(parts) == 0 {
324
+ return promiseAwaitabilityMay
325
+ }
326
+ classification := classifyPromiseAwaitability(checker, location, parts[0])
327
+ for _, part := range parts[1:] {
328
+ if classifyPromiseAwaitability(checker, location, part) != classification {
329
+ return promiseAwaitabilityMay
330
+ }
331
+ }
332
+ return classification
333
+ }
334
+ if isThenableAtLocation(checker, location, t) {
335
+ return promiseAwaitabilityAlways
336
+ }
337
+ return promiseAwaitabilityNever
338
+ }
339
+
340
+ // isThenableAtLocation mirrors ts-api-utils' thenable predicate. A `then`
341
+ // property is valid only when one call signature accepts a first parameter
342
+ // whose apparent type is callable. Resolving both symbols at `location`
343
+ // mirrors upstream's contextual symbol lookup.
344
+ func isThenableAtLocation(checker *shimchecker.Checker, location *shimast.Node, t *shimchecker.Type) bool {
345
+ if checker == nil || t == nil {
346
+ return false
347
+ }
348
+ apparent := checker.GetApparentType(t)
349
+ if apparent == nil {
350
+ return false
351
+ }
352
+ for _, part := range promiseUnionParts(apparent) {
353
+ if part == nil {
354
+ continue
355
+ }
356
+ thenProperty := checker.GetPropertyOfType(part, "then")
357
+ if thenProperty == nil {
358
+ continue
359
+ }
360
+ thenType := checker.GetTypeOfSymbolAtLocation(thenProperty, location)
361
+ for _, thenPart := range promiseUnionParts(thenType) {
362
+ if thenPart == nil {
363
+ continue
364
+ }
365
+ for _, signature := range checker.GetSignaturesOfType(thenPart, shimchecker.SignatureKindCall) {
366
+ parameters := signature.Parameters()
367
+ if len(parameters) > 0 && isCallableCallbackParameter(checker, location, parameters[0]) {
368
+ return true
369
+ }
370
+ }
371
+ }
372
+ }
373
+ return false
374
+ }
375
+
376
+ func isCallableCallbackParameter(
377
+ checker *shimchecker.Checker,
378
+ location *shimast.Node,
379
+ parameter *shimast.Symbol,
380
+ ) bool {
381
+ if checker == nil || parameter == nil {
382
+ return false
383
+ }
384
+ callbackType := checker.GetTypeOfSymbolAtLocation(parameter, location)
385
+ if callbackType == nil {
386
+ return false
387
+ }
388
+ callbackType = checker.GetApparentType(callbackType)
389
+ if callbackType == nil {
390
+ return false
391
+ }
392
+ if declaration := parameter.ValueDeclaration; declaration != nil {
393
+ if parameterDeclaration := declaration.AsParameterDeclaration(); parameterDeclaration != nil && parameterDeclaration.DotDotDotToken != nil {
394
+ callbackType = checker.GetNumberIndexType(callbackType)
395
+ if callbackType == nil {
396
+ return false
397
+ }
398
+ }
399
+ }
400
+ for _, part := range promiseUnionParts(callbackType) {
401
+ if part != nil && len(checker.GetSignaturesOfType(part, shimchecker.SignatureKindCall)) > 0 {
402
+ return true
403
+ }
404
+ }
405
+ return false
406
+ }
407
+
408
+ func constrainedPromiseType(checker *shimchecker.Checker, t *shimchecker.Type) *shimchecker.Type {
409
+ if checker == nil || t == nil || t.Flags()&shimchecker.TypeFlagsTypeParameter == 0 {
410
+ return t
411
+ }
412
+ if constraint := checker.GetBaseConstraintOfType(t); constraint != nil && constraint != t {
413
+ return constraint
414
+ }
415
+ return t
416
+ }
417
+
418
+ func promiseUnionParts(t *shimchecker.Type) []*shimchecker.Type {
419
+ if t == nil {
420
+ return nil
421
+ }
422
+ if t.Flags()&shimchecker.TypeFlagsUnion != 0 {
423
+ return t.Types()
424
+ }
425
+ return []*shimchecker.Type{t}
426
+ }
427
+
428
+ func promiseTypePartsAllHaveAwaitability(
429
+ checker *shimchecker.Checker,
430
+ location *shimast.Node,
431
+ t *shimchecker.Type,
432
+ want promiseAwaitability,
433
+ ) bool {
434
+ parts := promiseUnionParts(t)
435
+ if len(parts) == 0 {
436
+ return false
437
+ }
438
+ for _, part := range parts {
439
+ if classifyPromiseAwaitability(checker, location, part) != want {
440
+ return false
441
+ }
442
+ }
443
+ return true
444
+ }
445
+
446
+ func promiseTypePartsHaveAwaitability(
447
+ checker *shimchecker.Checker,
448
+ location *shimast.Node,
449
+ t *shimchecker.Type,
450
+ want promiseAwaitability,
451
+ ) bool {
452
+ for _, part := range promiseUnionParts(t) {
453
+ if classifyPromiseAwaitability(checker, location, part) == want {
454
+ return true
455
+ }
456
+ }
457
+ return false
458
+ }
459
+
460
+ func promiseAggregatorCallParts(call *shimast.CallExpression) (*shimast.Node, string, bool) {
461
+ if call == nil || call.Expression == nil {
462
+ return nil, "", false
463
+ }
464
+ callee := stripParens(call.Expression)
465
+ if callee == nil {
466
+ return nil, "", false
467
+ }
468
+ var object *shimast.Node
469
+ var method string
470
+ switch callee.Kind {
471
+ case shimast.KindPropertyAccessExpression:
472
+ access := callee.AsPropertyAccessExpression()
473
+ if access == nil {
474
+ return nil, "", false
475
+ }
476
+ object = access.Expression
477
+ method = identifierText(access.Name())
478
+ case shimast.KindElementAccessExpression:
479
+ access := callee.AsElementAccessExpression()
480
+ if access == nil || access.ArgumentExpression == nil {
481
+ return nil, "", false
482
+ }
483
+ object = access.Expression
484
+ switch access.ArgumentExpression.Kind {
485
+ case shimast.KindStringLiteral, shimast.KindNoSubstitutionTemplateLiteral:
486
+ method = stringLiteralText(access.ArgumentExpression)
487
+ }
488
+ }
489
+ switch method {
490
+ case "all", "allSettled", "any", "race":
491
+ return object, method, object != nil
492
+ }
493
+ return nil, "", false
494
+ }
495
+
496
+ func isNativePromiseConstructorLike(checker *shimchecker.Checker, t *shimchecker.Type) bool {
497
+ return isNativePromiseConstructorLikeSeen(checker, t, map[*shimchecker.Type]struct{}{})
498
+ }
499
+
500
+ func isNativePromiseConstructorLikeSeen(
501
+ checker *shimchecker.Checker,
502
+ t *shimchecker.Type,
503
+ seen map[*shimchecker.Type]struct{},
504
+ ) bool {
505
+ if checker == nil || t == nil {
506
+ return false
507
+ }
508
+ if _, ok := seen[t]; ok {
509
+ return false
510
+ }
511
+ seen[t] = struct{}{}
512
+ defer delete(seen, t)
513
+
514
+ flags := t.Flags()
515
+ if flags&shimchecker.TypeFlagsIntersection != 0 {
516
+ for _, part := range t.Types() {
517
+ if isNativePromiseConstructorLikeSeen(checker, part, seen) {
518
+ return true
519
+ }
520
+ }
521
+ return false
522
+ }
523
+ if flags&shimchecker.TypeFlagsUnion != 0 {
524
+ parts := t.Types()
525
+ if len(parts) == 0 {
526
+ return false
527
+ }
528
+ for _, part := range parts {
529
+ if !isNativePromiseConstructorLikeSeen(checker, part, seen) {
530
+ return false
531
+ }
532
+ }
103
533
  return true
104
534
  }
105
- return isThenableType(checker, t)
535
+ if flags&shimchecker.TypeFlagsTypeParameter != 0 {
536
+ return isNativePromiseConstructorLikeSeen(checker, checker.GetBaseConstraintOfType(t), seen)
537
+ }
538
+
539
+ symbol := t.Symbol()
540
+ if symbol == nil {
541
+ return false
542
+ }
543
+ if symbol.Name == "PromiseConstructor" && checker.IsLibSymbolForHoverVerbosity(symbol) {
544
+ return true
545
+ }
546
+ declared := checker.GetDeclaredTypeOfSymbol(symbol)
547
+ if declared == nil || declared.ObjectFlags()&shimchecker.ObjectFlagsClassOrInterface == 0 {
548
+ return false
549
+ }
550
+ for _, base := range checker.GetBaseTypes(declared) {
551
+ if isNativePromiseConstructorLikeSeen(checker, base, seen) {
552
+ return true
553
+ }
554
+ }
555
+ return false
556
+ }
557
+
558
+ func isIterablePromiseAggregatorInput(
559
+ checker *shimchecker.Checker,
560
+ location *shimast.Node,
561
+ t *shimchecker.Type,
562
+ ) bool {
563
+ parts := promiseUnionParts(t)
564
+ if len(parts) == 0 {
565
+ return false
566
+ }
567
+ for _, part := range parts {
568
+ if !hasCallableWellKnownSymbolProperty(checker, location, part, "iterator") {
569
+ return false
570
+ }
571
+ }
572
+ return true
573
+ }
574
+
575
+ // promiseIterableElementTypes extracts the values produced by synchronous
576
+ // iteration. Tuples retain slot-by-slot precision; every other iterable goes
577
+ // through TypeScript-Go's checked `[Symbol.iterator]` traversal so a concrete
578
+ // container's unrelated generic arguments are never mistaken for its yield
579
+ // type.
580
+ func promiseIterableElementTypes(checker *shimchecker.Checker, t *shimchecker.Type) []*shimchecker.Type {
581
+ if checker == nil || t == nil {
582
+ return nil
583
+ }
584
+ if shimchecker.IsTupleType(t) {
585
+ return checker.GetTypeArguments(t)
586
+ }
587
+ if elementType := shimchecker.Checker_getSynchronousIterationYieldType(checker, t); elementType != nil {
588
+ return []*shimchecker.Type{elementType}
589
+ }
590
+ return nil
106
591
  }
107
592
 
108
593
  // isThenableType reports whether t has a callable `then` property, which is
@@ -123,20 +608,73 @@ func isThenableType(checker *shimchecker.Checker, t *shimchecker.Type) bool {
123
608
  return len(checker.GetSignaturesOfType(propType, shimchecker.SignatureKindCall)) > 0
124
609
  }
125
610
 
126
- // noFloatingPromises: an `ExpressionStatement` whose value is a Promise but
127
- // is neither awaited, wrapped in `void`, nor terminated by a rejection-aware
128
- // handler (`.catch`, `.finally`, or `.then(_, onRejected)`) silently swallows
129
- // any rejection produced by the Promise. The Node runtime emits
130
- // `unhandledRejection` only for the outermost discarded Promise, so a single
131
- // missing handler cascades into hard-to-trace failures. typescript-eslint
132
- // recommended-type-checked:
611
+ // noFloatingPromises rejects discarded built-in Promises and, when requested,
612
+ // catchable structural thenables. A Promise is handled only by await, the
613
+ // configured void escape hatch, or a callable rejection handler; finally
614
+ // preserves the receiver's handled state rather than swallowing rejection.
615
+ // typescript-eslint recommended-type-checked:
133
616
  // https://typescript-eslint.io/rules/no-floating-promises/
134
617
  //
135
- // Only `ExpressionStatement` is visited: assignments, return values, and
136
- // argument positions are never "floating" because the surrounding context
137
- // captures the Promise. The handler set mirrors `await-thenable`'s shape so
138
- // the Checker calls stay confined to one file.
139
- type noFloatingPromises struct{}
618
+ // Only ExpressionStatement is visited because assignments, returns, and call
619
+ // arguments transfer ownership. Recursive analysis is still required inside a
620
+ // statement: sequence expressions hide earlier values, logical and conditional
621
+ // branches hide Promise-producing alternatives, and arrays retain every
622
+ // Promise element even though the container itself is not awaitable.
623
+ type noFloatingPromises struct{ optionsRule }
624
+
625
+ type noFloatingPromisesOptions struct {
626
+ AllowForKnownSafeCalls []promiseTypeOrValueSpecifier `json:"allowForKnownSafeCalls"`
627
+ AllowForKnownSafePromises []promiseTypeOrValueSpecifier `json:"allowForKnownSafePromises"`
628
+ CheckThenables bool `json:"checkThenables"`
629
+ IgnoreIIFE bool `json:"ignoreIIFE"`
630
+ IgnoreVoid *bool `json:"ignoreVoid"`
631
+ }
632
+
633
+ type promiseTypeOrValueSpecifier struct {
634
+ From string
635
+ Names []string
636
+ Path string
637
+ PackageName string
638
+ Universal bool
639
+ }
640
+
641
+ func (s *promiseTypeOrValueSpecifier) UnmarshalJSON(data []byte) error {
642
+ var name string
643
+ if err := json.Unmarshal(data, &name); err == nil {
644
+ s.Names = []string{name}
645
+ s.Universal = true
646
+ return nil
647
+ }
648
+
649
+ var raw struct {
650
+ From string `json:"from"`
651
+ Name json.RawMessage `json:"name"`
652
+ Path string `json:"path"`
653
+ Package string `json:"package"`
654
+ }
655
+ if err := json.Unmarshal(data, &raw); err != nil {
656
+ return err
657
+ }
658
+ if err := json.Unmarshal(raw.Name, &name); err == nil {
659
+ s.Names = []string{name}
660
+ } else if err := json.Unmarshal(raw.Name, &s.Names); err != nil {
661
+ return err
662
+ }
663
+ s.From = raw.From
664
+ s.Path = raw.Path
665
+ s.PackageName = raw.Package
666
+ return nil
667
+ }
668
+
669
+ func (o noFloatingPromisesOptions) ignoresVoid() bool {
670
+ return o.IgnoreVoid == nil || *o.IgnoreVoid
671
+ }
672
+
673
+ type floatingPromiseResult struct {
674
+ unhandled bool
675
+ nonFunctionHandler bool
676
+ promiseArray bool
677
+ }
140
678
 
141
679
  func (noFloatingPromises) Name() string { return "typescript/no-floating-promises" }
142
680
  func (noFloatingPromises) NeedsTypeChecker() bool {
@@ -153,81 +691,2012 @@ func (noFloatingPromises) Check(ctx *Context, node *shimast.Node) {
153
691
  if stmt == nil || stmt.Expression == nil {
154
692
  return
155
693
  }
156
- expr := stripParens(stmt.Expression)
694
+ expr := unwrapFloatingPromiseExpression(stmt.Expression)
157
695
  if expr == nil {
158
696
  return
159
697
  }
160
- // Syntactic discharges short-circuit the Checker call: any of these
161
- // expression shapes already signals that the author opted out of the
162
- // "discarded" reading, so the rule never needs to walk the type system.
163
- if isPromiseHandledExpression(expr) {
698
+ var options noFloatingPromisesOptions
699
+ _ = ctx.DecodeOptions(&options)
700
+ if options.IgnoreIIFE && isImmediatelyInvokedFunctionExpression(expr) {
164
701
  return
165
702
  }
166
- t := ctx.Checker.GetTypeAtLocation(expr)
167
- if t == nil {
168
- return
169
- }
170
- if !isPromiseTypedExpression(ctx.Checker, t) {
703
+ result := analyzeFloatingPromise(ctx, expr, options)
704
+ if !result.unhandled {
171
705
  return
172
706
  }
173
- ctx.Report(node, "Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler, or be explicitly marked as ignored with the `void` operator.")
707
+ message := floatingPromiseMessage(options.ignoresVoid(), result)
708
+ ctx.Report(node, message)
174
709
  }
175
710
 
176
- // isPromiseHandledExpression reports whether the expression is syntactically
177
- // "handled" — already awaited, marked `void`, or terminated by a rejection-aware
178
- // promise method. The list of accepted methods matches typescript-eslint's
179
- // no-floating-promises: `catch` and `finally` always defuse the chain, while
180
- // `then` only defuses it when an explicit rejection handler is supplied as the
181
- // second argument.
182
- func isPromiseHandledExpression(expr *shimast.Node) bool {
183
- if expr == nil {
184
- return false
185
- }
186
- switch expr.Kind {
187
- case shimast.KindAwaitExpression, shimast.KindVoidExpression:
188
- return true
189
- case shimast.KindCallExpression:
190
- call := expr.AsCallExpression()
191
- if call == nil {
192
- return false
193
- }
194
- method, ok := promiseMethodCallName(call)
195
- if !ok {
196
- return false
197
- }
198
- switch method {
199
- case "catch", "finally":
200
- return true
201
- case "then":
202
- return call.Arguments != nil && len(call.Arguments.Nodes) >= 2
711
+ func floatingPromiseMessage(ignoreVoid bool, result floatingPromiseResult) string {
712
+ if result.promiseArray {
713
+ message := "An array of Promises may be unintentional. Consider handling the promises' fulfillment or rejection with Promise.all or similar"
714
+ if ignoreVoid {
715
+ return message + ", or explicitly marking the expression as ignored with the `void` operator."
203
716
  }
717
+ return message + "."
204
718
  }
205
- return false
719
+ message := "Promises must be awaited, end with a call to .catch, or end with a call to .then with a rejection handler."
720
+ if ignoreVoid {
721
+ message = "Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the `void` operator."
722
+ }
723
+ if result.nonFunctionHandler {
724
+ message += " A rejection handler that is not a function will be ignored."
725
+ }
726
+ return message
206
727
  }
207
728
 
208
- // promiseMethodCallName returns the trailing property name when the call's
209
- // callee is a property access (e.g. `getPromise().catch` -> "catch"). Unlike
210
- // `promiseCallMethod`, it does NOT gate the result on whether the receiver
211
- // looks like a Promise — the Checker handles that downstream when deciding
212
- // whether the whole expression is still Promise-typed. The named-receiver
213
- // constraint would otherwise reject legitimate handlers on user-typed
214
- // objects such as `repo.users.findById(id).catch(reportError)`.
215
- func promiseMethodCallName(call *shimast.CallExpression) (string, bool) {
216
- if call == nil || call.Expression == nil {
217
- return "", false
729
+ func analyzeFloatingPromise(
730
+ ctx *Context,
731
+ node *shimast.Node,
732
+ options noFloatingPromisesOptions,
733
+ ) floatingPromiseResult {
734
+ node = unwrapFloatingPromiseExpression(node)
735
+ if node == nil {
736
+ return floatingPromiseResult{}
218
737
  }
219
- _, method, ok := promisePropertyAccessParts(call.Expression)
220
- if !ok {
221
- return "", false
738
+
739
+ // A configured safe call stays safe wherever recursive expression analysis
740
+ // reaches it. The callee's symbol/source identity is unchanged by a comma,
741
+ // logical, conditional, or void parent, so the allowlist decision belongs
742
+ // at the call node rather than only at the root expression statement.
743
+ if isKnownSafePromiseCall(ctx, node, options.AllowForKnownSafeCalls) {
744
+ return floatingPromiseResult{}
222
745
  }
223
- return method, true
746
+
747
+ if isAssignmentExpression(node) {
748
+ return floatingPromiseResult{}
749
+ }
750
+
751
+ if node.Kind == shimast.KindBinaryExpression {
752
+ binary := node.AsBinaryExpression()
753
+ if binary != nil && binary.OperatorToken != nil && binary.OperatorToken.Kind == shimast.KindCommaToken {
754
+ if result := analyzeFloatingPromise(ctx, binary.Left, options); result.unhandled {
755
+ return result
756
+ }
757
+ return analyzeFloatingPromise(ctx, binary.Right, options)
758
+ }
759
+ }
760
+
761
+ if !options.ignoresVoid() && node.Kind == shimast.KindVoidExpression {
762
+ expression := node.AsVoidExpression()
763
+ if expression == nil {
764
+ return floatingPromiseResult{}
765
+ }
766
+ return analyzeFloatingPromise(ctx, expression.Expression, options)
767
+ }
768
+
769
+ t := ctx.Checker.GetTypeAtLocation(node)
770
+ if t == nil {
771
+ return floatingPromiseResult{}
772
+ }
773
+ if isFloatingPromiseArray(ctx, node, t, options) {
774
+ return floatingPromiseResult{unhandled: true, promiseArray: true}
775
+ }
776
+ if node.Kind == shimast.KindArrayLiteralExpression {
777
+ array := node.AsArrayLiteralExpression()
778
+ if array != nil && array.Elements != nil {
779
+ for _, element := range array.Elements.Nodes {
780
+ element = unwrapFloatingPromiseExpression(element)
781
+ if element != nil && element.Kind == shimast.KindSpreadElement {
782
+ if spread := element.AsSpreadElement(); spread != nil {
783
+ element = unwrapFloatingPromiseExpression(spread.Expression)
784
+ }
785
+ }
786
+ if element == nil {
787
+ continue
788
+ }
789
+ elementType := ctx.Checker.GetTypeAtLocation(element)
790
+ if elementType != nil && (isFloatingPromiseType(ctx, element, elementType, options) ||
791
+ isFloatingPromiseArray(ctx, element, elementType, options)) {
792
+ return floatingPromiseResult{unhandled: true, promiseArray: true}
793
+ }
794
+ if result := analyzeFloatingPromise(ctx, element, options); result.unhandled {
795
+ return floatingPromiseResult{unhandled: true, promiseArray: true}
796
+ }
797
+ }
798
+ }
799
+ }
800
+ if node.Kind == shimast.KindAwaitExpression {
801
+ return floatingPromiseResult{}
802
+ }
803
+ callResultIsFloating := isFloatingPromiseType(ctx, node, t, options)
804
+ if node.Kind == shimast.KindCallExpression {
805
+ return analyzeFloatingPromiseCall(ctx, node, options, callResultIsFloating)
806
+ }
807
+
808
+ switch node.Kind {
809
+ case shimast.KindConditionalExpression:
810
+ conditional := node.AsConditionalExpression()
811
+ if conditional == nil {
812
+ return floatingPromiseResult{unhandled: true}
813
+ }
814
+ if result := analyzeFloatingPromise(ctx, conditional.WhenFalse, options); result.unhandled {
815
+ return result
816
+ }
817
+ return analyzeFloatingPromise(ctx, conditional.WhenTrue, options)
818
+ case shimast.KindBinaryExpression:
819
+ binary := node.AsBinaryExpression()
820
+ if binary != nil && binary.OperatorToken != nil {
821
+ switch binary.OperatorToken.Kind {
822
+ case shimast.KindAmpersandAmpersandToken,
823
+ shimast.KindBarBarToken,
824
+ shimast.KindQuestionQuestionToken:
825
+ if result := analyzeFloatingPromise(ctx, binary.Left, options); result.unhandled {
826
+ return result
827
+ }
828
+ return analyzeFloatingPromise(ctx, binary.Right, options)
829
+ }
830
+ }
831
+ }
832
+ if !callResultIsFloating {
833
+ return floatingPromiseResult{}
834
+ }
835
+ return floatingPromiseResult{unhandled: true}
836
+ }
837
+
838
+ func analyzeFloatingPromiseCall(
839
+ ctx *Context,
840
+ node *shimast.Node,
841
+ options noFloatingPromisesOptions,
842
+ callResultIsFloating bool,
843
+ ) floatingPromiseResult {
844
+ call := node.AsCallExpression()
845
+ if call == nil {
846
+ return floatingPromiseResult{unhandled: callResultIsFloating}
847
+ }
848
+ receiver, method, ok := floatingPromiseMethodCall(call)
849
+ if !ok {
850
+ return floatingPromiseResult{unhandled: callResultIsFloating}
851
+ }
852
+
853
+ receiverType := ctx.Checker.GetTypeAtLocation(receiver)
854
+ if receiverType == nil {
855
+ return floatingPromiseResult{unhandled: callResultIsFloating}
856
+ }
857
+ apparent := ctx.Checker.GetApparentType(receiverType)
858
+ if apparent == nil {
859
+ return floatingPromiseResult{unhandled: callResultIsFloating}
860
+ }
861
+
862
+ var nonPromiseReceivers []*shimchecker.Type
863
+ sawPromiseReceiver := false
864
+ sawUnsafePromiseReceiver := false
865
+ receiverIsOptional := floatingPromisePropertyAccessIsOptional(call.Expression)
866
+ for _, part := range promiseUnionParts(apparent) {
867
+ if part == nil {
868
+ continue
869
+ }
870
+ if receiverIsOptional && part.Flags()&(shimchecker.TypeFlagsNull|shimchecker.TypeFlagsUndefined) != 0 {
871
+ continue
872
+ }
873
+
874
+ if typeMatchesSomePromiseSpecifier(ctx, part, options.AllowForKnownSafePromises) {
875
+ sawPromiseReceiver = true
876
+ continue
877
+ }
878
+ if isNativePromiseInstanceLike(ctx.Checker, part) ||
879
+ (options.CheckThenables && isCatchableThenableAtLocation(ctx.Checker, receiver, part)) {
880
+ sawPromiseReceiver = true
881
+ sawUnsafePromiseReceiver = true
882
+ continue
883
+ }
884
+ nonPromiseReceivers = append(nonPromiseReceivers, part)
885
+ }
886
+ if !sawPromiseReceiver {
887
+ return floatingPromiseResult{unhandled: callResultIsFloating}
888
+ }
889
+
890
+ // A Promise type covered by allowForKnownSafePromises may be discarded
891
+ // without proving a rejection handler. Preserve that contract when every
892
+ // Promise receiver branch and the call result are safe, while still checking
893
+ // unsafe Promise branches and unsafe results returned from a safe subtype.
894
+ if sawUnsafePromiseReceiver || callResultIsFloating {
895
+ switch method {
896
+ case "catch":
897
+ if result := rejectionHandlerResult(ctx, call, 0); result.unhandled {
898
+ return result
899
+ }
900
+ case "then":
901
+ if call.Arguments != nil && len(call.Arguments.Nodes) != 0 && call.Arguments.Nodes[0].Kind == shimast.KindSpreadElement {
902
+ return floatingPromiseResult{unhandled: true}
903
+ }
904
+ if result := rejectionHandlerResult(ctx, call, 1); result.unhandled {
905
+ return result
906
+ }
907
+ case "finally":
908
+ if result := analyzeFloatingPromise(ctx, receiver, options); result.unhandled {
909
+ return result
910
+ }
911
+ default:
912
+ return floatingPromiseResult{unhandled: true}
913
+ }
914
+ }
915
+
916
+ for _, part := range nonPromiseReceivers {
917
+ if floatingPromiseMethodReturnIsUnhandled(ctx, node, call, part, method, options) {
918
+ return floatingPromiseResult{unhandled: true}
919
+ }
920
+ }
921
+ return floatingPromiseResult{}
922
+ }
923
+
924
+ func rejectionHandlerResult(ctx *Context, call *shimast.CallExpression, index int) floatingPromiseResult {
925
+ if call == nil || call.Arguments == nil || len(call.Arguments.Nodes) <= index {
926
+ return floatingPromiseResult{unhandled: true}
927
+ }
928
+ handler := unwrapFloatingPromiseExpression(call.Arguments.Nodes[index])
929
+ if handler == nil || handler.Kind == shimast.KindSpreadElement {
930
+ return floatingPromiseResult{unhandled: true}
931
+ }
932
+ t := ctx.Checker.GetTypeAtLocation(handler)
933
+ if t != nil && len(ctx.Checker.GetSignaturesOfType(t, shimchecker.SignatureKindCall)) > 0 {
934
+ return floatingPromiseResult{}
935
+ }
936
+ return floatingPromiseResult{unhandled: true, nonFunctionHandler: true}
937
+ }
938
+
939
+ // floatingPromiseMethodCall extracts only the three Promise protocol method
940
+ // shapes. analyzeFloatingPromiseCall separately proves which receiver branches
941
+ // implement that protocol and inspects the return types of every other branch.
942
+ func floatingPromiseMethodCall(call *shimast.CallExpression) (*shimast.Node, string, bool) {
943
+ if call == nil || call.Expression == nil {
944
+ return nil, "", false
945
+ }
946
+ receiver, method, ok := floatingPromisePropertyAccessParts(call.Expression)
947
+ if !ok {
948
+ return nil, "", false
949
+ }
950
+ switch method {
951
+ case "catch", "then", "finally":
952
+ default:
953
+ return nil, "", false
954
+ }
955
+ receiver = unwrapFloatingPromiseExpression(receiver)
956
+ if receiver == nil {
957
+ return nil, "", false
958
+ }
959
+ return receiver, method, true
960
+ }
961
+
962
+ func floatingPromisePropertyAccessIsOptional(node *shimast.Node) bool {
963
+ node = stripParens(node)
964
+ if node == nil {
965
+ return false
966
+ }
967
+ switch node.Kind {
968
+ case shimast.KindPropertyAccessExpression:
969
+ access := node.AsPropertyAccessExpression()
970
+ return access != nil && access.QuestionDotToken != nil
971
+ case shimast.KindElementAccessExpression:
972
+ access := node.AsElementAccessExpression()
973
+ return access != nil && access.QuestionDotToken != nil
974
+ }
975
+ return false
976
+ }
977
+
978
+ // floatingPromiseMethodReturnIsUnhandled determines what a non-Promise
979
+ // receiver branch contributes to the union call. Method names alone are not a
980
+ // Promise escape: a provably applicable callable signature must return a value
981
+ // that is safe to discard under the same Promise and thenable options as the
982
+ // outer expression.
983
+ func floatingPromiseMethodReturnIsUnhandled(
984
+ ctx *Context,
985
+ node *shimast.Node,
986
+ call *shimast.CallExpression,
987
+ receiverType *shimchecker.Type,
988
+ method string,
989
+ options noFloatingPromisesOptions,
990
+ ) bool {
991
+ property := ctx.Checker.GetPropertyOfType(receiverType, method)
992
+ if property == nil {
993
+ return true
994
+ }
995
+ propertyType := ctx.Checker.GetTypeOfSymbolAtLocation(property, call.Expression)
996
+ if propertyType == nil {
997
+ return true
998
+ }
999
+
1000
+ sawBranch := false
1001
+ for _, part := range promiseUnionParts(propertyType) {
1002
+ if part == nil {
1003
+ continue
1004
+ }
1005
+ if call.QuestionDotToken != nil && part.Flags()&(shimchecker.TypeFlagsNull|shimchecker.TypeFlagsUndefined) != 0 {
1006
+ sawBranch = true
1007
+ continue
1008
+ }
1009
+ signatures := ctx.Checker.GetSignaturesOfType(part, shimchecker.SignatureKindCall)
1010
+ if len(signatures) == 0 {
1011
+ if apparent := ctx.Checker.GetApparentType(part); apparent != nil && apparent != part {
1012
+ signatures = ctx.Checker.GetSignaturesOfType(apparent, shimchecker.SignatureKindCall)
1013
+ }
1014
+ }
1015
+ if len(signatures) == 0 {
1016
+ return true
1017
+ }
1018
+ sawBranch = true
1019
+ signature := floatingPromiseApplicableSignature(ctx, call, signatures)
1020
+ if signature == nil {
1021
+ return true
1022
+ }
1023
+ if floatingPromiseSignatureReturnIsUnhandled(ctx, node, call, signature, options) {
1024
+ return true
1025
+ }
1026
+ }
1027
+ return !sawBranch
1028
+ }
1029
+
1030
+ // floatingPromiseApplicableSignature selects an overload only when public
1031
+ // Checker queries prove its arity and argument types accept this call. Multiple
1032
+ // candidates are accepted only when their parameter contracts are equivalent.
1033
+ // This is deliberately a lint-side proof, not a second call resolver: uncertain
1034
+ // generic or spread cases remain unhandled instead of entering the private,
1035
+ // stateful resolveCall path with a candidate subset.
1036
+ func floatingPromiseApplicableSignature(
1037
+ ctx *Context,
1038
+ call *shimast.CallExpression,
1039
+ signatures []*shimchecker.Signature,
1040
+ ) *shimchecker.Signature {
1041
+ if ctx == nil || ctx.Checker == nil || call == nil || len(signatures) == 0 {
1042
+ return nil
1043
+ }
1044
+ var applicable []*shimchecker.Signature
1045
+ for _, signature := range signatures {
1046
+ switch floatingPromiseSignatureApplicability(ctx.Checker, call, signature) {
1047
+ case floatingPromiseCallApplicable:
1048
+ applicable = append(applicable, signature)
1049
+ case floatingPromiseCallIncompatible:
1050
+ continue
1051
+ case floatingPromiseCallUncertain:
1052
+ return nil
1053
+ default:
1054
+ return nil
1055
+ }
1056
+ }
1057
+ if len(applicable) == 1 {
1058
+ return applicable[0]
1059
+ }
1060
+ if len(applicable) == 0 || !floatingPromiseEquivalentOverloads(ctx.Checker, call, applicable) {
1061
+ return nil
1062
+ }
1063
+ // TypeScript selects the first declaration when otherwise-identical
1064
+ // overloads are repeated. Preserve that specified source-order behavior,
1065
+ // but do not guess between merely overlapping parameter types.
1066
+ return applicable[0]
1067
+ }
1068
+
1069
+ type floatingPromiseCallApplicability uint8
1070
+
1071
+ const (
1072
+ floatingPromiseCallUncertain floatingPromiseCallApplicability = iota
1073
+ floatingPromiseCallIncompatible
1074
+ floatingPromiseCallApplicable
1075
+ )
1076
+
1077
+ func floatingPromiseEquivalentOverloads(
1078
+ checker *shimchecker.Checker,
1079
+ call *shimast.CallExpression,
1080
+ signatures []*shimchecker.Signature,
1081
+ ) bool {
1082
+ if checker == nil || call == nil || len(signatures) < 2 {
1083
+ return false
1084
+ }
1085
+ first := signatures[0]
1086
+ firstParameters := first.Parameters()
1087
+ firstDeclaration := first.Declaration()
1088
+ stableSourceOrder := firstDeclaration != nil
1089
+ firstHasLiteralParameters := floatingPromiseSignatureHasLiteralParameters(first)
1090
+ if first.HasRestParameter() || len(first.TypeParameters()) != 0 {
1091
+ return false
1092
+ }
1093
+ for _, signature := range signatures[1:] {
1094
+ parameters := signature.Parameters()
1095
+ if len(parameters) != len(firstParameters) ||
1096
+ signature.HasRestParameter() != first.HasRestParameter() ||
1097
+ shimchecker.Checker_getMinArgumentCount(checker, signature) != shimchecker.Checker_getMinArgumentCount(checker, first) ||
1098
+ len(signature.TypeParameters()) != len(first.TypeParameters()) {
1099
+ return false
1100
+ }
1101
+ for index := range firstParameters {
1102
+ left := floatingPromiseParameterType(checker, call, first, index)
1103
+ right := floatingPromiseParameterType(checker, call, signature, index)
1104
+ if left == nil || right == nil ||
1105
+ left.Flags()&(shimchecker.TypeFlagsAny|shimchecker.TypeFlagsUnknown) != 0 ||
1106
+ right.Flags()&(shimchecker.TypeFlagsAny|shimchecker.TypeFlagsUnknown) != 0 ||
1107
+ !checker.IsTypeAssignableTo(left, right) ||
1108
+ !checker.IsTypeAssignableTo(right, left) {
1109
+ return false
1110
+ }
1111
+ }
1112
+ declaration := signature.Declaration()
1113
+ if declaration == nil || firstDeclaration == nil ||
1114
+ declaration.Parent != firstDeclaration.Parent {
1115
+ stableSourceOrder = false
1116
+ }
1117
+ if !floatingPromiseSignatureParametersHaveSameSyntax(first, signature) {
1118
+ stableSourceOrder = false
1119
+ }
1120
+ if !firstHasLiteralParameters && floatingPromiseSignatureHasLiteralParameters(signature) {
1121
+ stableSourceOrder = false
1122
+ }
1123
+ }
1124
+ if stableSourceOrder {
1125
+ return true
1126
+ }
1127
+ // TypeScript's reorderCandidates reverses declaration-block groups for one
1128
+ // merged symbol. When public data cannot prove stable source order, selecting
1129
+ // an arbitrary declaration is safe only if every possible selection has the
1130
+ // exact same return type.
1131
+ firstReturn := checker.GetReturnTypeOfSignature(first)
1132
+ if firstReturn == nil {
1133
+ return false
1134
+ }
1135
+ for _, signature := range signatures[1:] {
1136
+ if checker.GetReturnTypeOfSignature(signature) != firstReturn {
1137
+ return false
1138
+ }
1139
+ }
1140
+ return true
1141
+ }
1142
+
1143
+ func floatingPromiseSignatureParametersHaveSameSyntax(
1144
+ left *shimchecker.Signature,
1145
+ right *shimchecker.Signature,
1146
+ ) bool {
1147
+ if left == nil || right == nil || left.Declaration() == nil || right.Declaration() == nil {
1148
+ return false
1149
+ }
1150
+ leftParameters := left.Declaration().Parameters()
1151
+ rightParameters := right.Declaration().Parameters()
1152
+ if len(leftParameters) != len(rightParameters) {
1153
+ return false
1154
+ }
1155
+ for index := range leftParameters {
1156
+ if leftParameters[index] == nil || rightParameters[index] == nil {
1157
+ return false
1158
+ }
1159
+ leftType := leftParameters[index].Type()
1160
+ rightType := rightParameters[index].Type()
1161
+ if leftType == nil || rightType == nil || leftType.Kind != rightType.Kind {
1162
+ return false
1163
+ }
1164
+ leftText := shimast.NodeText(leftType)
1165
+ rightText := shimast.NodeText(rightType)
1166
+ if leftText == "" || rightText == "" || leftText != rightText {
1167
+ return false
1168
+ }
1169
+ }
1170
+ return true
1171
+ }
1172
+
1173
+ func floatingPromiseSignatureHasLiteralParameters(signature *shimchecker.Signature) bool {
1174
+ if signature == nil || signature.Declaration() == nil {
1175
+ return false
1176
+ }
1177
+ for _, parameter := range signature.Declaration().Parameters() {
1178
+ if parameter != nil && parameter.Type() != nil && parameter.Type().Kind == shimast.KindLiteralType {
1179
+ return true
1180
+ }
1181
+ }
1182
+ return false
1183
+ }
1184
+
1185
+ func floatingPromiseSignatureApplicability(
1186
+ checker *shimchecker.Checker,
1187
+ call *shimast.CallExpression,
1188
+ signature *shimchecker.Signature,
1189
+ ) floatingPromiseCallApplicability {
1190
+ if checker == nil || call == nil || signature == nil {
1191
+ return floatingPromiseCallUncertain
1192
+ }
1193
+ if signature.ThisParameter() != nil {
1194
+ return floatingPromiseCallUncertain
1195
+ }
1196
+ arguments := []*shimast.Node(nil)
1197
+ if call.Arguments != nil {
1198
+ arguments = call.Arguments.Nodes
1199
+ }
1200
+ parameters := signature.Parameters()
1201
+ if len(arguments) < shimchecker.Checker_getMinArgumentCount(checker, signature) {
1202
+ return floatingPromiseCallIncompatible
1203
+ }
1204
+ // Array and tuple rest parameters require TypeScript's position-sensitive
1205
+ // effective-rest expansion, including a bounded tuple's maximum arity. A
1206
+ // number-index union cannot prove either property without the resolver.
1207
+ if signature.HasRestParameter() {
1208
+ return floatingPromiseCallUncertain
1209
+ }
1210
+ if len(arguments) > len(parameters) {
1211
+ return floatingPromiseCallIncompatible
1212
+ }
1213
+ typeArguments := call.AsNode().TypeArguments()
1214
+ typeParameters := signature.TypeParameters()
1215
+ if len(typeArguments) > len(typeParameters) || (len(typeArguments) != 0 && len(typeParameters) == 0) {
1216
+ return floatingPromiseCallIncompatible
1217
+ }
1218
+ if len(typeParameters) != 0 {
1219
+ return floatingPromiseGenericSignatureApplicability(
1220
+ checker,
1221
+ call,
1222
+ signature,
1223
+ arguments,
1224
+ typeArguments,
1225
+ typeParameters,
1226
+ )
1227
+ }
1228
+ for index, argument := range arguments {
1229
+ if argument == nil {
1230
+ return floatingPromiseCallUncertain
1231
+ }
1232
+ if argument.Kind == shimast.KindSpreadElement {
1233
+ return floatingPromiseCallUncertain
1234
+ }
1235
+ if floatingPromiseExpressionNeedsCandidateContext(checker, argument) {
1236
+ return floatingPromiseCallUncertain
1237
+ }
1238
+ parameterType := floatingPromiseParameterType(checker, call, signature, index)
1239
+ argumentType := checker.GetTypeAtLocation(argument)
1240
+ if parameterType == nil || argumentType == nil {
1241
+ return floatingPromiseCallUncertain
1242
+ }
1243
+ if floatingPromiseFunctionArgument(argument) {
1244
+ if !floatingPromiseTypeMayAcceptFunction(checker, parameterType) {
1245
+ if checker.IsTypeAssignableTo(argumentType, parameterType) {
1246
+ continue
1247
+ }
1248
+ return floatingPromiseCallIncompatible
1249
+ }
1250
+ if parameterType.Flags()&(shimchecker.TypeFlagsAny|shimchecker.TypeFlagsUnknown) != 0 {
1251
+ if checker.IsTypeAssignableTo(argumentType, parameterType) {
1252
+ continue
1253
+ }
1254
+ return floatingPromiseCallIncompatible
1255
+ }
1256
+ applicability := floatingPromiseCallableArgumentApplicability(
1257
+ checker,
1258
+ call,
1259
+ argument,
1260
+ parameterType,
1261
+ nil,
1262
+ nil,
1263
+ nil,
1264
+ )
1265
+ if applicability != floatingPromiseCallApplicable {
1266
+ return applicability
1267
+ }
1268
+ continue
1269
+ }
1270
+ if checker.IsContextSensitive(argument) {
1271
+ return floatingPromiseCallUncertain
1272
+ }
1273
+ if !checker.IsTypeAssignableTo(argumentType, parameterType) {
1274
+ return floatingPromiseCallIncompatible
1275
+ }
1276
+ }
1277
+ return floatingPromiseCallApplicable
1278
+ }
1279
+
1280
+ // floatingPromiseGenericSignatureApplicability proves the deliberately small
1281
+ // generic subset whose result can be correlated without instantiating a
1282
+ // candidate through TypeScript's private resolver. Every argument must match a
1283
+ // fixed parameter or provide a direct, constraint-valid inference for a naked
1284
+ // type parameter. Callback inference is accepted only after its complete input
1285
+ // contract and return are checked.
1286
+ func floatingPromiseGenericSignatureApplicability(
1287
+ checker *shimchecker.Checker,
1288
+ call *shimast.CallExpression,
1289
+ signature *shimchecker.Signature,
1290
+ arguments []*shimast.Node,
1291
+ typeArgumentNodes []*shimast.Node,
1292
+ typeParameters []*shimchecker.Type,
1293
+ ) floatingPromiseCallApplicability {
1294
+ if len(typeArgumentNodes) != 0 && len(typeArgumentNodes) < checker.GetMinTypeArgumentCount(typeParameters) {
1295
+ return floatingPromiseCallIncompatible
1296
+ }
1297
+ explicit := make([]*shimchecker.Type, len(typeParameters))
1298
+ for index, typeArgumentNode := range typeArgumentNodes {
1299
+ if typeArgumentNode == nil {
1300
+ return floatingPromiseCallUncertain
1301
+ }
1302
+ typeArgument := checker.GetTypeFromTypeNode(typeArgumentNode)
1303
+ if typeArgument == nil {
1304
+ return floatingPromiseCallUncertain
1305
+ }
1306
+ applicability := floatingPromiseTypeParameterCandidateApplicability(
1307
+ checker,
1308
+ typeParameters[index],
1309
+ typeArgument,
1310
+ typeParameters,
1311
+ call.Expression,
1312
+ )
1313
+ if applicability != floatingPromiseCallApplicable {
1314
+ return applicability
1315
+ }
1316
+ explicit[index] = typeArgument
1317
+ }
1318
+ if len(typeArgumentNodes) != 0 {
1319
+ // TypeScript does not infer omitted type arguments after the caller has
1320
+ // supplied an explicit prefix. Its public FillMissingTypeArguments query
1321
+ // applies and instantiates every remaining default with the same mapper.
1322
+ explicit = checker.FillMissingTypeArguments(
1323
+ explicit[:len(typeArgumentNodes)],
1324
+ typeParameters,
1325
+ checker.GetMinTypeArgumentCount(typeParameters),
1326
+ floatingPromiseSignatureIsJavaScript(signature),
1327
+ )
1328
+ if len(explicit) != len(typeParameters) {
1329
+ return floatingPromiseCallUncertain
1330
+ }
1331
+ }
1332
+
1333
+ inferred := make(map[*shimchecker.Type][]*shimchecker.Type, len(typeParameters))
1334
+ for index, argument := range arguments {
1335
+ if argument == nil || argument.Kind == shimast.KindSpreadElement {
1336
+ return floatingPromiseCallUncertain
1337
+ }
1338
+ parameterType := floatingPromiseParameterType(checker, call, signature, index)
1339
+ if parameterType == nil {
1340
+ return floatingPromiseCallUncertain
1341
+ }
1342
+ applicability := floatingPromiseGenericArgumentApplicability(
1343
+ checker,
1344
+ call,
1345
+ argument,
1346
+ parameterType,
1347
+ typeParameters,
1348
+ explicit,
1349
+ inferred,
1350
+ )
1351
+ if applicability != floatingPromiseCallApplicable {
1352
+ return applicability
1353
+ }
1354
+ }
1355
+
1356
+ for index, typeParameter := range typeParameters {
1357
+ if explicit[index] != nil {
1358
+ continue
1359
+ }
1360
+ candidates := inferred[typeParameter]
1361
+ if len(candidates) != 0 {
1362
+ if !floatingPromiseInferencesHaveWitness(checker, candidates) {
1363
+ return floatingPromiseCallUncertain
1364
+ }
1365
+ continue
1366
+ }
1367
+ defaultType := checker.GetDefaultFromTypeParameter(typeParameter)
1368
+ if defaultType == nil {
1369
+ return floatingPromiseCallUncertain
1370
+ }
1371
+ applicability := floatingPromiseTypeParameterCandidateApplicability(
1372
+ checker,
1373
+ typeParameter,
1374
+ defaultType,
1375
+ typeParameters,
1376
+ call.Expression,
1377
+ )
1378
+ if applicability != floatingPromiseCallApplicable {
1379
+ return applicability
1380
+ }
1381
+ }
1382
+ return floatingPromiseCallApplicable
1383
+ }
1384
+
1385
+ func floatingPromiseGenericArgumentApplicability(
1386
+ checker *shimchecker.Checker,
1387
+ call *shimast.CallExpression,
1388
+ argument *shimast.Node,
1389
+ parameterType *shimchecker.Type,
1390
+ typeParameters []*shimchecker.Type,
1391
+ explicit []*shimchecker.Type,
1392
+ inferred map[*shimchecker.Type][]*shimchecker.Type,
1393
+ ) floatingPromiseCallApplicability {
1394
+ if floatingPromiseExpressionNeedsCandidateContext(checker, argument) {
1395
+ return floatingPromiseCallUncertain
1396
+ }
1397
+ argumentType := checker.GetTypeAtLocation(argument)
1398
+ if argumentType == nil {
1399
+ return floatingPromiseCallUncertain
1400
+ }
1401
+ if typeParameterIndex := floatingPromiseTypeParameterIndex(typeParameters, parameterType); typeParameterIndex >= 0 {
1402
+ if checker.IsContextSensitive(argument) {
1403
+ return floatingPromiseCallUncertain
1404
+ }
1405
+ typeParameter := typeParameters[typeParameterIndex]
1406
+ if explicitType := explicit[typeParameterIndex]; explicitType != nil {
1407
+ if checker.IsTypeAssignableTo(argumentType, explicitType) {
1408
+ return floatingPromiseCallApplicable
1409
+ }
1410
+ return floatingPromiseCallIncompatible
1411
+ }
1412
+ applicability := floatingPromiseTypeParameterCandidateApplicability(
1413
+ checker,
1414
+ typeParameter,
1415
+ argumentType,
1416
+ typeParameters,
1417
+ call.Expression,
1418
+ )
1419
+ if applicability != floatingPromiseCallApplicable {
1420
+ return applicability
1421
+ }
1422
+ inferred[typeParameter] = append(inferred[typeParameter], argumentType)
1423
+ return floatingPromiseCallApplicable
1424
+ }
1425
+ if parameterType.Flags()&(shimchecker.TypeFlagsAny|shimchecker.TypeFlagsUnknown) != 0 {
1426
+ if checker.IsTypeAssignableTo(argumentType, parameterType) {
1427
+ return floatingPromiseCallApplicable
1428
+ }
1429
+ return floatingPromiseCallIncompatible
1430
+ }
1431
+ if floatingPromiseTypeContainsOpaqueMappedObject(checker, parameterType, nil) {
1432
+ // Reverse inference from mapped types depends on private mapped-type
1433
+ // constraints and templates that the public Checker surface does not
1434
+ // expose. In particular, a mapped constituent can report no properties or
1435
+ // index infos while still inferring a method type parameter.
1436
+ return floatingPromiseCallUncertain
1437
+ }
1438
+
1439
+ apparentParameter := checker.GetApparentType(parameterType)
1440
+ if apparentParameter != nil && len(checker.GetSignaturesOfType(apparentParameter, shimchecker.SignatureKindCall)) != 0 {
1441
+ return floatingPromiseCallableArgumentApplicability(
1442
+ checker,
1443
+ call,
1444
+ argument,
1445
+ parameterType,
1446
+ typeParameters,
1447
+ explicit,
1448
+ inferred,
1449
+ )
1450
+ }
1451
+ if floatingPromiseTypeContainsAnyTypeParameter(checker, parameterType, typeParameters, call.Expression, nil) {
1452
+ return floatingPromiseCallUncertain
1453
+ }
1454
+ if checker.IsContextSensitive(argument) {
1455
+ // Object, array, and branching literals require the candidate-specific
1456
+ // contextual mapper that is intentionally unavailable here.
1457
+ return floatingPromiseCallUncertain
1458
+ }
1459
+ if checker.IsTypeAssignableTo(argumentType, parameterType) {
1460
+ return floatingPromiseCallApplicable
1461
+ }
1462
+ return floatingPromiseCallIncompatible
1463
+ }
1464
+
1465
+ func floatingPromiseCallableArgumentApplicability(
1466
+ checker *shimchecker.Checker,
1467
+ call *shimast.CallExpression,
1468
+ argument *shimast.Node,
1469
+ parameterType *shimchecker.Type,
1470
+ typeParameters []*shimchecker.Type,
1471
+ explicit []*shimchecker.Type,
1472
+ inferred map[*shimchecker.Type][]*shimchecker.Type,
1473
+ ) floatingPromiseCallApplicability {
1474
+ argumentType := checker.GetTypeAtLocation(argument)
1475
+ if argumentType == nil {
1476
+ return floatingPromiseCallUncertain
1477
+ }
1478
+ expectedType := checker.GetApparentType(parameterType)
1479
+ actualType := checker.GetApparentType(argumentType)
1480
+ if expectedType == nil || actualType == nil {
1481
+ return floatingPromiseCallUncertain
1482
+ }
1483
+ expectedSignatures := checker.GetSignaturesOfType(expectedType, shimchecker.SignatureKindCall)
1484
+ actualSignatures := checker.GetSignaturesOfType(actualType, shimchecker.SignatureKindCall)
1485
+ if len(expectedSignatures) != 1 {
1486
+ return floatingPromiseCallUncertain
1487
+ }
1488
+ if len(actualSignatures) == 0 {
1489
+ return floatingPromiseCallIncompatible
1490
+ }
1491
+ if len(actualSignatures) != 1 {
1492
+ return floatingPromiseCallUncertain
1493
+ }
1494
+ expected := expectedSignatures[0]
1495
+ actual := actualSignatures[0]
1496
+ if expected.HasRestParameter() || actual.HasRestParameter() ||
1497
+ expected.ThisParameter() != nil || actual.ThisParameter() != nil ||
1498
+ len(expected.TypeParameters()) != 0 || len(actual.TypeParameters()) != 0 {
1499
+ return floatingPromiseCallUncertain
1500
+ }
1501
+ if checker.GetTypePredicateOfSignature(expected) != nil ||
1502
+ checker.GetTypePredicateOfSignature(actual) != nil {
1503
+ return floatingPromiseCallUncertain
1504
+ }
1505
+ if len(checker.GetSignaturesOfType(expectedType, shimchecker.SignatureKindConstruct)) != 0 ||
1506
+ len(checker.GetIndexInfosOfType(expectedType)) != 0 {
1507
+ return floatingPromiseCallUncertain
1508
+ }
1509
+ for _, expectedProperty := range shimchecker.Checker_getPropertiesOfType(checker, expectedType) {
1510
+ expectedOptional := expectedProperty.Flags&shimast.SymbolFlagsOptional != 0
1511
+ actualProperty := checker.GetPropertyOfType(actualType, expectedProperty.Name)
1512
+ if actualProperty == nil {
1513
+ if expectedOptional {
1514
+ continue
1515
+ }
1516
+ return floatingPromiseCallIncompatible
1517
+ }
1518
+ if !expectedOptional && actualProperty.Flags&shimast.SymbolFlagsOptional != 0 {
1519
+ return floatingPromiseCallIncompatible
1520
+ }
1521
+ if floatingPromiseSymbolHasNonPublicDeclaration(expectedProperty) ||
1522
+ floatingPromiseSymbolHasNonPublicDeclaration(actualProperty) {
1523
+ return floatingPromiseCallUncertain
1524
+ }
1525
+ expectedPropertyType := checker.GetTypeOfSymbolAtLocation(expectedProperty, call.Expression)
1526
+ actualPropertyType := checker.GetTypeOfSymbolAtLocation(actualProperty, argument)
1527
+ if expectedPropertyType == nil || actualPropertyType == nil {
1528
+ return floatingPromiseCallUncertain
1529
+ }
1530
+ if floatingPromiseTypeContainsAnyTypeParameter(
1531
+ checker,
1532
+ expectedPropertyType,
1533
+ typeParameters,
1534
+ call.Expression,
1535
+ nil,
1536
+ ) {
1537
+ return floatingPromiseCallUncertain
1538
+ }
1539
+ if !checker.IsTypeAssignableTo(actualPropertyType, expectedPropertyType) {
1540
+ return floatingPromiseCallIncompatible
1541
+ }
1542
+ }
1543
+ expectedParameters := expected.Parameters()
1544
+ actualParameters := actual.Parameters()
1545
+ if shimchecker.Checker_getMinArgumentCount(checker, actual) >
1546
+ shimchecker.Checker_getMinArgumentCount(checker, expected) {
1547
+ // Function compatibility uses effective target parameter counts and then
1548
+ // applies optional-parameter and callback-variance rules. Comparing the
1549
+ // two minima alone cannot conclusively exclude this candidate.
1550
+ return floatingPromiseCallUncertain
1551
+ }
1552
+ comparableParameters := len(actualParameters)
1553
+ if comparableParameters > len(expectedParameters) {
1554
+ comparableParameters = len(expectedParameters)
1555
+ }
1556
+ for index := 0; index < comparableParameters; index++ {
1557
+ expectedParameter := floatingPromiseSignatureParameterType(
1558
+ checker,
1559
+ expected,
1560
+ index,
1561
+ call.Expression,
1562
+ )
1563
+ actualParameter := floatingPromiseSignatureParameterType(checker, actual, index, argument)
1564
+ if expectedParameter == nil || actualParameter == nil {
1565
+ return floatingPromiseCallUncertain
1566
+ }
1567
+ if floatingPromiseTypeContainsAnyTypeParameter(checker, expectedParameter, typeParameters, call.Expression, nil) {
1568
+ return floatingPromiseCallUncertain
1569
+ }
1570
+ if !checker.IsTypeAssignableTo(expectedParameter, actualParameter) {
1571
+ // Callback parameter variance depends on the target declaration kind
1572
+ // and strictFunctionTypes. A raw parameter-to-parameter comparison can
1573
+ // therefore disprove neither method bivariance nor a candidate-specific
1574
+ // contextual comparison.
1575
+ return floatingPromiseCallUncertain
1576
+ }
1577
+ }
1578
+
1579
+ expectedReturn := checker.GetReturnTypeOfSignature(expected)
1580
+ actualReturn := checker.GetReturnTypeOfSignature(actual)
1581
+ if expectedReturn == nil || actualReturn == nil {
1582
+ return floatingPromiseCallUncertain
1583
+ }
1584
+ implicitReturn := floatingPromiseFunctionHasImplicitReturnType(argument)
1585
+ returnNeedsContext := floatingPromiseFunctionReturnNeedsCandidateContext(checker, argument)
1586
+ if typeParameterIndex := floatingPromiseTypeParameterIndex(typeParameters, expectedReturn); typeParameterIndex >= 0 {
1587
+ typeParameter := typeParameters[typeParameterIndex]
1588
+ if explicitType := explicit[typeParameterIndex]; explicitType != nil {
1589
+ if explicitType.Flags()&shimchecker.TypeFlagsVoid != 0 {
1590
+ return floatingPromiseCallApplicable
1591
+ }
1592
+ if returnNeedsContext {
1593
+ return floatingPromiseCallUncertain
1594
+ }
1595
+ if checker.IsTypeAssignableTo(actualReturn, explicitType) {
1596
+ return floatingPromiseCallApplicable
1597
+ }
1598
+ if implicitReturn || checker.IsContextSensitive(argument) {
1599
+ return floatingPromiseCallUncertain
1600
+ }
1601
+ return floatingPromiseCallIncompatible
1602
+ }
1603
+ applicability := floatingPromiseTypeParameterCandidateApplicability(
1604
+ checker,
1605
+ typeParameter,
1606
+ actualReturn,
1607
+ typeParameters,
1608
+ call.Expression,
1609
+ )
1610
+ if applicability != floatingPromiseCallApplicable {
1611
+ if applicability == floatingPromiseCallIncompatible &&
1612
+ (implicitReturn || checker.IsContextSensitive(argument)) {
1613
+ return floatingPromiseCallUncertain
1614
+ }
1615
+ return applicability
1616
+ }
1617
+ if returnNeedsContext && checker.GetConstraintOfTypeParameter(typeParameter) != nil {
1618
+ return floatingPromiseCallUncertain
1619
+ }
1620
+ inferred[typeParameter] = append(inferred[typeParameter], actualReturn)
1621
+ return floatingPromiseCallApplicable
1622
+ }
1623
+ if floatingPromiseTypeContainsAnyTypeParameter(checker, expectedReturn, typeParameters, call.Expression, nil) {
1624
+ return floatingPromiseCallUncertain
1625
+ }
1626
+ if expectedReturn.Flags()&shimchecker.TypeFlagsVoid != 0 {
1627
+ return floatingPromiseCallApplicable
1628
+ }
1629
+ if returnNeedsContext {
1630
+ return floatingPromiseCallUncertain
1631
+ }
1632
+ if checker.IsTypeAssignableTo(actualReturn, expectedReturn) {
1633
+ return floatingPromiseCallApplicable
1634
+ }
1635
+ if implicitReturn || checker.IsContextSensitive(argument) {
1636
+ // The cached argument type belongs to the original call's contextual
1637
+ // signature. A different overload candidate can supply a different return
1638
+ // context (for example, string versus a string literal), so that cached
1639
+ // mismatch cannot prove the candidate is inapplicable.
1640
+ return floatingPromiseCallUncertain
1641
+ }
1642
+ return floatingPromiseCallIncompatible
1643
+ }
1644
+
1645
+ func floatingPromiseSymbolHasNonPublicDeclaration(symbol *shimast.Symbol) bool {
1646
+ if symbol == nil {
1647
+ return false
1648
+ }
1649
+ hasNonPublicModifier := func(declaration *shimast.Node) bool {
1650
+ return declaration != nil &&
1651
+ declaration.ModifierFlags()&(shimast.ModifierFlagsPrivate|shimast.ModifierFlagsProtected) != 0
1652
+ }
1653
+ if hasNonPublicModifier(symbol.ValueDeclaration) {
1654
+ return true
1655
+ }
1656
+ for _, declaration := range symbol.Declarations {
1657
+ if hasNonPublicModifier(declaration) {
1658
+ return true
1659
+ }
1660
+ }
1661
+ return false
1662
+ }
1663
+
1664
+ func floatingPromiseTypeParameterCandidateApplicability(
1665
+ checker *shimchecker.Checker,
1666
+ typeParameter *shimchecker.Type,
1667
+ candidate *shimchecker.Type,
1668
+ typeParameters []*shimchecker.Type,
1669
+ location *shimast.Node,
1670
+ ) floatingPromiseCallApplicability {
1671
+ if checker == nil || typeParameter == nil || candidate == nil {
1672
+ return floatingPromiseCallUncertain
1673
+ }
1674
+ constraint := checker.GetConstraintOfTypeParameter(typeParameter)
1675
+ if constraint == nil || constraint == typeParameter {
1676
+ return floatingPromiseCallApplicable
1677
+ }
1678
+ if floatingPromiseTypeContainsAnyTypeParameter(checker, constraint, typeParameters, location, nil) {
1679
+ return floatingPromiseCallUncertain
1680
+ }
1681
+ if checker.IsTypeAssignableTo(candidate, constraint) {
1682
+ return floatingPromiseCallApplicable
1683
+ }
1684
+ return floatingPromiseCallIncompatible
1685
+ }
1686
+
1687
+ func floatingPromiseTypeParameterIndex(typeParameters []*shimchecker.Type, candidate *shimchecker.Type) int {
1688
+ for index, typeParameter := range typeParameters {
1689
+ if candidate == typeParameter {
1690
+ return index
1691
+ }
1692
+ }
1693
+ return -1
1694
+ }
1695
+
1696
+ func floatingPromiseInferencesHaveWitness(checker *shimchecker.Checker, inferred []*shimchecker.Type) bool {
1697
+ for _, witness := range inferred {
1698
+ if witness == nil {
1699
+ continue
1700
+ }
1701
+ acceptsAll := true
1702
+ for _, candidate := range inferred {
1703
+ if candidate == nil || !checker.IsTypeAssignableTo(candidate, witness) {
1704
+ acceptsAll = false
1705
+ break
1706
+ }
1707
+ }
1708
+ if acceptsAll {
1709
+ return true
1710
+ }
1711
+ }
1712
+ return false
1713
+ }
1714
+
1715
+ func floatingPromiseTypeContainsOpaqueMappedObject(
1716
+ checker *shimchecker.Checker,
1717
+ t *shimchecker.Type,
1718
+ visited map[*shimchecker.Type]bool,
1719
+ ) bool {
1720
+ if checker == nil || t == nil {
1721
+ return false
1722
+ }
1723
+ if visited == nil {
1724
+ visited = make(map[*shimchecker.Type]bool)
1725
+ }
1726
+ if visited[t] {
1727
+ return false
1728
+ }
1729
+ visited[t] = true
1730
+ if t.Flags()&shimchecker.TypeFlagsObject != 0 &&
1731
+ t.ObjectFlags()&(shimchecker.ObjectFlagsMapped|shimchecker.ObjectFlagsReverseMapped) != 0 {
1732
+ return true
1733
+ }
1734
+ if t.Flags()&shimchecker.TypeFlagsUnionOrIntersection != 0 {
1735
+ for _, part := range t.Types() {
1736
+ if floatingPromiseTypeContainsOpaqueMappedObject(checker, part, visited) {
1737
+ return true
1738
+ }
1739
+ }
1740
+ }
1741
+ if t.Flags()&shimchecker.TypeFlagsObject != 0 && t.ObjectFlags()&shimchecker.ObjectFlagsReference != 0 {
1742
+ for _, typeArgument := range checker.GetTypeArguments(t) {
1743
+ if floatingPromiseTypeContainsOpaqueMappedObject(checker, typeArgument, visited) {
1744
+ return true
1745
+ }
1746
+ }
1747
+ }
1748
+ return false
1749
+ }
1750
+
1751
+ func floatingPromiseTypeContainsAnyTypeParameter(
1752
+ checker *shimchecker.Checker,
1753
+ t *shimchecker.Type,
1754
+ typeParameters []*shimchecker.Type,
1755
+ location *shimast.Node,
1756
+ visited map[*shimchecker.Type]bool,
1757
+ ) bool {
1758
+ if checker == nil || t == nil {
1759
+ return true
1760
+ }
1761
+ if floatingPromiseTypeParameterIndex(typeParameters, t) >= 0 ||
1762
+ t.Flags()&shimchecker.TypeFlagsTypeParameter != 0 {
1763
+ return true
1764
+ }
1765
+ if visited == nil {
1766
+ visited = make(map[*shimchecker.Type]bool)
1767
+ }
1768
+ if visited[t] {
1769
+ return false
1770
+ }
1771
+ visited[t] = true
1772
+ if t.Flags()&shimchecker.TypeFlagsObject != 0 &&
1773
+ t.ObjectFlags()&(shimchecker.ObjectFlagsMapped|shimchecker.ObjectFlagsReverseMapped) != 0 {
1774
+ return true
1775
+ }
1776
+
1777
+ if t.Flags()&shimchecker.TypeFlagsUnionOrIntersection != 0 {
1778
+ for _, part := range t.Types() {
1779
+ if floatingPromiseTypeContainsAnyTypeParameter(checker, part, typeParameters, location, visited) {
1780
+ return true
1781
+ }
1782
+ }
1783
+ }
1784
+ if t.Flags()&shimchecker.TypeFlagsObject != 0 && t.ObjectFlags()&shimchecker.ObjectFlagsReference != 0 {
1785
+ for _, typeArgument := range checker.GetTypeArguments(t) {
1786
+ if floatingPromiseTypeContainsAnyTypeParameter(checker, typeArgument, typeParameters, location, visited) {
1787
+ return true
1788
+ }
1789
+ }
1790
+ }
1791
+ if t.Flags()&(shimchecker.TypeFlagsConditional|
1792
+ shimchecker.TypeFlagsIndex|
1793
+ shimchecker.TypeFlagsIndexedAccess|
1794
+ shimchecker.TypeFlagsSubstitution|
1795
+ shimchecker.TypeFlagsStringMapping|
1796
+ shimchecker.TypeFlagsTemplateLiteral) != 0 {
1797
+ return true
1798
+ }
1799
+ for _, kind := range []shimchecker.SignatureKind{
1800
+ shimchecker.SignatureKindCall,
1801
+ shimchecker.SignatureKindConstruct,
1802
+ } {
1803
+ for _, signature := range checker.GetSignaturesOfType(t, kind) {
1804
+ if len(signature.TypeParameters()) != 0 {
1805
+ return true
1806
+ }
1807
+ for index := range signature.Parameters() {
1808
+ parameterType := floatingPromiseSignatureParameterType(checker, signature, index, location)
1809
+ if parameterType == nil ||
1810
+ floatingPromiseTypeContainsAnyTypeParameter(checker, parameterType, typeParameters, location, visited) {
1811
+ return true
1812
+ }
1813
+ }
1814
+ if returnType := checker.GetReturnTypeOfSignature(signature); returnType == nil ||
1815
+ floatingPromiseTypeContainsAnyTypeParameter(checker, returnType, typeParameters, location, visited) {
1816
+ return true
1817
+ }
1818
+ }
1819
+ }
1820
+ if t.Flags()&shimchecker.TypeFlagsObject != 0 {
1821
+ for _, property := range shimchecker.Checker_getPropertiesOfType(checker, t) {
1822
+ propertyType := checker.GetTypeOfSymbolAtLocation(property, location)
1823
+ if propertyType == nil ||
1824
+ floatingPromiseTypeContainsAnyTypeParameter(checker, propertyType, typeParameters, location, visited) {
1825
+ return true
1826
+ }
1827
+ }
1828
+ for _, indexInfo := range checker.GetIndexInfosOfType(t) {
1829
+ if indexInfo == nil ||
1830
+ floatingPromiseTypeContainsAnyTypeParameter(checker, indexInfo.KeyType(), typeParameters, location, visited) ||
1831
+ floatingPromiseTypeContainsAnyTypeParameter(checker, indexInfo.ValueType(), typeParameters, location, visited) {
1832
+ return true
1833
+ }
1834
+ }
1835
+ }
1836
+ return false
1837
+ }
1838
+
1839
+ func floatingPromiseFunctionArgument(node *shimast.Node) bool {
1840
+ node = unwrapFloatingPromiseExpression(node)
1841
+ return node != nil && (node.Kind == shimast.KindArrowFunction || node.Kind == shimast.KindFunctionExpression)
1842
+ }
1843
+
1844
+ // floatingPromiseExpressionNeedsCandidateContext reports expression shapes
1845
+ // whose type or validity can change when TypeScript rechecks them against an
1846
+ // overload parameter. Checker.IsContextSensitive has a narrower purpose: it
1847
+ // tracks nested untyped functions for inference and therefore returns false for
1848
+ // ordinary object and array literals. Candidate context also flows through
1849
+ // await/non-null wrappers, template expressions, and generic call return
1850
+ // inference. Treat those paths as uncertain instead of comparing a type cached
1851
+ // under the canonical call's different overload.
1852
+ func floatingPromiseExpressionNeedsCandidateContext(
1853
+ checker *shimchecker.Checker,
1854
+ node *shimast.Node,
1855
+ ) bool {
1856
+ node = unwrapFloatingPromiseExpression(node)
1857
+ if node == nil {
1858
+ return true
1859
+ }
1860
+ switch node.Kind {
1861
+ case shimast.KindObjectLiteralExpression,
1862
+ shimast.KindArrayLiteralExpression,
1863
+ shimast.KindConditionalExpression,
1864
+ shimast.KindTemplateExpression:
1865
+ return true
1866
+ case shimast.KindAwaitExpression,
1867
+ shimast.KindNonNullExpression,
1868
+ shimast.KindYieldExpression:
1869
+ return floatingPromiseExpressionChildNeedsCandidateContext(checker, node.Expression())
1870
+ case shimast.KindAsExpression,
1871
+ shimast.KindTypeAssertionExpression:
1872
+ return shimast.IsConstAssertion(node)
1873
+ case shimast.KindCallExpression,
1874
+ shimast.KindNewExpression,
1875
+ shimast.KindTaggedTemplateExpression:
1876
+ return floatingPromiseCallNeedsCandidateContext(checker, node)
1877
+ case shimast.KindBinaryExpression:
1878
+ binary := node.AsBinaryExpression()
1879
+ if binary == nil || binary.OperatorToken == nil {
1880
+ return true
1881
+ }
1882
+ switch binary.OperatorToken.Kind {
1883
+ case shimast.KindAmpersandAmpersandToken,
1884
+ shimast.KindBarBarToken,
1885
+ shimast.KindQuestionQuestionToken,
1886
+ shimast.KindCommaToken:
1887
+ return true
1888
+ }
1889
+ }
1890
+ return false
1891
+ }
1892
+
1893
+ func floatingPromiseExpressionChildNeedsCandidateContext(
1894
+ checker *shimchecker.Checker,
1895
+ node *shimast.Node,
1896
+ ) bool {
1897
+ node = unwrapFloatingPromiseExpression(node)
1898
+ return floatingPromiseFunctionArgument(node) ||
1899
+ floatingPromiseExpressionNeedsCandidateContext(checker, node)
1900
+ }
1901
+
1902
+ func floatingPromiseCallNeedsCandidateContext(
1903
+ checker *shimchecker.Checker,
1904
+ node *shimast.Node,
1905
+ ) bool {
1906
+ if checker == nil || node == nil {
1907
+ return true
1908
+ }
1909
+ var callee *shimast.Node
1910
+ if node.Kind == shimast.KindTaggedTemplateExpression {
1911
+ tagged := node.AsTaggedTemplateExpression()
1912
+ if tagged != nil {
1913
+ callee = tagged.Tag
1914
+ }
1915
+ } else {
1916
+ callee = node.Expression()
1917
+ }
1918
+ if callee == nil {
1919
+ return true
1920
+ }
1921
+ calleeType := checker.GetTypeAtLocation(callee)
1922
+ if calleeType == nil {
1923
+ return true
1924
+ }
1925
+ signatureKind := shimchecker.SignatureKindCall
1926
+ if node.Kind == shimast.KindNewExpression {
1927
+ signatureKind = shimchecker.SignatureKindConstruct
1928
+ }
1929
+ sawSignature := false
1930
+ for _, part := range promiseUnionParts(calleeType) {
1931
+ apparent := checker.GetApparentType(part)
1932
+ if apparent == nil {
1933
+ return true
1934
+ }
1935
+ for _, signature := range checker.GetSignaturesOfType(apparent, signatureKind) {
1936
+ if signature == nil {
1937
+ return true
1938
+ }
1939
+ sawSignature = true
1940
+ target := signature.Target()
1941
+ if len(signature.TypeParameters()) != 0 ||
1942
+ target != nil && len(target.TypeParameters()) != 0 {
1943
+ return true
1944
+ }
1945
+ }
1946
+ }
1947
+ return !sawSignature
1948
+ }
1949
+
1950
+ func floatingPromiseFunctionHasImplicitReturnType(node *shimast.Node) bool {
1951
+ node = unwrapFloatingPromiseExpression(node)
1952
+ return floatingPromiseFunctionArgument(node) && node.Type() == nil
1953
+ }
1954
+
1955
+ func floatingPromiseFunctionReturnNeedsCandidateContext(
1956
+ checker *shimchecker.Checker,
1957
+ node *shimast.Node,
1958
+ ) bool {
1959
+ node = unwrapFloatingPromiseExpression(node)
1960
+ if !floatingPromiseFunctionHasImplicitReturnType(node) {
1961
+ return false
1962
+ }
1963
+ if node.Kind == shimast.KindFunctionExpression {
1964
+ expression := node.AsFunctionExpression()
1965
+ if expression == nil || expression.AsteriskToken != nil {
1966
+ return true
1967
+ }
1968
+ }
1969
+ body := node.Body()
1970
+ if body == nil {
1971
+ return true
1972
+ }
1973
+ if body.Kind != shimast.KindBlock {
1974
+ return floatingPromiseExpressionNeedsCandidateContext(checker, body)
1975
+ }
1976
+ needsContext := false
1977
+ walkConsistentReturnBody(body, func(statement *shimast.Node) {
1978
+ if needsContext || statement == nil {
1979
+ return
1980
+ }
1981
+ if expression := statement.Expression(); expression != nil &&
1982
+ floatingPromiseExpressionNeedsCandidateContext(checker, expression) {
1983
+ needsContext = true
1984
+ }
1985
+ })
1986
+ return needsContext
1987
+ }
1988
+
1989
+ func floatingPromiseTypeMayAcceptFunction(checker *shimchecker.Checker, t *shimchecker.Type) bool {
1990
+ if checker == nil || t == nil {
1991
+ return false
1992
+ }
1993
+ if t.Flags()&(shimchecker.TypeFlagsAny|shimchecker.TypeFlagsUnknown) != 0 {
1994
+ return true
1995
+ }
1996
+ for _, part := range promiseUnionParts(t) {
1997
+ if part == nil {
1998
+ continue
1999
+ }
2000
+ apparent := checker.GetApparentType(part)
2001
+ if apparent != nil && len(checker.GetSignaturesOfType(apparent, shimchecker.SignatureKindCall)) != 0 {
2002
+ return true
2003
+ }
2004
+ }
2005
+ return false
2006
+ }
2007
+
2008
+ func floatingPromiseParameterType(
2009
+ checker *shimchecker.Checker,
2010
+ call *shimast.CallExpression,
2011
+ signature *shimchecker.Signature,
2012
+ index int,
2013
+ ) *shimchecker.Type {
2014
+ if checker == nil || call == nil || signature == nil || index < 0 {
2015
+ return nil
2016
+ }
2017
+ return floatingPromiseSignatureParameterType(checker, signature, index, call.Expression)
2018
+ }
2019
+
2020
+ func floatingPromiseSignatureParameterType(
2021
+ checker *shimchecker.Checker,
2022
+ signature *shimchecker.Signature,
2023
+ index int,
2024
+ location *shimast.Node,
2025
+ ) *shimchecker.Type {
2026
+ if checker == nil || signature == nil || index < 0 || location == nil {
2027
+ return nil
2028
+ }
2029
+ parameters := signature.Parameters()
2030
+ if len(parameters) == 0 {
2031
+ return nil
2032
+ }
2033
+ parameterIndex := index
2034
+ if parameterIndex >= len(parameters) {
2035
+ if !signature.HasRestParameter() {
2036
+ return nil
2037
+ }
2038
+ parameterIndex = len(parameters) - 1
2039
+ }
2040
+ parameterType := checker.GetTypeOfSymbolAtLocation(parameters[parameterIndex], location)
2041
+ if parameterType == nil {
2042
+ return nil
2043
+ }
2044
+ if signature.HasRestParameter() && parameterIndex == len(parameters)-1 && index >= parameterIndex {
2045
+ if elementType := checker.GetNumberIndexType(parameterType); elementType != nil {
2046
+ return elementType
2047
+ }
2048
+ }
2049
+ return parameterType
2050
+ }
2051
+
2052
+ func floatingPromiseSignatureIsJavaScript(signature *shimchecker.Signature) bool {
2053
+ if signature == nil {
2054
+ return false
2055
+ }
2056
+ declaration := signature.Declaration()
2057
+ return declaration != nil && declaration.Flags&shimast.NodeFlagsJavaScriptFile != 0
2058
+ }
2059
+
2060
+ // floatingPromiseSignatureReturnIsUnhandled proves the selected declaration's
2061
+ // return is safe without instantiating a signature through private Checker
2062
+ // machinery. Concrete returns use the ordinary Promise classifiers. A naked
2063
+ // method type parameter is accepted only when an explicit type argument or a
2064
+ // directly corresponding value/callback return supplies every inferred type;
2065
+ // all other generic shapes are conservatively unhandled.
2066
+ func floatingPromiseSignatureReturnIsUnhandled(
2067
+ ctx *Context,
2068
+ node *shimast.Node,
2069
+ call *shimast.CallExpression,
2070
+ signature *shimchecker.Signature,
2071
+ options noFloatingPromisesOptions,
2072
+ ) bool {
2073
+ returnType := ctx.Checker.GetReturnTypeOfSignature(signature)
2074
+ if returnType == nil {
2075
+ return true
2076
+ }
2077
+ typeParameters := signature.TypeParameters()
2078
+ if len(typeParameters) == 0 {
2079
+ return floatingPromiseInferenceIsUncertain(returnType, nil) ||
2080
+ isFloatingPromiseType(ctx, node, returnType, options) ||
2081
+ isFloatingPromiseArray(ctx, node, returnType, options)
2082
+ }
2083
+ for index, typeParameter := range typeParameters {
2084
+ if returnType != typeParameter {
2085
+ continue
2086
+ }
2087
+ inferred := floatingPromiseNakedReturnInferences(ctx.Checker, call, signature, typeParameter, index)
2088
+ if len(inferred) == 0 {
2089
+ return true
2090
+ }
2091
+ for _, inferredType := range inferred {
2092
+ if floatingPromiseInferenceIsUncertain(inferredType, nil) ||
2093
+ floatingPromiseTypeContainsAnyTypeParameter(ctx.Checker, inferredType, nil, node, nil) ||
2094
+ isFloatingPromiseType(ctx, node, inferredType, options) ||
2095
+ isFloatingPromiseArray(ctx, node, inferredType, options) {
2096
+ return true
2097
+ }
2098
+ }
2099
+ return false
2100
+ }
2101
+ // Resolving a nested generic return (for example Container<T>) correctly
2102
+ // requires TypeScript's full inference mapper. With no public candidate-set
2103
+ // API, treating that shape as safe would hide a possible Promise.
2104
+ return true
2105
+ }
2106
+
2107
+ func floatingPromiseInferenceIsUncertain(
2108
+ t *shimchecker.Type,
2109
+ visited map[*shimchecker.Type]bool,
2110
+ ) bool {
2111
+ if t == nil {
2112
+ return true
2113
+ }
2114
+ if t.Flags()&(shimchecker.TypeFlagsAny|
2115
+ shimchecker.TypeFlagsUnknown|
2116
+ shimchecker.TypeFlagsTypeParameter|
2117
+ shimchecker.TypeFlagsConditional|
2118
+ shimchecker.TypeFlagsIndexedAccess|
2119
+ shimchecker.TypeFlagsSubstitution) != 0 {
2120
+ return true
2121
+ }
2122
+ if t.Flags()&shimchecker.TypeFlagsUnionOrIntersection == 0 {
2123
+ return false
2124
+ }
2125
+ if visited == nil {
2126
+ visited = make(map[*shimchecker.Type]bool)
2127
+ }
2128
+ if visited[t] {
2129
+ return false
2130
+ }
2131
+ visited[t] = true
2132
+ for _, part := range t.Types() {
2133
+ if floatingPromiseInferenceIsUncertain(part, visited) {
2134
+ return true
2135
+ }
2136
+ }
2137
+ return false
2138
+ }
2139
+
2140
+ func floatingPromiseNakedReturnInferences(
2141
+ checker *shimchecker.Checker,
2142
+ call *shimast.CallExpression,
2143
+ signature *shimchecker.Signature,
2144
+ typeParameter *shimchecker.Type,
2145
+ typeParameterIndex int,
2146
+ ) []*shimchecker.Type {
2147
+ if checker == nil || call == nil || signature == nil || typeParameter == nil {
2148
+ return nil
2149
+ }
2150
+ typeArguments := call.AsNode().TypeArguments()
2151
+ if len(typeArguments) != 0 {
2152
+ provided := make([]*shimchecker.Type, len(typeArguments))
2153
+ for index, typeArgument := range typeArguments {
2154
+ if typeArgument == nil {
2155
+ return nil
2156
+ }
2157
+ provided[index] = checker.GetTypeFromTypeNode(typeArgument)
2158
+ if provided[index] == nil {
2159
+ return nil
2160
+ }
2161
+ }
2162
+ filled := checker.FillMissingTypeArguments(
2163
+ provided,
2164
+ signature.TypeParameters(),
2165
+ checker.GetMinTypeArgumentCount(signature.TypeParameters()),
2166
+ floatingPromiseSignatureIsJavaScript(signature),
2167
+ )
2168
+ if typeParameterIndex < 0 || typeParameterIndex >= len(filled) {
2169
+ return nil
2170
+ }
2171
+ return []*shimchecker.Type{filled[typeParameterIndex]}
2172
+ }
2173
+ if call.Arguments == nil {
2174
+ return nil
2175
+ }
2176
+ var inferred []*shimchecker.Type
2177
+ for index, argument := range call.Arguments.Nodes {
2178
+ if argument == nil || argument.Kind == shimast.KindSpreadElement {
2179
+ return nil
2180
+ }
2181
+ parameterType := floatingPromiseParameterType(checker, call, signature, index)
2182
+ argumentType := checker.GetTypeAtLocation(argument)
2183
+ if parameterType == nil || argumentType == nil {
2184
+ return nil
2185
+ }
2186
+ if parameterType == typeParameter {
2187
+ if !floatingPromiseTypeParameterAccepts(checker, typeParameter, argumentType) {
2188
+ return nil
2189
+ }
2190
+ inferred = append(inferred, argumentType)
2191
+ continue
2192
+ }
2193
+ for _, parameterPart := range promiseUnionParts(parameterType) {
2194
+ for _, parameterSignature := range checker.GetSignaturesOfType(parameterPart, shimchecker.SignatureKindCall) {
2195
+ if checker.GetReturnTypeOfSignature(parameterSignature) != typeParameter {
2196
+ continue
2197
+ }
2198
+ for _, argumentPart := range promiseUnionParts(argumentType) {
2199
+ for _, argumentSignature := range checker.GetSignaturesOfType(argumentPart, shimchecker.SignatureKindCall) {
2200
+ if actualReturn := checker.GetReturnTypeOfSignature(argumentSignature); actualReturn != nil {
2201
+ if !floatingPromiseTypeParameterAccepts(checker, typeParameter, actualReturn) {
2202
+ return nil
2203
+ }
2204
+ inferred = append(inferred, actualReturn)
2205
+ }
2206
+ }
2207
+ }
2208
+ }
2209
+ }
2210
+ }
2211
+ if len(inferred) == 0 {
2212
+ defaultType := checker.GetDefaultFromTypeParameter(typeParameter)
2213
+ if defaultType != nil && !floatingPromiseTypeContainsAnyTypeParameter(
2214
+ checker,
2215
+ defaultType,
2216
+ signature.TypeParameters(),
2217
+ call.Expression,
2218
+ nil,
2219
+ ) && floatingPromiseTypeParameterAccepts(checker, typeParameter, defaultType) {
2220
+ return []*shimchecker.Type{defaultType}
2221
+ }
2222
+ }
2223
+ return inferred
2224
+ }
2225
+
2226
+ func floatingPromiseTypeParameterAccepts(
2227
+ checker *shimchecker.Checker,
2228
+ typeParameter *shimchecker.Type,
2229
+ inferred *shimchecker.Type,
2230
+ ) bool {
2231
+ if checker == nil || typeParameter == nil || inferred == nil {
2232
+ return false
2233
+ }
2234
+ constraint := checker.GetConstraintOfTypeParameter(typeParameter)
2235
+ return constraint == nil || constraint == typeParameter || checker.IsTypeAssignableTo(inferred, constraint)
2236
+ }
2237
+
2238
+ func floatingPromisePropertyAccessParts(node *shimast.Node) (*shimast.Node, string, bool) {
2239
+ node = stripParens(node)
2240
+ if node == nil {
2241
+ return nil, "", false
2242
+ }
2243
+ switch node.Kind {
2244
+ case shimast.KindPropertyAccessExpression:
2245
+ access := node.AsPropertyAccessExpression()
2246
+ if access == nil {
2247
+ return nil, "", false
2248
+ }
2249
+ method := identifierText(access.Name())
2250
+ return access.Expression, method, method != ""
2251
+ case shimast.KindElementAccessExpression:
2252
+ access := node.AsElementAccessExpression()
2253
+ if access == nil || access.ArgumentExpression == nil {
2254
+ return nil, "", false
2255
+ }
2256
+ switch access.ArgumentExpression.Kind {
2257
+ case shimast.KindStringLiteral, shimast.KindNoSubstitutionTemplateLiteral:
2258
+ method := stringLiteralText(access.ArgumentExpression)
2259
+ return access.Expression, method, method != ""
2260
+ }
2261
+ }
2262
+ return nil, "", false
2263
+ }
2264
+
2265
+ func isFloatingPromiseType(
2266
+ ctx *Context,
2267
+ location *shimast.Node,
2268
+ t *shimchecker.Type,
2269
+ options noFloatingPromisesOptions,
2270
+ ) bool {
2271
+ if t == nil {
2272
+ return false
2273
+ }
2274
+ apparent := ctx.Checker.GetApparentType(t)
2275
+ if apparent == nil {
2276
+ return false
2277
+ }
2278
+ for _, part := range promiseUnionParts(apparent) {
2279
+ if part == nil || typeMatchesSomePromiseSpecifier(ctx, part, options.AllowForKnownSafePromises) {
2280
+ continue
2281
+ }
2282
+ if isNativePromiseInstanceLike(ctx.Checker, part) {
2283
+ return true
2284
+ }
2285
+ if options.CheckThenables && isCatchableThenableAtLocation(ctx.Checker, location, part) {
2286
+ return true
2287
+ }
2288
+ }
2289
+ return false
2290
+ }
2291
+
2292
+ func isCatchableThenableAtLocation(
2293
+ checker *shimchecker.Checker,
2294
+ location *shimast.Node,
2295
+ t *shimchecker.Type,
2296
+ ) bool {
2297
+ if checker == nil || t == nil {
2298
+ return false
2299
+ }
2300
+ apparent := checker.GetApparentType(t)
2301
+ if apparent == nil {
2302
+ return false
2303
+ }
2304
+ for _, part := range promiseUnionParts(apparent) {
2305
+ thenProperty := checker.GetPropertyOfType(part, "then")
2306
+ if thenProperty == nil {
2307
+ continue
2308
+ }
2309
+ thenType := checker.GetTypeOfSymbolAtLocation(thenProperty, location)
2310
+ for _, thenPart := range promiseUnionParts(thenType) {
2311
+ if thenPart == nil {
2312
+ continue
2313
+ }
2314
+ for _, signature := range checker.GetSignaturesOfType(thenPart, shimchecker.SignatureKindCall) {
2315
+ parameters := signature.Parameters()
2316
+ if len(parameters) >= 2 &&
2317
+ isCallableCallbackParameter(checker, location, parameters[0]) &&
2318
+ isCallableCallbackParameter(checker, location, parameters[1]) {
2319
+ return true
2320
+ }
2321
+ }
2322
+ }
2323
+ }
2324
+ return false
2325
+ }
2326
+
2327
+ func isFloatingPromiseArray(
2328
+ ctx *Context,
2329
+ location *shimast.Node,
2330
+ t *shimchecker.Type,
2331
+ options noFloatingPromisesOptions,
2332
+ ) bool {
2333
+ for _, part := range promiseUnionParts(t) {
2334
+ apparent := ctx.Checker.GetApparentType(part)
2335
+ if apparent == nil {
2336
+ continue
2337
+ }
2338
+ if shimchecker.Checker_isArrayType(ctx.Checker, apparent) {
2339
+ arguments := ctx.Checker.GetTypeArguments(apparent)
2340
+ if len(arguments) > 0 && isFloatingPromiseType(ctx, location, arguments[0], options) {
2341
+ return true
2342
+ }
2343
+ }
2344
+ if shimchecker.IsTupleType(apparent) {
2345
+ for _, element := range ctx.Checker.GetTypeArguments(apparent) {
2346
+ if isFloatingPromiseType(ctx, location, element, options) {
2347
+ return true
2348
+ }
2349
+ }
2350
+ }
2351
+ }
2352
+ return false
2353
+ }
2354
+
2355
+ func isNativePromiseInstanceLike(checker *shimchecker.Checker, t *shimchecker.Type) bool {
2356
+ return isNativePromiseInstanceLikeSeen(checker, t, map[*shimchecker.Type]struct{}{})
2357
+ }
2358
+
2359
+ func isNativePromiseInstanceLikeSeen(
2360
+ checker *shimchecker.Checker,
2361
+ t *shimchecker.Type,
2362
+ seen map[*shimchecker.Type]struct{},
2363
+ ) bool {
2364
+ if checker == nil || t == nil {
2365
+ return false
2366
+ }
2367
+ if _, ok := seen[t]; ok {
2368
+ return false
2369
+ }
2370
+ seen[t] = struct{}{}
2371
+ defer delete(seen, t)
2372
+
2373
+ flags := t.Flags()
2374
+ if flags&(shimchecker.TypeFlagsUnion|shimchecker.TypeFlagsIntersection) != 0 {
2375
+ for _, part := range t.Types() {
2376
+ if isNativePromiseInstanceLikeSeen(checker, part, seen) {
2377
+ return true
2378
+ }
2379
+ }
2380
+ return false
2381
+ }
2382
+ if flags&shimchecker.TypeFlagsTypeParameter != 0 {
2383
+ return isNativePromiseInstanceLikeSeen(checker, checker.GetBaseConstraintOfType(t), seen)
2384
+ }
2385
+
2386
+ symbol := t.Symbol()
2387
+ if symbol == nil {
2388
+ return false
2389
+ }
2390
+ if symbol.Name == "Promise" && checker.IsLibSymbolForHoverVerbosity(symbol) {
2391
+ return true
2392
+ }
2393
+ declared := checker.GetDeclaredTypeOfSymbol(symbol)
2394
+ if declared == nil || declared.ObjectFlags()&shimchecker.ObjectFlagsClassOrInterface == 0 {
2395
+ return false
2396
+ }
2397
+ for _, base := range checker.GetBaseTypes(declared) {
2398
+ if isNativePromiseInstanceLikeSeen(checker, base, seen) {
2399
+ return true
2400
+ }
2401
+ }
2402
+ return false
2403
+ }
2404
+
2405
+ func isKnownSafePromiseCall(
2406
+ ctx *Context,
2407
+ node *shimast.Node,
2408
+ specifiers []promiseTypeOrValueSpecifier,
2409
+ ) bool {
2410
+ if node == nil || node.Kind != shimast.KindCallExpression || len(specifiers) == 0 {
2411
+ return false
2412
+ }
2413
+ call := node.AsCallExpression()
2414
+ if call == nil || call.Expression == nil {
2415
+ return false
2416
+ }
2417
+ callee := unwrapFloatingPromiseExpression(call.Expression)
2418
+ if callee == nil {
2419
+ return false
2420
+ }
2421
+ if valueMatchesSomePromiseSpecifier(ctx, callee, specifiers) {
2422
+ return true
2423
+ }
2424
+ return typeMatchesSomePromiseSpecifier(ctx, ctx.Checker.GetTypeAtLocation(callee), specifiers)
2425
+ }
2426
+
2427
+ func valueMatchesSomePromiseSpecifier(
2428
+ ctx *Context,
2429
+ node *shimast.Node,
2430
+ specifiers []promiseTypeOrValueSpecifier,
2431
+ ) bool {
2432
+ staticName, symbol := promiseValueNameAndSymbol(ctx.Checker, node)
2433
+ for _, specifier := range specifiers {
2434
+ if promiseSpecifierMatchesSymbol(ctx, specifier, staticName, symbol) {
2435
+ return true
2436
+ }
2437
+ }
2438
+ return false
2439
+ }
2440
+
2441
+ func promiseValueNameAndSymbol(
2442
+ checker *shimchecker.Checker,
2443
+ node *shimast.Node,
2444
+ ) (string, *shimast.Symbol) {
2445
+ if checker == nil || node == nil {
2446
+ return "", nil
2447
+ }
2448
+ node = unwrapFloatingPromiseExpression(node)
2449
+ if node == nil {
2450
+ return "", nil
2451
+ }
2452
+ var nameNode *shimast.Node
2453
+ var staticName string
2454
+ switch node.Kind {
2455
+ case shimast.KindIdentifier:
2456
+ nameNode = node
2457
+ staticName = identifierText(node)
2458
+ case shimast.KindPropertyAccessExpression:
2459
+ access := node.AsPropertyAccessExpression()
2460
+ if access != nil {
2461
+ nameNode = access.Name()
2462
+ staticName = identifierText(nameNode)
2463
+ }
2464
+ case shimast.KindElementAccessExpression:
2465
+ access := node.AsElementAccessExpression()
2466
+ if access != nil && access.ArgumentExpression != nil {
2467
+ switch access.ArgumentExpression.Kind {
2468
+ case shimast.KindStringLiteral, shimast.KindNoSubstitutionTemplateLiteral:
2469
+ staticName = stringLiteralText(access.ArgumentExpression)
2470
+ }
2471
+ receiverType := checker.GetTypeAtLocation(access.Expression)
2472
+ if receiverType != nil && staticName != "" {
2473
+ return staticName, checker.GetPropertyOfType(receiverType, staticName)
2474
+ }
2475
+ }
2476
+ }
2477
+ symbol := checker.GetSymbolAtLocation(nameNode)
2478
+ if symbol != nil && symbol.Flags&shimast.SymbolFlagsAlias != 0 {
2479
+ symbol = checker.GetAliasedSymbol(symbol)
2480
+ }
2481
+ return staticName, symbol
2482
+ }
2483
+
2484
+ func typeMatchesSomePromiseSpecifier(
2485
+ ctx *Context,
2486
+ t *shimchecker.Type,
2487
+ specifiers []promiseTypeOrValueSpecifier,
2488
+ ) bool {
2489
+ for _, specifier := range specifiers {
2490
+ if typeMatchesPromiseSpecifier(ctx, t, specifier) {
2491
+ return true
2492
+ }
2493
+ }
2494
+ return false
2495
+ }
2496
+
2497
+ func typeMatchesPromiseSpecifier(
2498
+ ctx *Context,
2499
+ t *shimchecker.Type,
2500
+ specifier promiseTypeOrValueSpecifier,
2501
+ ) bool {
2502
+ if ctx == nil || ctx.Checker == nil || t == nil {
2503
+ return false
2504
+ }
2505
+ if t.Flags()&shimchecker.TypeFlagsUnion != 0 {
2506
+ parts := t.Types()
2507
+ if len(parts) == 0 {
2508
+ return false
2509
+ }
2510
+ for _, part := range parts {
2511
+ if !typeMatchesPromiseSpecifier(ctx, part, specifier) {
2512
+ return false
2513
+ }
2514
+ }
2515
+ return true
2516
+ }
2517
+
2518
+ symbol := shimchecker.Type_getTypeNameSymbol(t)
2519
+ if symbol == nil {
2520
+ symbol = t.Symbol()
2521
+ }
2522
+ if symbol != nil && symbol.Flags&shimast.SymbolFlagsAlias != 0 {
2523
+ symbol = ctx.Checker.GetAliasedSymbol(symbol)
2524
+ }
2525
+ if promiseSpecifierMatchesSymbol(ctx, specifier, "", symbol) {
2526
+ return true
2527
+ }
2528
+ if t.Flags()&shimchecker.TypeFlagsIntersection != 0 {
2529
+ for _, part := range t.Types() {
2530
+ if typeMatchesPromiseSpecifier(ctx, part, specifier) {
2531
+ return true
2532
+ }
2533
+ }
2534
+ }
2535
+ return false
2536
+ }
2537
+
2538
+ func promiseSpecifierMatchesSymbol(
2539
+ ctx *Context,
2540
+ specifier promiseTypeOrValueSpecifier,
2541
+ staticName string,
2542
+ symbol *shimast.Symbol,
2543
+ ) bool {
2544
+ symbolName := ""
2545
+ if symbol != nil {
2546
+ symbolName = symbol.Name
2547
+ }
2548
+ if !promiseSpecifierNameMatches(specifier.Names, staticName, symbolName) {
2549
+ return false
2550
+ }
2551
+ if specifier.Universal {
2552
+ return true
2553
+ }
2554
+ switch specifier.From {
2555
+ case "file":
2556
+ return promiseSymbolDeclaredInFile(ctx, symbol, specifier.Path)
2557
+ case "lib":
2558
+ return symbol != nil && ctx.Checker.IsLibSymbolForHoverVerbosity(symbol)
2559
+ case "package":
2560
+ return promiseSymbolDeclaredInPackage(symbol, specifier.PackageName)
2561
+ default:
2562
+ return false
2563
+ }
2564
+ }
2565
+
2566
+ func promiseSpecifierNameMatches(names []string, candidates ...string) bool {
2567
+ for _, name := range names {
2568
+ for _, candidate := range candidates {
2569
+ if name != "" && name == candidate {
2570
+ return true
2571
+ }
2572
+ }
2573
+ }
2574
+ return false
2575
+ }
2576
+
2577
+ func promiseSymbolDeclaredInFile(ctx *Context, symbol *shimast.Symbol, configuredPath string) bool {
2578
+ if ctx == nil || ctx.File == nil || symbol == nil {
2579
+ return false
2580
+ }
2581
+ for _, declaration := range symbol.Declarations {
2582
+ source := sourceFileForPromiseDeclaration(declaration)
2583
+ if source == nil || ctx.Checker.IsLibSymbolForHoverVerbosity(symbol) {
2584
+ continue
2585
+ }
2586
+ if configuredPath == "" {
2587
+ if ctx.CurrentDirectory == "" || promisePathWithin(ctx.CurrentDirectory, source.FileName()) {
2588
+ return true
2589
+ }
2590
+ continue
2591
+ }
2592
+ if promiseDeclarationPathMatches(ctx.CurrentDirectory, source.FileName(), configuredPath) {
2593
+ return true
2594
+ }
2595
+ }
2596
+ return false
2597
+ }
2598
+
2599
+ func promiseDeclarationPathMatches(currentDirectory, declarationFile, configuredPath string) bool {
2600
+ configuredPath = filepath.Clean(filepath.FromSlash(configuredPath))
2601
+ if !filepath.IsAbs(configuredPath) {
2602
+ configuredPath = filepath.Join(currentDirectory, configuredPath)
2603
+ }
2604
+ return promiseCanonicalPath(declarationFile) == promiseCanonicalPath(configuredPath)
2605
+ }
2606
+
2607
+ func promisePathWithin(currentDirectory, declarationFile string) bool {
2608
+ root := promiseCanonicalPath(currentDirectory)
2609
+ declaration := promiseCanonicalPath(declarationFile)
2610
+ relative, err := filepath.Rel(root, declaration)
2611
+ if err != nil || filepath.IsAbs(relative) {
2612
+ return false
2613
+ }
2614
+ return relative != ".." && !strings.HasPrefix(relative, ".."+string(filepath.Separator))
2615
+ }
2616
+
2617
+ func promiseCanonicalPath(path string) string {
2618
+ path = filepath.Clean(path)
2619
+ if runtime.GOOS == "windows" {
2620
+ return strings.ToLower(path)
2621
+ }
2622
+ return path
2623
+ }
2624
+
2625
+ func promiseSymbolDeclaredInPackage(symbol *shimast.Symbol, packageName string) bool {
2626
+ if symbol == nil || packageName == "" {
2627
+ return false
2628
+ }
2629
+ packagePath := "/node_modules/" + strings.Trim(packageName, "/") + "/"
2630
+ typesName := strings.TrimPrefix(packageName, "@")
2631
+ typesName = strings.Replace(typesName, "/", "__", 1)
2632
+ typesPath := "/node_modules/@types/" + typesName + "/"
2633
+ for _, declaration := range symbol.Declarations {
2634
+ for parent := declaration; parent != nil; parent = parent.Parent {
2635
+ if parent.Kind == shimast.KindModuleDeclaration {
2636
+ if name := parent.Name(); name != nil && stringLiteralText(name) == packageName {
2637
+ return true
2638
+ }
2639
+ }
2640
+ if parent.Kind == shimast.KindSourceFile {
2641
+ source := parent.AsSourceFile()
2642
+ if source == nil {
2643
+ break
2644
+ }
2645
+ fileName := "/" + strings.TrimLeft(filepath.ToSlash(source.FileName()), "/")
2646
+ if strings.Contains(fileName, packagePath) || strings.Contains(fileName, typesPath) {
2647
+ return true
2648
+ }
2649
+ break
2650
+ }
2651
+ }
2652
+ }
2653
+ return false
2654
+ }
2655
+
2656
+ func sourceFileForPromiseDeclaration(node *shimast.Node) *shimast.SourceFile {
2657
+ for current := node; current != nil; current = current.Parent {
2658
+ if current.Kind == shimast.KindSourceFile {
2659
+ return current.AsSourceFile()
2660
+ }
2661
+ }
2662
+ return nil
2663
+ }
2664
+
2665
+ func isImmediatelyInvokedFunctionExpression(node *shimast.Node) bool {
2666
+ if node == nil || node.Kind != shimast.KindCallExpression {
2667
+ return false
2668
+ }
2669
+ call := node.AsCallExpression()
2670
+ if call == nil {
2671
+ return false
2672
+ }
2673
+ callee := unwrapFloatingPromiseExpression(call.Expression)
2674
+ return callee != nil && (callee.Kind == shimast.KindArrowFunction || callee.Kind == shimast.KindFunctionExpression)
2675
+ }
2676
+
2677
+ func unwrapFloatingPromiseExpression(node *shimast.Node) *shimast.Node {
2678
+ for node != nil {
2679
+ switch node.Kind {
2680
+ case shimast.KindParenthesizedExpression,
2681
+ shimast.KindPartiallyEmittedExpression:
2682
+ next := node.Expression()
2683
+ if next == nil {
2684
+ return node
2685
+ }
2686
+ node = next
2687
+ default:
2688
+ return node
2689
+ }
2690
+ }
2691
+ return nil
224
2692
  }
225
2693
 
226
- // isPromiseTypedExpression reports whether `t` represents a Promise (or thenable)
227
- // at runtime. Mirrors `isAwaitable` but is intentionally stricter: `any` and
228
- // `unknown` are NOT treated as floating, because that would explode on
229
- // generic-helper boundaries; `never` is also skipped because a function whose
230
- // return type is `never` cannot float a real Promise.
2694
+ // isPromiseTypedExpression reports whether `t` represents a Promise (or
2695
+ // structurally callable thenable) under the older shared boolean policy used by
2696
+ // the other Promise rules. no-floating-promises has its own option-aware native
2697
+ // Promise and catchable-thenable boundary above. Unlike
2698
+ // classifyPromiseAwaitability, `any`, `unknown`, and `never` are skipped because
2699
+ // they cannot prove that a real Promise is present.
231
2700
  func isPromiseTypedExpression(checker *shimchecker.Checker, t *shimchecker.Type) bool {
232
2701
  if checker == nil || t == nil {
233
2702
  return false