Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed false positive circular errors for await expressions with simple non-generic calls in CFA loops #51126

Merged
merged 4 commits into from Jan 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
12 changes: 7 additions & 5 deletions src/compiler/checker.ts
Expand Up @@ -417,6 +417,7 @@ import {
isAssignmentTarget,
isAsyncFunction,
isAutoAccessorPropertyDeclaration,
isAwaitExpression,
isBinaryExpression,
isBindableObjectDefinePropertyCall,
isBindableStaticElementAccessExpression,
Expand Down Expand Up @@ -36321,7 +36322,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
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);
Expand All @@ -36330,14 +36331,15 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}
}
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) :
return isCallChain(expr) ? getReturnTypeOfSingleNonGenericSignatureOfCallChain(expr) :
rbuckton marked this conversation as resolved.
Show resolved Hide resolved
getReturnTypeOfSingleNonGenericCallSignature(checkNonNullExpression(expr.expression));
if (type) {
return type;
}
}
else if (isAssertionExpression(expr) && !isConstTypeReference(expr.type)) {
return getTypeFromTypeNode((expr as TypeAssertion).type);
Expand Down
177 changes: 177 additions & 0 deletions 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<number>', 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<string>', 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<number>', 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<string>', 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<number>;
declare function foo(x: number): Promise<string>;

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<number>', 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<string>', 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<number>', 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<string>', 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<number> {
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<string>

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<void> {
let lastId: number | undefined = undefined;

while (true) {
const { entities } = await myQuery({
lastId,
});

lastId = entities[entities.length - 1];
}
}