Report when a potentially nullable type is used in an on clause

Change-Id: I6e9e779d5bb0f37cfd71d4d2dc38c1511c03d59f
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/99484
Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analyzer/lib/error/error.dart b/pkg/analyzer/lib/error/error.dart
index 3b88ce1..6d2713d 100644
--- a/pkg/analyzer/lib/error/error.dart
+++ b/pkg/analyzer/lib/error/error.dart
@@ -235,6 +235,7 @@
   CompileTimeErrorCode.NO_ANNOTATION_CONSTRUCTOR_ARGUMENTS,
   CompileTimeErrorCode.NO_DEFAULT_SUPER_CONSTRUCTOR_EXPLICIT,
   CompileTimeErrorCode.NO_DEFAULT_SUPER_CONSTRUCTOR_IMPLICIT,
+  CompileTimeErrorCode.NULLABLE_TYPE_IN_CATCH_CLAUSE,
   CompileTimeErrorCode.NULLABLE_TYPE_IN_EXTENDS_CLAUSE,
   CompileTimeErrorCode.NULLABLE_TYPE_IN_IMPLEMENTS_CLAUSE,
   CompileTimeErrorCode.NULLABLE_TYPE_IN_ON_CLAUSE,
diff --git a/pkg/analyzer/lib/src/error/codes.dart b/pkg/analyzer/lib/src/error/codes.dart
index f3a75cb..5eda88c4 100644
--- a/pkg/analyzer/lib/src/error/codes.dart
+++ b/pkg/analyzer/lib/src/error/codes.dart
@@ -2203,6 +2203,17 @@
               "making the called constructor not be a factory constructor.");
 
   /**
+   * It is an error if the type `T` in the on-catch clause `on T catch` is
+   * potentially nullable.
+   */
+  static const CompileTimeErrorCode NULLABLE_TYPE_IN_CATCH_CLAUSE =
+      const CompileTimeErrorCode(
+          'NULLABLE_TYPE_IN_CATCH_CLAUSE',
+          "A nullable type cannot be used in an 'on' clause because it isn't valid "
+              "to throw 'null'.",
+          correction: "Try removing the question mark.");
+
+  /**
    * It is a compile-time error for a class to extend, implement, or mixin a
    * type of the form T? for any T.
    */
diff --git a/pkg/analyzer/lib/src/generated/error_verifier.dart b/pkg/analyzer/lib/src/generated/error_verifier.dart
index 669874e..7f2f741 100644
--- a/pkg/analyzer/lib/src/generated/error_verifier.dart
+++ b/pkg/analyzer/lib/src/generated/error_verifier.dart
@@ -473,6 +473,7 @@
     try {
       _isInCatchClause = true;
       _checkForTypeAnnotationDeferredClass(node.exceptionType);
+      _checkForPotentiallyNullableType(node.exceptionType);
       super.visitCatchClause(node);
     } finally {
       _isInCatchClause = previousIsInCatchClause;
@@ -4718,6 +4719,18 @@
   }
 
   /**
+   * Verify that the [type] is not potentially nullable.
+   */
+  void _checkForPotentiallyNullableType(TypeAnnotation type) {
+    if (_options.experimentStatus.non_nullable &&
+        type?.type != null &&
+        _typeSystem.isPotentiallyNullable(type.type)) {
+      _errorReporter.reportErrorForNode(
+          CompileTimeErrorCode.NULLABLE_TYPE_IN_CATCH_CLAUSE, type);
+    }
+  }
+
+  /**
    * Check that the given named optional [parameter] does not begin with '_'.
    *
    * See [CompileTimeErrorCode.PRIVATE_OPTIONAL_PARAMETER].
@@ -5195,7 +5208,7 @@
   }
 
   /**
-   * Verify that the given type [name] is not a deferred type.
+   * Verify that the [type] is not a deferred type.
    *
    * See [StaticWarningCode.TYPE_ANNOTATION_DEFERRED_CLASS].
    */
diff --git a/pkg/analyzer/test/src/diagnostics/nullable_type_in_catch_clause_test.dart b/pkg/analyzer/test/src/diagnostics/nullable_type_in_catch_clause_test.dart
new file mode 100644
index 0000000..b500cd6
--- /dev/null
+++ b/pkg/analyzer/test/src/diagnostics/nullable_type_in_catch_clause_test.dart
@@ -0,0 +1,60 @@
+// Copyright (c) 2019, 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.
+
+import 'package:analyzer/src/dart/analysis/experiments.dart';
+import 'package:analyzer/src/error/codes.dart';
+import 'package:analyzer/src/generated/engine.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../dart/resolution/driver_resolution.dart';
+
+main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(NullableTypeInCatchClauseTest);
+  });
+}
+
+@reflectiveTest
+class NullableTypeInCatchClauseTest extends DriverResolutionTest {
+  @override
+  AnalysisOptionsImpl get analysisOptions =>
+      AnalysisOptionsImpl()..enabledExperiments = [EnableString.non_nullable];
+
+  test_noOnClause() async {
+    assertNoErrorsInCode('''
+f() {
+  try {
+  } catch (e) {
+  }
+}
+''');
+  }
+
+  test_on_class() async {
+    assertErrorsInCode('''
+class A {}
+f() {
+  try {
+  } on A? {
+  }
+}
+''', [
+      error(CompileTimeErrorCode.NULLABLE_TYPE_IN_CATCH_CLAUSE, 32, 2),
+    ]);
+  }
+
+  test_on_typeParameter() async {
+    assertErrorsInCode('''
+class A<B> {
+  m() {
+    try {
+    } on B {
+    }
+  }
+}
+''', [
+      error(CompileTimeErrorCode.NULLABLE_TYPE_IN_CATCH_CLAUSE, 40, 1),
+    ]);
+  }
+}
diff --git a/pkg/analyzer/test/src/diagnostics/test_all.dart b/pkg/analyzer/test/src/diagnostics/test_all.dart
index 14f3f20..56a1409 100644
--- a/pkg/analyzer/test/src/diagnostics/test_all.dart
+++ b/pkg/analyzer/test/src/diagnostics/test_all.dart
@@ -86,6 +86,8 @@
 import 'null_aware_in_condition_test.dart' as null_aware_in_condition;
 import 'null_aware_in_logical_operator_test.dart'
     as null_aware_in_logical_operator;
+import 'nullable_type_in_catch_clause_test.dart'
+    as nullable_type_in_catch_clause;
 import 'nullable_type_in_extends_clause_test.dart'
     as nullable_type_in_extends_clause;
 import 'nullable_type_in_implements_clause_test.dart'
@@ -208,6 +210,7 @@
     null_aware_before_operator.main();
     null_aware_in_condition.main();
     null_aware_in_logical_operator.main();
+    nullable_type_in_catch_clause.main();
     nullable_type_in_extends_clause.main();
     nullable_type_in_implements_clause.main();
     nullable_type_in_on_clause.main();