[sound flow analysis] Language tests.
The tests that don't end in `_disabled_test.dart` exercise all the new
behaviors introduced with the `sound-flow-analysis` feature. The tests
that _do_ end in `_disabled_test.dart` verify that when the language
version is 3.8, the old behavior of flow analysis is preserved.
For the sake of thoroughness, I've also elected to include tests for a
few behaviors that aren't new, just because they seem logically
related to the new behaviors (for example, in the tests for the `==`
and `!=` operator, I've included a test that `null == null` is known
to evaluate to `true`, even though this behavior existed even before
the `sound-flow-analysis` feature was implemented). These tests are
marked with the comment "(this behavior predates sound-flow-analysis)"
in the `_disabled_test.dart` files.
Bug: https://github.com/dart-lang/sdk/issues/60438
Change-Id: If2895355bb1a2eeb2415a5a1012d73ae87c244dd
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/423040
Reviewed-by: Erik Ernst <eernst@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
diff --git a/tests/language/sound_flow_analysis/equality_comparisons_disabled_test.dart b/tests/language/sound_flow_analysis/equality_comparisons_disabled_test.dart
new file mode 100644
index 0000000..6d9083f
--- /dev/null
+++ b/tests/language/sound_flow_analysis/equality_comparisons_disabled_test.dart
@@ -0,0 +1,631 @@
+// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Exercises flow analysis of equality comparisons (binary expressions using
+// `==` or `!=`) when `sound-flow-analysis` is disabled.
+
+// @dart = 3.8
+
+import '../static_type_helper.dart';
+
+// `<nonNullable> == <Null>` is not known to evaluate to `false`.
+testNonNullableEqualsNull({
+ required int intValue,
+ required int Function() intFunction,
+ required Null nullValue,
+ required Null Function() nullFunction,
+}) {
+ {
+ // <var> == null
+ int? shouldBeDemoted1 = 0;
+ int? shouldBeDemoted2 = 0;
+ if (intValue == null) {
+ shouldBeDemoted1 = null;
+ } else {
+ shouldBeDemoted2 = null;
+ }
+ shouldBeDemoted1.expectStaticType<Exactly<int?>>();
+ shouldBeDemoted2.expectStaticType<Exactly<int?>>();
+ }
+
+ {
+ // <var> == <var>
+ int? shouldBeDemoted1 = 0;
+ int? shouldBeDemoted2 = 0;
+ if (intValue == nullValue) {
+ shouldBeDemoted1 = null;
+ } else {
+ shouldBeDemoted2 = null;
+ }
+ shouldBeDemoted1.expectStaticType<Exactly<int?>>();
+ shouldBeDemoted2.expectStaticType<Exactly<int?>>();
+ }
+
+ {
+ // <var> == <expr>
+ int? shouldBeDemoted1 = 0;
+ int? shouldBeDemoted2 = 0;
+ if (intValue == nullFunction()) {
+ shouldBeDemoted1 = null;
+ } else {
+ shouldBeDemoted2 = null;
+ }
+ shouldBeDemoted1.expectStaticType<Exactly<int?>>();
+ shouldBeDemoted2.expectStaticType<Exactly<int?>>();
+ }
+
+ {
+ // <expr> == null
+ int? shouldBeDemoted1 = 0;
+ int? shouldBeDemoted2 = 0;
+ if (intFunction() == null) {
+ shouldBeDemoted1 = null;
+ } else {
+ shouldBeDemoted2 = null;
+ }
+ shouldBeDemoted1.expectStaticType<Exactly<int?>>();
+ shouldBeDemoted2.expectStaticType<Exactly<int?>>();
+ }
+
+ {
+ // <expr> == <var>
+ int? shouldBeDemoted1 = 0;
+ int? shouldBeDemoted2 = 0;
+ if (intFunction() == nullValue) {
+ shouldBeDemoted1 = null;
+ } else {
+ shouldBeDemoted2 = null;
+ }
+ shouldBeDemoted1.expectStaticType<Exactly<int?>>();
+ shouldBeDemoted2.expectStaticType<Exactly<int?>>();
+ }
+
+ {
+ // <expr> == <expr>
+ int? shouldBeDemoted1 = 0;
+ int? shouldBeDemoted2 = 0;
+ if (intFunction() == nullFunction()) {
+ shouldBeDemoted1 = null;
+ } else {
+ shouldBeDemoted2 = null;
+ }
+ shouldBeDemoted1.expectStaticType<Exactly<int?>>();
+ shouldBeDemoted2.expectStaticType<Exactly<int?>>();
+ }
+}
+
+// `<Null> == <nonNullable>` is not known to evaluate to `false`.
+testNullEqualsNonNullable({
+ required int intValue,
+ required int Function() intFunction,
+ required Null nullValue,
+ required Null Function() nullFunction,
+}) {
+ {
+ // null == <var>
+ int? shouldBeDemoted1 = 0;
+ int? shouldBeDemoted2 = 0;
+ if (null == intValue) {
+ shouldBeDemoted1 = null;
+ } else {
+ shouldBeDemoted2 = null;
+ }
+ shouldBeDemoted1.expectStaticType<Exactly<int?>>();
+ shouldBeDemoted2.expectStaticType<Exactly<int?>>();
+ }
+
+ {
+ // <var> == <var>
+ int? shouldBeDemoted1 = 0;
+ int? shouldBeDemoted2 = 0;
+ if (nullValue == intValue) {
+ shouldBeDemoted1 = null;
+ } else {
+ shouldBeDemoted2 = null;
+ }
+ shouldBeDemoted1.expectStaticType<Exactly<int?>>();
+ shouldBeDemoted2.expectStaticType<Exactly<int?>>();
+ }
+
+ {
+ // <expr> == <var>
+ int? shouldBeDemoted1 = 0;
+ int? shouldBeDemoted2 = 0;
+ if (nullFunction() == intValue) {
+ shouldBeDemoted1 = null;
+ } else {
+ shouldBeDemoted2 = null;
+ }
+ shouldBeDemoted1.expectStaticType<Exactly<int?>>();
+ shouldBeDemoted2.expectStaticType<Exactly<int?>>();
+ }
+
+ {
+ // null == <expr>
+ int? shouldBeDemoted1 = 0;
+ int? shouldBeDemoted2 = 0;
+ if (null == intFunction()) {
+ shouldBeDemoted1 = null;
+ } else {
+ shouldBeDemoted2 = null;
+ }
+ shouldBeDemoted1.expectStaticType<Exactly<int?>>();
+ shouldBeDemoted2.expectStaticType<Exactly<int?>>();
+ }
+
+ {
+ // <var> == <expr>
+ int? shouldBeDemoted1 = 0;
+ int? shouldBeDemoted2 = 0;
+ if (nullValue == intFunction()) {
+ shouldBeDemoted1 = null;
+ } else {
+ shouldBeDemoted2 = null;
+ }
+ shouldBeDemoted1.expectStaticType<Exactly<int?>>();
+ shouldBeDemoted2.expectStaticType<Exactly<int?>>();
+ }
+
+ {
+ // <expr> == <expr>
+ int? shouldBeDemoted1 = 0;
+ int? shouldBeDemoted2 = 0;
+ if (nullFunction() == intFunction()) {
+ shouldBeDemoted1 = null;
+ } else {
+ shouldBeDemoted2 = null;
+ }
+ shouldBeDemoted1.expectStaticType<Exactly<int?>>();
+ shouldBeDemoted2.expectStaticType<Exactly<int?>>();
+ }
+}
+
+// `<nonNullable> != <Null>` is not known to evaluate to `true`.
+testNonNullableNotEqualsNull({
+ required int intValue,
+ required int Function() intFunction,
+ required Null nullValue,
+ required Null Function() nullFunction,
+}) {
+ {
+ // <var> != null
+ int? shouldBeDemoted1 = 0;
+ int? shouldBeDemoted2 = 0;
+ if (intValue != null) {
+ shouldBeDemoted1 = null;
+ } else {
+ shouldBeDemoted2 = null;
+ }
+ shouldBeDemoted1.expectStaticType<Exactly<int?>>();
+ shouldBeDemoted2.expectStaticType<Exactly<int?>>();
+ }
+
+ {
+ // <var> != <var>
+ int? shouldBeDemoted1 = 0;
+ int? shouldBeDemoted2 = 0;
+ if (intValue != nullValue) {
+ shouldBeDemoted1 = null;
+ } else {
+ shouldBeDemoted2 = null;
+ }
+ shouldBeDemoted1.expectStaticType<Exactly<int?>>();
+ shouldBeDemoted2.expectStaticType<Exactly<int?>>();
+ }
+
+ {
+ // <var> != <expr>
+ int? shouldBeDemoted1 = 0;
+ int? shouldBeDemoted2 = 0;
+ if (intValue != nullFunction()) {
+ shouldBeDemoted1 = null;
+ } else {
+ shouldBeDemoted2 = null;
+ }
+ shouldBeDemoted1.expectStaticType<Exactly<int?>>();
+ shouldBeDemoted2.expectStaticType<Exactly<int?>>();
+ }
+
+ {
+ // <expr> != null
+ int? shouldBeDemoted1 = 0;
+ int? shouldBeDemoted2 = 0;
+ if (intFunction() != null) {
+ shouldBeDemoted1 = null;
+ } else {
+ shouldBeDemoted2 = null;
+ }
+ shouldBeDemoted1.expectStaticType<Exactly<int?>>();
+ shouldBeDemoted2.expectStaticType<Exactly<int?>>();
+ }
+
+ {
+ // <expr> != <var>
+ int? shouldBeDemoted1 = 0;
+ int? shouldBeDemoted2 = 0;
+ if (intFunction() != nullValue) {
+ shouldBeDemoted1 = null;
+ } else {
+ shouldBeDemoted2 = null;
+ }
+ shouldBeDemoted1.expectStaticType<Exactly<int?>>();
+ shouldBeDemoted2.expectStaticType<Exactly<int?>>();
+ }
+
+ {
+ // <expr> != <expr>
+ int? shouldBeDemoted1 = 0;
+ int? shouldBeDemoted2 = 0;
+ if (intFunction() != nullFunction()) {
+ shouldBeDemoted1 = null;
+ } else {
+ shouldBeDemoted2 = null;
+ }
+ shouldBeDemoted1.expectStaticType<Exactly<int?>>();
+ shouldBeDemoted2.expectStaticType<Exactly<int?>>();
+ }
+}
+
+// `<Null> != <nonNullable>` is not known to evaluate to `true`.
+testNullNotEqualsNonNullable({
+ required int intValue,
+ required int Function() intFunction,
+ required Null nullValue,
+ required Null Function() nullFunction,
+}) {
+ {
+ // null != <var>
+ int? shouldBeDemoted1 = 0;
+ int? shouldBeDemoted2 = 0;
+ if (null != intValue) {
+ shouldBeDemoted1 = null;
+ } else {
+ shouldBeDemoted2 = null;
+ }
+ shouldBeDemoted1.expectStaticType<Exactly<int?>>();
+ shouldBeDemoted2.expectStaticType<Exactly<int?>>();
+ }
+
+ {
+ // <var> != <var>
+ int? shouldBeDemoted1 = 0;
+ int? shouldBeDemoted2 = 0;
+ if (nullValue != intValue) {
+ shouldBeDemoted1 = null;
+ } else {
+ shouldBeDemoted2 = null;
+ }
+ shouldBeDemoted1.expectStaticType<Exactly<int?>>();
+ shouldBeDemoted2.expectStaticType<Exactly<int?>>();
+ }
+
+ {
+ // <expr> != <var>
+ int? shouldBeDemoted1 = 0;
+ int? shouldBeDemoted2 = 0;
+ if (nullFunction() != intValue) {
+ shouldBeDemoted1 = null;
+ } else {
+ shouldBeDemoted2 = null;
+ }
+ shouldBeDemoted1.expectStaticType<Exactly<int?>>();
+ shouldBeDemoted2.expectStaticType<Exactly<int?>>();
+ }
+
+ {
+ // null != <expr>
+ int? shouldBeDemoted1 = 0;
+ int? shouldBeDemoted2 = 0;
+ if (null != intFunction()) {
+ shouldBeDemoted1 = null;
+ } else {
+ shouldBeDemoted2 = null;
+ }
+ shouldBeDemoted1.expectStaticType<Exactly<int?>>();
+ shouldBeDemoted2.expectStaticType<Exactly<int?>>();
+ }
+
+ {
+ // <var> != <expr>
+ int? shouldBeDemoted1 = 0;
+ int? shouldBeDemoted2 = 0;
+ if (nullValue != intFunction()) {
+ shouldBeDemoted1 = null;
+ } else {
+ shouldBeDemoted2 = null;
+ }
+ shouldBeDemoted1.expectStaticType<Exactly<int?>>();
+ shouldBeDemoted2.expectStaticType<Exactly<int?>>();
+ }
+
+ {
+ // <expr> != <expr>
+ int? shouldBeDemoted1 = 0;
+ int? shouldBeDemoted2 = 0;
+ if (nullFunction() != intFunction()) {
+ shouldBeDemoted1 = null;
+ } else {
+ shouldBeDemoted2 = null;
+ }
+ shouldBeDemoted1.expectStaticType<Exactly<int?>>();
+ shouldBeDemoted2.expectStaticType<Exactly<int?>>();
+ }
+}
+
+// `<Null> == <Null>` is known to evaluate to `true`.
+// (this behavior predates sound-flow-analysis)
+testNullEqualsNull({
+ required Null nullValue,
+ required Null Function() nullFunction,
+}) {
+ {
+ // null == null
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (null == null) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // null == <var>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (null == nullValue) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // null == <expr>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (null == nullFunction()) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <var> == null
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullValue == null) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <var> == <var>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullValue == nullValue) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <var> == <expr>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullValue == nullFunction()) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <expr> == null
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullFunction() == null) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <expr> == <var>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullFunction() == nullValue) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <expr> == <expr>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullFunction() == nullFunction()) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+}
+
+// `<Null> != <Null>` is known to evaluate to `false`.
+// (this behavior predates sound-flow-analysis)
+testNullNotEqualsNull({
+ required Null nullValue,
+ required Null Function() nullFunction,
+}) {
+ {
+ // null != null
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (null != null) {
+ shouldNotBeDemoted = null; // Unreachable
+ } else {
+ shouldBePromoted = 0; // Reachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // null != <var>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (null != nullValue) {
+ shouldNotBeDemoted = null; // Unreachable
+ } else {
+ shouldBePromoted = 0; // Reachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // null != <expr>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (null != nullFunction()) {
+ shouldNotBeDemoted = null; // Unreachable
+ } else {
+ shouldBePromoted = 0; // Reachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <var> != null
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullValue != null) {
+ shouldNotBeDemoted = null; // Unreachable
+ } else {
+ shouldBePromoted = 0; // Reachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <var> != <var>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullValue != nullValue) {
+ shouldNotBeDemoted = null; // Unreachable
+ } else {
+ shouldBePromoted = 0; // Reachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <var> != <expr>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullValue != nullFunction()) {
+ shouldNotBeDemoted = null; // Unreachable
+ } else {
+ shouldBePromoted = 0; // Reachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <expr> != null
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullFunction() != null) {
+ shouldNotBeDemoted = null; // Unreachable
+ } else {
+ shouldBePromoted = 0; // Reachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <expr> != <var>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullFunction() != nullValue) {
+ shouldNotBeDemoted = null; // Unreachable
+ } else {
+ shouldBePromoted = 0; // Reachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <expr> != <expr>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullFunction() != nullFunction()) {
+ shouldNotBeDemoted = null; // Unreachable
+ } else {
+ shouldBePromoted = 0; // Reachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+}
+
+main() {
+ testNonNullableEqualsNull(
+ intValue: 0,
+ intFunction: () => 0,
+ nullValue: null,
+ nullFunction: () => null,
+ );
+ testNullEqualsNonNullable(
+ intValue: 0,
+ intFunction: () => 0,
+ nullValue: null,
+ nullFunction: () => null,
+ );
+ testNonNullableNotEqualsNull(
+ intValue: 0,
+ intFunction: () => 0,
+ nullValue: null,
+ nullFunction: () => null,
+ );
+ testNullNotEqualsNonNullable(
+ intValue: 0,
+ intFunction: () => 0,
+ nullValue: null,
+ nullFunction: () => null,
+ );
+ testNullEqualsNull(nullValue: null, nullFunction: () => null);
+ testNullNotEqualsNull(nullValue: null, nullFunction: () => null);
+}
diff --git a/tests/language/sound_flow_analysis/equality_comparisons_test.dart b/tests/language/sound_flow_analysis/equality_comparisons_test.dart
new file mode 100644
index 0000000..3734dc5
--- /dev/null
+++ b/tests/language/sound_flow_analysis/equality_comparisons_test.dart
@@ -0,0 +1,629 @@
+// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Exercises flow analysis of equality comparisons (binary expressions using
+// `==` or `!=`) when `sound-flow-analysis` is enabled.
+
+// SharedOptions=--enable-experiment=sound-flow-analysis
+
+import '../static_type_helper.dart';
+
+// `<nonNullable> == <Null>` is known to evaluate to `false`.
+testNonNullableEqualsNull({
+ required int intValue,
+ required int Function() intFunction,
+ required Null nullValue,
+ required Null Function() nullFunction,
+}) {
+ {
+ // <var> == null
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (intValue == null) {
+ shouldNotBeDemoted = null; // Unreachable
+ } else {
+ shouldBePromoted = 0; // Reachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <var> == <var>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (intValue == nullValue) {
+ shouldNotBeDemoted = null; // Unreachable
+ } else {
+ shouldBePromoted = 0; // Reachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <var> == <expr>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (intValue == nullFunction()) {
+ shouldNotBeDemoted = null; // Unreachable
+ } else {
+ shouldBePromoted = 0; // Reachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <expr> == null
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (intFunction() == null) {
+ shouldNotBeDemoted = null; // Unreachable
+ } else {
+ shouldBePromoted = 0; // Reachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <expr> == <var>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (intFunction() == nullValue) {
+ shouldNotBeDemoted = null; // Unreachable
+ } else {
+ shouldBePromoted = 0; // Reachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <expr> == <expr>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (intFunction() == nullFunction()) {
+ shouldNotBeDemoted = null; // Unreachable
+ } else {
+ shouldBePromoted = 0; // Reachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+}
+
+// `<Null> == <nonNullable>` is known to evaluate to `false`.
+testNullEqualsNonNullable({
+ required int intValue,
+ required int Function() intFunction,
+ required Null nullValue,
+ required Null Function() nullFunction,
+}) {
+ {
+ // null == <var>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (null == intValue) {
+ shouldNotBeDemoted = null; // Unreachable
+ } else {
+ shouldBePromoted = 0; // Reachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <var> == <var>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullValue == intValue) {
+ shouldNotBeDemoted = null; // Unreachable
+ } else {
+ shouldBePromoted = 0; // Reachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <expr> == <var>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullFunction() == intValue) {
+ shouldNotBeDemoted = null; // Unreachable
+ } else {
+ shouldBePromoted = 0; // Reachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // null == <expr>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (null == intFunction()) {
+ shouldNotBeDemoted = null; // Unreachable
+ } else {
+ shouldBePromoted = 0; // Reachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <var> == <expr>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullValue == intFunction()) {
+ shouldNotBeDemoted = null; // Unreachable
+ } else {
+ shouldBePromoted = 0; // Reachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <expr> == <expr>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullFunction() == intFunction()) {
+ shouldNotBeDemoted = null; // Unreachable
+ } else {
+ shouldBePromoted = 0; // Reachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+}
+
+// `<nonNullable> != <Null>` is known to evaluate to `true`.
+testNonNullableNotEqualsNull({
+ required int intValue,
+ required int Function() intFunction,
+ required Null nullValue,
+ required Null Function() nullFunction,
+}) {
+ {
+ // <var> != null
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (intValue != null) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <var> != <var>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (intValue != nullValue) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <var> != <expr>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (intValue != nullFunction()) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <expr> != null
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (intFunction() != null) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <expr> != <var>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (intFunction() != nullValue) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <expr> != <expr>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (intFunction() != nullFunction()) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+}
+
+// `<Null> != <nonNullable>` is known to evaluate to `true`.
+testNullNotEqualsNonNullable({
+ required int intValue,
+ required int Function() intFunction,
+ required Null nullValue,
+ required Null Function() nullFunction,
+}) {
+ {
+ // null != <var>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (null != intValue) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <var> != <var>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullValue != intValue) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <expr> != <var>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullFunction() != intValue) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // null != <expr>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (null != intFunction()) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <var> != <expr>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullValue != intFunction()) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <expr> != <expr>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullFunction() != intFunction()) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+}
+
+// `<Null> == <Null>` is known to evaluate to `true`.
+testNullEqualsNull({
+ required Null nullValue,
+ required Null Function() nullFunction,
+}) {
+ {
+ // null == null
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (null == null) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // null == <var>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (null == nullValue) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // null == <expr>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (null == nullFunction()) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <var> == null
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullValue == null) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <var> == <var>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullValue == nullValue) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <var> == <expr>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullValue == nullFunction()) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <expr> == null
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullFunction() == null) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <expr> == <var>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullFunction() == nullValue) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <expr> == <expr>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullFunction() == nullFunction()) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+}
+
+// `<Null> != <Null>` is known to evaluate to `false`.
+testNullNotEqualsNull({
+ required Null nullValue,
+ required Null Function() nullFunction,
+}) {
+ {
+ // null != null
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (null != null) {
+ shouldNotBeDemoted = null; // Unreachable
+ } else {
+ shouldBePromoted = 0; // Reachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // null != <var>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (null != nullValue) {
+ shouldNotBeDemoted = null; // Unreachable
+ } else {
+ shouldBePromoted = 0; // Reachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // null != <expr>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (null != nullFunction()) {
+ shouldNotBeDemoted = null; // Unreachable
+ } else {
+ shouldBePromoted = 0; // Reachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <var> != null
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullValue != null) {
+ shouldNotBeDemoted = null; // Unreachable
+ } else {
+ shouldBePromoted = 0; // Reachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <var> != <var>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullValue != nullValue) {
+ shouldNotBeDemoted = null; // Unreachable
+ } else {
+ shouldBePromoted = 0; // Reachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <var> != <expr>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullValue != nullFunction()) {
+ shouldNotBeDemoted = null; // Unreachable
+ } else {
+ shouldBePromoted = 0; // Reachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <expr> != null
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullFunction() != null) {
+ shouldNotBeDemoted = null; // Unreachable
+ } else {
+ shouldBePromoted = 0; // Reachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <expr> != <var>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullFunction() != nullValue) {
+ shouldNotBeDemoted = null; // Unreachable
+ } else {
+ shouldBePromoted = 0; // Reachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <expr> != <expr>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullFunction() != nullFunction()) {
+ shouldNotBeDemoted = null; // Unreachable
+ } else {
+ shouldBePromoted = 0; // Reachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+}
+
+main() {
+ testNonNullableEqualsNull(
+ intValue: 0,
+ intFunction: () => 0,
+ nullValue: null,
+ nullFunction: () => null,
+ );
+ testNullEqualsNonNullable(
+ intValue: 0,
+ intFunction: () => 0,
+ nullValue: null,
+ nullFunction: () => null,
+ );
+ testNonNullableNotEqualsNull(
+ intValue: 0,
+ intFunction: () => 0,
+ nullValue: null,
+ nullFunction: () => null,
+ );
+ testNullNotEqualsNonNullable(
+ intValue: 0,
+ intFunction: () => 0,
+ nullValue: null,
+ nullFunction: () => null,
+ );
+ testNullEqualsNull(nullValue: null, nullFunction: () => null);
+ testNullNotEqualsNull(nullValue: null, nullFunction: () => null);
+}
diff --git a/tests/language/sound_flow_analysis/if_null_disabled_test.dart b/tests/language/sound_flow_analysis/if_null_disabled_test.dart
new file mode 100644
index 0000000..bcb9f73
--- /dev/null
+++ b/tests/language/sound_flow_analysis/if_null_disabled_test.dart
@@ -0,0 +1,66 @@
+// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Exercises flow analysis of if-null expressions (expressions involving `??`)
+// when `sound-flow-analysis` is disabled.
+
+// @dart = 3.8
+
+// ignore_for_file: dead_null_aware_expression
+
+import '../static_type_helper.dart';
+
+// `<nonNullable> ?? <expr>` is not known to skip <expr>.
+testIfNull({required int intValue, required int Function() intFunction}) {
+ {
+ // <var> ?? <expr>
+ int? shouldBeDemoted = 0;
+ intValue ?? (shouldBeDemoted = null, 0).$2;
+ shouldBeDemoted.expectStaticType<Exactly<int?>>();
+ }
+
+ {
+ // <expr> ?? <expr>
+ int? shouldBeDemoted = 0;
+ intFunction() ?? (shouldBeDemoted = null, 0).$2;
+ shouldBeDemoted.expectStaticType<Exactly<int?>>();
+ }
+}
+
+// `<nonNullable> ??= <expr>` is not known to skip <expr>.
+testIfNullAssign({required int intValue, required List<int> listOfIntValue}) {
+ {
+ // <var> ??= <expr>
+ int? shouldBeDemoted = 0;
+ intValue ??= (shouldBeDemoted = null, 0).$2;
+ shouldBeDemoted.expectStaticType<Exactly<int?>>();
+ }
+
+ {
+ // <promotedVar> ??= <expr>
+ int? nullableIntValue = 0; // promote to `int`
+ int? shouldBeDemoted = 0;
+ nullableIntValue ??= (shouldBeDemoted = null, 0).$2;
+ shouldBeDemoted.expectStaticType<Exactly<int?>>();
+ }
+
+ {
+ // <property> ??= <expr>
+ int? shouldBeDemoted = 0;
+ listOfIntValue.first ??= (shouldBeDemoted = null, 0).$2;
+ shouldBeDemoted.expectStaticType<Exactly<int?>>();
+ }
+
+ {
+ // <indexOperation> ??= <expr>
+ int? shouldBeDemoted = 0;
+ listOfIntValue[0] ??= (shouldBeDemoted = null, 0).$2;
+ shouldBeDemoted.expectStaticType<Exactly<int?>>();
+ }
+}
+
+main() {
+ testIfNull(intValue: 0, intFunction: () => 0);
+ testIfNullAssign(intValue: 0, listOfIntValue: [0]);
+}
diff --git a/tests/language/sound_flow_analysis/if_null_test.dart b/tests/language/sound_flow_analysis/if_null_test.dart
new file mode 100644
index 0000000..1534055
--- /dev/null
+++ b/tests/language/sound_flow_analysis/if_null_test.dart
@@ -0,0 +1,66 @@
+// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Exercises flow analysis of if-null expressions (expressions involving `??`)
+// when `sound-flow-analysis` is enabled.
+
+// SharedOptions=--enable-experiment=sound-flow-analysis
+
+// ignore_for_file: dead_null_aware_expression
+
+import '../static_type_helper.dart';
+
+// `<nonNullable> ?? <expr>` is known to skip <expr>.
+testIfNull({required int intValue, required int Function() intFunction}) {
+ {
+ // <var> ?? <expr>
+ int? shouldNotBeDemoted = 0;
+ intValue ?? (shouldNotBeDemoted = null, 0).$2;
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <expr> ?? <expr>
+ int? shouldNotBeDemoted = 0;
+ intFunction() ?? (shouldNotBeDemoted = null, 0).$2;
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ }
+}
+
+// `<nonNullable> ??= <expr>` is known to skip <expr>.
+testIfNullAssign({required int intValue, required List<int> listOfIntValue}) {
+ {
+ // <var> ??= <expr>
+ int? shouldNotBeDemoted = 0;
+ intValue ??= (shouldNotBeDemoted = null, 0).$2;
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <promotedVar> ??= <expr>
+ int? nullableIntValue = 0; // promote to `int`
+ int? shouldNotBeDemoted = 0;
+ nullableIntValue ??= (shouldNotBeDemoted = null, 0).$2;
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <property> ??= <expr>
+ int? shouldNotBeDemoted = 0;
+ listOfIntValue.first ??= (shouldNotBeDemoted = null, 0).$2;
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <indexOperation> ??= <expr>
+ int? shouldNotBeDemoted = 0;
+ listOfIntValue[0] ??= (shouldNotBeDemoted = null, 0).$2;
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ }
+}
+
+main() {
+ testIfNull(intValue: 0, intFunction: () => 0);
+ testIfNullAssign(intValue: 0, listOfIntValue: [0]);
+}
diff --git a/tests/language/sound_flow_analysis/null_aware_access_disabled_test.dart b/tests/language/sound_flow_analysis/null_aware_access_disabled_test.dart
new file mode 100644
index 0000000..365dbb9
--- /dev/null
+++ b/tests/language/sound_flow_analysis/null_aware_access_disabled_test.dart
@@ -0,0 +1,174 @@
+// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Exercises flow analysis of null aware accesses (expressions involving `?.`)
+// when `sound-flow-analysis` is disabled.
+
+// @dart = 3.8
+
+// ignore_for_file: invalid_null_aware_operator
+
+import '../static_type_helper.dart';
+
+// `<nonNullable>?.bitLength.gcd(<expr>)` is not known to invoke <expr>.
+testProperty({required int intValue, required int Function() intFunction}) {
+ {
+ // <var>?.bitLength.gcd(<expr>)
+ int? shouldNotBePromoted;
+ intValue?.bitLength.gcd(shouldNotBePromoted = 0);
+ shouldNotBePromoted.expectStaticType<Exactly<int?>>();
+ }
+
+ {
+ // <expr>?.bitLength.gcd(<expr>)
+ int? shouldNotBePromoted;
+ intFunction()?.bitLength.gcd(shouldNotBePromoted = 0);
+ shouldNotBePromoted.expectStaticType<Exactly<int?>>();
+ }
+}
+
+// `<nonNullable>?.gcd(<expr>)` is not known to invoke <expr>.
+testCall({required int intValue, required int Function() intFunction}) {
+ {
+ // <var>?.gcd(<expr>)
+ int? shouldNotBePromoted;
+ intValue?.gcd(shouldNotBePromoted = 0);
+ shouldNotBePromoted.expectStaticType<Exactly<int?>>();
+ }
+
+ {
+ // <expr>?.gcd(<expr>)
+ int? shouldNotBePromoted;
+ intFunction()?.gcd(shouldNotBePromoted = 0);
+ shouldNotBePromoted.expectStaticType<Exactly<int?>>();
+ }
+}
+
+// `<nonNullable>?[<expr>]` is not known to invoke <expr>.
+testIndexGet({
+ required List<int> listOfIntValue,
+ required List<int> Function() listOfIntFunction,
+}) {
+ {
+ // <var>?[<expr>]
+ int? shouldNotBePromoted;
+ listOfIntValue?[shouldNotBePromoted = 0];
+ shouldNotBePromoted.expectStaticType<Exactly<int?>>();
+ }
+
+ {
+ // <expr>?[<expr>]
+ int? shouldNotBePromoted;
+ listOfIntFunction()?[shouldNotBePromoted = 0];
+ shouldNotBePromoted.expectStaticType<Exactly<int?>>();
+ }
+}
+
+// `<nonNullable>?[<expr>] = 0` is not known to invoke <expr>.
+testIndexSet({
+ required List<int> listOfIntValue,
+ required List<int> Function() listOfIntFunction,
+}) {
+ {
+ // <var>?[<expr>] = 0
+ int? shouldNotBePromoted;
+ listOfIntValue?[shouldNotBePromoted = 0] = 0;
+ shouldNotBePromoted.expectStaticType<Exactly<int?>>();
+ }
+
+ {
+ // <expr>?[<expr>] = 0
+ int? shouldNotBePromoted;
+ listOfIntFunction()?[shouldNotBePromoted = 0] = 0;
+ shouldNotBePromoted.expectStaticType<Exactly<int?>>();
+ }
+}
+
+// `<nonNullable>?..bitLength.gcd(<expr>)` is not known to invoke <expr>.
+testCascadedProperty({
+ required int intValue,
+ required int Function() intFunction,
+}) {
+ {
+ // <var>?..bitLength.gcd(<expr>)
+ int? shouldNotBePromoted;
+ intValue?..bitLength.gcd(shouldNotBePromoted = 0);
+ shouldNotBePromoted.expectStaticType<Exactly<int?>>();
+ }
+
+ {
+ // <expr>?..bitLength.gcd(<expr>)
+ int? shouldNotBePromoted;
+ intFunction()?..bitLength.gcd(shouldNotBePromoted = 0);
+ shouldNotBePromoted.expectStaticType<Exactly<int?>>();
+ }
+}
+
+// `<nonNullable>?..gcd(<expr>)` is not known to invoke <expr>.
+testCascadedCall({required int intValue, required int Function() intFunction}) {
+ {
+ // <var>?..gcd(<expr>)
+ int? shouldNotBePromoted;
+ intValue?..gcd(shouldNotBePromoted = 0);
+ shouldNotBePromoted.expectStaticType<Exactly<int?>>();
+ }
+
+ {
+ // <expr>?..gcd(<expr>)
+ int? shouldNotBePromoted;
+ intFunction()?..gcd(shouldNotBePromoted = 0);
+ shouldNotBePromoted.expectStaticType<Exactly<int?>>();
+ }
+}
+
+// `<nonNullable>?..[<expr>]` is not known to invoke <expr>.
+testCascadedIndexGet({
+ required List<int> listOfIntValue,
+ required List<int> Function() listOfIntFunction,
+}) {
+ {
+ // <var>?..[<expr>]
+ int? shouldNotBePromoted;
+ listOfIntValue?..[shouldNotBePromoted = 0];
+ shouldNotBePromoted.expectStaticType<Exactly<int?>>();
+ }
+
+ {
+ // <expr>?..[<expr>]
+ int? shouldNotBePromoted;
+ listOfIntFunction()?..[shouldNotBePromoted = 0];
+ shouldNotBePromoted.expectStaticType<Exactly<int?>>();
+ }
+}
+
+// `<nonNullable>?..[<expr>] = 0` is not known to invoke <expr>.
+testCascadedIndexSet({
+ required List<int> listOfIntValue,
+ required List<int> Function() listOfIntFunction,
+}) {
+ {
+ // <var>?..[<expr>] = 0
+ int? shouldNotBePromoted;
+ listOfIntValue?..[shouldNotBePromoted = 0] = 0;
+ shouldNotBePromoted.expectStaticType<Exactly<int?>>();
+ }
+
+ {
+ // <expr>?..[<expr>] = 0
+ int? shouldNotBePromoted;
+ listOfIntFunction()?..[shouldNotBePromoted = 0] = 0;
+ shouldNotBePromoted.expectStaticType<Exactly<int?>>();
+ }
+}
+
+main() {
+ testProperty(intValue: 0, intFunction: () => 0);
+ testCall(intValue: 0, intFunction: () => 0);
+ testIndexGet(listOfIntValue: [0], listOfIntFunction: () => [0]);
+ testIndexSet(listOfIntValue: [0], listOfIntFunction: () => [0]);
+ testCascadedProperty(intValue: 0, intFunction: () => 0);
+ testCascadedCall(intValue: 0, intFunction: () => 0);
+ testCascadedIndexGet(listOfIntValue: [0], listOfIntFunction: () => [0]);
+ testCascadedIndexSet(listOfIntValue: [0], listOfIntFunction: () => [0]);
+}
diff --git a/tests/language/sound_flow_analysis/null_aware_access_test.dart b/tests/language/sound_flow_analysis/null_aware_access_test.dart
new file mode 100644
index 0000000..16778d8
--- /dev/null
+++ b/tests/language/sound_flow_analysis/null_aware_access_test.dart
@@ -0,0 +1,174 @@
+// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Exercises flow analysis of null aware accesses (expressions involving `?.`)
+// when `sound-flow-analysis` is enabled.
+
+// SharedOptions=--enable-experiment=sound-flow-analysis
+
+// ignore_for_file: invalid_null_aware_operator
+
+import '../static_type_helper.dart';
+
+// `<nonNullable>?.bitLength.gcd(<expr>)` is known to invoke <expr>.
+testProperty({required int intValue, required int Function() intFunction}) {
+ {
+ // <var>?.bitLength.gcd(<expr>)
+ int? shouldBePromoted;
+ intValue?.bitLength.gcd(shouldBePromoted = 0);
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <expr>?.bitLength.gcd(<expr>)
+ int? shouldBePromoted;
+ intFunction()?.bitLength.gcd(shouldBePromoted = 0);
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+}
+
+// `<nonNullable>?.gcd(<expr>)` is known to invoke <expr>.
+testCall({required int intValue, required int Function() intFunction}) {
+ {
+ // <var>?.gcd(<expr>)
+ int? shouldBePromoted;
+ intValue?.gcd(shouldBePromoted = 0);
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <expr>?.gcd(<expr>)
+ int? shouldBePromoted;
+ intFunction()?.gcd(shouldBePromoted = 0);
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+}
+
+// `<nonNullable>?[<expr>]` is known to invoke <expr>.
+testIndexGet({
+ required List<int> listOfIntValue,
+ required List<int> Function() listOfIntFunction,
+}) {
+ {
+ // <var>?[<expr>]
+ int? shouldBePromoted;
+ listOfIntValue?[shouldBePromoted = 0];
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <expr>?[<expr>]
+ int? shouldBePromoted;
+ listOfIntFunction()?[shouldBePromoted = 0];
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+}
+
+// `<nonNullable>?[<expr>] = 0` is known to invoke <expr>.
+testIndexSet({
+ required List<int> listOfIntValue,
+ required List<int> Function() listOfIntFunction,
+}) {
+ {
+ // <var>?[<expr>] = 0
+ int? shouldBePromoted;
+ listOfIntValue?[shouldBePromoted = 0] = 0;
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <expr>?[<expr>] = 0
+ int? shouldBePromoted;
+ listOfIntFunction()?[shouldBePromoted = 0] = 0;
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+}
+
+// `<nonNullable>?..bitLength.gcd(<expr>)` is known to invoke <expr>.
+testCascadedProperty({
+ required int intValue,
+ required int Function() intFunction,
+}) {
+ {
+ // <var>?..bitLength.gcd(<expr>)
+ int? shouldBePromoted;
+ intValue?..bitLength.gcd(shouldBePromoted = 0);
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <expr>?..bitLength.gcd(<expr>)
+ int? shouldBePromoted;
+ intFunction()?..bitLength.gcd(shouldBePromoted = 0);
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+}
+
+// `<nonNullable>?..gcd(<expr>)` is known to invoke <expr>.
+testCascadedCall({required int intValue, required int Function() intFunction}) {
+ {
+ // <var>?..gcd(<expr>)
+ int? shouldBePromoted;
+ intValue?..gcd(shouldBePromoted = 0);
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <expr>?..gcd(<expr>)
+ int? shouldBePromoted;
+ intFunction()?..gcd(shouldBePromoted = 0);
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+}
+
+// `<nonNullable>?..[<expr>]` is known to invoke <expr>.
+testCascadedIndexGet({
+ required List<int> listOfIntValue,
+ required List<int> Function() listOfIntFunction,
+}) {
+ {
+ // <var>?..[<expr>]
+ int? shouldBePromoted;
+ listOfIntValue?..[shouldBePromoted = 0];
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <expr>?..[<expr>]
+ int? shouldBePromoted;
+ listOfIntFunction()?..[shouldBePromoted = 0];
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+}
+
+// `<nonNullable>?..[<expr>] = 0` is known to invoke <expr>.
+testCascadedIndexSet({
+ required List<int> listOfIntValue,
+ required List<int> Function() listOfIntFunction,
+}) {
+ {
+ // <var>?..[<expr>] = 0
+ int? shouldBePromoted;
+ listOfIntValue?..[shouldBePromoted = 0] = 0;
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <expr>?..[<expr>] = 0
+ int? shouldBePromoted;
+ listOfIntFunction()?..[shouldBePromoted = 0] = 0;
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+}
+
+main() {
+ testProperty(intValue: 0, intFunction: () => 0);
+ testCall(intValue: 0, intFunction: () => 0);
+ testIndexGet(listOfIntValue: [0], listOfIntFunction: () => [0]);
+ testIndexSet(listOfIntValue: [0], listOfIntFunction: () => [0]);
+ testCascadedProperty(intValue: 0, intFunction: () => 0);
+ testCascadedCall(intValue: 0, intFunction: () => 0);
+ testCascadedIndexGet(listOfIntValue: [0], listOfIntFunction: () => [0]);
+ testCascadedIndexSet(listOfIntValue: [0], listOfIntFunction: () => [0]);
+}
diff --git a/tests/language/sound_flow_analysis/null_aware_map_entry_disabled_test.dart b/tests/language/sound_flow_analysis/null_aware_map_entry_disabled_test.dart
new file mode 100644
index 0000000..6a9eba1
--- /dev/null
+++ b/tests/language/sound_flow_analysis/null_aware_map_entry_disabled_test.dart
@@ -0,0 +1,59 @@
+// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Exercises flow analysis of null-aware map entries when `sound-flow-analysis`
+// is disabled.
+
+// @dart = 3.8
+
+// ignore_for_file: invalid_null_aware_operator
+
+import '../static_type_helper.dart';
+
+// `{ ?<nonNullable>: <expr> }` is known to invoke <expr>.
+// (this behavior predates sound-flow-analysis)
+testNonNullable({required int intValue, required int Function() intFunction}) {
+ {
+ // { ?<var>: <expr> }
+ int? shouldBePromoted;
+ ({?intValue: shouldBePromoted = 0});
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // { ?<expr>: <expr> }
+ int? shouldBePromoted;
+ ({?intFunction(): shouldBePromoted = 0});
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+}
+
+// `{ ?<Null>: <expr> }` is not known to skip <expr>.
+testNull({required Null nullValue, required Null Function() nullFunction}) {
+ {
+ // { ?null: <expr> }
+ int? shouldBeDemoted = 0;
+ ({?null: (shouldBeDemoted = null, 0).$2});
+ shouldBeDemoted.expectStaticType<Exactly<int?>>();
+ }
+
+ {
+ // { ?<var>: <expr> }
+ int? shouldBeDemoted = 0;
+ ({?nullValue: (shouldBeDemoted = null, 0).$2});
+ shouldBeDemoted.expectStaticType<Exactly<int?>>();
+ }
+
+ {
+ // { ?<expr>: <expr> }
+ int? shouldBeDemoted = 0;
+ ({?nullFunction(): (shouldBeDemoted = null, 0).$2});
+ shouldBeDemoted.expectStaticType<Exactly<int?>>();
+ }
+}
+
+main() {
+ testNonNullable(intValue: 0, intFunction: () => 0);
+ testNull(nullValue: null, nullFunction: () => null);
+}
diff --git a/tests/language/sound_flow_analysis/null_aware_map_entry_test.dart b/tests/language/sound_flow_analysis/null_aware_map_entry_test.dart
new file mode 100644
index 0000000..349ce18
--- /dev/null
+++ b/tests/language/sound_flow_analysis/null_aware_map_entry_test.dart
@@ -0,0 +1,58 @@
+// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Exercises flow analysis of null-aware map entries when `sound-flow-analysis`
+// is enabled.
+
+// SharedOptions=--enable-experiment=sound-flow-analysis
+
+// ignore_for_file: invalid_null_aware_operator
+
+import '../static_type_helper.dart';
+
+// `{ ?<nonNullable>: <expr> }` is known to invoke <expr>.
+testNonNullable({required int intValue, required int Function() intFunction}) {
+ {
+ // { ?<var>: <expr> }
+ int? shouldBePromoted;
+ ({?intValue: shouldBePromoted = 0});
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // { ?<expr>: <expr> }
+ int? shouldBePromoted;
+ ({?intFunction(): shouldBePromoted = 0});
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+}
+
+// `{ ?<Null>: <expr> }` is known to skip <expr>.
+testNull({required Null nullValue, required Null Function() nullFunction}) {
+ {
+ // { ?null: <expr> }
+ int? shouldNotBeDemoted = 0;
+ ({?null: (shouldNotBeDemoted = null, 0).$2});
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // { ?<var>: <expr> }
+ int? shouldNotBeDemoted = 0;
+ ({?nullValue: (shouldNotBeDemoted = null, 0).$2});
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // { ?<expr>: <expr> }
+ int? shouldNotBeDemoted = 0;
+ ({?nullFunction(): (shouldNotBeDemoted = null, 0).$2});
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ }
+}
+
+main() {
+ testNonNullable(intValue: 0, intFunction: () => 0);
+ testNull(nullValue: null, nullFunction: () => null);
+}
diff --git a/tests/language/sound_flow_analysis/pattern_matching_non_nullable_disabled_test.dart b/tests/language/sound_flow_analysis/pattern_matching_non_nullable_disabled_test.dart
new file mode 100644
index 0000000..83f00a2
--- /dev/null
+++ b/tests/language/sound_flow_analysis/pattern_matching_non_nullable_disabled_test.dart
@@ -0,0 +1,154 @@
+// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Exercises flow analysis of patterns that match a non-nullable scrutinee when
+// `sound-flow-analysis` is disabled.
+
+// @dart = 3.8
+
+// ignore_for_file: unnecessary_null_check_pattern
+
+import '../static_type_helper.dart';
+
+const Null null_ = null;
+
+// `<pattern>?` is not known to match a non-nullable expression.
+testNullCheck() {
+ {
+ // <nonNullable> case <pattern>?
+ int? shouldBeDemoted1 = 0;
+ int? shouldBeDemoted2 = 0;
+ if (0 case _?) {
+ shouldBeDemoted1 = null;
+ } else {
+ shouldBeDemoted2 = null;
+ }
+ shouldBeDemoted1.expectStaticType<Exactly<int?>>();
+ shouldBeDemoted2.expectStaticType<Exactly<int?>>();
+ }
+}
+
+// `<nonNullable> <var>` is known to match a suitably typed non-nullable expression.
+// (this behavior predates sound-flow-analysis)
+testDeclaredVar() {
+ {
+ // <nonNullable> case <nonNullable> <var>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (0 case int i) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+}
+
+// `[...]` is known to match a suitably typed non-nullable expression.
+// (this behavior predates sound-flow-analysis)
+testList() {
+ {
+ // [] case [...]
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if ([] case [...]) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+}
+
+// `<nonNullable>()` is known to match a suitably typed non-nullable expression.
+// (this behavior predates sound-flow-analysis)
+testObject() {
+ {
+ // <nonNullable> case <nonNullable>()
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (0 case int()) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+}
+
+// `(<pattern>,)` is known to match a suitably typed non-nullable expression.
+// (this behavior predates sound-flow-analysis)
+testRecord() {
+ {
+ // (<expr>,) case (<pattern>,)
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if ((0,) case (_,)) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+}
+
+// `<nonNullable> _` is known to match a suitably typed non-nullable expression.
+// (this behavior predates sound-flow-analysis)
+testWildcard() {
+ {
+ // <nonNullable> case <nonNullable> _
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (0 case int _) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+}
+
+// `!= <Null>` is not known to match a non-nullable expression.
+testNotEqualNull() {
+ {
+ // <nonNullable> case != <null literal>
+ int? shouldBeDemoted1 = 0;
+ int? shouldBeDemoted2 = 0;
+ if (0 case != null) {
+ shouldBeDemoted1 = null;
+ } else {
+ shouldBeDemoted2 = null;
+ }
+ shouldBeDemoted1.expectStaticType<Exactly<int?>>();
+ shouldBeDemoted2.expectStaticType<Exactly<int?>>();
+ }
+
+ {
+ // <nonNullable> case != <null const>
+ int? shouldBeDemoted1 = 0;
+ int? shouldBeDemoted2 = 0;
+ if (0 case != null_) {
+ shouldBeDemoted1 = null;
+ } else {
+ shouldBeDemoted2 = null;
+ }
+ shouldBeDemoted1.expectStaticType<Exactly<int?>>();
+ shouldBeDemoted2.expectStaticType<Exactly<int?>>();
+ }
+}
+
+main() {
+ testNullCheck();
+ testDeclaredVar();
+ testList();
+ testObject();
+ testRecord();
+ testWildcard();
+ testNotEqualNull();
+}
diff --git a/tests/language/sound_flow_analysis/pattern_matching_non_nullable_test.dart b/tests/language/sound_flow_analysis/pattern_matching_non_nullable_test.dart
new file mode 100644
index 0000000..937c526
--- /dev/null
+++ b/tests/language/sound_flow_analysis/pattern_matching_non_nullable_test.dart
@@ -0,0 +1,149 @@
+// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Exercises flow analysis of patterns that match a non-nullable scrutinee when
+// `sound-flow-analysis` is enabled.
+
+// SharedOptions=--enable-experiment=sound-flow-analysis
+
+// ignore_for_file: unnecessary_null_check_pattern
+
+import '../static_type_helper.dart';
+
+const Null null_ = null;
+
+// `<pattern>?` is known to match a non-nullable expression.
+testNullCheck() {
+ {
+ // <nonNullable> case <pattern>?
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (0 case _?) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+}
+
+// `<nonNullable> <var>` is known to match a suitably typed non-nullable expression.
+testDeclaredVar() {
+ {
+ // <nonNullable> case <nonNullable> <var>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (0 case int i) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+}
+
+// `[...]` is known to match a suitably typed non-nullable expression.
+testList() {
+ {
+ // [] case [...]
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if ([] case [...]) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+}
+
+// `<nonNullable>()` is known to match a suitably typed non-nullable expression.
+testObject() {
+ {
+ // <nonNullable> case <nonNullable>()
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (0 case int()) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+}
+
+// `(<pattern>,)` is known to match a suitably typed non-nullable expression.
+testRecord() {
+ {
+ // (<expr>,) case (<pattern>,)
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if ((0,) case (_,)) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+}
+
+// `<nonNullable> _` is known to match a suitably typed non-nullable expression.
+testWildcard() {
+ {
+ // <nonNullable> case <nonNullable> _
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (0 case int _) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+}
+
+// `!= <Null>` is known to match a non-nullable expression.
+testNotEqualNull() {
+ {
+ // <nonNullable> case != <null literal>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (0 case != null) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <nonNullable> case != <null const>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (0 case != null_) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+}
+
+main() {
+ testNullCheck();
+ testDeclaredVar();
+ testList();
+ testObject();
+ testRecord();
+ testWildcard();
+ testNotEqualNull();
+}
diff --git a/tests/language/sound_flow_analysis/pattern_matching_null_disabled_test.dart b/tests/language/sound_flow_analysis/pattern_matching_null_disabled_test.dart
new file mode 100644
index 0000000..5331e95
--- /dev/null
+++ b/tests/language/sound_flow_analysis/pattern_matching_null_disabled_test.dart
@@ -0,0 +1,467 @@
+// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Exercises flow analysis of patterns that match a Null scrutinee when
+// `sound-flow-analysis` is disabled.
+
+// @dart = 3.8
+
+import '../static_type_helper.dart';
+
+typedef IntQuestion = int?;
+
+// `<pattern> as <nullable>` is known to match a null expression.
+// (this behavior predates sound-flow-analysis)
+testCast({required Null nullValue, required Null Function() nullFunction}) {
+ {
+ // null case <pattern> as <nullable>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (null case _ as Null) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // null case <pattern> as <nullable>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (null case _ as int?) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // null case <pattern> as <nullable>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (null case _ as IntQuestion) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <var> case <pattern> as <nullable>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullValue case _ as Null) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <var> case <pattern> as <nullable>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullValue case _ as int?) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <var> case <pattern> as <nullable>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullValue case _ as IntQuestion) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <expr> case <pattern> as <nullable>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullFunction() case _ as Null) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <expr> case <pattern> as <nullable>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullFunction() case _ as int?) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <expr> case <pattern> as <nullable>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullFunction() case _ as IntQuestion) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+}
+
+// `<nullable> <var>` is known to match a null expression.
+// (this behavior predates sound-flow-analysis)
+testDeclaredVar({
+ required Null nullValue,
+ required Null Function() nullFunction,
+}) {
+ {
+ // null case <nullable> <var>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (null case Null v) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // null case <nullable> <var>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (null case int? v) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // null case <nullable> <var>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (null case IntQuestion v) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <var> case <nullable> <var>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullValue case Null v) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <var> case <nullable> <var>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullValue case int? v) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <var> case <nullable> <var>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullValue case IntQuestion v) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <expr> case <nullable> <var>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullFunction() case Null v) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <expr> case <nullable> <var>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullFunction() case int? v) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <expr> case <nullable> <var>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullFunction() case IntQuestion v) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+}
+
+// `<nullable>()` is known to match a null expression.
+// (this behavior predates sound-flow-analysis)
+testObject({required Null nullValue, required Null Function() nullFunction}) {
+ {
+ // null case <nullable>()
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (null case Null()) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // null case <nullable>()
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (null case IntQuestion()) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <var> case <nullable>()
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullValue case Null()) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <var> case <nullable>()
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullValue case IntQuestion()) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <expr> case <nullable>()
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullFunction() case Null()) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <expr> case <nullable>()
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullFunction() case IntQuestion()) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+}
+
+// `<nullable> _` is known to match a null expression.
+// (this behavior predates sound-flow-analysis)
+testWildcard({required Null nullValue, required Null Function() nullFunction}) {
+ {
+ // null case <nullable> _
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (null case Null _) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // null case <nullable> _
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (null case int? _) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // null case <nullable> _
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (null case IntQuestion _) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <var> case <nullable> _
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullValue case Null _) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <var> case <nullable> _
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullValue case int? _) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <var> case <nullable> _
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullValue case IntQuestion _) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <expr> case <nullable> _
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullFunction() case Null _) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <expr> case <nullable> _
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullFunction() case int? _) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <expr> case <nullable> _
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullFunction() case IntQuestion _) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+}
+
+main() {
+ testCast(nullValue: null, nullFunction: () => null);
+ testDeclaredVar(nullValue: null, nullFunction: () => null);
+ testObject(nullValue: null, nullFunction: () => null);
+ testWildcard(nullValue: null, nullFunction: () => null);
+}
diff --git a/tests/language/sound_flow_analysis/pattern_matching_null_test.dart b/tests/language/sound_flow_analysis/pattern_matching_null_test.dart
new file mode 100644
index 0000000..1bb7eb9
--- /dev/null
+++ b/tests/language/sound_flow_analysis/pattern_matching_null_test.dart
@@ -0,0 +1,463 @@
+// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Exercises flow analysis of patterns that match a Null scrutinee when
+// `sound-flow-analysis` is enabled.
+
+// SharedOptions=--enable-experiment=sound-flow-analysis
+
+import '../static_type_helper.dart';
+
+typedef IntQuestion = int?;
+
+// `<pattern> as <nullable>` is known to match a null expression.
+testCast({required Null nullValue, required Null Function() nullFunction}) {
+ {
+ // null case <pattern> as <nullable>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (null case _ as Null) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // null case <pattern> as <nullable>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (null case _ as int?) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // null case <pattern> as <nullable>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (null case _ as IntQuestion) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <var> case <pattern> as <nullable>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullValue case _ as Null) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <var> case <pattern> as <nullable>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullValue case _ as int?) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <var> case <pattern> as <nullable>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullValue case _ as IntQuestion) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <expr> case <pattern> as <nullable>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullFunction() case _ as Null) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <expr> case <pattern> as <nullable>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullFunction() case _ as int?) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <expr> case <pattern> as <nullable>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullFunction() case _ as IntQuestion) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+}
+
+// `<nullable> <var>` is known to match a null expression.
+testDeclaredVar({
+ required Null nullValue,
+ required Null Function() nullFunction,
+}) {
+ {
+ // null case <nullable> <var>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (null case Null v) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // null case <nullable> <var>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (null case int? v) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // null case <nullable> <var>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (null case IntQuestion v) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <var> case <nullable> <var>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullValue case Null v) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <var> case <nullable> <var>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullValue case int? v) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <var> case <nullable> <var>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullValue case IntQuestion v) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <expr> case <nullable> <var>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullFunction() case Null v) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <expr> case <nullable> <var>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullFunction() case int? v) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <expr> case <nullable> <var>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullFunction() case IntQuestion v) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+}
+
+// `<nullable>()` is known to match a null expression.
+testObject({required Null nullValue, required Null Function() nullFunction}) {
+ {
+ // null case <nullable>()
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (null case Null()) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // null case <nullable>()
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (null case IntQuestion()) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <var> case <nullable>()
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullValue case Null()) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <var> case <nullable>()
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullValue case IntQuestion()) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <expr> case <nullable>()
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullFunction() case Null()) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <expr> case <nullable>()
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullFunction() case IntQuestion()) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+}
+
+// `<nullable> _` is known to match a null expression.
+testWildcard({required Null nullValue, required Null Function() nullFunction}) {
+ {
+ // null case <nullable> _
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (null case Null _) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // null case <nullable> _
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (null case int? _) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // null case <nullable> _
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (null case IntQuestion _) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <var> case <nullable> _
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullValue case Null _) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <var> case <nullable> _
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullValue case int? _) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <var> case <nullable> _
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullValue case IntQuestion _) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <expr> case <nullable> _
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullFunction() case Null _) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <expr> case <nullable> _
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullFunction() case int? _) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <expr> case <nullable> _
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullFunction() case IntQuestion _) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+}
+
+main() {
+ testCast(nullValue: null, nullFunction: () => null);
+ testDeclaredVar(nullValue: null, nullFunction: () => null);
+ testObject(nullValue: null, nullFunction: () => null);
+ testWildcard(nullValue: null, nullFunction: () => null);
+}
diff --git a/tests/language/sound_flow_analysis/pattern_not_matching_non_nullable_disabled_test.dart b/tests/language/sound_flow_analysis/pattern_not_matching_non_nullable_disabled_test.dart
new file mode 100644
index 0000000..29edbb7
--- /dev/null
+++ b/tests/language/sound_flow_analysis/pattern_not_matching_non_nullable_disabled_test.dart
@@ -0,0 +1,144 @@
+// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Exercises flow analysis of patterns that fail to match a non-nullable
+// scrutinee when `sound-flow-analysis` is disabled.
+
+// @dart = 3.8
+
+import 'package:expect/expect.dart';
+
+import '../static_type_helper.dart';
+
+const Null null_ = null;
+
+// `<Null>` is not known to mismatch a non-nullable expression.
+testConstNull() {
+ {
+ // <nonNullable> case <null literal>
+ int? shouldBeDemoted1 = 0;
+ int? shouldBeDemoted2 = 0;
+ if (0 case null) {
+ shouldBeDemoted1 = null;
+ } else {
+ shouldBeDemoted2 = null;
+ }
+ shouldBeDemoted1.expectStaticType<Exactly<int?>>();
+ shouldBeDemoted2.expectStaticType<Exactly<int?>>();
+ }
+
+ {
+ // <nonNullable> case <null const>
+ int? shouldBeDemoted1 = 0;
+ int? shouldBeDemoted2 = 0;
+ if (0 case null_) {
+ shouldBeDemoted1 = null;
+ } else {
+ shouldBeDemoted2 = null;
+ }
+ shouldBeDemoted1.expectStaticType<Exactly<int?>>();
+ shouldBeDemoted2.expectStaticType<Exactly<int?>>();
+ }
+}
+
+// `Null <var>` is not known to mismatch a non-nullable expression.
+testDeclaredVar() {
+ {
+ // <nonNullable> case Null <var>
+ int? shouldBeDemoted1 = 0;
+ int? shouldBeDemoted2 = 0;
+ if (0 case Null n) {
+ shouldBeDemoted1 = null;
+ } else {
+ shouldBeDemoted2 = null;
+ }
+ shouldBeDemoted1.expectStaticType<Exactly<int?>>();
+ shouldBeDemoted2.expectStaticType<Exactly<int?>>();
+ }
+}
+
+// `<pattern> as Null` is not known to throw when matching a non-nullable expression.
+testCast({required bool true_}) {
+ {
+ // <nonNullable> case _ as Null
+ Expect.throws(() {
+ int? shouldBeDemoted = 0;
+ if (true_) {
+ if (0 case _ as Null) {}
+ shouldBeDemoted = null; // Reachable
+ }
+ shouldBeDemoted.expectStaticType<Exactly<int?>>();
+ });
+ }
+}
+
+// `Null()` is not known to mismatch a non-nullable expression.
+testObject() {
+ {
+ // <nonNullable> case Null()
+ int? shouldBeDemoted1 = 0;
+ int? shouldBeDemoted2 = 0;
+ if (0 case Null()) {
+ shouldBeDemoted1 = null;
+ } else {
+ shouldBeDemoted2 = null;
+ }
+ shouldBeDemoted1.expectStaticType<Exactly<int?>>();
+ shouldBeDemoted2.expectStaticType<Exactly<int?>>();
+ }
+}
+
+// `Null _` is not known to mismatch a non-nullable expression.
+testWildcard() {
+ {
+ // <nonNullable> case Null _
+ int? shouldBeDemoted1 = 0;
+ int? shouldBeDemoted2 = 0;
+ if (0 case Null _) {
+ shouldBeDemoted1 = null;
+ } else {
+ shouldBeDemoted2 = null;
+ }
+ shouldBeDemoted1.expectStaticType<Exactly<int?>>();
+ shouldBeDemoted2.expectStaticType<Exactly<int?>>();
+ }
+}
+
+// `== <Null>` is not known to mismatch a non-nullable expression.
+testEqualNull() {
+ {
+ // <nonNullable> case == <null literal>
+ int? shouldBeDemoted1 = 0;
+ int? shouldBeDemoted2 = 0;
+ if (0 case == null) {
+ shouldBeDemoted1 = null;
+ } else {
+ shouldBeDemoted2 = null;
+ }
+ shouldBeDemoted1.expectStaticType<Exactly<int?>>();
+ shouldBeDemoted2.expectStaticType<Exactly<int?>>();
+ }
+
+ {
+ // <nonNullable> case == <null const>
+ int? shouldBeDemoted1 = 0;
+ int? shouldBeDemoted2 = 0;
+ if (0 case == null_) {
+ shouldBeDemoted1 = null;
+ } else {
+ shouldBeDemoted2 = null;
+ }
+ shouldBeDemoted1.expectStaticType<Exactly<int?>>();
+ shouldBeDemoted2.expectStaticType<Exactly<int?>>();
+ }
+}
+
+main() {
+ testConstNull();
+ testDeclaredVar();
+ testCast(true_: true);
+ testObject();
+ testWildcard();
+ testEqualNull();
+}
diff --git a/tests/language/sound_flow_analysis/pattern_not_matching_non_nullable_test.dart b/tests/language/sound_flow_analysis/pattern_not_matching_non_nullable_test.dart
new file mode 100644
index 0000000..730cd85
--- /dev/null
+++ b/tests/language/sound_flow_analysis/pattern_not_matching_non_nullable_test.dart
@@ -0,0 +1,144 @@
+// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Exercises flow analysis of patterns that fail to match a non-nullable
+// scrutinee when `sound-flow-analysis` is enabled.
+
+// SharedOptions=--enable-experiment=sound-flow-analysis
+
+import 'package:expect/expect.dart';
+
+import '../static_type_helper.dart';
+
+const Null null_ = null;
+
+// `<Null>` is known to mismatch a non-nullable expression.
+testConstNull() {
+ {
+ // <nonNullable> case <null literal>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (0 case null) {
+ shouldNotBeDemoted = null; // Unreachable
+ } else {
+ shouldBePromoted = 0; // Reachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <nonNullable> case <null const>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (0 case null_) {
+ shouldNotBeDemoted = null; // Unreachable
+ } else {
+ shouldBePromoted = 0; // Reachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+}
+
+// `Null <var>` is known to mismatch a non-nullable expression.
+testDeclaredVar() {
+ {
+ // <nonNullable> case Null <var>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (0 case Null n) {
+ shouldNotBeDemoted = null; // Unreachable
+ } else {
+ shouldBePromoted = 0; // Reachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+}
+
+// `<pattern> as Null` is known to throw when matching a non-nullable expression.
+testCast({required bool true_}) {
+ {
+ // <nonNullable> case _ as Null
+ Expect.throws(() {
+ int? shouldNotBeDemoted = 0;
+ if (true_) {
+ if (0 case _ as Null) {}
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ });
+ }
+}
+
+// `Null()` is known to mismatch a non-nullable expression.
+testObject() {
+ {
+ // <nonNullable> case Null()
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (0 case Null()) {
+ shouldNotBeDemoted = null; // Unreachable
+ } else {
+ shouldBePromoted = 0; // Reachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+}
+
+// `Null _` is known to mismatch a non-nullable expression.
+testWildcard() {
+ {
+ // <nonNullable> case Null _
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (0 case Null _) {
+ shouldNotBeDemoted = null; // Unreachable
+ } else {
+ shouldBePromoted = 0; // Reachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+}
+
+// `== <Null>` is known to mismatch a non-nullable expression.
+testEqualNull() {
+ {
+ // <nonNullable> case == <null literal>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (0 case == null) {
+ shouldNotBeDemoted = null; // Unreachable
+ } else {
+ shouldBePromoted = 0; // Reachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <nonNullable> case == <null const>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (0 case == null_) {
+ shouldNotBeDemoted = null; // Unreachable
+ } else {
+ shouldBePromoted = 0; // Reachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+}
+
+main() {
+ testConstNull();
+ testDeclaredVar();
+ testCast(true_: true);
+ testObject();
+ testWildcard();
+ testEqualNull();
+}
diff --git a/tests/language/sound_flow_analysis/pattern_not_matching_null_disabled_test.dart b/tests/language/sound_flow_analysis/pattern_not_matching_null_disabled_test.dart
new file mode 100644
index 0000000..a1a389a
--- /dev/null
+++ b/tests/language/sound_flow_analysis/pattern_not_matching_null_disabled_test.dart
@@ -0,0 +1,274 @@
+// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Exercises flow analysis of patterns that fail to match a Null scrutinee when
+// `sound-flow-analysis` is disabled.
+
+// @dart = 3.8
+
+import '../static_type_helper.dart';
+
+// `<nonNullable> <var>` is not known to mismatch a null expression.
+testDeclaredVar({
+ required Null nullValue,
+ required Null Function() nullFunction,
+}) {
+ {
+ // null case <nonNullable> <var>
+ int? shouldBeDemoted1 = 0;
+ int? shouldBeDemoted2 = 0;
+ if (null case int i) {
+ shouldBeDemoted1 = null;
+ } else {
+ shouldBeDemoted2 = null;
+ }
+ shouldBeDemoted1.expectStaticType<Exactly<int?>>();
+ shouldBeDemoted2.expectStaticType<Exactly<int?>>();
+ }
+
+ {
+ // <var> case <nonNullable> <var>
+ int? shouldBeDemoted1 = 0;
+ int? shouldBeDemoted2 = 0;
+ if (nullValue case int i) {
+ shouldBeDemoted1 = null;
+ } else {
+ shouldBeDemoted2 = null;
+ }
+ shouldBeDemoted1.expectStaticType<Exactly<int?>>();
+ shouldBeDemoted2.expectStaticType<Exactly<int?>>();
+ }
+
+ {
+ // <expr> case <nonNullable> <var>
+ int? shouldBeDemoted1 = 0;
+ int? shouldBeDemoted2 = 0;
+ if (nullFunction() case int i) {
+ shouldBeDemoted1 = null;
+ } else {
+ shouldBeDemoted2 = null;
+ }
+ shouldBeDemoted1.expectStaticType<Exactly<int?>>();
+ shouldBeDemoted2.expectStaticType<Exactly<int?>>();
+ }
+}
+
+// `[...]` is not known to mismatch a null expression.
+testList({required Null nullValue, required Null Function() nullFunction}) {
+ {
+ // null case [...]
+ int? shouldBeDemoted1 = 0;
+ int? shouldBeDemoted2 = 0;
+ if (null case [...]) {
+ shouldBeDemoted1 = null;
+ } else {
+ shouldBeDemoted2 = null;
+ }
+ shouldBeDemoted1.expectStaticType<Exactly<int?>>();
+ shouldBeDemoted2.expectStaticType<Exactly<int?>>();
+ }
+
+ {
+ // <var> case [...]
+ int? shouldBeDemoted1 = 0;
+ int? shouldBeDemoted2 = 0;
+ if (nullValue case [...]) {
+ shouldBeDemoted1 = null;
+ } else {
+ shouldBeDemoted2 = null;
+ }
+ shouldBeDemoted1.expectStaticType<Exactly<int?>>();
+ shouldBeDemoted2.expectStaticType<Exactly<int?>>();
+ }
+
+ {
+ // <expr> case [...]
+ int? shouldBeDemoted1 = 0;
+ int? shouldBeDemoted2 = 0;
+ if (nullFunction() case [...]) {
+ shouldBeDemoted1 = null;
+ } else {
+ shouldBeDemoted2 = null;
+ }
+ shouldBeDemoted1.expectStaticType<Exactly<int?>>();
+ shouldBeDemoted2.expectStaticType<Exactly<int?>>();
+ }
+}
+
+// `{...}` is not known to mismatch a null expression.
+testMap({required Null nullValue, required Null Function() nullFunction}) {
+ {
+ // null case {...}
+ int? shouldBeDemoted1 = 0;
+ int? shouldBeDemoted2 = 0;
+ if (null case {0: 0}) {
+ shouldBeDemoted1 = null;
+ } else {
+ shouldBeDemoted2 = null;
+ }
+ shouldBeDemoted1.expectStaticType<Exactly<int?>>();
+ shouldBeDemoted2.expectStaticType<Exactly<int?>>();
+ }
+
+ {
+ // <var> case {...}
+ int? shouldBeDemoted1 = 0;
+ int? shouldBeDemoted2 = 0;
+ if (nullValue case {0: 0}) {
+ shouldBeDemoted1 = null;
+ } else {
+ shouldBeDemoted2 = null;
+ }
+ shouldBeDemoted1.expectStaticType<Exactly<int?>>();
+ shouldBeDemoted2.expectStaticType<Exactly<int?>>();
+ }
+
+ {
+ // <expr> case {...}
+ int? shouldBeDemoted1 = 0;
+ int? shouldBeDemoted2 = 0;
+ if (nullFunction() case {0: 0}) {
+ shouldBeDemoted1 = null;
+ } else {
+ shouldBeDemoted2 = null;
+ }
+ shouldBeDemoted1.expectStaticType<Exactly<int?>>();
+ shouldBeDemoted2.expectStaticType<Exactly<int?>>();
+ }
+}
+
+// `<nonNullable>()` is not known to mismatch a null expression.
+testObject({required Null nullValue, required Null Function() nullFunction}) {
+ {
+ // null case <nonNullable>()
+ int? shouldBeDemoted1 = 0;
+ int? shouldBeDemoted2 = 0;
+ if (null case int()) {
+ shouldBeDemoted1 = null;
+ } else {
+ shouldBeDemoted2 = null;
+ }
+ shouldBeDemoted1.expectStaticType<Exactly<int?>>();
+ shouldBeDemoted2.expectStaticType<Exactly<int?>>();
+ }
+
+ {
+ // <var> case <nonNullable>()
+ int? shouldBeDemoted1 = 0;
+ int? shouldBeDemoted2 = 0;
+ if (nullValue case int()) {
+ shouldBeDemoted1 = null;
+ } else {
+ shouldBeDemoted2 = null;
+ }
+ shouldBeDemoted1.expectStaticType<Exactly<int?>>();
+ shouldBeDemoted2.expectStaticType<Exactly<int?>>();
+ }
+
+ {
+ // <expr> case <nonNullable>()
+ int? shouldBeDemoted1 = 0;
+ int? shouldBeDemoted2 = 0;
+ if (nullFunction() case int()) {
+ shouldBeDemoted1 = null;
+ } else {
+ shouldBeDemoted2 = null;
+ }
+ shouldBeDemoted1.expectStaticType<Exactly<int?>>();
+ shouldBeDemoted2.expectStaticType<Exactly<int?>>();
+ }
+}
+
+// `(<pattern>,)` is not known to mismatch a null expression.
+testRecord({required Null nullValue, required Null Function() nullFunction}) {
+ {
+ // null case (<pattern>,)
+ int? shouldBeDemoted1 = 0;
+ int? shouldBeDemoted2 = 0;
+ if (null case (_,)) {
+ shouldBeDemoted1 = null;
+ } else {
+ shouldBeDemoted2 = null;
+ }
+ shouldBeDemoted1.expectStaticType<Exactly<int?>>();
+ shouldBeDemoted2.expectStaticType<Exactly<int?>>();
+ }
+
+ {
+ // <var> case (<pattern>,)
+ int? shouldBeDemoted1 = 0;
+ int? shouldBeDemoted2 = 0;
+ if (nullValue case (_,)) {
+ shouldBeDemoted1 = null;
+ } else {
+ shouldBeDemoted2 = null;
+ }
+ shouldBeDemoted1.expectStaticType<Exactly<int?>>();
+ shouldBeDemoted2.expectStaticType<Exactly<int?>>();
+ }
+
+ {
+ // <expr> case (<pattern>,)
+ int? shouldBeDemoted1 = 0;
+ int? shouldBeDemoted2 = 0;
+ if (nullFunction() case (_,)) {
+ shouldBeDemoted1 = null;
+ } else {
+ shouldBeDemoted2 = null;
+ }
+ shouldBeDemoted1.expectStaticType<Exactly<int?>>();
+ shouldBeDemoted2.expectStaticType<Exactly<int?>>();
+ }
+}
+
+// `<nonNullable> _` is not known to mismatch a null expression.
+testWildcard({required Null nullValue, required Null Function() nullFunction}) {
+ {
+ // null case <nonNullable> _
+ int? shouldBeDemoted1 = 0;
+ int? shouldBeDemoted2 = 0;
+ if (null case int _) {
+ shouldBeDemoted1 = null;
+ } else {
+ shouldBeDemoted2 = null;
+ }
+ shouldBeDemoted1.expectStaticType<Exactly<int?>>();
+ shouldBeDemoted2.expectStaticType<Exactly<int?>>();
+ }
+
+ {
+ // <var> case <nonNullable> _
+ int? shouldBeDemoted1 = 0;
+ int? shouldBeDemoted2 = 0;
+ if (nullValue case int _) {
+ shouldBeDemoted1 = null;
+ } else {
+ shouldBeDemoted2 = null;
+ }
+ shouldBeDemoted1.expectStaticType<Exactly<int?>>();
+ shouldBeDemoted2.expectStaticType<Exactly<int?>>();
+ }
+
+ {
+ // <expr> case <nonNullable> _
+ int? shouldBeDemoted1 = 0;
+ int? shouldBeDemoted2 = 0;
+ if (nullFunction() case int _) {
+ shouldBeDemoted1 = null;
+ } else {
+ shouldBeDemoted2 = null;
+ }
+ shouldBeDemoted1.expectStaticType<Exactly<int?>>();
+ shouldBeDemoted2.expectStaticType<Exactly<int?>>();
+ }
+}
+
+main() {
+ testDeclaredVar(nullValue: null, nullFunction: () => null);
+ testList(nullValue: null, nullFunction: () => null);
+ testMap(nullValue: null, nullFunction: () => null);
+ testObject(nullValue: null, nullFunction: () => null);
+ testRecord(nullValue: null, nullFunction: () => null);
+ testWildcard(nullValue: null, nullFunction: () => null);
+}
diff --git a/tests/language/sound_flow_analysis/pattern_not_matching_null_test.dart b/tests/language/sound_flow_analysis/pattern_not_matching_null_test.dart
new file mode 100644
index 0000000..6d6a419
--- /dev/null
+++ b/tests/language/sound_flow_analysis/pattern_not_matching_null_test.dart
@@ -0,0 +1,274 @@
+// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Exercises flow analysis of patterns that fail to match a Null scrutinee when
+// `sound-flow-analysis` is enabled.
+
+// SharedOptions=--enable-experiment=sound-flow-analysis
+
+import '../static_type_helper.dart';
+
+// `<nonNullable> <var>` is known to mismatch a null expression.
+testDeclaredVar({
+ required Null nullValue,
+ required Null Function() nullFunction,
+}) {
+ {
+ // null case <nonNullable> <var>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (null case int i) {
+ shouldNotBeDemoted = null; // Unreachable
+ } else {
+ shouldBePromoted = 0; // Reachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <var> case <nonNullable> <var>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullValue case int i) {
+ shouldNotBeDemoted = null; // Unreachable
+ } else {
+ shouldBePromoted = 0; // Reachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <expr> case <nonNullable> <var>
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullFunction() case int i) {
+ shouldNotBeDemoted = null; // Unreachable
+ } else {
+ shouldBePromoted = 0; // Reachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+}
+
+// `[...]` is known to mismatch a null expression.
+testList({required Null nullValue, required Null Function() nullFunction}) {
+ {
+ // null case [...]
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (null case [...]) {
+ shouldNotBeDemoted = null; // Unreachable
+ } else {
+ shouldBePromoted = 0; // Reachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <var> case [...]
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullValue case [...]) {
+ shouldNotBeDemoted = null; // Unreachable
+ } else {
+ shouldBePromoted = 0; // Reachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <expr> case [...]
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullFunction() case [...]) {
+ shouldNotBeDemoted = null; // Unreachable
+ } else {
+ shouldBePromoted = 0; // Reachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+}
+
+// `{...}` is known to mismatch a null expression.
+testMap({required Null nullValue, required Null Function() nullFunction}) {
+ {
+ // null case {...}
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (null case {0: 0}) {
+ shouldNotBeDemoted = null; // Unreachable
+ } else {
+ shouldBePromoted = 0; // Reachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <var> case {...}
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullValue case {0: 0}) {
+ shouldNotBeDemoted = null; // Unreachable
+ } else {
+ shouldBePromoted = 0; // Reachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <expr> case {...}
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullFunction() case {0: 0}) {
+ shouldNotBeDemoted = null; // Unreachable
+ } else {
+ shouldBePromoted = 0; // Reachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+}
+
+// `<nonNullable>()` is known to mismatch a null expression.
+testObject({required Null nullValue, required Null Function() nullFunction}) {
+ {
+ // null case <nonNullable>()
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (null case int()) {
+ shouldNotBeDemoted = null; // Unreachable
+ } else {
+ shouldBePromoted = 0; // Reachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <var> case <nonNullable>()
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullValue case int()) {
+ shouldNotBeDemoted = null; // Unreachable
+ } else {
+ shouldBePromoted = 0; // Reachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <expr> case <nonNullable>()
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullFunction() case int()) {
+ shouldNotBeDemoted = null; // Unreachable
+ } else {
+ shouldBePromoted = 0; // Reachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+}
+
+// `(<pattern>,)` is known to mismatch a null expression.
+testRecord({required Null nullValue, required Null Function() nullFunction}) {
+ {
+ // null case (<pattern>,)
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (null case (_,)) {
+ shouldNotBeDemoted = null; // Unreachable
+ } else {
+ shouldBePromoted = 0; // Reachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <var> case (<pattern>,)
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullValue case (_,)) {
+ shouldNotBeDemoted = null; // Unreachable
+ } else {
+ shouldBePromoted = 0; // Reachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <expr> case (<pattern>,)
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullFunction() case (_,)) {
+ shouldNotBeDemoted = null; // Unreachable
+ } else {
+ shouldBePromoted = 0; // Reachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+}
+
+// `<nonNullable> _` is known to mismatch a null expression.
+testWildcard({required Null nullValue, required Null Function() nullFunction}) {
+ {
+ // null case <nonNullable> _
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (null case int _) {
+ shouldNotBeDemoted = null; // Unreachable
+ } else {
+ shouldBePromoted = 0; // Reachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <var> case <nonNullable> _
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullValue case int _) {
+ shouldNotBeDemoted = null; // Unreachable
+ } else {
+ shouldBePromoted = 0; // Reachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <expr> case <nonNullable> _
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullFunction() case int _) {
+ shouldNotBeDemoted = null; // Unreachable
+ } else {
+ shouldBePromoted = 0; // Reachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+}
+
+main() {
+ testDeclaredVar(nullValue: null, nullFunction: () => null);
+ testList(nullValue: null, nullFunction: () => null);
+ testMap(nullValue: null, nullFunction: () => null);
+ testObject(nullValue: null, nullFunction: () => null);
+ testRecord(nullValue: null, nullFunction: () => null);
+ testWildcard(nullValue: null, nullFunction: () => null);
+}
diff --git a/tests/language/sound_flow_analysis/type_tests_disabled_test.dart b/tests/language/sound_flow_analysis/type_tests_disabled_test.dart
new file mode 100644
index 0000000..53ef2fd
--- /dev/null
+++ b/tests/language/sound_flow_analysis/type_tests_disabled_test.dart
@@ -0,0 +1,249 @@
+// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Exercises flow analysis of type tests (`is` and `as` expressions) when
+// `sound-flow-analysis` is disabled.
+
+// @dart = 3.8
+
+import 'package:expect/expect.dart';
+
+import '../static_type_helper.dart';
+
+// `<nonNullable> as Null` is not known to throw an exception.
+testNonNullableAsNull({
+ required int intValue,
+ required int Function() intFunction,
+ required bool true_,
+}) {
+ {
+ // <var> as Null
+ Expect.throws(() {
+ int? shouldBeDemoted = 0;
+ if (true_) {
+ intValue as Null;
+ shouldBeDemoted = null; // Reachable
+ }
+ shouldBeDemoted.expectStaticType<Exactly<int?>>();
+ });
+ }
+
+ {
+ // <expr> as Null
+ Expect.throws(() {
+ int? shouldBeDemoted = 0;
+ if (true_) {
+ intFunction() as Null;
+ shouldBeDemoted = null; // Reachable
+ }
+ shouldBeDemoted.expectStaticType<Exactly<int?>>();
+ });
+ }
+}
+
+// `<nonNullable> is Null` is not known to evaluate to `false`.
+testNonNullableIsNull({
+ required int intValue,
+ required int Function() intFunction,
+}) {
+ {
+ // <var> is Null
+ int? shouldBeDemoted1 = 0;
+ int? shouldBeDemoted2 = 0;
+ if (intValue is Null) {
+ shouldBeDemoted1 = null;
+ } else {
+ shouldBeDemoted2 = null;
+ }
+ shouldBeDemoted1.expectStaticType<Exactly<int?>>();
+ shouldBeDemoted2.expectStaticType<Exactly<int?>>();
+ }
+
+ {
+ // <expr> is Null
+ int? shouldBeDemoted1 = 0;
+ int? shouldBeDemoted2 = 0;
+ if (intFunction() is Null) {
+ shouldBeDemoted1 = null;
+ } else {
+ shouldBeDemoted2 = null;
+ }
+ shouldBeDemoted1.expectStaticType<Exactly<int?>>();
+ shouldBeDemoted2.expectStaticType<Exactly<int?>>();
+ }
+}
+
+// `<nonNullable> is! Null` is not known to evaluate to `true`.
+testNonNullableIsNotNull({
+ required int intValue,
+ required int Function() intFunction,
+}) {
+ {
+ // <var> is! Null
+ int? shouldBeDemoted1 = 0;
+ int? shouldBeDemoted2 = 0;
+ if (intValue is! Null) {
+ shouldBeDemoted1 = null;
+ } else {
+ shouldBeDemoted2 = null;
+ }
+ shouldBeDemoted1.expectStaticType<Exactly<int?>>();
+ shouldBeDemoted2.expectStaticType<Exactly<int?>>();
+ }
+
+ {
+ // <expr> is! Null
+ int? shouldBeDemoted1 = 0;
+ int? shouldBeDemoted2 = 0;
+ if (intFunction() is! Null) {
+ shouldBeDemoted1 = null;
+ } else {
+ shouldBeDemoted2 = null;
+ }
+ shouldBeDemoted1.expectStaticType<Exactly<int?>>();
+ shouldBeDemoted2.expectStaticType<Exactly<int?>>();
+ }
+}
+
+// `<Null> as <nonNullable>` is not known to throw an exception.
+testNullAsNonNullable({
+ required Null nullValue,
+ required Null Function() nullFunction,
+ required bool true_,
+}) {
+ {
+ // null as int
+ Expect.throws(() {
+ int? shouldBeDemoted = 0;
+ if (true_) {
+ null as int;
+ shouldBeDemoted = null; // Reachable
+ }
+ shouldBeDemoted.expectStaticType<Exactly<int?>>();
+ });
+ }
+
+ {
+ // <var> as int
+ Expect.throws(() {
+ int? shouldBeDemoted = 0;
+ if (true_) {
+ nullValue as int;
+ shouldBeDemoted = null; // Reachable
+ }
+ shouldBeDemoted.expectStaticType<Exactly<int?>>();
+ });
+ }
+
+ {
+ // <expr> as int
+ Expect.throws(() {
+ int? shouldBeDemoted = 0;
+ if (true_) {
+ nullFunction() as int;
+ shouldBeDemoted = null; // Reachable
+ }
+ shouldBeDemoted.expectStaticType<Exactly<int?>>();
+ });
+ }
+}
+
+// `<Null> is <nonNullable>` is not known to evaluate to `false`.
+testNullIsNonNullable({
+ required Null nullValue,
+ required Null Function() nullFunction,
+}) {
+ {
+ // null is int
+ int? shouldBeDemoted1 = 0;
+ int? shouldBeDemoted2 = 0;
+ if (null is int) {
+ shouldBeDemoted1 = null;
+ } else {
+ shouldBeDemoted2 = null;
+ }
+ shouldBeDemoted1.expectStaticType<Exactly<int?>>();
+ shouldBeDemoted2.expectStaticType<Exactly<int?>>();
+ }
+
+ {
+ // <var> is int
+ int? shouldBeDemoted1 = 0;
+ int? shouldBeDemoted2 = 0;
+ if (nullValue is int) {
+ shouldBeDemoted1 = null;
+ } else {
+ shouldBeDemoted2 = null;
+ }
+ shouldBeDemoted1.expectStaticType<Exactly<int?>>();
+ shouldBeDemoted2.expectStaticType<Exactly<int?>>();
+ }
+
+ {
+ // <expr> is int
+ int? shouldBeDemoted1 = 0;
+ int? shouldBeDemoted2 = 0;
+ if (nullFunction() is int) {
+ shouldBeDemoted1 = null;
+ } else {
+ shouldBeDemoted2 = null;
+ }
+ shouldBeDemoted1.expectStaticType<Exactly<int?>>();
+ shouldBeDemoted2.expectStaticType<Exactly<int?>>();
+ }
+}
+
+// `<Null> is! <nonNullable>` is not known to evaluate to `true`.
+testNullIsNotNonNullable({
+ required Null nullValue,
+ required Null Function() nullFunction,
+}) {
+ {
+ // null is! int
+ int? shouldBeDemoted1 = 0;
+ int? shouldBeDemoted2 = 0;
+ if (null is! int) {
+ shouldBeDemoted1 = null;
+ } else {
+ shouldBeDemoted2 = null;
+ }
+ shouldBeDemoted1.expectStaticType<Exactly<int?>>();
+ shouldBeDemoted2.expectStaticType<Exactly<int?>>();
+ }
+
+ {
+ // <var> is! int
+ int? shouldBeDemoted1 = 0;
+ int? shouldBeDemoted2 = 0;
+ if (nullValue is! int) {
+ shouldBeDemoted1 = null;
+ } else {
+ shouldBeDemoted2 = null;
+ }
+ shouldBeDemoted1.expectStaticType<Exactly<int?>>();
+ shouldBeDemoted2.expectStaticType<Exactly<int?>>();
+ }
+
+ {
+ // <expr> is! int
+ int? shouldBeDemoted1 = 0;
+ int? shouldBeDemoted2 = 0;
+ if (nullFunction() is! int) {
+ shouldBeDemoted1 = null;
+ } else {
+ shouldBeDemoted2 = null;
+ }
+ shouldBeDemoted1.expectStaticType<Exactly<int?>>();
+ shouldBeDemoted2.expectStaticType<Exactly<int?>>();
+ }
+}
+
+main() {
+ testNonNullableAsNull(intValue: 0, intFunction: () => 0, true_: true);
+ testNonNullableIsNull(intValue: 0, intFunction: () => 0);
+ testNonNullableIsNotNull(intValue: 0, intFunction: () => 0);
+ testNullAsNonNullable(nullValue: null, nullFunction: () => null, true_: true);
+ testNullIsNonNullable(nullValue: null, nullFunction: () => null);
+ testNullIsNotNonNullable(nullValue: null, nullFunction: () => null);
+}
diff --git a/tests/language/sound_flow_analysis/type_tests_test.dart b/tests/language/sound_flow_analysis/type_tests_test.dart
new file mode 100644
index 0000000..76d2240
--- /dev/null
+++ b/tests/language/sound_flow_analysis/type_tests_test.dart
@@ -0,0 +1,249 @@
+// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Exercises flow analysis of type tests (`is` and `as` expressions) when
+// `sound-flow-analysis` is enabled.
+
+// SharedOptions=--enable-experiment=sound-flow-analysis
+
+import 'package:expect/expect.dart';
+
+import '../static_type_helper.dart';
+
+// `<nonNullable> as Null` is known to throw an exception.
+testNonNullableAsNull({
+ required int intValue,
+ required int Function() intFunction,
+ required bool true_,
+}) {
+ {
+ // <var> as Null
+ Expect.throws(() {
+ int? shouldNotBeDemoted = 0;
+ if (true_) {
+ intValue as Null;
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ });
+ }
+
+ {
+ // <expr> as Null
+ Expect.throws(() {
+ int? shouldNotBeDemoted = 0;
+ if (true_) {
+ intFunction() as Null;
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ });
+ }
+}
+
+// `<nonNullable> is Null` is known to evaluate to `false`.
+testNonNullableIsNull({
+ required int intValue,
+ required int Function() intFunction,
+}) {
+ {
+ // <var> is Null
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (intValue is Null) {
+ shouldNotBeDemoted = null; // Unreachable
+ } else {
+ shouldBePromoted = 0; // Reachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <expr> is Null
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (intFunction() is Null) {
+ shouldNotBeDemoted = null; // Unreachable
+ } else {
+ shouldBePromoted = 0; // Reachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+}
+
+// `<nonNullable> is! Null` is known to evaluate to `true`.
+testNonNullableIsNotNull({
+ required int intValue,
+ required int Function() intFunction,
+}) {
+ {
+ // <var> is! Null
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (intValue is! Null) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <expr> is! Null
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (intFunction() is! Null) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+}
+
+// `<Null> as <nonNullable>` is known to throw an exception.
+testNullAsNonNullable({
+ required Null nullValue,
+ required Null Function() nullFunction,
+ required bool true_,
+}) {
+ {
+ // null as int
+ Expect.throws(() {
+ int? shouldNotBeDemoted = 0;
+ if (true_) {
+ null as int;
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ });
+ }
+
+ {
+ // <var> as int
+ Expect.throws(() {
+ int? shouldNotBeDemoted = 0;
+ if (true_) {
+ nullValue as int;
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ });
+ }
+
+ {
+ // <expr> as int
+ Expect.throws(() {
+ int? shouldNotBeDemoted = 0;
+ if (true_) {
+ nullFunction() as int;
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ });
+ }
+}
+
+// `<Null> is <nonNullable>` is known to evaluate to `false`.
+testNullIsNonNullable({
+ required Null nullValue,
+ required Null Function() nullFunction,
+}) {
+ {
+ // null is int
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (null is int) {
+ shouldNotBeDemoted = null; // Unreachable
+ } else {
+ shouldBePromoted = 0; // Reachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <var> is int
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullValue is int) {
+ shouldNotBeDemoted = null; // Unreachable
+ } else {
+ shouldBePromoted = 0; // Reachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <expr> is int
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullFunction() is int) {
+ shouldNotBeDemoted = null; // Unreachable
+ } else {
+ shouldBePromoted = 0; // Reachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+}
+
+// `<Null> is! <nonNullable>` is known to evaluate to `true`.
+testNullIsNotNonNullable({
+ required Null nullValue,
+ required Null Function() nullFunction,
+}) {
+ {
+ // null is! int
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (null is! int) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <var> is! int
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullValue is! int) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+
+ {
+ // <expr> is! int
+ int? shouldNotBeDemoted = 0;
+ int? shouldBePromoted;
+ if (nullFunction() is! int) {
+ shouldBePromoted = 0; // Reachable
+ } else {
+ shouldNotBeDemoted = null; // Unreachable
+ }
+ shouldNotBeDemoted.expectStaticType<Exactly<int>>();
+ shouldBePromoted.expectStaticType<Exactly<int>>();
+ }
+}
+
+main() {
+ testNonNullableAsNull(intValue: 0, intFunction: () => 0, true_: true);
+ testNonNullableIsNull(intValue: 0, intFunction: () => 0);
+ testNonNullableIsNotNull(intValue: 0, intFunction: () => 0);
+ testNullAsNonNullable(nullValue: null, nullFunction: () => null, true_: true);
+ testNullIsNonNullable(nullValue: null, nullFunction: () => null);
+ testNullIsNotNonNullable(nullValue: null, nullFunction: () => null);
+}