diff --git a/internal/checker/flow.go b/internal/checker/flow.go index 1952dd2ba9..8df30cad40 100644 --- a/internal/checker/flow.go +++ b/internal/checker/flow.go @@ -2405,7 +2405,7 @@ func (c *Checker) typeMaybeAssignableTo(source *Type, target *Type) bool { func (c *Checker) getTypePredicateArgument(predicate *TypePredicate, callExpression *ast.Node) *ast.Node { if predicate.kind == TypePredicateKindIdentifier || predicate.kind == TypePredicateKindAssertsIdentifier { arguments := callExpression.Arguments() - if int(predicate.parameterIndex) < len(arguments) { + if predicate.parameterIndex >= 0 && int(predicate.parameterIndex) < len(arguments) { return arguments[predicate.parameterIndex] } } else { @@ -2496,7 +2496,7 @@ func (c *Checker) isReachableFlowNodeWorker(f *FlowState, flow *ast.FlowNode, no case flags&ast.FlowFlagsCall != 0: if signature := c.getEffectsSignature(flow.Node); signature != nil { if predicate := c.getTypePredicateOfSignature(signature); predicate != nil && predicate.kind == TypePredicateKindAssertsIdentifier && predicate.t == nil { - if arguments := flow.Node.Arguments(); int(predicate.parameterIndex) < len(arguments) && c.isFalseExpression(arguments[predicate.parameterIndex]) { + if arguments := flow.Node.Arguments(); predicate.parameterIndex >= 0 && int(predicate.parameterIndex) < len(arguments) && c.isFalseExpression(arguments[predicate.parameterIndex]) { return false } } diff --git a/testdata/baselines/reference/compiler/assertsPredicateParameterMismatch.errors.txt b/testdata/baselines/reference/compiler/assertsPredicateParameterMismatch.errors.txt new file mode 100644 index 0000000000..39b13ba38a --- /dev/null +++ b/testdata/baselines/reference/compiler/assertsPredicateParameterMismatch.errors.txt @@ -0,0 +1,23 @@ +assertsPredicateParameterMismatch.ts(7,12): error TS1225: Cannot find parameter 'condition'. + + +==== assertsPredicateParameterMismatch.ts (1 errors) ==== + // This test verifies that the checker doesn't panic when an assertion predicate + // references a parameter name that doesn't match any actual function parameter. + // This specifically tests the code path in isReachableFlowNodeWorker. + + function assertCondition( + _condition: boolean + ): asserts condition { // "condition" doesn't match parameter "_condition" + ~~~~~~~~~ +!!! error TS1225: Cannot find parameter 'condition'. + if (!_condition) { + throw new Error('Condition failed'); + } + } + + function test(): void { + assertCondition(false); + console.log("unreachable"); + } + \ No newline at end of file diff --git a/testdata/baselines/reference/compiler/assertsPredicateParameterMismatch.symbols b/testdata/baselines/reference/compiler/assertsPredicateParameterMismatch.symbols new file mode 100644 index 0000000000..bbeb60ccf3 --- /dev/null +++ b/testdata/baselines/reference/compiler/assertsPredicateParameterMismatch.symbols @@ -0,0 +1,34 @@ +//// [tests/cases/compiler/assertsPredicateParameterMismatch.ts] //// + +=== assertsPredicateParameterMismatch.ts === +// This test verifies that the checker doesn't panic when an assertion predicate +// references a parameter name that doesn't match any actual function parameter. +// This specifically tests the code path in isReachableFlowNodeWorker. + +function assertCondition( +>assertCondition : Symbol(assertCondition, Decl(assertsPredicateParameterMismatch.ts, 0, 0)) + + _condition: boolean +>_condition : Symbol(_condition, Decl(assertsPredicateParameterMismatch.ts, 4, 25)) + +): asserts condition { // "condition" doesn't match parameter "_condition" + if (!_condition) { +>_condition : Symbol(_condition, Decl(assertsPredicateParameterMismatch.ts, 4, 25)) + + throw new Error('Condition failed'); +>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) + } +} + +function test(): void { +>test : Symbol(test, Decl(assertsPredicateParameterMismatch.ts, 10, 1)) + + assertCondition(false); +>assertCondition : Symbol(assertCondition, Decl(assertsPredicateParameterMismatch.ts, 0, 0)) + + console.log("unreachable"); +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +} + diff --git a/testdata/baselines/reference/compiler/assertsPredicateParameterMismatch.types b/testdata/baselines/reference/compiler/assertsPredicateParameterMismatch.types new file mode 100644 index 0000000000..1f05e06e53 --- /dev/null +++ b/testdata/baselines/reference/compiler/assertsPredicateParameterMismatch.types @@ -0,0 +1,41 @@ +//// [tests/cases/compiler/assertsPredicateParameterMismatch.ts] //// + +=== assertsPredicateParameterMismatch.ts === +// This test verifies that the checker doesn't panic when an assertion predicate +// references a parameter name that doesn't match any actual function parameter. +// This specifically tests the code path in isReachableFlowNodeWorker. + +function assertCondition( +>assertCondition : (_condition: boolean) => asserts condition + + _condition: boolean +>_condition : boolean + +): asserts condition { // "condition" doesn't match parameter "_condition" + if (!_condition) { +>!_condition : boolean +>_condition : boolean + + throw new Error('Condition failed'); +>new Error('Condition failed') : Error +>Error : ErrorConstructor +>'Condition failed' : "Condition failed" + } +} + +function test(): void { +>test : () => void + + assertCondition(false); +>assertCondition(false) : void +>assertCondition : (_condition: boolean) => asserts condition +>false : false + + console.log("unreachable"); +>console.log("unreachable") : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>"unreachable" : "unreachable" +} + diff --git a/testdata/baselines/reference/compiler/typePredicateParameterMismatch.errors.txt b/testdata/baselines/reference/compiler/typePredicateParameterMismatch.errors.txt new file mode 100644 index 0000000000..6c3037be78 --- /dev/null +++ b/testdata/baselines/reference/compiler/typePredicateParameterMismatch.errors.txt @@ -0,0 +1,25 @@ +typePredicateParameterMismatch.ts(10,4): error TS1225: Cannot find parameter 'value'. + + +==== typePredicateParameterMismatch.ts (1 errors) ==== + // This test verifies that the checker doesn't panic when a type predicate + // references a parameter name that doesn't match any actual function parameter. + + type TypeA = { kind: 'a' }; + type TypeB = { kind: 'b' }; + type UnionType = TypeA | TypeB; + + function isTypeA( + _value: UnionType + ): value is TypeA { // "value" doesn't match parameter "_value" + ~~~~~ +!!! error TS1225: Cannot find parameter 'value'. + return true; + } + + function test(input: UnionType): void { + if (isTypeA(input)) { + console.log(input.kind); + } + } + \ No newline at end of file diff --git a/testdata/baselines/reference/compiler/typePredicateParameterMismatch.symbols b/testdata/baselines/reference/compiler/typePredicateParameterMismatch.symbols new file mode 100644 index 0000000000..6efd825513 --- /dev/null +++ b/testdata/baselines/reference/compiler/typePredicateParameterMismatch.symbols @@ -0,0 +1,51 @@ +//// [tests/cases/compiler/typePredicateParameterMismatch.ts] //// + +=== typePredicateParameterMismatch.ts === +// This test verifies that the checker doesn't panic when a type predicate +// references a parameter name that doesn't match any actual function parameter. + +type TypeA = { kind: 'a' }; +>TypeA : Symbol(TypeA, Decl(typePredicateParameterMismatch.ts, 0, 0)) +>kind : Symbol(kind, Decl(typePredicateParameterMismatch.ts, 3, 14)) + +type TypeB = { kind: 'b' }; +>TypeB : Symbol(TypeB, Decl(typePredicateParameterMismatch.ts, 3, 27)) +>kind : Symbol(kind, Decl(typePredicateParameterMismatch.ts, 4, 14)) + +type UnionType = TypeA | TypeB; +>UnionType : Symbol(UnionType, Decl(typePredicateParameterMismatch.ts, 4, 27)) +>TypeA : Symbol(TypeA, Decl(typePredicateParameterMismatch.ts, 0, 0)) +>TypeB : Symbol(TypeB, Decl(typePredicateParameterMismatch.ts, 3, 27)) + +function isTypeA( +>isTypeA : Symbol(isTypeA, Decl(typePredicateParameterMismatch.ts, 5, 31)) + + _value: UnionType +>_value : Symbol(_value, Decl(typePredicateParameterMismatch.ts, 7, 17)) +>UnionType : Symbol(UnionType, Decl(typePredicateParameterMismatch.ts, 4, 27)) + +): value is TypeA { // "value" doesn't match parameter "_value" +>TypeA : Symbol(TypeA, Decl(typePredicateParameterMismatch.ts, 0, 0)) + + return true; +} + +function test(input: UnionType): void { +>test : Symbol(test, Decl(typePredicateParameterMismatch.ts, 11, 1)) +>input : Symbol(input, Decl(typePredicateParameterMismatch.ts, 13, 14)) +>UnionType : Symbol(UnionType, Decl(typePredicateParameterMismatch.ts, 4, 27)) + + if (isTypeA(input)) { +>isTypeA : Symbol(isTypeA, Decl(typePredicateParameterMismatch.ts, 5, 31)) +>input : Symbol(input, Decl(typePredicateParameterMismatch.ts, 13, 14)) + + console.log(input.kind); +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>input.kind : Symbol(kind, Decl(typePredicateParameterMismatch.ts, 3, 14), Decl(typePredicateParameterMismatch.ts, 4, 14)) +>input : Symbol(input, Decl(typePredicateParameterMismatch.ts, 13, 14)) +>kind : Symbol(kind, Decl(typePredicateParameterMismatch.ts, 3, 14), Decl(typePredicateParameterMismatch.ts, 4, 14)) + } +} + diff --git a/testdata/baselines/reference/compiler/typePredicateParameterMismatch.types b/testdata/baselines/reference/compiler/typePredicateParameterMismatch.types new file mode 100644 index 0000000000..92c77ed7e9 --- /dev/null +++ b/testdata/baselines/reference/compiler/typePredicateParameterMismatch.types @@ -0,0 +1,48 @@ +//// [tests/cases/compiler/typePredicateParameterMismatch.ts] //// + +=== typePredicateParameterMismatch.ts === +// This test verifies that the checker doesn't panic when a type predicate +// references a parameter name that doesn't match any actual function parameter. + +type TypeA = { kind: 'a' }; +>TypeA : TypeA +>kind : "a" + +type TypeB = { kind: 'b' }; +>TypeB : TypeB +>kind : "b" + +type UnionType = TypeA | TypeB; +>UnionType : UnionType + +function isTypeA( +>isTypeA : (_value: UnionType) => value is TypeA + + _value: UnionType +>_value : UnionType + +): value is TypeA { // "value" doesn't match parameter "_value" + return true; +>true : true +} + +function test(input: UnionType): void { +>test : (input: UnionType) => void +>input : UnionType + + if (isTypeA(input)) { +>isTypeA(input) : boolean +>isTypeA : (_value: UnionType) => value is TypeA +>input : UnionType + + console.log(input.kind); +>console.log(input.kind) : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>input.kind : "a" | "b" +>input : UnionType +>kind : "a" | "b" + } +} + diff --git a/testdata/tests/cases/compiler/assertsPredicateParameterMismatch.ts b/testdata/tests/cases/compiler/assertsPredicateParameterMismatch.ts new file mode 100644 index 0000000000..9491593b96 --- /dev/null +++ b/testdata/tests/cases/compiler/assertsPredicateParameterMismatch.ts @@ -0,0 +1,19 @@ +// @strict: true +// @noemit: true + +// This test verifies that the checker doesn't panic when an assertion predicate +// references a parameter name that doesn't match any actual function parameter. +// This specifically tests the code path in isReachableFlowNodeWorker. + +function assertCondition( + _condition: boolean +): asserts condition { // "condition" doesn't match parameter "_condition" + if (!_condition) { + throw new Error('Condition failed'); + } +} + +function test(): void { + assertCondition(false); + console.log("unreachable"); +} diff --git a/testdata/tests/cases/compiler/typePredicateParameterMismatch.ts b/testdata/tests/cases/compiler/typePredicateParameterMismatch.ts new file mode 100644 index 0000000000..c4729d1995 --- /dev/null +++ b/testdata/tests/cases/compiler/typePredicateParameterMismatch.ts @@ -0,0 +1,21 @@ +// @strict: true +// @noemit: true + +// This test verifies that the checker doesn't panic when a type predicate +// references a parameter name that doesn't match any actual function parameter. + +type TypeA = { kind: 'a' }; +type TypeB = { kind: 'b' }; +type UnionType = TypeA | TypeB; + +function isTypeA( + _value: UnionType +): value is TypeA { // "value" doesn't match parameter "_value" + return true; +} + +function test(input: UnionType): void { + if (isTypeA(input)) { + console.log(input.kind); + } +}