Skip to content

Unify semantic differences between produceDiagnostics and non-producing checkers #28950

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

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 30 additions & 12 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17139,7 +17139,14 @@ namespace ts {
function getContextualTypeForArgumentAtIndex(callTarget: CallLikeExpression, argIndex: number): Type {
// If we're already in the process of resolving the given signature, don't resolve again as
// that could cause infinite recursion. Instead, return anySignature.
const signature = getNodeLinks(callTarget).resolvedSignature === resolvingSignature ? resolvingSignature : getResolvedSignature(callTarget);
let signature = getNodeLinks(callTarget).resolvedSignature === resolvingSignature ? resolvingSignature : getResolvedSignature(callTarget);
if (signature === unknownSignature) {
const sigs: Signature[] = [];
signature = getResolvedSignature(callTarget, sigs); // If cached signature is the error signature, atempt to get the more general "best candidate" signature
if (sigs.length > 1) {
signature = createUnionOfSignaturesForOverloadFailure(sigs);
}
}
if (isJsxOpeningLikeElement(callTarget) && argIndex === 0) {
return getEffectiveFirstArgumentForJsxSignature(signature, callTarget);
}
Expand Down Expand Up @@ -19892,7 +19899,10 @@ namespace ts {
return createDiagnosticForNodeArray(getSourceFileOfNode(node), typeArguments, Diagnostics.Expected_0_type_arguments_but_got_1, paramCount, typeArguments.length);
}

function resolveCall(node: CallLikeExpression, signatures: ReadonlyArray<Signature>, candidatesOutArray: Signature[] | undefined, isForSignatureHelp: boolean, fallbackError?: DiagnosticMessage): Signature {
interface FailedSignature {
getFailureCandidate(): Signature;
}
function resolveCall(node: CallLikeExpression, signatures: ReadonlyArray<Signature>, candidatesOutArray: Signature[] | undefined, isForSignatureHelp: boolean, fallbackError?: DiagnosticMessage): Signature | FailedSignature {
const isTaggedTemplate = node.kind === SyntaxKind.TaggedTemplateExpression;
const isDecorator = node.kind === SyntaxKind.Decorator;
const isJsxOpeningOrSelfClosingElement = isJsxOpeningLikeElement(node);
Expand Down Expand Up @@ -20020,7 +20030,7 @@ namespace ts {
}
}

return produceDiagnostics || !args ? resolveErrorCall(node) : getCandidateForOverloadFailure(node, candidates, args, !!candidatesOutArray);
return { getFailureCandidate() { return getCandidateForOverloadFailure(node, candidates, args, !!candidatesOutArray); } };

function chooseOverload(candidates: Signature[], relation: Map<RelationComparisonResult>, signatureHelpTrailingComma = false) {
candidateForArgumentError = undefined;
Expand Down Expand Up @@ -20241,7 +20251,7 @@ namespace ts {
return maxParamsIndex;
}

function resolveCallExpression(node: CallExpression, candidatesOutArray: Signature[] | undefined, isForSignatureHelp: boolean): Signature {
function resolveCallExpression(node: CallExpression, candidatesOutArray: Signature[] | undefined, isForSignatureHelp: boolean): Signature | FailedSignature {
if (node.expression.kind === SyntaxKind.SuperKeyword) {
const superType = checkSuperExpression(node.expression);
if (isTypeAny(superType)) {
Expand Down Expand Up @@ -20335,7 +20345,7 @@ namespace ts {
!numCallSignatures && !numConstructSignatures && !(apparentFuncType.flags & (TypeFlags.Union | TypeFlags.Never)) && isTypeAssignableTo(funcType, globalFunctionType);
}

function resolveNewExpression(node: NewExpression, candidatesOutArray: Signature[] | undefined, isForSignatureHelp: boolean): Signature {
function resolveNewExpression(node: NewExpression, candidatesOutArray: Signature[] | undefined, isForSignatureHelp: boolean): Signature | FailedSignature {
if (node.arguments && languageVersion < ScriptTarget.ES5) {
const spreadIndex = getSpreadArgumentIndex(node.arguments);
if (spreadIndex >= 0) {
Expand Down Expand Up @@ -20398,7 +20408,7 @@ namespace ts {
const callSignatures = getSignaturesOfType(expressionType, SignatureKind.Call);
if (callSignatures.length) {
const signature = resolveCall(node, callSignatures, candidatesOutArray, isForSignatureHelp);
if (!noImplicitAny) {
if (!noImplicitAny && !signature.getFailureCandidate) {
if (signature.declaration && !isJSConstructor(signature.declaration) && getReturnTypeOfSignature(signature) !== voidType) {
error(node, Diagnostics.Only_a_void_function_can_be_called_with_the_new_keyword);
}
Expand Down Expand Up @@ -20507,7 +20517,7 @@ namespace ts {
}
}

function resolveTaggedTemplateExpression(node: TaggedTemplateExpression, candidatesOutArray: Signature[] | undefined, isForSignatureHelp: boolean): Signature {
function resolveTaggedTemplateExpression(node: TaggedTemplateExpression, candidatesOutArray: Signature[] | undefined, isForSignatureHelp: boolean): Signature | FailedSignature {
const tagType = checkExpression(node.tag);
const apparentType = getApparentType(tagType);

Expand Down Expand Up @@ -20559,7 +20569,7 @@ namespace ts {
/**
* Resolves a decorator as if it were a call expression.
*/
function resolveDecorator(node: Decorator, candidatesOutArray: Signature[] | undefined, isForSignatureHelp: boolean): Signature {
function resolveDecorator(node: Decorator, candidatesOutArray: Signature[] | undefined, isForSignatureHelp: boolean): Signature | FailedSignature {
const funcType = checkExpression(node.expression);
const apparentType = getApparentType(funcType);
if (apparentType === errorType) {
Expand Down Expand Up @@ -20617,7 +20627,7 @@ namespace ts {
);
}

function resolveJsxOpeningLikeElement(node: JsxOpeningLikeElement, candidatesOutArray: Signature[] | undefined, isForSignatureHelp: boolean): Signature {
function resolveJsxOpeningLikeElement(node: JsxOpeningLikeElement, candidatesOutArray: Signature[] | undefined, isForSignatureHelp: boolean): Signature | FailedSignature {
if (isJsxIntrinsicIdentifier(node.tagName)) {
const result = getIntrinsicAttributesTypeFromJsxOpeningLikeElement(node);
const fakeSignature = createSignatureForJSXIntrinsic(node, result);
Expand Down Expand Up @@ -20656,7 +20666,7 @@ namespace ts {
signature.parameters.length < getDecoratorArgumentCount(decorator, signature));
}

function resolveSignature(node: CallLikeExpression, candidatesOutArray: Signature[] | undefined, isForSignatureHelp: boolean): Signature {
function resolveSignature(node: CallLikeExpression, candidatesOutArray: Signature[] | undefined, isForSignatureHelp: boolean): Signature | FailedSignature {
switch (node.kind) {
case SyntaxKind.CallExpression:
return resolveCallExpression(node, candidatesOutArray, isForSignatureHelp);
Expand Down Expand Up @@ -20691,12 +20701,20 @@ namespace ts {
return cached;
}
links.resolvedSignature = resolvingSignature;
const result = resolveSignature(node, candidatesOutArray, isForSignatureHelp);
let result = resolveSignature(node, candidatesOutArray, isForSignatureHelp);
// If signature resolution originated in control flow type analysis (for example to compute the
// assigned type in a flow assignment) we don't cache the result as it may be based on temporary
// types from the control flow analysis.
let sentinelResult: Signature | undefined;
if (result.getFailureCandidate) {
const sentinel = result;
result = resolveErrorCall(node);
if (candidatesOutArray) {
sentinelResult = sentinel.getFailureCandidate(); // Failure candidate can't be cached, but still needs to be returned
}
}
links.resolvedSignature = flowLoopStart === flowLoopCount ? result : cached;
return result;
return sentinelResult || result;
}

/**
Expand Down
2 changes: 2 additions & 0 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4281,6 +4281,8 @@ namespace ts {
isolatedSignatureType?: ObjectType; // A manufactured type that just contains the signature for purposes of signature comparison
/* @internal */
instantiations?: Map<Signature>; // Generic signature instantiation cache
/* @internal */
getFailureCandidate?: undefined; // Make discriminable with the `FailedSignature` sentinel inside the checker
}

export const enum IndexKind {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ baz(array); // Error
baz(["string", 1, true, ...array]); // Error
>baz(["string", 1, true, ...array]) : void
>baz : (x: [string, number, boolean]) => void
>["string", 1, true, ...array] : (string | number | boolean)[]
>["string", 1, true, ...array] : [string, number, true, ...(string | number | boolean)[]]
>"string" : "string"
>1 : 1
>true : true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,11 @@ const FooComponent = (props: { foo: "A" | "B" | "C" }) => <span>{props.foo}</spa
<FooComponent foo={"f"} />;
><FooComponent foo={"f"} /> : JSX.Element
>FooComponent : (props: { foo: "A" | "B" | "C"; }) => JSX.Element
>foo : string
>foo : "f"
>"f" : "f"

<FooComponent foo="f" />;
><FooComponent foo="f" /> : JSX.Element
>FooComponent : (props: { foo: "A" | "B" | "C"; }) => JSX.Element
>foo : string
>foo : "f"

Original file line number Diff line number Diff line change
Expand Up @@ -95,16 +95,16 @@ const b3 = <MainButton {...{goTo:"home"}} extra />; // goTo has type"home" | "c
>b3 : JSX.Element
><MainButton {...{goTo:"home"}} extra /> : JSX.Element
>MainButton : { (buttonProps: ButtonProps): JSX.Element; (linkProps: LinkProps): JSX.Element; }
>{goTo:"home"} : { goTo: string; }
>goTo : string
>{goTo:"home"} : { goTo: "home"; }
>goTo : "home"
>"home" : "home"
>extra : true

const b4 = <MainButton goTo="home" extra />; // goTo has type "home" | "contact"
>b4 : JSX.Element
><MainButton goTo="home" extra /> : JSX.Element
>MainButton : { (buttonProps: ButtonProps): JSX.Element; (linkProps: LinkProps): JSX.Element; }
>goTo : string
>goTo : "home"
>extra : true

export function NoOverload(buttonProps: ButtonProps): JSX.Element { return undefined }
Expand Down Expand Up @@ -138,8 +138,8 @@ const d1 = <NoOverload1 {...{goTo:"home"}} extra />; // goTo has type "home" |
>d1 : JSX.Element
><NoOverload1 {...{goTo:"home"}} extra /> : JSX.Element
>NoOverload1 : (linkProps: LinkProps) => JSX.Element
>{goTo:"home"} : { goTo: string; }
>goTo : string
>{goTo:"home"} : { goTo: "home"; }
>goTo : "home"
>"home" : "home"
>extra : true

8 changes: 4 additions & 4 deletions tests/baselines/reference/declarationsAndAssignments.types
Original file line number Diff line number Diff line change
Expand Up @@ -463,12 +463,12 @@ f14([2, ["abc", { x: 0 }]]);
f14([2, ["abc", { y: false }]]); // Error, no x
>f14([2, ["abc", { y: false }]]) : void
>f14 : ([a, [b, { x, y: c }]]: [number, [string, { x: any; y?: boolean; }]]) => void
>[2, ["abc", { y: false }]] : (number | (string | { y: boolean; })[])[]
>[2, ["abc", { y: false }]] : [number, [string, { y: false; }]]
>2 : 2
>["abc", { y: false }] : (string | { y: boolean; })[]
>["abc", { y: false }] : [string, { y: false; }]
>"abc" : "abc"
>{ y: false } : { y: boolean; }
>y : boolean
>{ y: false } : { y: false; }
>y : false
>false : false

module M {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ a1([1, 2, [["world"]]]);
a1([1, 2, [["world"]], 3]);
>a1([1, 2, [["world"]], 3]) : void
>a1 : ([a, b, [[c]]]: [number, number, string[][]]) => void
>[1, 2, [["world"]], 3] : (number | string[][])[]
>[1, 2, [["world"]], 3] : [number, number, string[][], number]
>1 : 1
>2 : 2
>[["world"]] : string[][]
Expand Down Expand Up @@ -302,11 +302,11 @@ c5([1, 2, [["string"]]]); // Implied type is is [any, any, [[any]]
c5([1, 2, [["string"]], false, true]); // Implied type is is [any, any, [[any]]]
>c5([1, 2, [["string"]], false, true]) : void
>c5 : ([a, b, [[c]]]: [any, any, [[any]]]) => void
>[1, 2, [["string"]], false, true] : (number | boolean | string[][])[]
>[1, 2, [["string"]], false, true] : [number, number, [[string]], boolean, boolean]
>1 : 1
>2 : 2
>[["string"]] : string[][]
>["string"] : string[]
>[["string"]] : [[string]]
>["string"] : [string]
>"string" : "string"
>false : false
>true : true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ a1([1, 2, [["world"]]]);
a1([1, 2, [["world"]], 3]);
>a1([1, 2, [["world"]], 3]) : void
>a1 : ([a, b, [[c]]]: [number, number, string[][]]) => void
>[1, 2, [["world"]], 3] : (number | string[][])[]
>[1, 2, [["world"]], 3] : [number, number, string[][], number]
>1 : 1
>2 : 2
>[["world"]] : string[][]
Expand Down Expand Up @@ -302,11 +302,11 @@ c5([1, 2, [["string"]]]); // Implied type is is [any, any, [[any]]
c5([1, 2, [["string"]], false, true]); // Implied type is is [any, any, [[any]]]
>c5([1, 2, [["string"]], false, true]) : void
>c5 : ([a, b, [[c]]]: [any, any, [[any]]]) => void
>[1, 2, [["string"]], false, true] : (number | boolean | string[][])[]
>[1, 2, [["string"]], false, true] : [number, number, [[string]], boolean, boolean]
>1 : 1
>2 : 2
>[["string"]] : string[][]
>["string"] : string[]
>[["string"]] : [[string]]
>["string"] : [string]
>"string" : "string"
>false : false
>true : true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ a1([1, 2, [["world"]]]);
a1([1, 2, [["world"]], 3]);
>a1([1, 2, [["world"]], 3]) : void
>a1 : ([a, b, [[c]]]: [number, number, string[][]]) => void
>[1, 2, [["world"]], 3] : (number | string[][])[]
>[1, 2, [["world"]], 3] : [number, number, string[][], number]
>1 : 1
>2 : 2
>[["world"]] : string[][]
Expand Down Expand Up @@ -285,11 +285,11 @@ c5([1, 2, [["string"]]]); // Implied type is is [any, any, [[any]]
c5([1, 2, [["string"]], false, true]); // Implied type is is [any, any, [[any]]]
>c5([1, 2, [["string"]], false, true]) : void
>c5 : ([a, b, [[c]]]: [any, any, [[any]]]) => void
>[1, 2, [["string"]], false, true] : (number | boolean | string[][])[]
>[1, 2, [["string"]], false, true] : [number, number, [[string]], boolean, boolean]
>1 : 1
>2 : 2
>[["string"]] : string[][]
>["string"] : string[]
>[["string"]] : [[string]]
>["string"] : [string]
>"string" : "string"
>false : false
>true : true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ function a0([a, b, [[c]]]: [number, number, string[][]]) { }
a0([1, "string", [["world"]]); // Error
>a0([1, "string", [["world"]]) : void
>a0 : ([a, b, [[c]]]: [number, number, string[][]]) => void
>[1, "string", [["world"]] : (string | number | string[][])[]
>[1, "string", [["world"]] : [number, string, string[][]]
>1 : 1
>"string" : "string"
>[["world"]] : string[][]
Expand All @@ -23,7 +23,7 @@ a0([1, "string", [["world"]]); // Error
a0([1, 2, [["world"]], "string"]); // Error
>a0([1, 2, [["world"]], "string"]) : void
>a0 : ([a, b, [[c]]]: [number, number, string[][]]) => void
>[1, 2, [["world"]], "string"] : (string | number | string[][])[]
>[1, 2, [["world"]], "string"] : [number, number, string[][], string]
>1 : 1
>2 : 2
>[["world"]] : string[][]
Expand Down Expand Up @@ -183,7 +183,7 @@ c3({ b: true }); // Error, implied type is { b: number|string }.
c5([1, 2, false, true]); // Error, implied type is [any, any, [[any]]]
>c5([1, 2, false, true]) : void
>c5 : ([a, b, [[c]]]: [any, any, [[any]]]) => void
>[1, 2, false, true] : (number | boolean)[]
>[1, 2, false, true] : [number, number, boolean, boolean]
>1 : 1
>2 : 2
>false : false
Expand All @@ -192,11 +192,11 @@ c5([1, 2, false, true]); // Error, implied type is [any, any, [[any]]]
c6([1, 2, [["string"]]]); // Error, implied type is [any, any, [[number]]] // Use initializer
>c6([1, 2, [["string"]]]) : void
>c6 : ([a, b, [[c]]]: [any, any, [[number?]]]) => void
>[1, 2, [["string"]]] : (number | string[][])[]
>[1, 2, [["string"]]] : [number, number, [[string]]]
>1 : 1
>2 : 2
>[["string"]] : string[][]
>["string"] : string[]
>[["string"]] : [[string]]
>["string"] : [string]
>"string" : "string"

// A parameter can be marked optional by following its name or binding pattern with a question mark (?)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,11 @@ a1(...array);
a9([1, 2, [["string"]], false, true]); // Parameter type is [any, any, [[any]]]
>a9([1, 2, [["string"]], false, true]) : void
>a9 : ([a, b, [[c]]]: [any, any, [[any]]]) => void
>[1, 2, [["string"]], false, true] : (number | boolean | string[][])[]
>[1, 2, [["string"]], false, true] : [number, number, [[string]], boolean, boolean]
>1 : 1
>2 : 2
>[["string"]] : string[][]
>["string"] : string[]
>[["string"]] : [[string]]
>["string"] : [string]
>"string" : "string"
>false : false
>true : true
Expand All @@ -109,7 +109,7 @@ a10([1, 2, [["string"]], false, true]); // Parameter type is any[]
a10([1, 2, 3, false, true]); // Parameter type is any[]
>a10([1, 2, 3, false, true]) : void
>a10 : ([a, b, [[c]], ...x]: [any, any, [[any]], ...any[]]) => void
>[1, 2, 3, false, true] : (number | boolean)[]
>[1, 2, 3, false, true] : [number, number, number, boolean, boolean]
>1 : 1
>2 : 2
>3 : 3
Expand All @@ -119,7 +119,7 @@ a10([1, 2, 3, false, true]); // Parameter type is any[]
a10([1, 2]); // Parameter type is any[]
>a10([1, 2]) : void
>a10 : ([a, b, [[c]], ...x]: [any, any, [[any]], ...any[]]) => void
>[1, 2] : number[]
>[1, 2] : [number, number]
>1 : 1
>2 : 2

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,11 @@ a1(...array);
a9([1, 2, [["string"]], false, true]); // Parameter type is [any, any, [[any]]]
>a9([1, 2, [["string"]], false, true]) : void
>a9 : ([a, b, [[c]]]: [any, any, [[any]]]) => void
>[1, 2, [["string"]], false, true] : (number | boolean | string[][])[]
>[1, 2, [["string"]], false, true] : [number, number, [[string]], boolean, boolean]
>1 : 1
>2 : 2
>[["string"]] : string[][]
>["string"] : string[]
>[["string"]] : [[string]]
>["string"] : [string]
>"string" : "string"
>false : false
>true : true
Expand All @@ -109,7 +109,7 @@ a10([1, 2, [["string"]], false, true]); // Parameter type is any[]
a10([1, 2, 3, false, true]); // Parameter type is any[]
>a10([1, 2, 3, false, true]) : void
>a10 : ([a, b, [[c]], ...x]: [any, any, [[any]], ...any[]]) => void
>[1, 2, 3, false, true] : (number | boolean)[]
>[1, 2, 3, false, true] : [number, number, number, boolean, boolean]
>1 : 1
>2 : 2
>3 : 3
Expand All @@ -119,7 +119,7 @@ a10([1, 2, 3, false, true]); // Parameter type is any[]
a10([1, 2]); // Parameter type is any[]
>a10([1, 2]) : void
>a10 : ([a, b, [[c]], ...x]: [any, any, [[any]], ...any[]]) => void
>[1, 2] : number[]
>[1, 2] : [number, number]
>1 : 1
>2 : 2

Expand Down
Loading