Add migration support for the `@required` annotation.

If a named parameter is annotated as `@required`, then this overrides
the NamedNoDefaultParameterHeuristic; the parameter is considered
required regardless of what happens at call sites.  A duplicate
`@required` annotation is not inserted.

Change-Id: Ib1385d0a65dd9001bb7abede9de2a319f65a1f86
Reviewed-on: https://dart-review.googlesource.com/c/92844
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Dan Rubel <danrubel@google.com>
Commit-Queue: Paul Berry <paulberry@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 57d05a6..e1e0f34 100644
--- a/pkg/analysis_server/lib/src/nullability/constraint_gatherer.dart
+++ b/pkg/analysis_server/lib/src/nullability/constraint_gatherer.dart
@@ -189,7 +189,10 @@
   DecoratedType visitDefaultFormalParameter(DefaultFormalParameter node) {
     var defaultValue = node.defaultValue;
     if (defaultValue == null) {
-      if (node.declaredElement.isOptionalPositional ||
+      if (node.declaredElement.hasRequired) {
+        // Nothing to do; the implicit default value of `null` will never be
+        // reached.
+      } else if (node.declaredElement.isOptionalPositional ||
           assumptions.namedNoDefaultParameterHeuristic ==
               NamedNoDefaultParameterHeuristic.assumeNullable) {
         _recordFact(getOrComputeElementType(node.declaredElement).nullable);
diff --git a/pkg/analysis_server/lib/src/nullability/constraint_variable_gatherer.dart b/pkg/analysis_server/lib/src/nullability/constraint_variable_gatherer.dart
index 08db78d..1715526 100644
--- a/pkg/analysis_server/lib/src/nullability/constraint_variable_gatherer.dart
+++ b/pkg/analysis_server/lib/src/nullability/constraint_variable_gatherer.dart
@@ -58,14 +58,19 @@
   DecoratedType visitDefaultFormalParameter(DefaultFormalParameter node) {
     var decoratedType = node.parameter.accept(this);
     ConstraintVariable optional;
-    if (node.defaultValue != null) {
+    if (node.declaredElement.hasRequired) {
+      optional = null;
+    } else if (node.defaultValue != null) {
       optional = ConstraintVariable.always;
     } else {
       optional = decoratedType.nullable;
       _variables.recordPossiblyOptional(_source, node, optional);
     }
-    _currentFunctionType
-        .namedParameterOptionalVariables[node.declaredElement.name] = optional;
+    if (optional != null) {
+      _currentFunctionType
+              .namedParameterOptionalVariables[node.declaredElement.name] =
+          optional;
+    }
     return null;
   }
 
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 ddfc799..12c9333 100644
--- a/pkg/analysis_server/test/src/nullability/migration_visitor_test.dart
+++ b/pkg/analysis_server/test/src/nullability/migration_visitor_test.dart
@@ -265,6 +265,32 @@
     assertNoConstraints(decoratedTypeAnnotation('int').nullable);
   }
 
+  test_functionDeclaration_parameter_named_no_default_required_assume_nullable() async {
+    addMetaPackage();
+    await analyze('''
+import 'package:meta/meta.dart';
+void f({@required int i}) {}
+''',
+        assumptions: NullabilityMigrationAssumptions(
+            namedNoDefaultParameterHeuristic:
+                NamedNoDefaultParameterHeuristic.assumeNullable));
+
+    assertNoConstraints(decoratedTypeAnnotation('int').nullable);
+  }
+
+  test_functionDeclaration_parameter_named_no_default_required_assume_required() async {
+    addMetaPackage();
+    await analyze('''
+import 'package:meta/meta.dart';
+void f({@required int i}) {}
+''',
+        assumptions: NullabilityMigrationAssumptions(
+            namedNoDefaultParameterHeuristic:
+                NamedNoDefaultParameterHeuristic.assumeRequired));
+
+    assertNoConstraints(decoratedTypeAnnotation('int').nullable);
+  }
+
   test_functionDeclaration_parameter_positionalOptional_default_notNull() async {
     await analyze('''
 void f([int i = 1]) {}
@@ -327,7 +353,7 @@
     assertConstraint([nullable_j], nullable_i);
   }
 
-  test_functionInvocation_parameter_named_optional() async {
+  test_functionInvocation_parameter_named_missing() async {
     await analyze('''
 void f({int i}) {}
 void g() {
@@ -338,6 +364,23 @@
     assertConstraint([], optional_i);
   }
 
+  test_functionInvocation_parameter_named_missing_required() async {
+    addMetaPackage();
+    verifyNoTestUnitErrors = false;
+    await analyze('''
+import 'package:meta/meta.dart';
+void f({@required int i}) {}
+void g() {
+  f();
+}
+''');
+    // The call at `f()` is presumed to be in error; no constraint is recorded.
+    var optional_i = possiblyOptionalParameter('int i');
+    expect(optional_i, isNull);
+    var nullable_i = decoratedTypeAnnotation('int i').nullable;
+    assertNoConstraints(nullable_i);
+  }
+
   test_functionInvocation_parameter_null() async {
     await analyze('''
 void f(int i) {}
@@ -638,6 +681,20 @@
         same(decoratedType.nullable));
   }
 
+  test_topLevelFunction_parameterType_named_no_default_required() async {
+    addMetaPackage();
+    await analyze('''
+import 'package:meta/meta.dart';
+void f({@required String s}) {}
+''');
+    var decoratedType = decoratedTypeAnnotation('String');
+    var functionType = decoratedFunctionType('f');
+    expect(functionType.namedParameters['s'], same(decoratedType));
+    expect(decoratedType.nullable, isNotNull);
+    expect(decoratedType.nullable, isNot(same(ConstraintVariable.always)));
+    expect(functionType.namedParameterOptionalVariables['s'], isNull);
+  }
+
   test_topLevelFunction_parameterType_named_with_default() async {
     await analyze('''
 void f({String s: 'x'}) {}
diff --git a/pkg/analysis_server/test/src/nullability/provisional_api_test.dart b/pkg/analysis_server/test/src/nullability/provisional_api_test.dart
index ba2915f..8f9f86e 100644
--- a/pkg/analysis_server/test/src/nullability/provisional_api_test.dart
+++ b/pkg/analysis_server/test/src/nullability/provisional_api_test.dart
@@ -274,6 +274,55 @@
                 NamedNoDefaultParameterHeuristic.assumeRequired));
   }
 
+  test_named_parameter_no_default_unused_required_option2_assume_nullable() async {
+    // The `@required` annotation overrides the assumption of nullability.
+    // The call at `f()` is presumed to be in error.
+    addMetaPackage();
+    var content = '''
+import 'package:meta/meta.dart';
+void f({@required String s}) {}
+main() {
+  f();
+}
+''';
+    var expected = '''
+import 'package:meta/meta.dart';
+void f({@required String s}) {}
+main() {
+  f();
+}
+''';
+    await _checkSingleFileChanges(content, expected,
+        assumptions: NullabilityMigrationAssumptions(
+            namedNoDefaultParameterHeuristic:
+                NamedNoDefaultParameterHeuristic.assumeNullable));
+  }
+
+  test_named_parameter_no_default_unused_required_option2_assume_required() async {
+    // Since the `@required` annotation is already present, it is not added
+    // again.
+    // The call at `f()` is presumed to be in error.
+    addMetaPackage();
+    var content = '''
+import 'package:meta/meta.dart';
+void f({@required String s}) {}
+main() {
+  f();
+}
+''';
+    var expected = '''
+import 'package:meta/meta.dart';
+void f({@required String s}) {}
+main() {
+  f();
+}
+''';
+    await _checkSingleFileChanges(content, expected,
+        assumptions: NullabilityMigrationAssumptions(
+            namedNoDefaultParameterHeuristic:
+                NamedNoDefaultParameterHeuristic.assumeRequired));
+  }
+
   test_named_parameter_no_default_used_non_null_option2_assume_nullable() async {
     var content = '''
 void f({String s}) {}
@@ -312,6 +361,30 @@
                 NamedNoDefaultParameterHeuristic.assumeRequired));
   }
 
+  test_named_parameter_no_default_used_non_null_required_option2_assume_required() async {
+    // Even if we are using the "assumeRequired" heuristic, we should not add a
+    // duplicate `@required` annotation.
+    addMetaPackage();
+    var content = '''
+import 'package:meta/meta.dart';
+void f({@required String s}) {}
+main() {
+  f(s: 'x');
+}
+''';
+    var expected = '''
+import 'package:meta/meta.dart';
+void f({@required String s}) {}
+main() {
+  f(s: 'x');
+}
+''';
+    await _checkSingleFileChanges(content, expected,
+        assumptions: NullabilityMigrationAssumptions(
+            namedNoDefaultParameterHeuristic:
+                NamedNoDefaultParameterHeuristic.assumeRequired));
+  }
+
   test_named_parameter_no_default_used_null_option2() async {
     var content = '''
 void f({String s}) {}
@@ -328,6 +401,54 @@
     await _checkSingleFileChanges(content, expected);
   }
 
+  test_named_parameter_no_default_used_null_required_option2_assume_nullable() async {
+    // Explicitly passing `null` forces the parameter to be nullable even though
+    // it is required.
+    addMetaPackage();
+    var content = '''
+import 'package:meta/meta.dart';
+void f({@required String s}) {}
+main() {
+  f(s: null);
+}
+''';
+    var expected = '''
+import 'package:meta/meta.dart';
+void f({@required String? s}) {}
+main() {
+  f(s: null);
+}
+''';
+    await _checkSingleFileChanges(content, expected,
+        assumptions: NullabilityMigrationAssumptions(
+            namedNoDefaultParameterHeuristic:
+                NamedNoDefaultParameterHeuristic.assumeNullable));
+  }
+
+  test_named_parameter_no_default_used_null_required_option2_assume_required() async {
+    // Explicitly passing `null` forces the parameter to be nullable even though
+    // it is required.
+    addMetaPackage();
+    var content = '''
+import 'package:meta/meta.dart';
+void f({@required String s}) {}
+main() {
+  f(s: null);
+}
+''';
+    var expected = '''
+import 'package:meta/meta.dart';
+void f({@required String? s}) {}
+main() {
+  f(s: null);
+}
+''';
+    await _checkSingleFileChanges(content, expected,
+        assumptions: NullabilityMigrationAssumptions(
+            namedNoDefaultParameterHeuristic:
+                NamedNoDefaultParameterHeuristic.assumeRequired));
+  }
+
   test_named_parameter_with_non_null_default_unused_option2() async {
     var content = '''
 void f({String s: 'foo'}) {}