[_fe_analyzer_shared] Improve exhaustiveness for null-assert This improves the exhaustiveness handling for null assert patterns by extending the space of the subpattern with the null space. This reflects the fact that null assert will throw on `null` and can therefore be considered to cover that case. Change-Id: If344eaaa55dcc70474f45519b53586e7d36e6e80 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/286400 Commit-Queue: Johnni Winther <johnniwinther@google.com> Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/_fe_analyzer_shared/test/exhaustiveness/data/null_assert.dart b/pkg/_fe_analyzer_shared/test/exhaustiveness/data/null_assert.dart index bc803a8..b9ce4d2 100644 --- a/pkg/_fe_analyzer_shared/test/exhaustiveness/data/null_assert.dart +++ b/pkg/_fe_analyzer_shared/test/exhaustiveness/data/null_assert.dart
@@ -8,23 +8,29 @@ A(this.field); } +sealed class B {} +class C extends B {} +class D extends B {} + simpleAssert(o1, o2) { var a = /* fields={}, subtypes={Object,Null}, type=Object? */switch (o1) { - _! /*space=??*/=> 0, - _ /*space=()*/=> 1 + _! /*space=()*/=> 0, + _ /* + error=unreachable, + space=() + */=> 1 }; var b = /* - error=non-exhaustive:Object, fields={}, subtypes={Object,Null}, type=Object? */switch (o2) { - _! /*space=??*/=> 0, + _! /*space=()*/=> 0, }; } @@ -36,7 +42,7 @@ subtypes={Object,Null}, type=Object? */switch (o1) { - A(field: 42)! /*space=??*/=> 0, + A(field: 42)! /*cfe.space=A(field: IntConstant(42))|Null*//*analyzer.space=A(field: int (42))|Null*/=> 0, _ /*space=()*/=> 1 }; @@ -46,6 +52,103 @@ subtypes={Object,Null}, type=Object? */switch (o2) { - A(field: 42)! /*space=??*/=> 0, + A(field: 42)! /*cfe.space=A(field: IntConstant(42))|Null*//*analyzer.space=A(field: int (42))|Null*/=> 0, }; -} \ No newline at end of file +} + +nullableBool(bool? b1, bool? b2) { + /* + expandedSubtypes={true,false,Null}, + fields={}, + subtypes={bool,Null}, + type=bool? + */ + switch (b1) { + /*space=true?*/ + case true!: + break; + /*space=false*/ + case false: + break; + } + /* + error=non-exhaustive:false, + expandedSubtypes={true,false,Null}, + fields={}, + subtypes={bool,Null}, + type=bool? + */ + switch (b2) { + /*space=true?*/ + case true!: + break; + } +} + +nullableA(A? a1, A? a2, A? a3) { + var a = /* + fields={}, + subtypes={A,Null}, + type=A? + */switch (a1) { + A()! /*space=A?*/=> 0, + }; + var b = /* + fields={}, + subtypes={A,Null}, + type=A? + */switch (a2) { + A(:var field)! /*space=A(field: int)|Null*/=> 0, + }; + var c = /* + error=non-exhaustive:A(field: int), + fields={}, + subtypes={A,Null}, + type=A? + */switch (a3) { + A(field: 42)! + /*cfe.space=A(field: IntConstant(42))|Null*/ + /*analyzer.space=A(field: int (42))|Null*/ + => 0, + }; +} + +nullableB(B? b1, B? b2, B? b3) { + /* + expandedSubtypes={C,D,Null}, + fields={}, + subtypes={B,Null}, + type=B? + */ + switch (b1) { + /*space=B?*/ + case B()!: + break; + } + /* + expandedSubtypes={C,D,Null}, + fields={}, + subtypes={B,Null}, + type=B? + */ + switch (b2) { + /*space=C?*/ + case C()!: + break; + /*space=D*/ + case D(): + break; + } + /* + error=non-exhaustive:D, + expandedSubtypes={C,D,Null}, + fields={}, + subtypes={B,Null}, + type=B? + */ + switch (b3) { + /*space=C?*/ + case C()!: + break; + } +}
diff --git a/pkg/analyzer/lib/src/generated/exhaustiveness.dart b/pkg/analyzer/lib/src/generated/exhaustiveness.dart index 37f7e43..72d5136 100644 --- a/pkg/analyzer/lib/src/generated/exhaustiveness.dart +++ b/pkg/analyzer/lib/src/generated/exhaustiveness.dart
@@ -380,8 +380,10 @@ return convertPattern(pattern.pattern, nonNull: true); } else if (pattern is ParenthesizedPattern) { return convertPattern(pattern.pattern, nonNull: nonNull); - } else if (pattern is NullAssertPattern || - pattern is CastPattern || + } else if (pattern is NullAssertPattern) { + Space space = convertPattern(pattern.pattern, nonNull: true); + return Space.union([space, Space.nullSpace]); + } else if (pattern is CastPattern || pattern is RelationalPattern || pattern is LogicalAndPattern) { // These pattern do not add to the exhaustiveness coverage.
diff --git a/pkg/front_end/lib/src/fasta/kernel/exhaustiveness.dart b/pkg/front_end/lib/src/fasta/kernel/exhaustiveness.dart index f7d7505..985fbbc 100644 --- a/pkg/front_end/lib/src/fasta/kernel/exhaustiveness.dart +++ b/pkg/front_end/lib/src/fasta/kernel/exhaustiveness.dart
@@ -473,8 +473,12 @@ } else if (pattern is NullCheckPattern) { return convertPatternToSpace(cache, pattern.pattern, constants, context, nonNull: true); - } else if (pattern is NullAssertPattern || - pattern is CastPattern || + } else if (pattern is NullAssertPattern) { + Space space = convertPatternToSpace( + cache, pattern.pattern, constants, context, + nonNull: true); + return new Space.union([space, Space.nullSpace]); + } else if (pattern is CastPattern || pattern is InvalidPattern || pattern is RelationalPattern || pattern is AndPattern) {