From d234453328408dd1bbc7858db428b482bfcf0c6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Mon, 10 Oct 2022 12:55:52 +0200 Subject: [PATCH 1/3] Fixed false positive circular errors for await expressions with simple non-generic calls in CFA loops --- src/compiler/checker.ts | 16 +- ...controlFlowIterationErrorsAsync.errors.txt | 177 ++++++++ .../controlFlowIterationErrorsAsync.symbols | 321 +++++++++++++++ .../controlFlowIterationErrorsAsync.types | 388 ++++++++++++++++++ ...mplicitAnyFromCircularInference.errors.txt | 3 +- .../implicitAnyFromCircularInference.js | 2 - .../implicitAnyFromCircularInference.symbols | 11 +- .../implicitAnyFromCircularInference.types | 1 - .../implicitAnyFromCircularInference.ts | 1 - .../controlFlowIterationErrorsAsync.ts | 137 +++++++ 10 files changed, 1037 insertions(+), 20 deletions(-) create mode 100644 tests/baselines/reference/controlFlowIterationErrorsAsync.errors.txt create mode 100644 tests/baselines/reference/controlFlowIterationErrorsAsync.symbols create mode 100644 tests/baselines/reference/controlFlowIterationErrorsAsync.types create mode 100644 tests/cases/conformance/controlFlow/controlFlowIterationErrorsAsync.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 9ebaca719f2b9..57eae0511693c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -35339,7 +35339,7 @@ namespace ts { return type; } - function getQuickTypeOfExpression(node: Expression) { + function getQuickTypeOfExpression(node: Expression): Type | undefined { let expr = skipParentheses(node, /*excludeJSDocTypeAssertions*/ true); if (isJSDocTypeAssertion(expr)) { const type = getJSDocTypeAssertionType(expr); @@ -35348,20 +35348,20 @@ namespace ts { } } expr = skipParentheses(node); + if (isAwaitExpression(expr)) { + const type = getQuickTypeOfExpression(expr.expression); + return type ? getAwaitedType(type) : undefined; + } // Optimize for the common case of a call to a function with a single non-generic call // signature where we can just fetch the return type without checking the arguments. - if (isCallExpression(expr) && expr.expression.kind !== SyntaxKind.SuperKeyword && !isRequireCall(expr, /*checkArgumentIsStringLiteralLike*/ true) && !isSymbolOrSymbolForCall(expr)) { - const type = isCallChain(expr) ? getReturnTypeOfSingleNonGenericSignatureOfCallChain(expr) : + else if (isCallExpression(expr) && expr.expression.kind !== SyntaxKind.SuperKeyword && !isRequireCall(expr, /*checkArgumentIsStringLiteralLike*/ true) && !isSymbolOrSymbolForCall(expr)) { + return isCallChain(expr) ? getReturnTypeOfSingleNonGenericSignatureOfCallChain(expr) : getReturnTypeOfSingleNonGenericCallSignature(checkNonNullExpression(expr.expression)); - if (type) { - return type; - } } else if (isAssertionExpression(expr) && !isConstTypeReference(expr.type)) { return getTypeFromTypeNode((expr as TypeAssertion).type); } - else if (node.kind === SyntaxKind.NumericLiteral || node.kind === SyntaxKind.StringLiteral || - node.kind === SyntaxKind.TrueKeyword || node.kind === SyntaxKind.FalseKeyword) { + else if (isLiteralExpression(node)) { return checkExpression(node); } return undefined; diff --git a/tests/baselines/reference/controlFlowIterationErrorsAsync.errors.txt b/tests/baselines/reference/controlFlowIterationErrorsAsync.errors.txt new file mode 100644 index 0000000000000..6db07eef2b4c8 --- /dev/null +++ b/tests/baselines/reference/controlFlowIterationErrorsAsync.errors.txt @@ -0,0 +1,177 @@ +tests/cases/conformance/controlFlow/controlFlowIterationErrorsAsync.ts(11,23): error TS2345: Argument of type 'string | number' is not assignable to parameter of type 'string'. + Type 'number' is not assignable to type 'string'. +tests/cases/conformance/controlFlow/controlFlowIterationErrorsAsync.ts(22,23): error TS2345: Argument of type 'string | number' is not assignable to parameter of type 'string'. + Type 'number' is not assignable to type 'string'. +tests/cases/conformance/controlFlow/controlFlowIterationErrorsAsync.ts(34,23): error TS2769: No overload matches this call. + Overload 1 of 2, '(x: string): Promise', gave the following error. + Argument of type 'string | number' is not assignable to parameter of type 'string'. + Type 'number' is not assignable to type 'string'. + Overload 2 of 2, '(x: number): Promise', gave the following error. + Argument of type 'string | number' is not assignable to parameter of type 'number'. + Type 'string' is not assignable to type 'number'. +tests/cases/conformance/controlFlow/controlFlowIterationErrorsAsync.ts(45,23): error TS2769: No overload matches this call. + Overload 1 of 2, '(x: string): Promise', gave the following error. + Argument of type 'string | number' is not assignable to parameter of type 'string'. + Type 'number' is not assignable to type 'string'. + Overload 2 of 2, '(x: number): Promise', gave the following error. + Argument of type 'string | number' is not assignable to parameter of type 'number'. + Type 'string' is not assignable to type 'number'. + + +==== tests/cases/conformance/controlFlow/controlFlowIterationErrorsAsync.ts (4 errors) ==== + let cond: boolean; + + async function len(s: string) { + return s.length; + } + + async function f1() { + let x: string | number | boolean; + x = ""; + while (cond) { + x = await len(x); + ~ +!!! error TS2345: Argument of type 'string | number' is not assignable to parameter of type 'string'. +!!! error TS2345: Type 'number' is not assignable to type 'string'. + x; + } + x; + } + + async function f2() { + let x: string | number | boolean; + x = ""; + while (cond) { + x; + x = await len(x); + ~ +!!! error TS2345: Argument of type 'string | number' is not assignable to parameter of type 'string'. +!!! error TS2345: Type 'number' is not assignable to type 'string'. + } + x; + } + + declare function foo(x: string): Promise; + declare function foo(x: number): Promise; + + async function g1() { + let x: string | number | boolean; + x = ""; + while (cond) { + x = await foo(x); + ~ +!!! error TS2769: No overload matches this call. +!!! error TS2769: Overload 1 of 2, '(x: string): Promise', gave the following error. +!!! error TS2769: Argument of type 'string | number' is not assignable to parameter of type 'string'. +!!! error TS2769: Type 'number' is not assignable to type 'string'. +!!! error TS2769: Overload 2 of 2, '(x: number): Promise', gave the following error. +!!! error TS2769: Argument of type 'string | number' is not assignable to parameter of type 'number'. +!!! error TS2769: Type 'string' is not assignable to type 'number'. + x; + } + x; + } + + async function g2() { + let x: string | number | boolean; + x = ""; + while (cond) { + x; + x = await foo(x); + ~ +!!! error TS2769: No overload matches this call. +!!! error TS2769: Overload 1 of 2, '(x: string): Promise', gave the following error. +!!! error TS2769: Argument of type 'string | number' is not assignable to parameter of type 'string'. +!!! error TS2769: Type 'number' is not assignable to type 'string'. +!!! error TS2769: Overload 2 of 2, '(x: number): Promise', gave the following error. +!!! error TS2769: Argument of type 'string | number' is not assignable to parameter of type 'number'. +!!! error TS2769: Type 'string' is not assignable to type 'number'. + } + x; + } + + async function asNumber(x: string | number): Promise { + return +x; + } + + async function h1() { + let x: string | number | boolean; + x = "0"; + while (cond) { + x = +x + 1; + x; + } + } + + async function h2() { + let x: string | number | boolean; + x = "0"; + while (cond) { + x = await asNumber(x) + 1; + x; + } + } + + async function h3() { + let x: string | number | boolean; + x = "0"; + while (cond) { + let y = await asNumber(x); + x = y + 1; + x; + } + } + + async function h4() { + let x: string | number | boolean; + x = "0"; + while (cond) { + x; + let y = await asNumber(x); + x = y + 1; + x; + } + } + + // repro #51115 + + async function get_things(_: number | undefined) { + return [0]; + } + + async function foobar() { + let before: number | undefined = undefined; + for (let i = 0; i < 2; i++) { + const results = await get_things(before); + before = results[0]; + } + } + + // repro #43047#issuecomment-821453073 + + declare function foox(x: string | undefined): Promise + + async () => { + let bar: string | undefined = undefined; + do { + const baz = await foox(bar); + bar = baz + } while (bar) + } + + // repro #43047#issuecomment-874221939 + + declare function myQuery(input: { lastId: number | undefined }): Promise<{ entities: number[] }>; + + async function myFunc(): Promise { + let lastId: number | undefined = undefined; + + while (true) { + const { entities } = await myQuery({ + lastId, + }); + + lastId = entities[entities.length - 1]; + } + } + \ No newline at end of file diff --git a/tests/baselines/reference/controlFlowIterationErrorsAsync.symbols b/tests/baselines/reference/controlFlowIterationErrorsAsync.symbols new file mode 100644 index 0000000000000..f189e2b77ac75 --- /dev/null +++ b/tests/baselines/reference/controlFlowIterationErrorsAsync.symbols @@ -0,0 +1,321 @@ +=== tests/cases/conformance/controlFlow/controlFlowIterationErrorsAsync.ts === +let cond: boolean; +>cond : Symbol(cond, Decl(controlFlowIterationErrorsAsync.ts, 0, 3)) + +async function len(s: string) { +>len : Symbol(len, Decl(controlFlowIterationErrorsAsync.ts, 0, 18)) +>s : Symbol(s, Decl(controlFlowIterationErrorsAsync.ts, 2, 19)) + + return s.length; +>s.length : Symbol(String.length, Decl(lib.es5.d.ts, --, --)) +>s : Symbol(s, Decl(controlFlowIterationErrorsAsync.ts, 2, 19)) +>length : Symbol(String.length, Decl(lib.es5.d.ts, --, --)) +} + +async function f1() { +>f1 : Symbol(f1, Decl(controlFlowIterationErrorsAsync.ts, 4, 1)) + + let x: string | number | boolean; +>x : Symbol(x, Decl(controlFlowIterationErrorsAsync.ts, 7, 7)) + + x = ""; +>x : Symbol(x, Decl(controlFlowIterationErrorsAsync.ts, 7, 7)) + + while (cond) { +>cond : Symbol(cond, Decl(controlFlowIterationErrorsAsync.ts, 0, 3)) + + x = await len(x); +>x : Symbol(x, Decl(controlFlowIterationErrorsAsync.ts, 7, 7)) +>len : Symbol(len, Decl(controlFlowIterationErrorsAsync.ts, 0, 18)) +>x : Symbol(x, Decl(controlFlowIterationErrorsAsync.ts, 7, 7)) + + x; +>x : Symbol(x, Decl(controlFlowIterationErrorsAsync.ts, 7, 7)) + } + x; +>x : Symbol(x, Decl(controlFlowIterationErrorsAsync.ts, 7, 7)) +} + +async function f2() { +>f2 : Symbol(f2, Decl(controlFlowIterationErrorsAsync.ts, 14, 1)) + + let x: string | number | boolean; +>x : Symbol(x, Decl(controlFlowIterationErrorsAsync.ts, 17, 7)) + + x = ""; +>x : Symbol(x, Decl(controlFlowIterationErrorsAsync.ts, 17, 7)) + + while (cond) { +>cond : Symbol(cond, Decl(controlFlowIterationErrorsAsync.ts, 0, 3)) + + x; +>x : Symbol(x, Decl(controlFlowIterationErrorsAsync.ts, 17, 7)) + + x = await len(x); +>x : Symbol(x, Decl(controlFlowIterationErrorsAsync.ts, 17, 7)) +>len : Symbol(len, Decl(controlFlowIterationErrorsAsync.ts, 0, 18)) +>x : Symbol(x, Decl(controlFlowIterationErrorsAsync.ts, 17, 7)) + } + x; +>x : Symbol(x, Decl(controlFlowIterationErrorsAsync.ts, 17, 7)) +} + +declare function foo(x: string): Promise; +>foo : Symbol(foo, Decl(controlFlowIterationErrorsAsync.ts, 24, 1), Decl(controlFlowIterationErrorsAsync.ts, 26, 49)) +>x : Symbol(x, Decl(controlFlowIterationErrorsAsync.ts, 26, 21)) +>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2018.promise.d.ts, --, --)) + +declare function foo(x: number): Promise; +>foo : Symbol(foo, Decl(controlFlowIterationErrorsAsync.ts, 24, 1), Decl(controlFlowIterationErrorsAsync.ts, 26, 49)) +>x : Symbol(x, Decl(controlFlowIterationErrorsAsync.ts, 27, 21)) +>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2018.promise.d.ts, --, --)) + +async function g1() { +>g1 : Symbol(g1, Decl(controlFlowIterationErrorsAsync.ts, 27, 49)) + + let x: string | number | boolean; +>x : Symbol(x, Decl(controlFlowIterationErrorsAsync.ts, 30, 7)) + + x = ""; +>x : Symbol(x, Decl(controlFlowIterationErrorsAsync.ts, 30, 7)) + + while (cond) { +>cond : Symbol(cond, Decl(controlFlowIterationErrorsAsync.ts, 0, 3)) + + x = await foo(x); +>x : Symbol(x, Decl(controlFlowIterationErrorsAsync.ts, 30, 7)) +>foo : Symbol(foo, Decl(controlFlowIterationErrorsAsync.ts, 24, 1), Decl(controlFlowIterationErrorsAsync.ts, 26, 49)) +>x : Symbol(x, Decl(controlFlowIterationErrorsAsync.ts, 30, 7)) + + x; +>x : Symbol(x, Decl(controlFlowIterationErrorsAsync.ts, 30, 7)) + } + x; +>x : Symbol(x, Decl(controlFlowIterationErrorsAsync.ts, 30, 7)) +} + +async function g2() { +>g2 : Symbol(g2, Decl(controlFlowIterationErrorsAsync.ts, 37, 1)) + + let x: string | number | boolean; +>x : Symbol(x, Decl(controlFlowIterationErrorsAsync.ts, 40, 7)) + + x = ""; +>x : Symbol(x, Decl(controlFlowIterationErrorsAsync.ts, 40, 7)) + + while (cond) { +>cond : Symbol(cond, Decl(controlFlowIterationErrorsAsync.ts, 0, 3)) + + x; +>x : Symbol(x, Decl(controlFlowIterationErrorsAsync.ts, 40, 7)) + + x = await foo(x); +>x : Symbol(x, Decl(controlFlowIterationErrorsAsync.ts, 40, 7)) +>foo : Symbol(foo, Decl(controlFlowIterationErrorsAsync.ts, 24, 1), Decl(controlFlowIterationErrorsAsync.ts, 26, 49)) +>x : Symbol(x, Decl(controlFlowIterationErrorsAsync.ts, 40, 7)) + } + x; +>x : Symbol(x, Decl(controlFlowIterationErrorsAsync.ts, 40, 7)) +} + +async function asNumber(x: string | number): Promise { +>asNumber : Symbol(asNumber, Decl(controlFlowIterationErrorsAsync.ts, 47, 1)) +>x : Symbol(x, Decl(controlFlowIterationErrorsAsync.ts, 49, 24)) +>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2018.promise.d.ts, --, --)) + + return +x; +>x : Symbol(x, Decl(controlFlowIterationErrorsAsync.ts, 49, 24)) +} + +async function h1() { +>h1 : Symbol(h1, Decl(controlFlowIterationErrorsAsync.ts, 51, 1)) + + let x: string | number | boolean; +>x : Symbol(x, Decl(controlFlowIterationErrorsAsync.ts, 54, 7)) + + x = "0"; +>x : Symbol(x, Decl(controlFlowIterationErrorsAsync.ts, 54, 7)) + + while (cond) { +>cond : Symbol(cond, Decl(controlFlowIterationErrorsAsync.ts, 0, 3)) + + x = +x + 1; +>x : Symbol(x, Decl(controlFlowIterationErrorsAsync.ts, 54, 7)) +>x : Symbol(x, Decl(controlFlowIterationErrorsAsync.ts, 54, 7)) + + x; +>x : Symbol(x, Decl(controlFlowIterationErrorsAsync.ts, 54, 7)) + } +} + +async function h2() { +>h2 : Symbol(h2, Decl(controlFlowIterationErrorsAsync.ts, 60, 1)) + + let x: string | number | boolean; +>x : Symbol(x, Decl(controlFlowIterationErrorsAsync.ts, 63, 7)) + + x = "0"; +>x : Symbol(x, Decl(controlFlowIterationErrorsAsync.ts, 63, 7)) + + while (cond) { +>cond : Symbol(cond, Decl(controlFlowIterationErrorsAsync.ts, 0, 3)) + + x = await asNumber(x) + 1; +>x : Symbol(x, Decl(controlFlowIterationErrorsAsync.ts, 63, 7)) +>asNumber : Symbol(asNumber, Decl(controlFlowIterationErrorsAsync.ts, 47, 1)) +>x : Symbol(x, Decl(controlFlowIterationErrorsAsync.ts, 63, 7)) + + x; +>x : Symbol(x, Decl(controlFlowIterationErrorsAsync.ts, 63, 7)) + } +} + +async function h3() { +>h3 : Symbol(h3, Decl(controlFlowIterationErrorsAsync.ts, 69, 1)) + + let x: string | number | boolean; +>x : Symbol(x, Decl(controlFlowIterationErrorsAsync.ts, 72, 7)) + + x = "0"; +>x : Symbol(x, Decl(controlFlowIterationErrorsAsync.ts, 72, 7)) + + while (cond) { +>cond : Symbol(cond, Decl(controlFlowIterationErrorsAsync.ts, 0, 3)) + + let y = await asNumber(x); +>y : Symbol(y, Decl(controlFlowIterationErrorsAsync.ts, 75, 11)) +>asNumber : Symbol(asNumber, Decl(controlFlowIterationErrorsAsync.ts, 47, 1)) +>x : Symbol(x, Decl(controlFlowIterationErrorsAsync.ts, 72, 7)) + + x = y + 1; +>x : Symbol(x, Decl(controlFlowIterationErrorsAsync.ts, 72, 7)) +>y : Symbol(y, Decl(controlFlowIterationErrorsAsync.ts, 75, 11)) + + x; +>x : Symbol(x, Decl(controlFlowIterationErrorsAsync.ts, 72, 7)) + } +} + +async function h4() { +>h4 : Symbol(h4, Decl(controlFlowIterationErrorsAsync.ts, 79, 1)) + + let x: string | number | boolean; +>x : Symbol(x, Decl(controlFlowIterationErrorsAsync.ts, 82, 7)) + + x = "0"; +>x : Symbol(x, Decl(controlFlowIterationErrorsAsync.ts, 82, 7)) + + while (cond) { +>cond : Symbol(cond, Decl(controlFlowIterationErrorsAsync.ts, 0, 3)) + + x; +>x : Symbol(x, Decl(controlFlowIterationErrorsAsync.ts, 82, 7)) + + let y = await asNumber(x); +>y : Symbol(y, Decl(controlFlowIterationErrorsAsync.ts, 86, 11)) +>asNumber : Symbol(asNumber, Decl(controlFlowIterationErrorsAsync.ts, 47, 1)) +>x : Symbol(x, Decl(controlFlowIterationErrorsAsync.ts, 82, 7)) + + x = y + 1; +>x : Symbol(x, Decl(controlFlowIterationErrorsAsync.ts, 82, 7)) +>y : Symbol(y, Decl(controlFlowIterationErrorsAsync.ts, 86, 11)) + + x; +>x : Symbol(x, Decl(controlFlowIterationErrorsAsync.ts, 82, 7)) + } +} + +// repro #51115 + +async function get_things(_: number | undefined) { +>get_things : Symbol(get_things, Decl(controlFlowIterationErrorsAsync.ts, 90, 1)) +>_ : Symbol(_, Decl(controlFlowIterationErrorsAsync.ts, 94, 26)) + + return [0]; +} + +async function foobar() { +>foobar : Symbol(foobar, Decl(controlFlowIterationErrorsAsync.ts, 96, 1)) + + let before: number | undefined = undefined; +>before : Symbol(before, Decl(controlFlowIterationErrorsAsync.ts, 99, 7)) +>undefined : Symbol(undefined) + + for (let i = 0; i < 2; i++) { +>i : Symbol(i, Decl(controlFlowIterationErrorsAsync.ts, 100, 12)) +>i : Symbol(i, Decl(controlFlowIterationErrorsAsync.ts, 100, 12)) +>i : Symbol(i, Decl(controlFlowIterationErrorsAsync.ts, 100, 12)) + + const results = await get_things(before); +>results : Symbol(results, Decl(controlFlowIterationErrorsAsync.ts, 101, 13)) +>get_things : Symbol(get_things, Decl(controlFlowIterationErrorsAsync.ts, 90, 1)) +>before : Symbol(before, Decl(controlFlowIterationErrorsAsync.ts, 99, 7)) + + before = results[0]; +>before : Symbol(before, Decl(controlFlowIterationErrorsAsync.ts, 99, 7)) +>results : Symbol(results, Decl(controlFlowIterationErrorsAsync.ts, 101, 13)) + } +} + +// repro #43047#issuecomment-821453073 + +declare function foox(x: string | undefined): Promise +>foox : Symbol(foox, Decl(controlFlowIterationErrorsAsync.ts, 104, 1)) +>x : Symbol(x, Decl(controlFlowIterationErrorsAsync.ts, 108, 22)) +>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2018.promise.d.ts, --, --)) + +async () => { + let bar: string | undefined = undefined; +>bar : Symbol(bar, Decl(controlFlowIterationErrorsAsync.ts, 111, 5)) +>undefined : Symbol(undefined) + + do { + const baz = await foox(bar); +>baz : Symbol(baz, Decl(controlFlowIterationErrorsAsync.ts, 113, 9)) +>foox : Symbol(foox, Decl(controlFlowIterationErrorsAsync.ts, 104, 1)) +>bar : Symbol(bar, Decl(controlFlowIterationErrorsAsync.ts, 111, 5)) + + bar = baz +>bar : Symbol(bar, Decl(controlFlowIterationErrorsAsync.ts, 111, 5)) +>baz : Symbol(baz, Decl(controlFlowIterationErrorsAsync.ts, 113, 9)) + + } while (bar) +>bar : Symbol(bar, Decl(controlFlowIterationErrorsAsync.ts, 111, 5)) +} + +// repro #43047#issuecomment-874221939 + +declare function myQuery(input: { lastId: number | undefined }): Promise<{ entities: number[] }>; +>myQuery : Symbol(myQuery, Decl(controlFlowIterationErrorsAsync.ts, 116, 1)) +>input : Symbol(input, Decl(controlFlowIterationErrorsAsync.ts, 120, 25)) +>lastId : Symbol(lastId, Decl(controlFlowIterationErrorsAsync.ts, 120, 33)) +>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2018.promise.d.ts, --, --)) +>entities : Symbol(entities, Decl(controlFlowIterationErrorsAsync.ts, 120, 74)) + +async function myFunc(): Promise { +>myFunc : Symbol(myFunc, Decl(controlFlowIterationErrorsAsync.ts, 120, 97)) +>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2018.promise.d.ts, --, --)) + + let lastId: number | undefined = undefined; +>lastId : Symbol(lastId, Decl(controlFlowIterationErrorsAsync.ts, 123, 5)) +>undefined : Symbol(undefined) + + while (true) { + const { entities } = await myQuery({ +>entities : Symbol(entities, Decl(controlFlowIterationErrorsAsync.ts, 126, 11)) +>myQuery : Symbol(myQuery, Decl(controlFlowIterationErrorsAsync.ts, 116, 1)) + + lastId, +>lastId : Symbol(lastId, Decl(controlFlowIterationErrorsAsync.ts, 126, 40)) + + }); + + lastId = entities[entities.length - 1]; +>lastId : Symbol(lastId, Decl(controlFlowIterationErrorsAsync.ts, 123, 5)) +>entities : Symbol(entities, Decl(controlFlowIterationErrorsAsync.ts, 126, 11)) +>entities.length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --)) +>entities : Symbol(entities, Decl(controlFlowIterationErrorsAsync.ts, 126, 11)) +>length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --)) + } +} + diff --git a/tests/baselines/reference/controlFlowIterationErrorsAsync.types b/tests/baselines/reference/controlFlowIterationErrorsAsync.types new file mode 100644 index 0000000000000..21419b540c3cb --- /dev/null +++ b/tests/baselines/reference/controlFlowIterationErrorsAsync.types @@ -0,0 +1,388 @@ +=== tests/cases/conformance/controlFlow/controlFlowIterationErrorsAsync.ts === +let cond: boolean; +>cond : boolean + +async function len(s: string) { +>len : (s: string) => Promise +>s : string + + return s.length; +>s.length : number +>s : string +>length : number +} + +async function f1() { +>f1 : () => Promise + + let x: string | number | boolean; +>x : string | number | boolean + + x = ""; +>x = "" : "" +>x : string | number | boolean +>"" : "" + + while (cond) { +>cond : boolean + + x = await len(x); +>x = await len(x) : number +>x : string | number | boolean +>await len(x) : number +>len(x) : Promise +>len : (s: string) => Promise +>x : string | number + + x; +>x : number + } + x; +>x : string | number +} + +async function f2() { +>f2 : () => Promise + + let x: string | number | boolean; +>x : string | number | boolean + + x = ""; +>x = "" : "" +>x : string | number | boolean +>"" : "" + + while (cond) { +>cond : boolean + + x; +>x : string | number + + x = await len(x); +>x = await len(x) : number +>x : string | number | boolean +>await len(x) : number +>len(x) : Promise +>len : (s: string) => Promise +>x : string | number + } + x; +>x : string | number +} + +declare function foo(x: string): Promise; +>foo : { (x: string): Promise; (x: number): Promise; } +>x : string + +declare function foo(x: number): Promise; +>foo : { (x: string): Promise; (x: number): Promise; } +>x : number + +async function g1() { +>g1 : () => Promise + + let x: string | number | boolean; +>x : string | number | boolean + + x = ""; +>x = "" : "" +>x : string | number | boolean +>"" : "" + + while (cond) { +>cond : boolean + + x = await foo(x); +>x = await foo(x) : never +>x : string | number | boolean +>await foo(x) : never +>foo(x) : Promise & Promise +>foo : { (x: string): Promise; (x: number): Promise; } +>x : string | number + + x; +>x : never + } + x; +>x : string | number +} + +async function g2() { +>g2 : () => Promise + + let x: string | number | boolean; +>x : string | number | boolean + + x = ""; +>x = "" : "" +>x : string | number | boolean +>"" : "" + + while (cond) { +>cond : boolean + + x; +>x : string | number + + x = await foo(x); +>x = await foo(x) : never +>x : string | number | boolean +>await foo(x) : never +>foo(x) : Promise & Promise +>foo : { (x: string): Promise; (x: number): Promise; } +>x : string | number + } + x; +>x : string | number +} + +async function asNumber(x: string | number): Promise { +>asNumber : (x: string | number) => Promise +>x : string | number + + return +x; +>+x : number +>x : string | number +} + +async function h1() { +>h1 : () => Promise + + let x: string | number | boolean; +>x : string | number | boolean + + x = "0"; +>x = "0" : "0" +>x : string | number | boolean +>"0" : "0" + + while (cond) { +>cond : boolean + + x = +x + 1; +>x = +x + 1 : number +>x : string | number | boolean +>+x + 1 : number +>+x : number +>x : string | number +>1 : 1 + + x; +>x : number + } +} + +async function h2() { +>h2 : () => Promise + + let x: string | number | boolean; +>x : string | number | boolean + + x = "0"; +>x = "0" : "0" +>x : string | number | boolean +>"0" : "0" + + while (cond) { +>cond : boolean + + x = await asNumber(x) + 1; +>x = await asNumber(x) + 1 : number +>x : string | number | boolean +>await asNumber(x) + 1 : number +>await asNumber(x) : number +>asNumber(x) : Promise +>asNumber : (x: string | number) => Promise +>x : string | number +>1 : 1 + + x; +>x : number + } +} + +async function h3() { +>h3 : () => Promise + + let x: string | number | boolean; +>x : string | number | boolean + + x = "0"; +>x = "0" : "0" +>x : string | number | boolean +>"0" : "0" + + while (cond) { +>cond : boolean + + let y = await asNumber(x); +>y : number +>await asNumber(x) : number +>asNumber(x) : Promise +>asNumber : (x: string | number) => Promise +>x : string | number + + x = y + 1; +>x = y + 1 : number +>x : string | number | boolean +>y + 1 : number +>y : number +>1 : 1 + + x; +>x : number + } +} + +async function h4() { +>h4 : () => Promise + + let x: string | number | boolean; +>x : string | number | boolean + + x = "0"; +>x = "0" : "0" +>x : string | number | boolean +>"0" : "0" + + while (cond) { +>cond : boolean + + x; +>x : string | number + + let y = await asNumber(x); +>y : number +>await asNumber(x) : number +>asNumber(x) : Promise +>asNumber : (x: string | number) => Promise +>x : string | number + + x = y + 1; +>x = y + 1 : number +>x : string | number | boolean +>y + 1 : number +>y : number +>1 : 1 + + x; +>x : number + } +} + +// repro #51115 + +async function get_things(_: number | undefined) { +>get_things : (_: number | undefined) => Promise +>_ : number | undefined + + return [0]; +>[0] : number[] +>0 : 0 +} + +async function foobar() { +>foobar : () => Promise + + let before: number | undefined = undefined; +>before : number | undefined +>undefined : undefined + + for (let i = 0; i < 2; i++) { +>i : number +>0 : 0 +>i < 2 : boolean +>i : number +>2 : 2 +>i++ : number +>i : number + + const results = await get_things(before); +>results : number[] +>await get_things(before) : number[] +>get_things(before) : Promise +>get_things : (_: number | undefined) => Promise +>before : number | undefined + + before = results[0]; +>before = results[0] : number +>before : number | undefined +>results[0] : number +>results : number[] +>0 : 0 + } +} + +// repro #43047#issuecomment-821453073 + +declare function foox(x: string | undefined): Promise +>foox : (x: string | undefined) => Promise +>x : string | undefined + +async () => { +>async () => { let bar: string | undefined = undefined; do { const baz = await foox(bar); bar = baz } while (bar)} : () => Promise + + let bar: string | undefined = undefined; +>bar : string | undefined +>undefined : undefined + + do { + const baz = await foox(bar); +>baz : string +>await foox(bar) : string +>foox(bar) : Promise +>foox : (x: string | undefined) => Promise +>bar : string | undefined + + bar = baz +>bar = baz : string +>bar : string | undefined +>baz : string + + } while (bar) +>bar : string +} + +// repro #43047#issuecomment-874221939 + +declare function myQuery(input: { lastId: number | undefined }): Promise<{ entities: number[] }>; +>myQuery : (input: { lastId: number | undefined;}) => Promise<{ entities: number[];}> +>input : { lastId: number | undefined; } +>lastId : number | undefined +>entities : number[] + +async function myFunc(): Promise { +>myFunc : () => Promise + + let lastId: number | undefined = undefined; +>lastId : number | undefined +>undefined : undefined + + while (true) { +>true : true + + const { entities } = await myQuery({ +>entities : number[] +>await myQuery({ lastId, }) : { entities: number[]; } +>myQuery({ lastId, }) : Promise<{ entities: number[]; }> +>myQuery : (input: { lastId: number | undefined; }) => Promise<{ entities: number[]; }> +>{ lastId, } : { lastId: number | undefined; } + + lastId, +>lastId : number | undefined + + }); + + lastId = entities[entities.length - 1]; +>lastId = entities[entities.length - 1] : number +>lastId : number | undefined +>entities[entities.length - 1] : number +>entities : number[] +>entities.length - 1 : number +>entities.length : number +>entities : number[] +>length : number +>1 : 1 + } +} + diff --git a/tests/baselines/reference/implicitAnyFromCircularInference.errors.txt b/tests/baselines/reference/implicitAnyFromCircularInference.errors.txt index 73665d0bfe5a8..18693df2f5f76 100644 --- a/tests/baselines/reference/implicitAnyFromCircularInference.errors.txt +++ b/tests/baselines/reference/implicitAnyFromCircularInference.errors.txt @@ -7,7 +7,7 @@ tests/cases/compiler/implicitAnyFromCircularInference.ts(17,5): error TS7023: 'f tests/cases/compiler/implicitAnyFromCircularInference.ts(22,5): error TS7023: 'f2' implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions. tests/cases/compiler/implicitAnyFromCircularInference.ts(25,10): error TS7023: 'h' implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions. tests/cases/compiler/implicitAnyFromCircularInference.ts(27,14): error TS7023: 'foo' implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions. -tests/cases/compiler/implicitAnyFromCircularInference.ts(45,9): error TS7023: 'x' implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions. +tests/cases/compiler/implicitAnyFromCircularInference.ts(44,9): error TS7023: 'x' implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions. ==== tests/cases/compiler/implicitAnyFromCircularInference.ts (10 errors) ==== @@ -67,7 +67,6 @@ tests/cases/compiler/implicitAnyFromCircularInference.ts(45,9): error TS7023: 'x function foo(x: A): string { return "abc"; } class C { - // Error expected s = foo(this); } diff --git a/tests/baselines/reference/implicitAnyFromCircularInference.js b/tests/baselines/reference/implicitAnyFromCircularInference.js index 7833694772e03..ebc1b055e2529 100644 --- a/tests/baselines/reference/implicitAnyFromCircularInference.js +++ b/tests/baselines/reference/implicitAnyFromCircularInference.js @@ -37,7 +37,6 @@ interface A { function foo(x: A): string { return "abc"; } class C { - // Error expected s = foo(this); } @@ -76,7 +75,6 @@ function h() { function foo(x) { return "abc"; } var C = /** @class */ (function () { function C() { - // Error expected this.s = foo(this); } return C; diff --git a/tests/baselines/reference/implicitAnyFromCircularInference.symbols b/tests/baselines/reference/implicitAnyFromCircularInference.symbols index 014fb5aa41bcd..8affdd71c6dbb 100644 --- a/tests/baselines/reference/implicitAnyFromCircularInference.symbols +++ b/tests/baselines/reference/implicitAnyFromCircularInference.symbols @@ -72,7 +72,6 @@ function foo(x: A): string { return "abc"; } class C { >C : Symbol(C, Decl(implicitAnyFromCircularInference.ts, 35, 44)) - // Error expected s = foo(this); >s : Symbol(C.s, Decl(implicitAnyFromCircularInference.ts, 37, 9)) >foo : Symbol(foo, Decl(implicitAnyFromCircularInference.ts, 33, 1)) @@ -80,16 +79,16 @@ class C { } class D { ->D : Symbol(D, Decl(implicitAnyFromCircularInference.ts, 40, 1)) +>D : Symbol(D, Decl(implicitAnyFromCircularInference.ts, 39, 1)) // Error expected get x() { ->x : Symbol(D.x, Decl(implicitAnyFromCircularInference.ts, 42, 9)) +>x : Symbol(D.x, Decl(implicitAnyFromCircularInference.ts, 41, 9)) return this.x; ->this.x : Symbol(D.x, Decl(implicitAnyFromCircularInference.ts, 42, 9)) ->this : Symbol(D, Decl(implicitAnyFromCircularInference.ts, 40, 1)) ->x : Symbol(D.x, Decl(implicitAnyFromCircularInference.ts, 42, 9)) +>this.x : Symbol(D.x, Decl(implicitAnyFromCircularInference.ts, 41, 9)) +>this : Symbol(D, Decl(implicitAnyFromCircularInference.ts, 39, 1)) +>x : Symbol(D.x, Decl(implicitAnyFromCircularInference.ts, 41, 9)) } } diff --git a/tests/baselines/reference/implicitAnyFromCircularInference.types b/tests/baselines/reference/implicitAnyFromCircularInference.types index c4ecb84141f67..906307b0725d5 100644 --- a/tests/baselines/reference/implicitAnyFromCircularInference.types +++ b/tests/baselines/reference/implicitAnyFromCircularInference.types @@ -78,7 +78,6 @@ function foo(x: A): string { return "abc"; } class C { >C : C - // Error expected s = foo(this); >s : string >foo(this) : string diff --git a/tests/cases/compiler/implicitAnyFromCircularInference.ts b/tests/cases/compiler/implicitAnyFromCircularInference.ts index a4c8b1ba78b9f..88127728ebb2b 100644 --- a/tests/cases/compiler/implicitAnyFromCircularInference.ts +++ b/tests/cases/compiler/implicitAnyFromCircularInference.ts @@ -39,7 +39,6 @@ interface A { function foo(x: A): string { return "abc"; } class C { - // Error expected s = foo(this); } diff --git a/tests/cases/conformance/controlFlow/controlFlowIterationErrorsAsync.ts b/tests/cases/conformance/controlFlow/controlFlowIterationErrorsAsync.ts new file mode 100644 index 0000000000000..9ed052be032f5 --- /dev/null +++ b/tests/cases/conformance/controlFlow/controlFlowIterationErrorsAsync.ts @@ -0,0 +1,137 @@ +// @strict: true +// @noEmit: true +// @lib: esnext + +let cond: boolean; + +async function len(s: string) { + return s.length; +} + +async function f1() { + let x: string | number | boolean; + x = ""; + while (cond) { + x = await len(x); + x; + } + x; +} + +async function f2() { + let x: string | number | boolean; + x = ""; + while (cond) { + x; + x = await len(x); + } + x; +} + +declare function foo(x: string): Promise; +declare function foo(x: number): Promise; + +async function g1() { + let x: string | number | boolean; + x = ""; + while (cond) { + x = await foo(x); + x; + } + x; +} + +async function g2() { + let x: string | number | boolean; + x = ""; + while (cond) { + x; + x = await foo(x); + } + x; +} + +async function asNumber(x: string | number): Promise { + return +x; +} + +async function h1() { + let x: string | number | boolean; + x = "0"; + while (cond) { + x = +x + 1; + x; + } +} + +async function h2() { + let x: string | number | boolean; + x = "0"; + while (cond) { + x = await asNumber(x) + 1; + x; + } +} + +async function h3() { + let x: string | number | boolean; + x = "0"; + while (cond) { + let y = await asNumber(x); + x = y + 1; + x; + } +} + +async function h4() { + let x: string | number | boolean; + x = "0"; + while (cond) { + x; + let y = await asNumber(x); + x = y + 1; + x; + } +} + +// repro #51115 + +async function get_things(_: number | undefined) { + return [0]; +} + +async function foobar() { + let before: number | undefined = undefined; + for (let i = 0; i < 2; i++) { + const results = await get_things(before); + before = results[0]; + } +} + +// repro #43047#issuecomment-821453073 + +declare function foox(x: string | undefined): Promise + +async () => { + let bar: string | undefined = undefined; + do { + const baz = await foox(bar); + bar = baz + } while (bar) +} + +// repro #43047#issuecomment-874221939 + +declare function myQuery(input: { lastId: number | undefined }): Promise<{ entities: number[] }>; + +async function myFunc(): Promise { + let lastId: number | undefined = undefined; + + while (true) { + const { entities } = await myQuery({ + lastId, + }); + + lastId = entities[entities.length - 1]; + } +} From 75c096f603a5a4e697d822d9b51ad1ec4bdb1c6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Mon, 9 Jan 2023 15:20:23 +0100 Subject: [PATCH 2/3] Bring back boolean literal check in the `getQuickTypeOfExpression` --- src/compiler/checker.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 1f14db151b1fb..ca03aee2d35f2 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -427,6 +427,7 @@ import { isBlock, isBlockOrCatchScoped, isBlockScopedContainerTopLevel, + isBooleanLiteral, isCallChain, isCallExpression, isCallLikeExpression, @@ -36345,7 +36346,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { else if (isAssertionExpression(expr) && !isConstTypeReference(expr.type)) { return getTypeFromTypeNode((expr as TypeAssertion).type); } - else if (isLiteralExpression(node)) { + else if (isLiteralExpression(node) || isBooleanLiteral(node)) { return checkExpression(node); } return undefined; From c5fd66cf120bde785f7c519eb5acb91deac6ffe8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Mon, 9 Jan 2023 18:40:21 +0100 Subject: [PATCH 3/3] revert unrelated changes --- src/compiler/checker.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ca03aee2d35f2..96383425181aa 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -427,7 +427,6 @@ import { isBlock, isBlockOrCatchScoped, isBlockScopedContainerTopLevel, - isBooleanLiteral, isCallChain, isCallExpression, isCallLikeExpression, @@ -568,7 +567,6 @@ import { isLet, isLineBreak, isLiteralComputedPropertyDeclarationName, - isLiteralExpression, isLiteralExpressionOfObject, isLiteralImportTypeNode, isLiteralTypeNode, @@ -36339,14 +36337,15 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } // Optimize for the common case of a call to a function with a single non-generic call // signature where we can just fetch the return type without checking the arguments. - else if (isCallExpression(expr) && expr.expression.kind !== SyntaxKind.SuperKeyword && !isRequireCall(expr, /*checkArgumentIsStringLiteralLike*/ true) && !isSymbolOrSymbolForCall(expr)) { + if (isCallExpression(expr) && expr.expression.kind !== SyntaxKind.SuperKeyword && !isRequireCall(expr, /*checkArgumentIsStringLiteralLike*/ true) && !isSymbolOrSymbolForCall(expr)) { return isCallChain(expr) ? getReturnTypeOfSingleNonGenericSignatureOfCallChain(expr) : getReturnTypeOfSingleNonGenericCallSignature(checkNonNullExpression(expr.expression)); } else if (isAssertionExpression(expr) && !isConstTypeReference(expr.type)) { return getTypeFromTypeNode((expr as TypeAssertion).type); } - else if (isLiteralExpression(node) || isBooleanLiteral(node)) { + else if (node.kind === SyntaxKind.NumericLiteral || node.kind === SyntaxKind.StringLiteral || + node.kind === SyntaxKind.TrueKeyword || node.kind === SyntaxKind.FalseKeyword) { return checkExpression(node); } return undefined;