@ttsc/lint 0.18.4 → 0.19.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +125 -43
- package/go.mod +5 -4
- package/internal/rulecode/rulecode.go +88 -0
- package/lib/index.d.ts +10 -1
- package/lib/index.js +45 -17
- package/lib/index.js.map +1 -1
- package/lib/structures/ITtscLintConfig.d.ts +3 -0
- package/lib/structures/TtscLintRuleSetting.d.ts +3 -2
- package/lib/structures/format/ITtscLintFormat.d.ts +3 -2
- package/lib/structures/format/ITtscLintFormatSortImports.d.ts +20 -4
- package/lib/structures/rules/ITtscLintBoundariesRuleOptions.d.ts +80 -8
- package/lib/structures/rules/ITtscLintBoundariesRules.d.ts +6 -8
- package/lib/structures/rules/ITtscLintContributorRules.d.ts +18 -2
- package/lib/structures/rules/ITtscLintCoreRuleOptions.d.ts +347 -0
- package/lib/structures/rules/ITtscLintCoreRuleOptions.js +3 -0
- package/lib/structures/rules/ITtscLintCoreRuleOptions.js.map +1 -0
- package/lib/structures/rules/ITtscLintCoreRules.d.ts +101 -67
- package/lib/structures/rules/ITtscLintRegexpRules.d.ts +7 -2
- package/lib/structures/rules/ITtscLintRuleOptionsMap.d.ts +53 -7
- package/lib/structures/rules/ITtscLintRules.d.ts +6 -2
- package/lib/structures/rules/ITtscLintSolidRules.d.ts +0 -8
- package/lib/structures/rules/ITtscLintTypeScriptRuleOptions.d.ts +206 -0
- package/lib/structures/rules/ITtscLintTypeScriptRuleOptions.js +8 -0
- package/lib/structures/rules/ITtscLintTypeScriptRuleOptions.js.map +1 -0
- package/lib/structures/rules/ITtscLintTypeScriptRules.d.ts +55 -42
- package/lib/structures/rules/ITtscLintUnicornRuleOptions.d.ts +276 -0
- package/lib/structures/rules/ITtscLintUnicornRuleOptions.js +3 -0
- package/lib/structures/rules/ITtscLintUnicornRuleOptions.js.map +1 -0
- package/lib/structures/rules/ITtscLintUnicornRules.d.ts +44 -36
- package/lib/structures/rules/index.d.ts +3 -0
- package/lib/structures/rules/index.js +3 -0
- package/lib/structures/rules/index.js.map +1 -1
- package/linthost/ast_helpers.go +521 -23
- package/linthost/ast_selector_matcher.go +1628 -0
- package/linthost/ast_selector_parser.go +628 -0
- package/linthost/comment_scan.go +154 -0
- package/linthost/compile.go +129 -79
- package/linthost/config.go +352 -88
- package/linthost/config_format.go +15 -1
- package/linthost/contrib_adapter.go +47 -8
- package/linthost/declaration_rules.go +12 -11
- package/linthost/directives.go +9 -67
- package/linthost/dispatch.go +37 -3
- package/linthost/engine.go +368 -53
- package/linthost/escape_string.go +77 -0
- package/linthost/fix.go +133 -23
- package/linthost/flags_gen.go +18 -17
- package/linthost/format.go +73 -167
- package/linthost/format_editor_settings.go +134 -26
- package/linthost/grapheme_tables_gen.go +1920 -0
- package/linthost/host.go +159 -61
- package/linthost/literal_escape_scan.go +117 -0
- package/linthost/lsp.go +545 -98
- package/linthost/numeric_literal_precision.go +228 -0
- package/linthost/polyfill_browserslist.go +1439 -0
- package/linthost/polyfill_browserslist_config.go +603 -0
- package/linthost/polyfill_corejs.go +750 -0
- package/linthost/polyfill_data.go +113 -0
- package/linthost/polyfill_data_gen.json +39868 -0
- package/linthost/print_nodes_array.go +7 -1
- package/linthost/print_nodes_object.go +7 -1
- package/linthost/project_engine.go +231 -0
- package/linthost/project_rules.go +78 -0
- package/linthost/regex_clean.go +191 -0
- package/linthost/regex_tree.go +1544 -0
- package/linthost/regex_tree_optimizer.go +1685 -0
- package/linthost/regexp_cache.go +44 -0
- package/linthost/rule_codes.go +105 -0
- package/linthost/rule_codes.json +745 -0
- package/linthost/rules_ban_ts_comment.go +312 -0
- package/linthost/rules_boundaries.go +128 -43
- package/linthost/rules_boundaries_dependencies.go +1013 -0
- package/linthost/rules_core_extra.go +62 -92
- package/linthost/rules_cypress.go +1 -1
- package/linthost/rules_default_case.go +87 -11
- package/linthost/rules_dupes.go +38 -13
- package/linthost/rules_empty.go +188 -20
- package/linthost/rules_escape.go +62 -48
- package/linthost/rules_format_arrow_parens.go +19 -5
- package/linthost/rules_format_bracket_spacing.go +1 -1
- package/linthost/rules_format_clause_join.go +1 -1
- package/linthost/rules_format_declaration_header.go +31 -19
- package/linthost/rules_format_indent.go +1 -1
- package/linthost/rules_format_jsdoc.go +1 -1
- package/linthost/rules_format_orphan_semi.go +1 -1
- package/linthost/rules_format_parameter_properties.go +17 -7
- package/linthost/rules_format_print_width.go +1 -1
- package/linthost/rules_format_quote_props.go +1 -1
- package/linthost/rules_format_quotes.go +1 -1
- package/linthost/rules_format_semi.go +106 -79
- package/linthost/rules_format_sort_imports.go +79 -45
- package/linthost/rules_format_statement_split.go +1 -1
- package/linthost/rules_format_ternary_nullish_parens.go +1 -1
- package/linthost/rules_format_trailing_comma.go +64 -1
- package/linthost/rules_format_whitespace.go +1 -1
- package/linthost/rules_functional.go +21 -22
- package/linthost/rules_gap.go +80 -3
- package/linthost/rules_grouped_accessor_pairs.go +84 -25
- package/linthost/rules_guard_for_in.go +64 -147
- package/linthost/rules_jsx_a11y.go +232 -22
- package/linthost/rules_logic.go +84 -38
- package/linthost/rules_nextjs.go +105 -16
- package/linthost/rules_no_class_assign.go +70 -0
- package/linthost/rules_no_duplicate_imports.go +266 -0
- package/linthost/rules_no_else_return.go +156 -38
- package/linthost/rules_no_extend_native.go +113 -17
- package/linthost/rules_no_fallthrough.go +1013 -0
- package/linthost/rules_no_func_assign.go +70 -0
- package/linthost/rules_no_import_assign.go +278 -86
- package/linthost/rules_no_inner_declarations.go +186 -0
- package/linthost/rules_no_loop_func.go +438 -53
- package/linthost/rules_no_mixed_operators.go +208 -52
- package/linthost/rules_no_param_reassign.go +376 -61
- package/linthost/rules_no_promise_executor_return.go +156 -0
- package/linthost/rules_no_restricted_imports.go +1013 -57
- package/linthost/rules_no_restricted_syntax.go +190 -26
- package/linthost/rules_no_useless_constructor.go +6 -36
- package/linthost/rules_problems.go +154 -311
- package/linthost/rules_promise.go +2578 -109
- package/linthost/rules_react_extras.go +71 -45
- package/linthost/rules_react_hooks.go +18 -2
- package/linthost/rules_react_perf.go +1 -0
- package/linthost/rules_react_refresh.go +1 -1
- package/linthost/rules_regexp.go +230 -14
- package/linthost/rules_security.go +45 -19
- package/linthost/rules_self.go +21 -1
- package/linthost/rules_solid.go +13 -7
- package/linthost/rules_storybook.go +1 -1
- package/linthost/rules_suggestions.go +490 -84
- package/linthost/rules_testing_library.go +12 -1
- package/linthost/rules_ts.go +227 -54
- package/linthost/rules_ts_async.go +0 -131
- package/linthost/rules_ts_no_base_to_string.go +3 -4
- package/linthost/rules_ts_no_misused_promises.go +817 -0
- package/linthost/rules_ts_no_restricted_types.go +320 -34
- package/linthost/rules_ts_no_unnecessary_qualifier.go +132 -55
- package/linthost/rules_ts_no_unsafe_assignment.go +608 -60
- package/linthost/rules_ts_require_array_sort_compare.go +5 -5
- package/linthost/rules_ts_switch_exhaustiveness_check.go +556 -86
- package/linthost/rules_unicorn_better_regex.go +224 -16
- package/linthost/rules_unicorn_consistent_destructuring.go +1002 -7
- package/linthost/rules_unicorn_consistent_existence_index_check.go +208 -29
- package/linthost/rules_unicorn_consistent_function_scoping.go +1041 -8
- package/linthost/rules_unicorn_consistent_template_literal_escape.go +178 -7
- package/linthost/rules_unicorn_escape_case.go +30 -19
- package/linthost/rules_unicorn_filename_case.go +763 -16
- package/linthost/rules_unicorn_import_style.go +783 -17
- package/linthost/rules_unicorn_isolated_functions.go +934 -14
- package/linthost/rules_unicorn_no_hex_escape.go +26 -11
- package/linthost/rules_unicorn_no_typeof_undefined.go +234 -25
- package/linthost/rules_unicorn_no_unnecessary_polyfills.go +376 -21
- package/linthost/rules_unicorn_no_unused_properties.go +756 -19
- package/linthost/rules_unicorn_number_literal_case.go +58 -39
- package/linthost/rules_unicorn_prefer_at.go +7 -3
- package/linthost/rules_unicorn_prefer_number_properties.go +381 -48
- package/linthost/rules_unicorn_prefer_optional_catch_binding.go +24 -24
- package/linthost/rules_unicorn_prefer_simple_condition_first.go +597 -19
- package/linthost/rules_unicorn_prefer_string_raw.go +147 -16
- package/linthost/rules_unicorn_prevent_abbreviations.go +1963 -95
- package/linthost/rules_unicorn_string_content.go +611 -15
- package/linthost/rules_unicorn_switch_case_break_position.go +172 -15
- package/linthost/rules_unicorn_template_indent.go +923 -18
- package/linthost/rules_unicorn_text_encoding_identifier_case.go +202 -42
- package/linthost/rules_unicorn_throw_new_error.go +218 -26
- package/linthost/rules_var.go +829 -142
- package/linthost/string_length.go +222 -0
- package/package.json +4 -3
- package/rule/project.go +206 -0
- package/rule/rule.go +58 -1
- package/src/index.ts +58 -17
- package/src/structures/ITtscLintConfig.ts +3 -0
- package/src/structures/TtscLintRuleSetting.ts +3 -2
- package/src/structures/format/ITtscLintFormat.ts +3 -2
- package/src/structures/format/ITtscLintFormatSortImports.ts +21 -4
- package/src/structures/rules/ITtscLintBoundariesRuleOptions.ts +122 -8
- package/src/structures/rules/ITtscLintBoundariesRules.ts +6 -8
- package/src/structures/rules/ITtscLintContributorRules.ts +18 -2
- package/src/structures/rules/ITtscLintCoreRuleOptions.ts +465 -0
- package/src/structures/rules/ITtscLintCoreRules.ts +122 -67
- package/src/structures/rules/ITtscLintRegexpRules.ts +7 -2
- package/src/structures/rules/ITtscLintRuleOptionsMap.ts +88 -7
- package/src/structures/rules/ITtscLintRules.ts +7 -2
- package/src/structures/rules/ITtscLintSolidRules.ts +0 -9
- package/src/structures/rules/ITtscLintTypeScriptRuleOptions.ts +248 -0
- package/src/structures/rules/ITtscLintTypeScriptRules.ts +64 -42
- package/src/structures/rules/ITtscLintUnicornRuleOptions.ts +350 -0
- package/src/structures/rules/ITtscLintUnicornRules.ts +60 -36
- package/src/structures/rules/index.ts +3 -0
|
@@ -1,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{
|
|
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
|
|
87
|
+
if classifyPromiseAwaitability(ctx.Checker, expr.Expression, operandType) != promiseAwaitabilityNever {
|
|
43
88
|
return
|
|
44
89
|
}
|
|
45
|
-
message := "Unexpected `await` of a non-Promise (non
|
|
46
|
-
//
|
|
47
|
-
//
|
|
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
|
-
|
|
52
|
-
if startPos < 0 ||
|
|
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.
|
|
99
|
+
ctx.ReportSuggestion(
|
|
57
100
|
node,
|
|
58
101
|
message,
|
|
59
|
-
|
|
102
|
+
"Remove unnecessary `await`.",
|
|
103
|
+
TextEdit{Pos: startPos, End: awaitEnd, Text: ""},
|
|
60
104
|
)
|
|
61
105
|
}
|
|
62
106
|
|
|
63
|
-
//
|
|
64
|
-
//
|
|
65
|
-
//
|
|
66
|
-
//
|
|
67
|
-
//
|
|
68
|
-
//
|
|
69
|
-
//
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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
|
-
//
|
|
78
|
-
//
|
|
79
|
-
//
|
|
80
|
-
//
|
|
81
|
-
func
|
|
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
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
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
|
|
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
|
|
264
|
+
if hasCallableWellKnownSymbolProperty(checker, location, part, symbolName) {
|
|
97
265
|
return true
|
|
98
266
|
}
|
|
99
267
|
}
|
|
100
268
|
return false
|
|
101
269
|
}
|
|
102
|
-
|
|
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
|
-
|
|
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
|
|
127
|
-
//
|
|
128
|
-
//
|
|
129
|
-
//
|
|
130
|
-
//
|
|
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
|
|
136
|
-
//
|
|
137
|
-
//
|
|
138
|
-
//
|
|
139
|
-
|
|
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 :=
|
|
694
|
+
expr := unwrapFloatingPromiseExpression(stmt.Expression)
|
|
157
695
|
if expr == nil {
|
|
158
696
|
return
|
|
159
697
|
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
if isPromiseHandledExpression(expr) {
|
|
698
|
+
var options noFloatingPromisesOptions
|
|
699
|
+
_ = ctx.DecodeOptions(&options)
|
|
700
|
+
if options.IgnoreIIFE && isImmediatelyInvokedFunctionExpression(expr) {
|
|
164
701
|
return
|
|
165
702
|
}
|
|
166
|
-
|
|
167
|
-
if
|
|
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
|
-
|
|
707
|
+
message := floatingPromiseMessage(options.ignoresVoid(), result)
|
|
708
|
+
ctx.Report(node, message)
|
|
174
709
|
}
|
|
175
710
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
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
|
-
|
|
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
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
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
|
-
|
|
220
|
-
|
|
221
|
-
|
|
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
|
-
|
|
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
|
|
227
|
-
//
|
|
228
|
-
//
|
|
229
|
-
//
|
|
230
|
-
//
|
|
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
|