diff --git a/pkg/analyzer/lib/src/dart/resolver/invocation_inference_helper.dart b/pkg/analyzer/lib/src/dart/resolver/invocation_inference_helper.dart index da2e57c3272..29e89fe6c7e 100644 --- a/pkg/analyzer/lib/src/dart/resolver/invocation_inference_helper.dart +++ b/pkg/analyzer/lib/src/dart/resolver/invocation_inference_helper.dart @@ -217,7 +217,7 @@ class InvocationInferenceHelper { expression.staticType = DynamicTypeImpl.instance; } else { expression.staticType = type; - if (identical(type, NeverTypeImpl.instance)) { + if (_typeSystem.isBottom(type)) { _flowAnalysis?.flow?.handleExit(); } } diff --git a/pkg/analyzer/lib/src/generated/static_type_analyzer.dart b/pkg/analyzer/lib/src/generated/static_type_analyzer.dart index eafa02854f6..4c605a3393d 100644 --- a/pkg/analyzer/lib/src/generated/static_type_analyzer.dart +++ b/pkg/analyzer/lib/src/generated/static_type_analyzer.dart @@ -112,7 +112,7 @@ class StaticTypeAnalyzer extends SimpleAstVisitor { expression.staticType = _dynamicType; } else { expression.staticType = type; - if (identical(type, NeverTypeImpl.instance)) { + if (_typeSystem.isBottom(type)) { _flowAnalysis?.flow?.handleExit(); } } diff --git a/tests/language/nnbd/flow_analysis/unreachable_via_getter_get_error_test.dart b/tests/language/nnbd/flow_analysis/unreachable_via_getter_get_error_test.dart new file mode 100644 index 00000000000..fe6c7dba559 --- /dev/null +++ b/tests/language/nnbd/flow_analysis/unreachable_via_getter_get_error_test.dart @@ -0,0 +1,30 @@ +// Copyright (c) 2020, 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. + +// This test verifies that if a read is performed on a getter whose type is +// `Never?`, the resulting code block is considered reachable by flow analysis. + +// SharedOptions=--enable-experiment=non-nullable + +Never? get neverQuestionGetter => null; + +void explicitNeverQuestionType(Object x, bool b) { + if (x is! int) { + if (b) { + neverQuestionGetter; + } else { + return; + } + } + // Since the read of `neverQuestionGetter` was reachable, `x` is not promoted + // to `int`. + x.isEven; +// ^^^^^^ +// [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER +// [cfe] unspecified +} + +main() { + explicitNeverQuestionType(0, true); +} diff --git a/tests/language/nnbd/flow_analysis/unreachable_via_getter_get_test.dart b/tests/language/nnbd/flow_analysis/unreachable_via_getter_get_test.dart new file mode 100644 index 00000000000..af541803ce8 --- /dev/null +++ b/tests/language/nnbd/flow_analysis/unreachable_via_getter_get_test.dart @@ -0,0 +1,46 @@ +// Copyright (c) 2020, 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. + +// This test verifies that if a read is performed on a getter whose type is +// `Never`, the resulting code block is considered unreachable by flow analysis. + +// SharedOptions=--enable-experiment=non-nullable +import 'package:expect/expect.dart'; + +Never get neverGetter => throw 'x'; + +void explicitNeverType(Object x, bool b) { + if (x is! int) { + if (b) { + neverGetter; // Unreachable + } else { + return; + } + } + // Since the read of `neverGetter` was unreachable, `x` is now promoted to + // `int`. + Expect.isTrue(x.isEven); +} + +class TypeVarExtendsNever { + T get tGetter => throw 'x'; + + void test(Object x, bool b) { + if (x is! int) { + if (b) { + tGetter; // Unreachable + } else { + return; + } + } + // Since the read of `tGetter` was unreachable, `x` is now promoted to + // `int`. + Expect.isTrue(x.isEven); + } +} + +main() { + explicitNeverType(0, false); + TypeVarExtendsNever().test(0, false); +} diff --git a/tests/language/nnbd/flow_analysis/unreachable_via_invocation_error_test.dart b/tests/language/nnbd/flow_analysis/unreachable_via_invocation_error_test.dart new file mode 100644 index 00000000000..e5b9c32cba4 --- /dev/null +++ b/tests/language/nnbd/flow_analysis/unreachable_via_invocation_error_test.dart @@ -0,0 +1,30 @@ +// Copyright (c) 2020, 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. + +// This test verifies that if a method is invoked whose return type is `Never?`, +// the resulting code block is considered reachable by flow analysis. + +// SharedOptions=--enable-experiment=non-nullable + +Never? neverQuestionFunction() => null; + +void explicitNeverQuestionType(Object x, bool b) { + if (x is! int) { + if (b) { + neverQuestionFunction(); + } else { + return; + } + } + // Since completion of `neverQuestionFunction` was reachable, `x` is not + // promoted to `int`. + x.isEven; +// ^^^^^^ +// [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER +// [cfe] unspecified +} + +main() { + explicitNeverQuestionType(0, true); +} diff --git a/tests/language/nnbd/flow_analysis/unreachable_via_invocation_test.dart b/tests/language/nnbd/flow_analysis/unreachable_via_invocation_test.dart new file mode 100644 index 00000000000..d059c4da54c --- /dev/null +++ b/tests/language/nnbd/flow_analysis/unreachable_via_invocation_test.dart @@ -0,0 +1,46 @@ +// Copyright (c) 2020, 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. + +// This test verifies that if a method is invoked whose return type is `Never`, +// the resulting code block is considered unreachable by flow analysis. + +// SharedOptions=--enable-experiment=non-nullable +import 'package:expect/expect.dart'; + +Never neverFunction() => throw 'x'; + +void explicitNeverType(Object x, bool b) { + if (x is! int) { + if (b) { + neverFunction(); // Unreachable + } else { + return; + } + } + // Since completion of `neverFunction` was unreachable, `x` is now promoted to + // `int`. + Expect.isTrue(x.isEven); +} + +class TypeVarExtendsNever { + T tMethod() => throw 'x'; + + void test(Object x, bool b) { + if (x is! int) { + if (b) { + tMethod(); // Unreachable + } else { + return; + } + } + // Since completion of `tMethod` was unreachable, `x` is now promoted to + // `int`. + Expect.isTrue(x.isEven); + } +} + +main() { + explicitNeverType(0, false); + TypeVarExtendsNever().test(0, false); +} diff --git a/tests/language/nnbd/flow_analysis/unreachable_via_variable_get_error_test.dart b/tests/language/nnbd/flow_analysis/unreachable_via_variable_get_error_test.dart new file mode 100644 index 00000000000..2f7e6902117 --- /dev/null +++ b/tests/language/nnbd/flow_analysis/unreachable_via_variable_get_error_test.dart @@ -0,0 +1,28 @@ +// Copyright (c) 2020, 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. + +// This test verifies that if a read is performed on a variable whose type is +// `Never?`, the resulting code block is considered reachable by flow analysis. + +// SharedOptions=--enable-experiment=non-nullable + +void explicitNeverQuestionType(Object x, bool b) { + Never? y = null; + if (x is! int) { + if (b) { + y; + } else { + return; + } + } + // Since the read of `y` was reachable, `x` is not promoted to `int`. + x.isEven; +// ^^^^^^ +// [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER +// [cfe] unspecified +} + +main() { + explicitNeverQuestionType(0, true); +} diff --git a/tests/language/nnbd/flow_analysis/unreachable_via_variable_get_test.dart b/tests/language/nnbd/flow_analysis/unreachable_via_variable_get_test.dart new file mode 100644 index 00000000000..333029278e1 --- /dev/null +++ b/tests/language/nnbd/flow_analysis/unreachable_via_variable_get_test.dart @@ -0,0 +1,53 @@ +// Copyright (c) 2020, 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. + +// This test verifies that if a read is performed on a variable whose type is +// `Never`, the resulting code block is considered unreachable by flow analysis. + +// SharedOptions=--enable-experiment=non-nullable +import 'package:expect/expect.dart'; + +void explicitNeverType(Never Function() f, Object x, bool b1, bool b2) { + late Never y; + // Loop so that flow analysis no longer can tell that y is definitely + // unassigned + while (true) { + if (x is! int) { + if (b1) { + y; // Unreachable + } else { + return; + } + } + // Since the read of `y` was unreachable, `x` is now promoted to `int`. + Expect.isTrue(x.isEven); + if (b2) return; + y = f(); + } +} + +void typeVarExtendsNever( + T Function() f, Object x, bool b1, bool b2) { + late T y; + // Loop so that flow analysis no longer can tell that y is definitely + // unassigned + while (true) { + if (x is! int) { + if (b1) { + y; // Unreachable + } else { + return; + } + } + // Since the read of `y` was unreachable, `x` is now promoted to `int`. + Expect.isTrue(x.isEven); + if (b2) return; + y = f(); + } +} + +main() { + explicitNeverType(() => throw 'x', 0, false, true); + typeVarExtendsNever(() => throw 'x', 0, false, true); +}