[release/6.0] Set generic type arguments nullability for value types (#58390)
authorgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Tue, 14 Sep 2021 06:52:58 +0000 (23:52 -0700)
committerGitHub <noreply@github.com>
Tue, 14 Sep 2021 06:52:58 +0000 (23:52 -0700)
* Set generic type arguments nullability for value types

* Skip test on mono

* Apply commment

* Separate var name parts with _

* Reflection nullability API: improve test coverage, fix bug found (#58479)

* Add/remove some tests, fix generics indexing bug

Co-authored-by: Buyaa Namnan <bunamnan@microsoft.com>
src/libraries/System.Private.CoreLib/src/System/Reflection/NullabilityInfoContext.cs
src/libraries/System.Runtime/tests/System/Reflection/NullabilityInfoContextTests.cs

index 203bafd..b09bb83 100644 (file)
@@ -347,19 +347,23 @@ namespace System.Reflection
         private NullabilityInfo GetNullabilityInfo(MemberInfo memberInfo, Type type, IList<CustomAttributeData> customAttributes, int index)
         {
             NullabilityState state = NullabilityState.Unknown;
+            NullabilityInfo? elementState = null;
+            NullabilityInfo[] genericArgumentsState = Array.Empty<NullabilityInfo>();
+            Type? underlyingType = type;
 
             if (type.IsValueType)
             {
-                if (Nullable.GetUnderlyingType(type) != null)
+                underlyingType = Nullable.GetUnderlyingType(type);
+
+                if (underlyingType != null)
                 {
                     state = NullabilityState.Nullable;
                 }
                 else
                 {
+                    underlyingType = type;
                     state = NullabilityState.NotNull;
                 }
-
-                return new NullabilityInfo(type, state, state, null, Array.Empty<NullabilityInfo>());
             }
             else
             {
@@ -368,32 +372,38 @@ namespace System.Reflection
                     state = GetNullableContext(memberInfo);
                 }
 
-                NullabilityInfo? elementState = null;
-                NullabilityInfo[]? genericArgumentsState = null;
-
                 if (type.IsArray)
                 {
                     elementState = GetNullabilityInfo(memberInfo, type.GetElementType()!, customAttributes, index + 1);
                 }
-                else if (type.IsGenericType)
+            }
+
+            if (underlyingType.IsGenericType)
+            {
+                Type[] genericArguments = underlyingType.GetGenericArguments();
+                genericArgumentsState = new NullabilityInfo[genericArguments.Length];
+
+                for (int i = 0, offset = 0; i < genericArguments.Length; i++)
                 {
-                    Type[] genericArguments = type.GetGenericArguments();
-                    genericArgumentsState = new NullabilityInfo[genericArguments.Length];
+                    Type t = Nullable.GetUnderlyingType(genericArguments[i]) ?? genericArguments[i];
 
-                    for (int i = 0; i < genericArguments.Length; i++)
+                    if (!t.IsValueType || t.IsGenericType)
                     {
-                        genericArgumentsState[i] = GetNullabilityInfo(memberInfo, genericArguments[i], customAttributes, i + 1);
+                        offset++;
                     }
-                }
 
-                NullabilityInfo nullability = new NullabilityInfo(type, state, state, elementState, genericArgumentsState ?? Array.Empty<NullabilityInfo>());
-                if (state != NullabilityState.Unknown)
-                {
-                    TryLoadGenericMetaTypeNullability(memberInfo, nullability);
+                    genericArgumentsState[i] = GetNullabilityInfo(memberInfo, genericArguments[i], customAttributes, index + offset);
                 }
+            }
+
+            NullabilityInfo nullability = new NullabilityInfo(type, state, state, elementState, genericArgumentsState);
 
-                return nullability;
+            if (!type.IsValueType && state != NullabilityState.Unknown)
+            {
+                TryLoadGenericMetaTypeNullability(memberInfo, nullability);
             }
+
+            return nullability;
         }
 
         private static bool ParseNullableState(IList<CustomAttributeData> customAttributes, int index, ref NullabilityState state)
@@ -472,7 +482,7 @@ namespace System.Reflection
             {
                 NullabilityState state = nullability.ReadState;
 
-                if (!ParseNullableState(metaType.GetCustomAttributesData(), 0, ref state))
+                if (state == NullabilityState.NotNull && !ParseNullableState(metaType.GetCustomAttributesData(), 0, ref state))
                 {
                     state = GetNullableContext(metaType);
                 }
index 1651b18..f444a56 100644 (file)
@@ -33,7 +33,7 @@ namespace System.Reflection.Tests
             yield return new object[] { "FieldAllowNull2", NullabilityState.NotNull, NullabilityState.Nullable, typeof(string) };
             yield return new object[] { "FieldNotNull", NullabilityState.NotNull, NullabilityState.Nullable, typeof(string) };
             yield return new object[] { "FieldMaybeNull", NullabilityState.Nullable, NullabilityState.NotNull, typeof(string) };
-            yield return new object[] { "FieldMaybeNull", NullabilityState.Nullable, NullabilityState.NotNull, typeof(string) };
+            yield return new object[] { "FieldMaybeNull2", NullabilityState.Nullable, NullabilityState.NotNull, typeof(string) };
             yield return new object[] { "FieldNotNull2", NullabilityState.NotNull, NullabilityState.Nullable, typeof(string) };
         }
 
@@ -121,7 +121,7 @@ namespace System.Reflection.Tests
         }
 
         [Theory]
-        [SkipOnMono("Disabling NullablePublicOnly feature not work for Mono tests")]
+        [SkipOnMono("Nullability attributes trimmed on Mono")]
         [MemberData(nameof(ArrayPropertyTestData))]
         public void ArrayPropertyTest(string propertyName, NullabilityState elementState, NullabilityState propertyState)
         {
@@ -143,7 +143,7 @@ namespace System.Reflection.Tests
         }
 
         [Theory]
-        [SkipOnMono("Disabling NullablePublicOnly feature not work for Mono tests")]
+        [SkipOnMono("Nullability attributes trimmed on Mono")]
         [MemberData(nameof(GenericArrayPropertyTestData))]
         public void GenericArrayPropertyTest(string propertyName, NullabilityState elementState, NullabilityState propertyState)
         {
@@ -167,7 +167,7 @@ namespace System.Reflection.Tests
         }
 
         [Theory]
-        [SkipOnMono("Disabling NullablePublicOnly feature not work for Mono tests")]
+        [SkipOnMono("Nullability attributes trimmed on Mono")]
         [MemberData(nameof(JaggedArrayPropertyTestData))]
         public void JaggedArrayPropertyTest(string propertyName, NullabilityState innermodtElementState, NullabilityState elementState, NullabilityState propertyState)
         {
@@ -192,7 +192,7 @@ namespace System.Reflection.Tests
         }
 
         [Theory]
-        [SkipOnMono("Disabling NullablePublicOnly feature not work for Mono tests")]
+        [SkipOnMono("Nullability attributes trimmed on Mono")]
         [MemberData(nameof(TuplePropertyTestData))]
         public void TuplePropertyTest(string propertyName, NullabilityState genericParam1, NullabilityState genericParam2, NullabilityState genericParam3, NullabilityState propertyState)
         {
@@ -217,7 +217,7 @@ namespace System.Reflection.Tests
         }
 
         [Theory]
-        [SkipOnMono("Disabling NullablePublicOnly feature not work for Mono tests")]
+        [SkipOnMono("Nullability attributes trimmed on Mono")]
         [MemberData(nameof(GenericTuplePropertyTestData))]
         public void GenericTuplePropertyTest(string propertyName, NullabilityState genericParam1, NullabilityState genericParam2, NullabilityState genericParam3, NullabilityState propertyState)
         {
@@ -243,7 +243,7 @@ namespace System.Reflection.Tests
         }
 
         [Theory]
-        [SkipOnMono("Disabling NullablePublicOnly feature not work for Mono tests")]
+        [SkipOnMono("Nullability attributes trimmed on Mono")]
         [MemberData(nameof(DictionaryPropertyTestData))]
         public void DictionaryPropertyTest(string propertyName, NullabilityState keyState, NullabilityState valueElement, NullabilityState valueState, NullabilityState propertyState)
         {
@@ -269,7 +269,7 @@ namespace System.Reflection.Tests
         }
 
         [Theory]
-        [SkipOnMono("Disabling NullablePublicOnly feature not work for Mono tests")]
+        [SkipOnMono("Nullability attributes trimmed on Mono")]
         [MemberData(nameof(GenericDictionaryPropertyTestData))]
         public void GenericDictionaryPropertyTest(string propertyName, NullabilityState keyState, NullabilityState valueElement, NullabilityState valueState, NullabilityState propertyState)
         {
@@ -433,16 +433,16 @@ namespace System.Reflection.Tests
             Assert.Equal(type, nullability.Type);
         }
 
-        public static IEnumerable<object[]> GenericNotnullConstraintTestData()
+        public static IEnumerable<object[]> GenericNotnullConstraintFieldsTestData()
         {
-            yield return new object[] { "FieldNullable", NullabilityState.NotNull, NullabilityState.NotNull, typeof(string) };
+            yield return new object[] { "FieldNullable", NullabilityState.Nullable, NullabilityState.Nullable, typeof(string) };
             yield return new object[] { "FieldUnknown", NullabilityState.Unknown, NullabilityState.Unknown, typeof(string) };
             yield return new object[] { "FieldNullableEnabled", NullabilityState.NotNull, NullabilityState.NotNull, typeof(string) };
         }
 
         [Theory]
-        [MemberData(nameof(GenericNotnullConstraintTestData))]
-        public void GenericNotNullConstraintTest(string fieldName, NullabilityState readState, NullabilityState writeState, Type type)
+        [MemberData(nameof(GenericNotnullConstraintFieldsTestData))]
+        public void GenericNotNullConstraintFieldsTest(string fieldName, NullabilityState readState, NullabilityState writeState, Type type)
         {
             FieldInfo field = typeof(GenericTestConstrainedNotNull<string>).GetField(fieldName, flags)!;
             NullabilityInfo nullability = nullabilityContext.Create(field);
@@ -451,7 +451,25 @@ namespace System.Reflection.Tests
             Assert.Equal(type, nullability.Type);
         }
 
-        public static IEnumerable<object[]> GenericStructConstraintTestData()
+        public static IEnumerable<object[]> GenericNotnullConstraintPropertiesTestData()
+        {
+            yield return new object[] { "PropertyNullable", NullabilityState.Nullable, NullabilityState.Nullable, typeof(string) };
+            yield return new object[] { "PropertyUnknown", NullabilityState.Unknown, NullabilityState.Unknown, typeof(string) };
+            yield return new object[] { "PropertyNullableEnabled", NullabilityState.NotNull, NullabilityState.NotNull, typeof(string) };
+        }
+
+        [Theory]
+        [MemberData(nameof(GenericNotnullConstraintPropertiesTestData))]
+        public void GenericNotNullConstraintPropertiesTest(string propertyName, NullabilityState readState, NullabilityState writeState, Type type)
+        {
+            PropertyInfo property = typeof(GenericTestConstrainedNotNull<string>).GetProperty(propertyName, flags)!;
+            NullabilityInfo nullability = nullabilityContext.Create(property);
+            Assert.Equal(readState, nullability.ReadState);
+            Assert.Equal(writeState, nullability.WriteState);
+            Assert.Equal(type, nullability.Type);
+        }
+
+        public static IEnumerable<object[]> GenericStructConstraintFieldsTestData()
         {
             yield return new object[] { "FieldNullable", NullabilityState.Nullable, NullabilityState.Nullable, typeof(int?) };
             yield return new object[] { "FieldUnknown", NullabilityState.NotNull, NullabilityState.NotNull, typeof(int) };
@@ -459,8 +477,8 @@ namespace System.Reflection.Tests
         }
 
         [Theory]
-        [MemberData(nameof(GenericStructConstraintTestData))]
-        public void GenericStructConstraintTest(string fieldName, NullabilityState readState, NullabilityState writeState, Type type)
+        [MemberData(nameof(GenericStructConstraintFieldsTestData))]
+        public void GenericStructConstraintFieldsTest(string fieldName, NullabilityState readState, NullabilityState writeState, Type type)
         {
             FieldInfo field = typeof(GenericTestConstrainedStruct<int>).GetField(fieldName, flags)!;
             NullabilityInfo nullability = nullabilityContext.Create(field);
@@ -469,6 +487,24 @@ namespace System.Reflection.Tests
             Assert.Equal(type, nullability.Type);
         }
 
+        public static IEnumerable<object[]> GenericStructConstraintPropertiesTestData()
+        {
+            yield return new object[] { "PropertyNullable", NullabilityState.Nullable, NullabilityState.Nullable, typeof(int?) };
+            yield return new object[] { "PropertyUnknown", NullabilityState.NotNull, NullabilityState.NotNull, typeof(int) };
+            yield return new object[] { "PropertyNullableEnabled", NullabilityState.NotNull, NullabilityState.NotNull, typeof(int) };
+        }
+
+        [Theory]
+        [MemberData(nameof(GenericStructConstraintPropertiesTestData))]
+        public void GenericStructConstraintPropertiesTest(string propertyName, NullabilityState readState, NullabilityState writeState, Type type)
+        {
+            PropertyInfo property = typeof(GenericTestConstrainedStruct<int>).GetProperty(propertyName, flags)!;
+            NullabilityInfo nullability = nullabilityContext.Create(property);
+            Assert.Equal(readState, nullability.ReadState);
+            Assert.Equal(writeState, nullability.WriteState);
+            Assert.Equal(type, nullability.Type);
+        }
+
         [Fact]
         [SkipOnMono("Nullability attributes trimmed on Mono")]
         public void GenericListTest()
@@ -480,8 +516,8 @@ namespace System.Reflection.Tests
             Assert.Equal(NullabilityState.Nullable, nullability.WriteState);
             Assert.Equal(typeof(string), nullability.Type);
 
-            Type lisNontNull = typeof(List<string>);
-            MethodInfo addNotNull = lisNontNull.GetMethod("Add")!;
+            Type listNotNull = typeof(List<string>);
+            MethodInfo addNotNull = listNotNull.GetMethod("Add")!;
             nullability = nullabilityContext.Create(addNotNull.GetParameters()[0]);
             Assert.Equal(NullabilityState.Nullable, nullability.ReadState);
             Assert.Equal(typeof(string), nullability.Type);
@@ -544,7 +580,7 @@ namespace System.Reflection.Tests
         }
 
         [Theory]
-        [SkipOnMono("Disabling NullablePublicOnly feature not work for Mono tests")]
+        [SkipOnMono("Nullability attributes trimmed on Mono")]
         [MemberData(nameof(MethodReturnParameterTestData))]
         public void MethodReturnParameterTest(string methodName, NullabilityState elementState, NullabilityState readState)
         {
@@ -557,6 +593,31 @@ namespace System.Reflection.Tests
             Assert.Empty(nullability.GenericTypeArguments);
         }
 
+        public static IEnumerable<object[]> MethodReturnsTupleTestData()
+        {
+            // public Tuple<string?, string>? MethodReturnsTupleNullNonNull() => null;
+            yield return new object[] { "MethodReturnsTupleNullNonNull", NullabilityState.Nullable, NullabilityState.NotNull, NullabilityState.Nullable };
+            // public Tuple<string?, int> MethodReturnsTupleNullNonNot() => null!
+            yield return new object[] { "MethodReturnsTupleNullNonNot", NullabilityState.Nullable, NullabilityState.NotNull, NullabilityState.NotNull };
+            // public (int?, string)? MethodReturnsValueTupleNullNonNull() => null;
+            yield return new object[] { "MethodReturnsValueTupleNullNonNull", NullabilityState.Nullable, NullabilityState.NotNull, NullabilityState.Nullable };
+            // public (string?, string) MethodReturnsValueTupleNullNonNon() => (null, string.Empty);
+            yield return new object[] { "MethodReturnsValueTupleNullNonNon", NullabilityState.Nullable, NullabilityState.NotNull, NullabilityState.NotNull };
+        }
+
+        [Theory]
+        [SkipOnMono("Nullability attributes trimmed on Mono")]
+        [MemberData(nameof(MethodReturnsTupleTestData))]
+        public void MethodReturnsTupleTest(string methodName, NullabilityState param1, NullabilityState param2, NullabilityState readState)
+        {
+            MethodInfo method = testType.GetMethod(methodName, flags)!;
+            NullabilityInfo nullability = nullabilityContext.Create(method.ReturnParameter);
+            Assert.Equal(readState, nullability.ReadState);
+            Assert.Null(nullability.ElementType);
+            Assert.Equal(param1, nullability.GenericTypeArguments[0].ReadState);
+            Assert.Equal(param2, nullability.GenericTypeArguments[1].ReadState);
+        }
+
         public static IEnumerable<object[]> MethodGenericReturnParameterTestData()
         {
             yield return new object[] { "MethodReturnsUnknown", NullabilityState.Unknown, NullabilityState.Unknown };
@@ -569,7 +630,7 @@ namespace System.Reflection.Tests
         }
 
         [Theory]
-        [SkipOnMono("Disabling NullablePublicOnly feature not work for Mono tests")]
+        [SkipOnMono("Nullability attributes trimmed on Mono")]
         [MemberData(nameof(MethodGenericReturnParameterTestData))]
         public void MethodGenericReturnParameterTest(string methodName, NullabilityState readState, NullabilityState elementState)
         {
@@ -592,7 +653,7 @@ namespace System.Reflection.Tests
         }
 
         [Theory]
-        [SkipOnMono("Disabling NullablePublicOnly feature not work for Mono tests")]
+        [SkipOnMono("Nullability attributes trimmed on Mono")]
         [MemberData(nameof(MethodParametersTestData))]
         public void MethodParametersTest(string methodName, NullabilityState stringState, NullabilityState dictKey, NullabilityState dictValueElement, NullabilityState dictValue, NullabilityState dictionaryState)
         {
@@ -616,7 +677,7 @@ namespace System.Reflection.Tests
         }
 
         [Theory]
-        [SkipOnMono("Disabling NullablePublicOnly feature not work for Mono tests")]
+        [SkipOnMono("Nullability attributes trimmed on Mono")]
         [MemberData(nameof(MethodGenericParametersTestData))]
         public void MethodGenericParametersTest(string methodName, NullabilityState param1State, NullabilityState dictKey, NullabilityState dictValue, NullabilityState dictionaryState)
         {
@@ -705,6 +766,8 @@ namespace System.Reflection.Tests
         public static IEnumerable<object[]> DifferentContextTestData()
         {
             yield return new object[] { "PropertyDisabled", NullabilityState.Unknown, NullabilityState.Unknown, typeof(string) };
+            yield return new object[] { "PropertyDisabledAllowNull", NullabilityState.Unknown, NullabilityState.Unknown, typeof(string) };
+            yield return new object[] { "PropertyDisabledMaybeNull", NullabilityState.Unknown, NullabilityState.Unknown, typeof(string) };
             yield return new object[] { "PropertyEnabledAllowNull", NullabilityState.NotNull, NullabilityState.Nullable, typeof(string) };
             yield return new object[] { "PropertyEnabledNotNull", NullabilityState.NotNull, NullabilityState.Nullable, typeof(string) };
             yield return new object[] { "PropertyEnabledMaybeNull", NullabilityState.Nullable, NullabilityState.NotNull, typeof(string) };
@@ -766,15 +829,15 @@ namespace System.Reflection.Tests
             Assert.Equal(NullabilityState.Nullable, notNullIfNotNull.WriteState);
             Assert.Equal(NullabilityState.Nullable, nullabilityContext.Create(allowNullParameter[1]).ReadState);
 
-            // [return: NotNullIfNotNull("nullable")] public string? NullablNotNullIfNotNullReturn(string? nullable, [NotNull] ref string? readNotNull)
-            ParameterInfo[] nullablNotNullIfNotNullReturn = type.GetMethod("NullablNotNullIfNotNullReturn", flags)!.GetParameters();
-            NullabilityInfo returnNotNullIfNotNull = nullabilityContext.Create(type.GetMethod("NullablNotNullIfNotNullReturn", flags)!.ReturnParameter);
-            NullabilityInfo readNotNull = nullabilityContext.Create(nullablNotNullIfNotNullReturn[1]);
+            // [return: NotNullIfNotNull("nullable")] public string? NullableNotNullIfNotNullReturn(string? nullable, [NotNull] ref string? readNotNull)
+            ParameterInfo[] nullableNotNullIfNotNullReturn = type.GetMethod("NullableNotNullIfNotNullReturn", flags)!.GetParameters();
+            NullabilityInfo returnNotNullIfNotNull = nullabilityContext.Create(type.GetMethod("NullableNotNullIfNotNullReturn", flags)!.ReturnParameter);
+            NullabilityInfo readNotNull = nullabilityContext.Create(nullableNotNullIfNotNullReturn[1]);
             Assert.Equal(NullabilityState.Nullable, returnNotNullIfNotNull.ReadState);
             Assert.Equal(NullabilityState.Nullable, returnNotNullIfNotNull.WriteState);
             Assert.Equal(NullabilityState.NotNull, readNotNull.ReadState);
             Assert.Equal(NullabilityState.Nullable, readNotNull.WriteState);
-            Assert.Equal(NullabilityState.Nullable, nullabilityContext.Create(nullablNotNullIfNotNullReturn[0]).ReadState);
+            Assert.Equal(NullabilityState.Nullable, nullabilityContext.Create(nullableNotNullIfNotNullReturn[0]).ReadState);
 
             // public bool TryGetOutParameters(string id, [NotNullWhen(true)] out string? value, [MaybeNullWhen(false)] out string value2)
             ParameterInfo[] tryGetOutParameters = type.GetMethod("TryGetOutParameters", flags)!.GetParameters();
@@ -787,6 +850,51 @@ namespace System.Reflection.Tests
             Assert.Equal(NullabilityState.NotNull, nullabilityContext.Create(tryGetOutParameters[0]).ReadState);
         }
 
+        public static IEnumerable<object[]> NestedGenericsReturnParameterData()
+        {
+            // public IEnumerable<Tuple<(string name, object? value), int>?> MethodReturnsEnumerableNonTupleNonNonNullValueTupleNonNullNon() => null!;
+            yield return new object[] { "MethodReturnsEnumerableNonTupleNonNonNullValueTupleNonNullNon",
+                NullabilityState.NotNull, NullabilityState.NotNull, NullabilityState.NotNull, NullabilityState.Nullable, NullabilityState.NotNull, NullabilityState.Nullable };
+
+            // public IEnumerable<Tuple<(string? name, object value)?, int>?>? MethodReturnsEnumerableNullTupleNullNonNullValueTupleNullNonNull() => null!;
+            yield return new object[] { "MethodReturnsEnumerableNullTupleNullNonNullValueTupleNullNonNull",
+                NullabilityState.Nullable, NullabilityState.Nullable, NullabilityState.NotNull, NullabilityState.Nullable, NullabilityState.Nullable, NullabilityState.NotNull };
+
+            // public IEnumerable<Tuple<Tuple<string, object?>, int>?> MethodReturnsEnumerableNonTupleNonNonNullTupleNonNullNon() => null!;
+            yield return new object[] { "MethodReturnsEnumerableNonTupleNonNonNullTupleNonNullNon",
+                NullabilityState.NotNull, NullabilityState.NotNull, NullabilityState.NotNull, NullabilityState.Nullable, NullabilityState.NotNull, NullabilityState.Nullable };
+
+            // public IEnumerable<GenericStruct<Tuple<string, object?>?, int>?>? MethodReturnsEnumerableNullStructNullNonNonTupleNonNullNull() => null;
+            yield return new object[] { "MethodReturnsEnumerableNullStructNullNonNullTupleNonNullNull",
+                NullabilityState.Nullable, NullabilityState.Nullable, NullabilityState.NotNull, NullabilityState.Nullable, NullabilityState.NotNull, NullabilityState.Nullable };
+
+            // public IEnumerable<Tuple<GenericStruct<string, object?>?, int>?>? MethodReturnsEnumerableNullTupleNullNonNullStructNonNullNull() => null;
+            yield return new object[] { "MethodReturnsEnumerableNullTupleNullNonNullStructNonNullNull",
+                NullabilityState.Nullable, NullabilityState.Nullable, NullabilityState.NotNull, NullabilityState.Nullable, NullabilityState.NotNull, NullabilityState.Nullable };
+
+            // public IEnumerable<(GenericStruct<string, object?> str, int? count)> MethodReturnsEnumerableNonValueTupleNonNullNonTupleNonNullNon() => null!;
+            yield return new object[] { "MethodReturnsEnumerableNonValueTupleNonNullNonStructNonNullNon",
+                NullabilityState.NotNull, NullabilityState.NotNull, NullabilityState.Nullable, NullabilityState.NotNull, NullabilityState.NotNull, NullabilityState.Nullable };
+        }
+
+        [Theory]
+        [MemberData(nameof(NestedGenericsReturnParameterData))]
+        [SkipOnMono("Nullability attributes trimmed on Mono")]
+        public void NestedGenericsReturnParameterTest(string methodName, NullabilityState enumState, NullabilityState innerTupleState,
+            NullabilityState intState, NullabilityState outerTupleState, NullabilityState stringState, NullabilityState objectState)
+        {
+            NullabilityInfo enumerabeNullability = nullabilityContext.Create(typeof(TypeWithNotNullContext).GetMethod(methodName, flags)!.ReturnParameter);
+            Assert.Equal(enumState, enumerabeNullability.ReadState);
+            NullabilityInfo tupleNullability = enumerabeNullability.GenericTypeArguments[0];
+            Assert.Equal(outerTupleState, tupleNullability.ReadState);
+            Assert.Equal(innerTupleState, tupleNullability.GenericTypeArguments[0].ReadState);
+            Assert.Equal(intState, tupleNullability.GenericTypeArguments[1].ReadState);
+            NullabilityInfo valueTupleNullability = tupleNullability.GenericTypeArguments[0];
+            Assert.Equal(innerTupleState, valueTupleNullability.ReadState);
+            Assert.Equal(stringState, valueTupleNullability.GenericTypeArguments[0].ReadState);
+            Assert.Equal(objectState, valueTupleNullability.GenericTypeArguments[1].ReadState);
+        }
+
         public static IEnumerable<object[]> RefReturnData()
         {
             yield return new object[] { "RefReturnUnknown", NullabilityState.Unknown, NullabilityState.Unknown, NullabilityState.Unknown, NullabilityState.Unknown };
@@ -812,12 +920,42 @@ namespace System.Reflection.Tests
             Assert.Equal(paramReadState, paramNullability.ReadState);
             Assert.Equal(paramWriteState, paramNullability.WriteState);
         }
+
+        public static IEnumerable<object[]> ValueTupleTestData()
+        {
+            // public (int, string) UnknownValueTuple; [0]
+            yield return new object[] { "UnknownValueTuple", NullabilityState.NotNull, NullabilityState.Unknown, NullabilityState.NotNull };
+            // public (string?, object) NullNonNonValueTuple; [0, 2, 1]
+            yield return new object[] { "Null_Non_Non_ValueTuple", NullabilityState.Nullable, NullabilityState.NotNull, NullabilityState.NotNull };
+            // public (string?, object)? Null_Non_Null_ValueTuple; [0, 2, 1]
+            yield return new object[] { "Null_Non_Null_ValueTuple", NullabilityState.Nullable, NullabilityState.NotNull, NullabilityState.Nullable };
+            // public (int, int?)? Non_Null_Null_ValueTuple; [0]
+            yield return new object[] { "Non_Null_Null_ValueTuple", NullabilityState.NotNull, NullabilityState.Nullable, NullabilityState.Nullable };
+            // public (int, string) Non_Non_Non_ValueTuple; [0, 1]
+            yield return new object[] { "Non_Non_Non_ValueTuple", NullabilityState.NotNull, NullabilityState.NotNull, NullabilityState.NotNull };
+            // public (int, string?) Non_Null_Non_ValueTuple; [0, 2]
+            yield return new object[] { "Non_Null_Non_ValueTuple", NullabilityState.NotNull, NullabilityState.Nullable, NullabilityState.NotNull };
+        }
+
+        [Theory]
+        [MemberData(nameof(ValueTupleTestData))]
+        [SkipOnMono("Nullability attributes trimmed on Mono")]
+        public void TestValueTupleGenericTypeParameters(string fieldName, NullabilityState param1, NullabilityState param2, NullabilityState fieldState)
+        {
+            var tupleInfo = nullabilityContext.Create(testType.GetField(fieldName)!);
+
+            Assert.Equal(fieldState, tupleInfo.ReadState);
+            Assert.Equal(param1, tupleInfo.GenericTypeArguments[0].ReadState);
+            Assert.Equal(param2, tupleInfo.GenericTypeArguments[1].ReadState);
+        }
     }
 
 #pragma warning disable CS0649, CS0067, CS0414
     public class TypeWithNullableContext
     {
 #nullable disable
+        [AllowNull] public string PropertyDisabledAllowNull { get; set; }
+        [MaybeNull] public string PropertyDisabledMaybeNull { get; set; }
         public string PropertyDisabled { get; set; }
         public ref string RefReturnUnknown(ref string id) { return ref id; }
 #nullable enable
@@ -830,12 +968,13 @@ namespace System.Reflection.Tests
         bool NotNullWhenParameter([DisallowNull] string? disallowNull, [NotNullWhen(true)] ref string? notNullWhen, Type? nullableType) { return false; }
         public bool MaybeNullParameters([MaybeNull] string maybeNull, [MaybeNullWhen(false)] out string maybeNullWhen, Type? nullableType) { maybeNullWhen = null; return false; }
         public string? AllowNullParameter([AllowNull] string allowNull, [NotNullIfNotNull("allowNull")] string? notNullIfNotNull) { return null; }
-        [return: NotNullIfNotNull("nullable")] public string? NullablNotNullIfNotNullReturn(string? nullable, [NotNull] ref string? readNotNull) { readNotNull = string.Empty; return null!; }
+        [return: NotNullIfNotNull("nullable")] public string? NullableNotNullIfNotNullReturn(string? nullable, [NotNull] ref string? readNotNull) { readNotNull = string.Empty; return null!; }
         public ref string? RefReturnNullable([AllowNull] ref string id) { return ref id!; }
         [return: MaybeNull] public ref string RefReturnMaybeNull([DisallowNull] ref string? id) { return ref id; }
         [return: NotNull] public ref string? RefReturnNotNull([NotNull] ref string? id) { id = string.Empty; return ref id!; }
         public ref string RefReturnNotNullable([MaybeNull] ref string id) { return ref id; }
         public bool TryGetOutParameters(string id, [NotNullWhen(true)] out string? value, [MaybeNullWhen(false)] out string value2) { value = null; value2 = null; return false; }
+        public IEnumerable<Tuple<(string name, object? value), object>?> MethodReturnsEnumerableNonTupleNonNonNullValueTupleNonNullNon() => null!;
     }
 
     public class TypeWithNoContext
@@ -852,8 +991,6 @@ namespace System.Reflection.Tests
         public string? PropertyEnabledNullable { get; set; }
         public string PropertyEnabledNonNullable { get; set; } = null!;
 #nullable disable
-        [return: NotNull, MaybeNull]
-        public string MethodNullableDisabled([AllowNull] string value, string ret) { return null; }
     }
 
     public class TypeWithNotNullContext
@@ -865,6 +1002,7 @@ namespace System.Reflection.Tests
         protected Tuple<string, string, string> PropertyTupleUnknown { get; set; }
         protected internal IDictionary<Type, string[]> PropertyDictionaryUnknown { get; set; }
 
+        public (int, string) UnknownValueTuple;
         internal TypeWithNotNullContext FieldUnknown;
         public int FieldValueTypeUnknown;
 
@@ -915,6 +1053,11 @@ namespace System.Reflection.Tests
         private IDictionary<Type, string[]>? PropertyDictionaryNonNonNonNull { get; set; }
         public IDictionary<Type, string[]> PropertyDictionaryNonNonNonNon { get; set; } = null!;
 
+        public (string?, object) Null_Non_Non_ValueTuple;
+        public (string?, object)? Null_Non_Null_ValueTuple;
+        public (int, int?)? Non_Null_Null_ValueTuple;
+        public (int, string) Non_Non_Non_ValueTuple;
+        public (int, string?) Non_Null_Non_ValueTuple;
         private const string? FieldNullable = null;
         protected static NullabilityInfoContextTests FieldNonNullable = null!;
         public static double FieldValueTypeNotNull;
@@ -936,14 +1079,24 @@ namespace System.Reflection.Tests
         [return: NotNull, MaybeNull] public string[]? MethodReturnsNonNotNull() => null!; // only NotNull is applicable
         [return: MaybeNull] public string[] MethodReturnsNonMaybeNull() => null;
         public string[] MethodReturnsNonNon() => null!;
-        public Tuple<string?, string>? MethodTupleNullNonNull() => null;
-        public IEnumerable<Tuple<(string name, object? value), object>?> MethodEnumerableNonNonNullUnknownNullNonNullNon() => null!;
+        public Tuple<string?, string>? MethodReturnsTupleNullNonNull() => null;
+        public Tuple<string?, int> MethodReturnsTupleNullNonNot() => null!;
+        public (int?, string)? MethodReturnsValueTupleNullNonNull() => null;
+        public (string?, string) MethodReturnsValueTupleNullNonNon() => (null, string.Empty);
+        public IEnumerable<Tuple<(string name, object? value), int>?> MethodReturnsEnumerableNonTupleNonNonNullValueTupleNonNullNon() => null!;
+        public IEnumerable<Tuple<(string? name, object value)?, int>?>? MethodReturnsEnumerableNullTupleNullNonNullValueTupleNullNonNull() => null!;
+        public IEnumerable<Tuple<Tuple<string, object?>, int>?> MethodReturnsEnumerableNonTupleNonNonNullTupleNonNullNon() => null!;
+        public IEnumerable<GenericStruct<Tuple<string, object?>?, int>?>? MethodReturnsEnumerableNullStructNullNonNullTupleNonNullNull() => null;
+        public IEnumerable<Tuple<GenericStruct<string, object?>?, int>?>? MethodReturnsEnumerableNullTupleNullNonNullStructNonNullNull() => null;
+        public IEnumerable<(GenericStruct<string, object?> str, int? count)> MethodReturnsEnumerableNonValueTupleNonNullNonStructNonNullNon() => null!;
         public void MethodNullNonNullNonNon(string? s, IDictionary<Type, string?[]> dict) { }
         public void MethodNonNullNonNullNotNull(string s, [NotNull] IDictionary<Type, string[]?>? dict) { dict = new Dictionary<Type, string[]?>(); }
         public void MethodNullNonNullNullNon(string? s, IDictionary<Type, string?[]?> dict) { }
         public void MethodAllowNullNonNonNonNull([AllowNull] string s, IDictionary<Type, string[]>? dict) { }
     }
 
+    public struct GenericStruct<T, Y> { }
+
     internal class GenericTest<T>
     {
 #nullable disable
@@ -969,14 +1122,14 @@ namespace System.Reflection.Tests
         public Tuple<T?, string?, string?>? PropertyTupleNullNullNullNull { get; set; }
         public Tuple<T, T?, string> PropertyTupleNonNullNonNon { get; set; } = null!;
         Tuple<string?, T, T?>? PropertyTupleNullNonNullNull { get; set; }
-        public Tuple<T, string?, string>? PropertyTupleNonNullNonNull { get; set; }
-        public Tuple<string, string, T> PropertyTupleNonNonNonNon { get; set; } = null!;
+        public Tuple<T, int?, string>? PropertyTupleNonNullNonNull { get; set; }
+        public Tuple<int, string, T> PropertyTupleNonNonNonNon { get; set; } = null!;
         private IDictionary<T?, string?[]?> PropertyDictionaryNullNullNullNon { get; set; } = null!;
         static IDictionary<Type, T?[]>? PropertyDictionaryNonNullNonNull { get; set; }
         public static IDictionary<T?, T[]>? PropertyDictionaryNullNonNonNull { get; set; }
         public IDictionary<Type, T?[]> PropertyDictionaryNonNullNonNon { get; set; } = null!;
         protected IDictionary<T, T[]>? PropertyDictionaryNonNonNonNull { get; set; }
-        public IDictionary<T, string[]> PropertyDictionaryNonNonNonNon { get; set; } = null!;
+        public IDictionary<T, int[]> PropertyDictionaryNonNonNonNon { get; set; } = null!;
 
         static T? FieldNullable = default;
         public T FieldNonNullable = default!;
@@ -1007,6 +1160,7 @@ namespace System.Reflection.Tests
         public T FieldNullableEnabled = default!;
         public T? FieldNullable;
         public T PropertyNullableEnabled { get; set; } = default!;
+        public T? PropertyNullable { get; set; } = default!;
     }
 
     internal class GenericTestConstrainedStruct<T> where T : struct
@@ -1019,5 +1173,6 @@ namespace System.Reflection.Tests
         public T FieldNullableEnabled;
         public T? FieldNullable;
         public T PropertyNullableEnabled { get; set; }
+        public T? PropertyNullable { get; set; }
     }
 }