Add migration tool support for bare "return;" statements

Change-Id: I10e2adeaf3c682ccac011b307c43052178b0974a
Reviewed-on: https://dart-review.googlesource.com/c/93040
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Dan Rubel <danrubel@google.com>
diff --git a/pkg/analysis_server/lib/src/nullability/constraint_gatherer.dart b/pkg/analysis_server/lib/src/nullability/constraint_gatherer.dart
index e1e0f34..12f4ca5 100644
--- a/pkg/analysis_server/lib/src/nullability/constraint_gatherer.dart
+++ b/pkg/analysis_server/lib/src/nullability/constraint_gatherer.dart
@@ -49,6 +49,9 @@
   /// For convenience, a [DecoratedType] representing non-nullable `Type`.
   final DecoratedType _nonNullableTypeType;
 
+  /// For convenience, a [DecoratedType] representing `Null`.
+  final DecoratedType _nullType;
+
   /// The [DecoratedType] of the innermost function or method being visited, or
   /// `null` if the visitor is not inside any function or method.
   ///
@@ -72,7 +75,9 @@
       this._constraints, this._source, this._permissive, this.assumptions)
       : _notNullType = DecoratedType(typeProvider.objectType, null),
         _nonNullableBoolType = DecoratedType(typeProvider.boolType, null),
-        _nonNullableTypeType = DecoratedType(typeProvider.typeType, null);
+        _nonNullableTypeType = DecoratedType(typeProvider.typeType, null),
+        _nullType =
+            DecoratedType(typeProvider.nullType, ConstraintVariable.always);
 
   /// Gets the decorated type of [element] from [_variables], performing any
   /// necessary substitutions.
@@ -324,7 +329,7 @@
 
   @override
   DecoratedType visitNullLiteral(NullLiteral node) {
-    return DecoratedType(node.staticType, ConstraintVariable.always);
+    return _nullType;
   }
 
   @override
@@ -334,9 +339,11 @@
 
   @override
   DecoratedType visitReturnStatement(ReturnStatement node) {
-    // TODO(paulberry): handle implicit return
-    assert(node.expression != null); // TODO(paulberry)
-    _handleAssignment(_currentFunctionType.returnType, node.expression);
+    if (node.expression == null) {
+      _checkAssignment(_currentFunctionType.returnType, _nullType, null);
+    } else {
+      _handleAssignment(_currentFunctionType.returnType, node.expression);
+    }
     return null;
   }
 
@@ -444,6 +451,7 @@
   /// call sites.
   bool _isSimple(DecoratedType type) {
     if (type.type.isBottom) return true;
+    if (type.type.isVoid) return true;
     if (type.type is! InterfaceType) return false;
     if ((type.type as InterfaceType).typeParameters.isNotEmpty) return false;
     return true;
diff --git a/pkg/analysis_server/test/src/nullability/migration_visitor_test.dart b/pkg/analysis_server/test/src/nullability/migration_visitor_test.dart
index 12c9333..e64f358 100644
--- a/pkg/analysis_server/test/src/nullability/migration_visitor_test.dart
+++ b/pkg/analysis_server/test/src/nullability/migration_visitor_test.dart
@@ -552,6 +552,18 @@
         [ConstraintVariable.always], decoratedTypeAnnotation('int').nullable);
   }
 
+  test_return_implicit_null() async {
+    verifyNoTestUnitErrors = false;
+    await analyze('''
+int f() {
+  return;
+}
+''');
+
+    assertConstraint(
+        [ConstraintVariable.always], decoratedTypeAnnotation('int').nullable);
+  }
+
   test_return_null() async {
     await analyze('''
 int f() {