From 0b21aa8d48779bf66acadc444dee12265aaf5f4f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= <mateuszburzynski@gmail.com>
Date: Fri, 5 Jul 2024 09:25:32 +0200
Subject: [PATCH 1/2] Fixed detection of optional chains containing a reference

---
 src/compiler/checker.ts                       |   2 +-
 .../controlFlowOptionalChain4.symbols         | 106 +++++++++++
 .../reference/controlFlowOptionalChain4.types | 171 ++++++++++++++++++
 .../controlFlow/controlFlowOptionalChain4.ts  |  33 ++++
 4 files changed, 311 insertions(+), 1 deletion(-)
 create mode 100644 tests/baselines/reference/controlFlowOptionalChain4.symbols
 create mode 100644 tests/baselines/reference/controlFlowOptionalChain4.types
 create mode 100644 tests/cases/conformance/controlFlow/controlFlowOptionalChain4.ts

diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index 770e1a7c1c60a..58925fb79f726 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -27143,7 +27143,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
     function optionalChainContainsReference(source: Node, target: Node) {
         while (isOptionalChain(source)) {
             source = source.expression;
-            if (isMatchingReference(source, target)) {
+            if (isMatchingReference(target, source)) {
                 return true;
             }
         }
diff --git a/tests/baselines/reference/controlFlowOptionalChain4.symbols b/tests/baselines/reference/controlFlowOptionalChain4.symbols
new file mode 100644
index 0000000000000..7cc47dbf9c4d2
--- /dev/null
+++ b/tests/baselines/reference/controlFlowOptionalChain4.symbols
@@ -0,0 +1,106 @@
+//// [tests/cases/conformance/controlFlow/controlFlowOptionalChain4.ts] ////
+
+=== controlFlowOptionalChain4.ts ===
+// https://github.com/microsoft/TypeScript/issues/56998
+
+type Type = {
+>Type : Symbol(Type, Decl(controlFlowOptionalChain4.ts, 0, 0))
+
+  id: number;
+>id : Symbol(id, Decl(controlFlowOptionalChain4.ts, 2, 13))
+
+};
+
+type InferenceInfo = {
+>InferenceInfo : Symbol(InferenceInfo, Decl(controlFlowOptionalChain4.ts, 4, 2))
+
+  typeParameter: Type;
+>typeParameter : Symbol(typeParameter, Decl(controlFlowOptionalChain4.ts, 6, 22))
+>Type : Symbol(Type, Decl(controlFlowOptionalChain4.ts, 0, 0))
+
+  impliedArity?: number;
+>impliedArity : Symbol(impliedArity, Decl(controlFlowOptionalChain4.ts, 7, 22))
+
+};
+
+declare function getInferenceInfoForType(type: Type): InferenceInfo | undefined;
+>getInferenceInfoForType : Symbol(getInferenceInfoForType, Decl(controlFlowOptionalChain4.ts, 9, 2))
+>type : Symbol(type, Decl(controlFlowOptionalChain4.ts, 11, 41))
+>Type : Symbol(Type, Decl(controlFlowOptionalChain4.ts, 0, 0))
+>InferenceInfo : Symbol(InferenceInfo, Decl(controlFlowOptionalChain4.ts, 4, 2))
+
+function fn1(t1: Type, t2: Type) {
+>fn1 : Symbol(fn1, Decl(controlFlowOptionalChain4.ts, 11, 80))
+>t1 : Symbol(t1, Decl(controlFlowOptionalChain4.ts, 13, 13))
+>Type : Symbol(Type, Decl(controlFlowOptionalChain4.ts, 0, 0))
+>t2 : Symbol(t2, Decl(controlFlowOptionalChain4.ts, 13, 22))
+>Type : Symbol(Type, Decl(controlFlowOptionalChain4.ts, 0, 0))
+
+  let info = getInferenceInfoForType(t1);
+>info : Symbol(info, Decl(controlFlowOptionalChain4.ts, 14, 5))
+>getInferenceInfoForType : Symbol(getInferenceInfoForType, Decl(controlFlowOptionalChain4.ts, 9, 2))
+>t1 : Symbol(t1, Decl(controlFlowOptionalChain4.ts, 13, 13))
+
+  if (info?.impliedArity !== undefined) {
+>info?.impliedArity : Symbol(impliedArity, Decl(controlFlowOptionalChain4.ts, 7, 22))
+>info : Symbol(info, Decl(controlFlowOptionalChain4.ts, 14, 5))
+>impliedArity : Symbol(impliedArity, Decl(controlFlowOptionalChain4.ts, 7, 22))
+>undefined : Symbol(undefined)
+
+    info.impliedArity;
+>info.impliedArity : Symbol(impliedArity, Decl(controlFlowOptionalChain4.ts, 7, 22))
+>info : Symbol(info, Decl(controlFlowOptionalChain4.ts, 14, 5))
+>impliedArity : Symbol(impliedArity, Decl(controlFlowOptionalChain4.ts, 7, 22))
+
+  } else if ((info = getInferenceInfoForType(t2))?.impliedArity !== undefined) {
+>(info = getInferenceInfoForType(t2))?.impliedArity : Symbol(impliedArity, Decl(controlFlowOptionalChain4.ts, 7, 22))
+>info : Symbol(info, Decl(controlFlowOptionalChain4.ts, 14, 5))
+>getInferenceInfoForType : Symbol(getInferenceInfoForType, Decl(controlFlowOptionalChain4.ts, 9, 2))
+>t2 : Symbol(t2, Decl(controlFlowOptionalChain4.ts, 13, 22))
+>impliedArity : Symbol(impliedArity, Decl(controlFlowOptionalChain4.ts, 7, 22))
+>undefined : Symbol(undefined)
+
+    info.impliedArity;
+>info.impliedArity : Symbol(impliedArity, Decl(controlFlowOptionalChain4.ts, 7, 22))
+>info : Symbol(info, Decl(controlFlowOptionalChain4.ts, 14, 5))
+>impliedArity : Symbol(impliedArity, Decl(controlFlowOptionalChain4.ts, 7, 22))
+  }
+}
+
+function fn2(t1: Type, t2: Type) {
+>fn2 : Symbol(fn2, Decl(controlFlowOptionalChain4.ts, 20, 1))
+>t1 : Symbol(t1, Decl(controlFlowOptionalChain4.ts, 22, 13))
+>Type : Symbol(Type, Decl(controlFlowOptionalChain4.ts, 0, 0))
+>t2 : Symbol(t2, Decl(controlFlowOptionalChain4.ts, 22, 22))
+>Type : Symbol(Type, Decl(controlFlowOptionalChain4.ts, 0, 0))
+
+  let info = getInferenceInfoForType(t1);
+>info : Symbol(info, Decl(controlFlowOptionalChain4.ts, 23, 5))
+>getInferenceInfoForType : Symbol(getInferenceInfoForType, Decl(controlFlowOptionalChain4.ts, 9, 2))
+>t1 : Symbol(t1, Decl(controlFlowOptionalChain4.ts, 22, 13))
+
+  if (info?.impliedArity !== undefined) {
+>info?.impliedArity : Symbol(impliedArity, Decl(controlFlowOptionalChain4.ts, 7, 22))
+>info : Symbol(info, Decl(controlFlowOptionalChain4.ts, 23, 5))
+>impliedArity : Symbol(impliedArity, Decl(controlFlowOptionalChain4.ts, 7, 22))
+>undefined : Symbol(undefined)
+
+    info.impliedArity;
+>info.impliedArity : Symbol(impliedArity, Decl(controlFlowOptionalChain4.ts, 7, 22))
+>info : Symbol(info, Decl(controlFlowOptionalChain4.ts, 23, 5))
+>impliedArity : Symbol(impliedArity, Decl(controlFlowOptionalChain4.ts, 7, 22))
+
+  } else if ((info = getInferenceInfoForType(t2))?.impliedArity) {
+>(info = getInferenceInfoForType(t2))?.impliedArity : Symbol(impliedArity, Decl(controlFlowOptionalChain4.ts, 7, 22))
+>info : Symbol(info, Decl(controlFlowOptionalChain4.ts, 23, 5))
+>getInferenceInfoForType : Symbol(getInferenceInfoForType, Decl(controlFlowOptionalChain4.ts, 9, 2))
+>t2 : Symbol(t2, Decl(controlFlowOptionalChain4.ts, 22, 22))
+>impliedArity : Symbol(impliedArity, Decl(controlFlowOptionalChain4.ts, 7, 22))
+
+    info.impliedArity;
+>info.impliedArity : Symbol(impliedArity, Decl(controlFlowOptionalChain4.ts, 7, 22))
+>info : Symbol(info, Decl(controlFlowOptionalChain4.ts, 23, 5))
+>impliedArity : Symbol(impliedArity, Decl(controlFlowOptionalChain4.ts, 7, 22))
+  }
+}
+
diff --git a/tests/baselines/reference/controlFlowOptionalChain4.types b/tests/baselines/reference/controlFlowOptionalChain4.types
new file mode 100644
index 0000000000000..078a7126d609a
--- /dev/null
+++ b/tests/baselines/reference/controlFlowOptionalChain4.types
@@ -0,0 +1,171 @@
+//// [tests/cases/conformance/controlFlow/controlFlowOptionalChain4.ts] ////
+
+=== controlFlowOptionalChain4.ts ===
+// https://github.com/microsoft/TypeScript/issues/56998
+
+type Type = {
+>Type : Type
+>     : ^^^^
+
+  id: number;
+>id : number
+>   : ^^^^^^
+
+};
+
+type InferenceInfo = {
+>InferenceInfo : InferenceInfo
+>              : ^^^^^^^^^^^^^
+
+  typeParameter: Type;
+>typeParameter : Type
+>              : ^^^^
+
+  impliedArity?: number;
+>impliedArity : number | undefined
+>             : ^^^^^^^^^^^^^^^^^^
+
+};
+
+declare function getInferenceInfoForType(type: Type): InferenceInfo | undefined;
+>getInferenceInfoForType : (type: Type) => InferenceInfo | undefined
+>                        : ^    ^^    ^^^^^                         
+>type : Type
+>     : ^^^^
+
+function fn1(t1: Type, t2: Type) {
+>fn1 : (t1: Type, t2: Type) => void
+>    : ^  ^^    ^^  ^^    ^^^^^^^^^
+>t1 : Type
+>   : ^^^^
+>t2 : Type
+>   : ^^^^
+
+  let info = getInferenceInfoForType(t1);
+>info : InferenceInfo | undefined
+>     : ^^^^^^^^^^^^^^^^^^^^^^^^^
+>getInferenceInfoForType(t1) : InferenceInfo | undefined
+>                            : ^^^^^^^^^^^^^^^^^^^^^^^^^
+>getInferenceInfoForType : (type: Type) => InferenceInfo | undefined
+>                        : ^    ^^    ^^^^^                         
+>t1 : Type
+>   : ^^^^
+
+  if (info?.impliedArity !== undefined) {
+>info?.impliedArity !== undefined : boolean
+>                                 : ^^^^^^^
+>info?.impliedArity : number | undefined
+>                   : ^^^^^^^^^^^^^^^^^^
+>info : InferenceInfo | undefined
+>     : ^^^^^^^^^^^^^^^^^^^^^^^^^
+>impliedArity : number | undefined
+>             : ^^^^^^^^^^^^^^^^^^
+>undefined : undefined
+>          : ^^^^^^^^^
+
+    info.impliedArity;
+>info.impliedArity : number
+>                  : ^^^^^^
+>info : InferenceInfo
+>     : ^^^^^^^^^^^^^
+>impliedArity : number
+>             : ^^^^^^
+
+  } else if ((info = getInferenceInfoForType(t2))?.impliedArity !== undefined) {
+>(info = getInferenceInfoForType(t2))?.impliedArity !== undefined : boolean
+>                                                                 : ^^^^^^^
+>(info = getInferenceInfoForType(t2))?.impliedArity : number | undefined
+>                                                   : ^^^^^^^^^^^^^^^^^^
+>(info = getInferenceInfoForType(t2)) : InferenceInfo | undefined
+>                                     : ^^^^^^^^^^^^^^^^^^^^^^^^^
+>info = getInferenceInfoForType(t2) : InferenceInfo | undefined
+>                                   : ^^^^^^^^^^^^^^^^^^^^^^^^^
+>info : InferenceInfo | undefined
+>     : ^^^^^^^^^^^^^^^^^^^^^^^^^
+>getInferenceInfoForType(t2) : InferenceInfo | undefined
+>                            : ^^^^^^^^^^^^^^^^^^^^^^^^^
+>getInferenceInfoForType : (type: Type) => InferenceInfo | undefined
+>                        : ^    ^^    ^^^^^                         
+>t2 : Type
+>   : ^^^^
+>impliedArity : number | undefined
+>             : ^^^^^^^^^^^^^^^^^^
+>undefined : undefined
+>          : ^^^^^^^^^
+
+    info.impliedArity;
+>info.impliedArity : number
+>                  : ^^^^^^
+>info : InferenceInfo
+>     : ^^^^^^^^^^^^^
+>impliedArity : number
+>             : ^^^^^^
+  }
+}
+
+function fn2(t1: Type, t2: Type) {
+>fn2 : (t1: Type, t2: Type) => void
+>    : ^  ^^    ^^  ^^    ^^^^^^^^^
+>t1 : Type
+>   : ^^^^
+>t2 : Type
+>   : ^^^^
+
+  let info = getInferenceInfoForType(t1);
+>info : InferenceInfo | undefined
+>     : ^^^^^^^^^^^^^^^^^^^^^^^^^
+>getInferenceInfoForType(t1) : InferenceInfo | undefined
+>                            : ^^^^^^^^^^^^^^^^^^^^^^^^^
+>getInferenceInfoForType : (type: Type) => InferenceInfo | undefined
+>                        : ^    ^^    ^^^^^                         
+>t1 : Type
+>   : ^^^^
+
+  if (info?.impliedArity !== undefined) {
+>info?.impliedArity !== undefined : boolean
+>                                 : ^^^^^^^
+>info?.impliedArity : number | undefined
+>                   : ^^^^^^^^^^^^^^^^^^
+>info : InferenceInfo | undefined
+>     : ^^^^^^^^^^^^^^^^^^^^^^^^^
+>impliedArity : number | undefined
+>             : ^^^^^^^^^^^^^^^^^^
+>undefined : undefined
+>          : ^^^^^^^^^
+
+    info.impliedArity;
+>info.impliedArity : number
+>                  : ^^^^^^
+>info : InferenceInfo
+>     : ^^^^^^^^^^^^^
+>impliedArity : number
+>             : ^^^^^^
+
+  } else if ((info = getInferenceInfoForType(t2))?.impliedArity) {
+>(info = getInferenceInfoForType(t2))?.impliedArity : number | undefined
+>                                                   : ^^^^^^^^^^^^^^^^^^
+>(info = getInferenceInfoForType(t2)) : InferenceInfo | undefined
+>                                     : ^^^^^^^^^^^^^^^^^^^^^^^^^
+>info = getInferenceInfoForType(t2) : InferenceInfo | undefined
+>                                   : ^^^^^^^^^^^^^^^^^^^^^^^^^
+>info : InferenceInfo | undefined
+>     : ^^^^^^^^^^^^^^^^^^^^^^^^^
+>getInferenceInfoForType(t2) : InferenceInfo | undefined
+>                            : ^^^^^^^^^^^^^^^^^^^^^^^^^
+>getInferenceInfoForType : (type: Type) => InferenceInfo | undefined
+>                        : ^    ^^    ^^^^^                         
+>t2 : Type
+>   : ^^^^
+>impliedArity : number | undefined
+>             : ^^^^^^^^^^^^^^^^^^
+
+    info.impliedArity;
+>info.impliedArity : number
+>                  : ^^^^^^
+>info : InferenceInfo
+>     : ^^^^^^^^^^^^^
+>impliedArity : number
+>             : ^^^^^^
+  }
+}
+
diff --git a/tests/cases/conformance/controlFlow/controlFlowOptionalChain4.ts b/tests/cases/conformance/controlFlow/controlFlowOptionalChain4.ts
new file mode 100644
index 0000000000000..e1742904e8ee1
--- /dev/null
+++ b/tests/cases/conformance/controlFlow/controlFlowOptionalChain4.ts
@@ -0,0 +1,33 @@
+// @strict: true
+// @noEmit: true
+
+// https://github.com/microsoft/TypeScript/issues/56998
+
+type Type = {
+  id: number;
+};
+
+type InferenceInfo = {
+  typeParameter: Type;
+  impliedArity?: number;
+};
+
+declare function getInferenceInfoForType(type: Type): InferenceInfo | undefined;
+
+function fn1(t1: Type, t2: Type) {
+  let info = getInferenceInfoForType(t1);
+  if (info?.impliedArity !== undefined) {
+    info.impliedArity;
+  } else if ((info = getInferenceInfoForType(t2))?.impliedArity !== undefined) {
+    info.impliedArity;
+  }
+}
+
+function fn2(t1: Type, t2: Type) {
+  let info = getInferenceInfoForType(t1);
+  if (info?.impliedArity !== undefined) {
+    info.impliedArity;
+  } else if ((info = getInferenceInfoForType(t2))?.impliedArity) {
+    info.impliedArity;
+  }
+}

From ae4bcced24d23a42ea2c75956136e0d280db92bc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= <mateuszburzynski@gmail.com>
Date: Thu, 26 Dec 2024 13:38:06 +0100
Subject: [PATCH 2/2] add a test case

---
 .../controlFlowOptionalChain4.symbols         | 35 +++++++++++
 .../reference/controlFlowOptionalChain4.types | 58 +++++++++++++++++++
 .../controlFlow/controlFlowOptionalChain4.ts  | 16 +++++
 3 files changed, 109 insertions(+)

diff --git a/tests/baselines/reference/controlFlowOptionalChain4.symbols b/tests/baselines/reference/controlFlowOptionalChain4.symbols
index 7cc47dbf9c4d2..0cfa04823a44a 100644
--- a/tests/baselines/reference/controlFlowOptionalChain4.symbols
+++ b/tests/baselines/reference/controlFlowOptionalChain4.symbols
@@ -104,3 +104,38 @@ function fn2(t1: Type, t2: Type) {
   }
 }
 
+// https://github.com/microsoft/TypeScript/issues/60855
+
+type Option = { type: "Some"; value: number } | { type: "None" };
+>Option : Symbol(Option, Decl(lib.dom.d.ts, --, --), Decl(controlFlowOptionalChain4.ts, 29, 1))
+>type : Symbol(type, Decl(controlFlowOptionalChain4.ts, 33, 15))
+>value : Symbol(value, Decl(controlFlowOptionalChain4.ts, 33, 29))
+>type : Symbol(type, Decl(controlFlowOptionalChain4.ts, 33, 49))
+
+declare function someOptionalOption(): Option | undefined;
+>someOptionalOption : Symbol(someOptionalOption, Decl(controlFlowOptionalChain4.ts, 33, 65))
+>Option : Symbol(Option, Decl(lib.dom.d.ts, --, --), Decl(controlFlowOptionalChain4.ts, 29, 1))
+
+function test60855(): number | undefined {
+>test60855 : Symbol(test60855, Decl(controlFlowOptionalChain4.ts, 35, 58))
+
+  let option: Option | undefined;
+>option : Symbol(option, Decl(controlFlowOptionalChain4.ts, 38, 5))
+>Option : Symbol(Option, Decl(lib.dom.d.ts, --, --), Decl(controlFlowOptionalChain4.ts, 29, 1))
+
+  if ((option = someOptionalOption())?.type === "Some") {
+>(option = someOptionalOption())?.type : Symbol(type, Decl(controlFlowOptionalChain4.ts, 33, 15), Decl(controlFlowOptionalChain4.ts, 33, 49))
+>option : Symbol(option, Decl(controlFlowOptionalChain4.ts, 38, 5))
+>someOptionalOption : Symbol(someOptionalOption, Decl(controlFlowOptionalChain4.ts, 33, 65))
+>type : Symbol(type, Decl(controlFlowOptionalChain4.ts, 33, 15), Decl(controlFlowOptionalChain4.ts, 33, 49))
+
+    return option.value;
+>option.value : Symbol(value, Decl(controlFlowOptionalChain4.ts, 33, 29))
+>option : Symbol(option, Decl(controlFlowOptionalChain4.ts, 38, 5))
+>value : Symbol(value, Decl(controlFlowOptionalChain4.ts, 33, 29))
+  }
+
+  return undefined;
+>undefined : Symbol(undefined)
+}
+
diff --git a/tests/baselines/reference/controlFlowOptionalChain4.types b/tests/baselines/reference/controlFlowOptionalChain4.types
index 078a7126d609a..bda29da9b6907 100644
--- a/tests/baselines/reference/controlFlowOptionalChain4.types
+++ b/tests/baselines/reference/controlFlowOptionalChain4.types
@@ -169,3 +169,61 @@ function fn2(t1: Type, t2: Type) {
   }
 }
 
+// https://github.com/microsoft/TypeScript/issues/60855
+
+type Option = { type: "Some"; value: number } | { type: "None" };
+>Option : Option
+>       : ^^^^^^
+>type : "Some"
+>     : ^^^^^^
+>value : number
+>      : ^^^^^^
+>type : "None"
+>     : ^^^^^^
+
+declare function someOptionalOption(): Option | undefined;
+>someOptionalOption : () => Option | undefined
+>                   : ^^^^^^                  
+
+function test60855(): number | undefined {
+>test60855 : () => number | undefined
+>          : ^^^^^^                  
+
+  let option: Option | undefined;
+>option : Option | undefined
+>       : ^^^^^^^^^^^^^^^^^^
+
+  if ((option = someOptionalOption())?.type === "Some") {
+>(option = someOptionalOption())?.type === "Some" : boolean
+>                                                 : ^^^^^^^
+>(option = someOptionalOption())?.type : "Some" | "None" | undefined
+>                                      : ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+>(option = someOptionalOption()) : Option | undefined
+>                                : ^^^^^^^^^^^^^^^^^^
+>option = someOptionalOption() : Option | undefined
+>                              : ^^^^^^^^^^^^^^^^^^
+>option : Option | undefined
+>       : ^^^^^^^^^^^^^^^^^^
+>someOptionalOption() : Option | undefined
+>                     : ^^^^^^^^^^^^^^^^^^
+>someOptionalOption : () => Option | undefined
+>                   : ^^^^^^                  
+>type : "Some" | "None" | undefined
+>     : ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+>"Some" : "Some"
+>       : ^^^^^^
+
+    return option.value;
+>option.value : number
+>             : ^^^^^^
+>option : { type: "Some"; value: number; }
+>       : ^^^^^^^^      ^^^^^^^^^      ^^^
+>value : number
+>      : ^^^^^^
+  }
+
+  return undefined;
+>undefined : undefined
+>          : ^^^^^^^^^
+}
+
diff --git a/tests/cases/conformance/controlFlow/controlFlowOptionalChain4.ts b/tests/cases/conformance/controlFlow/controlFlowOptionalChain4.ts
index e1742904e8ee1..612beafc9d164 100644
--- a/tests/cases/conformance/controlFlow/controlFlowOptionalChain4.ts
+++ b/tests/cases/conformance/controlFlow/controlFlowOptionalChain4.ts
@@ -31,3 +31,19 @@ function fn2(t1: Type, t2: Type) {
     info.impliedArity;
   }
 }
+
+// https://github.com/microsoft/TypeScript/issues/60855
+
+type Option = { type: "Some"; value: number } | { type: "None" };
+
+declare function someOptionalOption(): Option | undefined;
+
+function test60855(): number | undefined {
+  let option: Option | undefined;
+
+  if ((option = someOptionalOption())?.type === "Some") {
+    return option.value;
+  }
+
+  return undefined;
+}