Add summary deserialization support for nullability.

In a follow-up CL I will add nullability support to the summary
linker.

Change-Id: I99f3dce1a695da65c7c09c11e73462c93aeb637f
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/101882
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/analyzer/lib/src/summary/resynthesize.dart b/pkg/analyzer/lib/src/summary/resynthesize.dart
index 6f8720e..b364311 100644
--- a/pkg/analyzer/lib/src/summary/resynthesize.dart
+++ b/pkg/analyzer/lib/src/summary/resynthesize.dart
@@ -1343,17 +1343,18 @@
         type = refinedType;
       }
     }
+    DartType result;
     if (type.paramReference != 0) {
-      return context.typeParameterContext
+      result = context.typeParameterContext
           .getTypeParameterType(type.paramReference);
     } else if (type.entityKind == EntityRefKind.genericFunctionType) {
       GenericFunctionTypeElement element =
           new GenericFunctionTypeElementImpl.forSerialized(context, type);
-      return element.type;
+      result = element.type;
     } else if (type.syntheticReturnType != null) {
       FunctionElementImpl element =
           new FunctionElementImpl_forLUB(context, type);
-      return element.type;
+      result = element.type;
     } else {
       DartType getTypeArgument(int i) {
         if (i < type.typeArguments.length) {
@@ -1369,12 +1370,18 @@
         return DynamicTypeImpl.instance;
       }
 
-      return referenceInfo.buildType(
+      result = referenceInfo.buildType(
           instantiateToBoundsAllowed,
           type.typeArguments.length,
           getTypeArgument,
           type.implicitFunctionTypeIndices);
     }
+    var nullabilitySuffix = _translateNullabilitySuffix(type.nullabilitySuffix);
+    var resultAsImpl = result as TypeImpl;
+    if (resultAsImpl.nullabilitySuffix != nullabilitySuffix) {
+      result = resultAsImpl.withNullability(nullabilitySuffix);
+    }
+    return result;
   }
 
   @override
@@ -1621,6 +1628,19 @@
     return null;
   }
 
+  NullabilitySuffix _translateNullabilitySuffix(
+      EntityRefNullabilitySuffix suffix) {
+    switch (suffix) {
+      case EntityRefNullabilitySuffix.none:
+        return NullabilitySuffix.none;
+      case EntityRefNullabilitySuffix.question:
+        return NullabilitySuffix.question;
+      case EntityRefNullabilitySuffix.starOrIrrelevant:
+        return NullabilitySuffix.star;
+    }
+    throw new StateError('Unrecognized nullability suffix');
+  }
+
   /**
    * If the given [kind] is a top-level or class member property accessor, and
    * the given [name] does not end with `=`, i.e. does not denote a setter,
diff --git a/pkg/analyzer/test/src/summary/element_text.dart b/pkg/analyzer/test/src/summary/element_text.dart
index 731f012..9629b2a 100644
--- a/pkg/analyzer/test/src/summary/element_text.dart
+++ b/pkg/analyzer/test/src/summary/element_text.dart
@@ -59,7 +59,8 @@
     bool withOffsets: false,
     bool withSyntheticAccessors: false,
     bool withSyntheticFields: false,
-    bool withTypes: false}) {
+    bool withTypes: false,
+    bool annotateNullability: false}) {
   var writer = new _ElementWriter(
       withCodeRanges: withCodeRanges,
       withConstElements: withConstElements,
@@ -67,7 +68,8 @@
       withOffsets: withOffsets,
       withSyntheticAccessors: withSyntheticAccessors,
       withSyntheticFields: withSyntheticFields,
-      withTypes: withTypes);
+      withTypes: withTypes,
+      annotateNullability: annotateNullability);
   writer.writeLibraryElement(library);
 
   String actualText = writer.buffer.toString();
@@ -135,6 +137,7 @@
   final bool withSyntheticAccessors;
   final bool withSyntheticFields;
   final bool withTypes;
+  final bool annotateNullability;
   final StringBuffer buffer = new StringBuffer();
 
   _ElementWriter(
@@ -144,7 +147,8 @@
       this.withOffsets: false,
       this.withSyntheticAccessors: false,
       this.withSyntheticFields: false,
-      this.withTypes: false});
+      this.withTypes: false,
+      this.annotateNullability: false});
 
   bool isDynamicType(DartType type) => type is DynamicTypeImpl;
 
@@ -947,6 +951,18 @@
     } else {
       buffer.write(type.displayName);
     }
+    if (annotateNullability) {
+      switch ((type as TypeImpl).nullabilitySuffix) {
+        case NullabilitySuffix.none:
+          break;
+        case NullabilitySuffix.question:
+          buffer.write('?');
+          break;
+        case NullabilitySuffix.star:
+          buffer.write('*');
+          break;
+      }
+    }
   }
 
   void writeType2(DartType type) {
diff --git a/pkg/analyzer/test/src/summary/resynthesize_ast2_test.dart b/pkg/analyzer/test/src/summary/resynthesize_ast2_test.dart
index 69d4a5b..352acf3 100644
--- a/pkg/analyzer/test/src/summary/resynthesize_ast2_test.dart
+++ b/pkg/analyzer/test/src/summary/resynthesize_ast2_test.dart
@@ -114,12 +114,41 @@
 
   @override
   @failingTest
+  test_class_ref_nullability_none() => super.test_class_ref_nullability_none();
+
+  @override
+  @failingTest
+  test_class_ref_nullability_question() =>
+      super.test_class_ref_nullability_question();
+
+  @override
+  @failingTest
+  test_generic_function_type_nullability_none() =>
+      super.test_generic_function_type_nullability_none();
+
+  @override
+  @failingTest
+  test_generic_function_type_nullability_question() =>
+      super.test_generic_function_type_nullability_question();
+
+  @override
+  @failingTest
   test_syntheticFunctionType_genericClosure() async {
     // TODO(scheglov) Bug in TypeSystem.getLeastUpperBound().
     // LUB(<T>(T) → int, <T>(T) → int) gives `(T) → int`, note absence of `<T>`.
     await super.test_syntheticFunctionType_genericClosure();
   }
 
+  @override
+  @failingTest
+  test_type_param_ref_nullability_none() =>
+      super.test_type_param_ref_nullability_none();
+
+  @override
+  @failingTest
+  test_type_param_ref_nullability_question() =>
+      super.test_type_param_ref_nullability_question();
+
   void _addLibraryUnits(
     Source definingSource,
     CompilationUnit definingUnit,
diff --git a/pkg/analyzer/test/src/summary/resynthesize_common.dart b/pkg/analyzer/test/src/summary/resynthesize_common.dart
index 817015b..c2d13b7 100644
--- a/pkg/analyzer/test/src/summary/resynthesize_common.dart
+++ b/pkg/analyzer/test/src/summary/resynthesize_common.dart
@@ -204,6 +204,8 @@
 /// applied to a class implementing [ResynthesizeTestStrategy], along with the
 /// mixin [ResynthesizeTestHelpers].
 mixin ResynthesizeTestCases implements ResynthesizeTestHelpers {
+  FeatureSet get disableNnbd => FeatureSet.forTesting(sdkVersion: '2.2.2');
+
   FeatureSet get enableNnbd =>
       FeatureSet.forTesting(additionalFeatures: [Feature.non_nullable]);
 
@@ -1476,6 +1478,54 @@
 ''');
   }
 
+  test_class_ref_nullability_none() async {
+    featureSet = enableNnbd;
+    var library = await checkLibrary('''
+class C {}
+C c;
+''');
+    checkElementText(
+        library,
+        '''
+class C {
+}
+C c;
+''',
+        annotateNullability: true);
+  }
+
+  test_class_ref_nullability_question() async {
+    featureSet = enableNnbd;
+    var library = await checkLibrary('''
+class C {}
+C? c;
+''');
+    checkElementText(
+        library,
+        '''
+class C {
+}
+C? c;
+''',
+        annotateNullability: true);
+  }
+
+  test_class_ref_nullability_star() async {
+    featureSet = disableNnbd;
+    var library = await checkLibrary('''
+class C {}
+C c;
+''');
+    checkElementText(
+        library,
+        '''
+class C {
+}
+C* c;
+''',
+        annotateNullability: true);
+  }
+
   test_class_setter_abstract() async {
     var library =
         await checkLibrary('abstract class C { void set x(int value); }');
@@ -6102,6 +6152,45 @@
     expect(y.type.toString(), 'dynamic');
   }
 
+  test_generic_function_type_nullability_none() async {
+    featureSet = enableNnbd;
+    var library = await checkLibrary('''
+void Function() f;
+''');
+    checkElementText(
+        library,
+        '''
+void Function() f;
+''',
+        annotateNullability: true);
+  }
+
+  test_generic_function_type_nullability_question() async {
+    featureSet = enableNnbd;
+    var library = await checkLibrary('''
+void Function()? f;
+''');
+    checkElementText(
+        library,
+        '''
+void Function()? f;
+''',
+        annotateNullability: true);
+  }
+
+  test_generic_function_type_nullability_star() async {
+    featureSet = disableNnbd;
+    var library = await checkLibrary('''
+void Function() f;
+''');
+    checkElementText(
+        library,
+        '''
+void Function()* f;
+''',
+        annotateNullability: true);
+  }
+
   test_generic_gClass_gMethodStatic() async {
     var library = await checkLibrary('''
 class C<T, U> {
@@ -9211,6 +9300,57 @@
 ''');
   }
 
+  test_type_param_ref_nullability_none() async {
+    featureSet = enableNnbd;
+    var library = await checkLibrary('''
+class C<T> {
+  T t;
+}
+''');
+    checkElementText(
+        library,
+        '''
+class C<T> {
+  T t;
+}
+''',
+        annotateNullability: true);
+  }
+
+  test_type_param_ref_nullability_question() async {
+    featureSet = enableNnbd;
+    var library = await checkLibrary('''
+class C<T> {
+  T? t;
+}
+''');
+    checkElementText(
+        library,
+        '''
+class C<T> {
+  T? t;
+}
+''',
+        annotateNullability: true);
+  }
+
+  test_type_param_ref_nullability_star() async {
+    featureSet = disableNnbd;
+    var library = await checkLibrary('''
+class C<T> {
+  T t;
+}
+''');
+    checkElementText(
+        library,
+        '''
+class C<T> {
+  T* t;
+}
+''',
+        annotateNullability: true);
+  }
+
   test_type_reference_lib_to_lib() async {
     var library = await checkLibrary('''
 class C {}