From 3ea086b857fe8a60ef8b2ba1ee7953f35f1a8a26 Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Thu, 25 Jul 2019 00:11:33 -0700 Subject: [PATCH] Use NullableAnnotation.NotAnnotated for value types (#37431) Use NullableAnnotation.NotAnnotated always for value type annotations in the public API. Previously, the annotation was NotAnnotated if the type reference was from source and Disabled if the type reference was from metadata. Fixes #34412 --- .../Portable/FlowAnalysis/NullableWalker.cs | 3 +- .../Portable/SymbolDisplay/SymbolDisplay.cs | 2 +- .../Portable/Symbols/ArrayTypeSymbol.cs | 2 +- .../CSharp/Portable/Symbols/EventSymbol.cs | 2 +- .../CSharp/Portable/Symbols/FieldSymbol.cs | 2 +- .../CSharp/Portable/Symbols/LocalSymbol.cs | 2 +- .../CSharp/Portable/Symbols/MethodSymbol.cs | 5 +- .../Portable/Symbols/NamedTypeSymbol.cs | 6 +- .../Symbols/NullableAnnotationExtensions.cs | 12 +- .../Portable/Symbols/ParameterSymbol.cs | 2 +- .../CSharp/Portable/Symbols/PropertySymbol.cs | 2 +- .../Symbols/ReducedExtensionMethodSymbol.cs | 2 +- .../Portable/Symbols/TypeParameterSymbol.cs | 6 +- .../Portable/Symbols/TypeWithAnnotations.cs | 17 +- .../Attributes/AttributeTests_Nullable.cs | 34 ++ .../Symbols/Source/NullablePublicAPITests.cs | 440 ++++++++++++++++-- .../Utilities/CSharp/CompilationTestUtils.cs | 12 +- 17 files changed, 466 insertions(+), 85 deletions(-) diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index 30dabe45853..a404736e25c 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -300,8 +300,7 @@ private void SetAnalyzedNullability(BoundExpression expr, VisitResult result, bo } } #endif - _analyzedNullabilityMapOpt[expr] = (new NullabilityInfo(result.LValueType.NullableAnnotation.ToPublicAnnotation(), - result.RValueType.State.ToPublicFlowState()), + _analyzedNullabilityMapOpt[expr] = (new NullabilityInfo(result.LValueType.ToPublicAnnotation(), result.RValueType.State.ToPublicFlowState()), // https://github.com/dotnet/roslyn/issues/35046 We're dropping the result if the type doesn't match up completely // with the existing type expr.Type?.Equals(result.RValueType.Type, TypeCompareKind.AllIgnoreOptions) == true ? result.RValueType.Type : expr.Type); diff --git a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplay.cs b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplay.cs index 5a9be0f8348..163efdf9f89 100644 --- a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplay.cs +++ b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplay.cs @@ -195,7 +195,7 @@ public static class SymbolDisplay bool minimal) { // https://github.com/dotnet/roslyn/issues/35035: Refactor this. We need to be able to handle non-TypeSymbol inputs - var annotation = (CodeAnalysis.NullableAnnotation?)TypeWithState.Create((TypeSymbol)symbol, nullableFlowState.ToInternalFlowState()).ToTypeWithAnnotations().NullableAnnotation.ToPublicAnnotation(); + var annotation = (CodeAnalysis.NullableAnnotation?)TypeWithState.Create((TypeSymbol)symbol, nullableFlowState.ToInternalFlowState()).ToTypeWithAnnotations().ToPublicAnnotation(); return ToDisplayParts(symbol, annotation, semanticModelOpt, positionOpt, format, minimal); } diff --git a/src/Compilers/CSharp/Portable/Symbols/ArrayTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/ArrayTypeSymbol.cs index ead0ba7d021..02ccc5dec1a 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ArrayTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ArrayTypeSymbol.cs @@ -477,7 +477,7 @@ ITypeSymbol IArrayTypeSymbol.ElementType CodeAnalysis.NullableAnnotation IArrayTypeSymbol.ElementNullableAnnotation { - get => ElementTypeWithAnnotations.NullableAnnotation.ToPublicAnnotation(); + get => ElementTypeWithAnnotations.ToPublicAnnotation(); } ImmutableArray IArrayTypeSymbol.CustomModifiers diff --git a/src/Compilers/CSharp/Portable/Symbols/EventSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/EventSymbol.cs index e843bd29377..f498b99ede7 100644 --- a/src/Compilers/CSharp/Portable/Symbols/EventSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/EventSymbol.cs @@ -351,7 +351,7 @@ ITypeSymbol IEventSymbol.Type } } - CodeAnalysis.NullableAnnotation IEventSymbol.NullableAnnotation => TypeWithAnnotations.NullableAnnotation.ToPublicAnnotation(); + CodeAnalysis.NullableAnnotation IEventSymbol.NullableAnnotation => TypeWithAnnotations.ToPublicAnnotation(); IMethodSymbol IEventSymbol.AddMethod { diff --git a/src/Compilers/CSharp/Portable/Symbols/FieldSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/FieldSymbol.cs index 54e07754a90..f70d2365b02 100644 --- a/src/Compilers/CSharp/Portable/Symbols/FieldSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/FieldSymbol.cs @@ -467,7 +467,7 @@ ITypeSymbol IFieldSymbol.Type } } - CodeAnalysis.NullableAnnotation IFieldSymbol.NullableAnnotation => TypeWithAnnotations.NullableAnnotation.ToPublicAnnotation(); + CodeAnalysis.NullableAnnotation IFieldSymbol.NullableAnnotation => TypeWithAnnotations.ToPublicAnnotation(); ImmutableArray IFieldSymbol.CustomModifiers { diff --git a/src/Compilers/CSharp/Portable/Symbols/LocalSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/LocalSymbol.cs index 889a5e5afe5..029f768acbc 100644 --- a/src/Compilers/CSharp/Portable/Symbols/LocalSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/LocalSymbol.cs @@ -372,7 +372,7 @@ ITypeSymbol ILocalSymbol.Type } } - CodeAnalysis.NullableAnnotation ILocalSymbol.NullableAnnotation => TypeWithAnnotations.NullableAnnotation.ToPublicAnnotation(); + CodeAnalysis.NullableAnnotation ILocalSymbol.NullableAnnotation => TypeWithAnnotations.ToPublicAnnotation(); bool ILocalSymbol.IsFunctionValue { diff --git a/src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs index bc875956252..9c5d475468a 100644 --- a/src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs @@ -1038,7 +1038,7 @@ ITypeSymbol IMethodSymbol.ReturnType } } - CodeAnalysis.NullableAnnotation IMethodSymbol.ReturnNullableAnnotation => ReturnTypeWithAnnotations.NullableAnnotation.ToPublicAnnotation(); + CodeAnalysis.NullableAnnotation IMethodSymbol.ReturnNullableAnnotation => ReturnTypeWithAnnotations.ToPublicAnnotation(); ImmutableArray IMethodSymbol.TypeArguments { @@ -1048,7 +1048,8 @@ ImmutableArray IMethodSymbol.TypeArguments } } - ImmutableArray IMethodSymbol.TypeArgumentNullableAnnotations => TypeArgumentsWithAnnotations.SelectAsArray(arg => arg.NullableAnnotation.ToPublicAnnotation()); + ImmutableArray IMethodSymbol.TypeArgumentNullableAnnotations => + TypeArgumentsWithAnnotations.SelectAsArray(arg => arg.ToPublicAnnotation()); ImmutableArray IMethodSymbol.TypeParameters { diff --git a/src/Compilers/CSharp/Portable/Symbols/NamedTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/NamedTypeSymbol.cs index 99ebbc333b9..67ad31a269d 100644 --- a/src/Compilers/CSharp/Portable/Symbols/NamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/NamedTypeSymbol.cs @@ -1582,10 +1582,8 @@ ImmutableArray INamedTypeSymbol.TypeArguments } } - ImmutableArray INamedTypeSymbol.TypeArgumentNullableAnnotations - { - get => this.TypeArgumentsWithAnnotationsNoUseSiteDiagnostics.SelectAsArray(a => a.NullableAnnotation.ToPublicAnnotation()); - } + ImmutableArray INamedTypeSymbol.TypeArgumentNullableAnnotations => + this.TypeArgumentsWithAnnotationsNoUseSiteDiagnostics.SelectAsArray(a => a.ToPublicAnnotation()); ImmutableArray INamedTypeSymbol.GetTypeArgumentCustomModifiers(int ordinal) { diff --git a/src/Compilers/CSharp/Portable/Symbols/NullableAnnotationExtensions.cs b/src/Compilers/CSharp/Portable/Symbols/NullableAnnotationExtensions.cs index 3848c09f8f5..afb61f55004 100644 --- a/src/Compilers/CSharp/Portable/Symbols/NullableAnnotationExtensions.cs +++ b/src/Compilers/CSharp/Portable/Symbols/NullableAnnotationExtensions.cs @@ -81,15 +81,21 @@ internal static NullabilityInfo ToNullabilityInfo(this CodeAnalysis.NullableAnno internal static NullabilityInfo ToNullabilityInfo(this NullableAnnotation annotation, TypeSymbol type) { var flowState = TypeWithAnnotations.Create(type, annotation).ToTypeWithState().State; - return new NullabilityInfo(annotation.ToPublicAnnotation(), flowState.ToPublicFlowState()); + return new NullabilityInfo(ToPublicAnnotation(type, annotation), flowState.ToPublicFlowState()); } - internal static CodeAnalysis.NullableAnnotation ToPublicAnnotation(this CSharp.NullableAnnotation annotation) => + internal static CodeAnalysis.NullableAnnotation ToPublicAnnotation(this TypeWithAnnotations type) => + ToPublicAnnotation(type.Type, type.NullableAnnotation); + + private static CodeAnalysis.NullableAnnotation ToPublicAnnotation(TypeSymbol type, NullableAnnotation annotation) => annotation switch { CSharp.NullableAnnotation.Annotated => CodeAnalysis.NullableAnnotation.Annotated, CSharp.NullableAnnotation.NotAnnotated => CodeAnalysis.NullableAnnotation.NotAnnotated, - CSharp.NullableAnnotation.Oblivious => CodeAnalysis.NullableAnnotation.Disabled, + // A value type may be oblivious or not annotated depending on whether the type reference + // is from source or metadata. (Binding using the #nullable context only when setting the annotation + // to avoid checking IsValueType early.) The annotation is normalized here in the public API. + CSharp.NullableAnnotation.Oblivious => type.IsValueType ? CodeAnalysis.NullableAnnotation.NotAnnotated : CodeAnalysis.NullableAnnotation.Disabled, _ => throw ExceptionUtilities.UnexpectedValue(annotation) }; diff --git a/src/Compilers/CSharp/Portable/Symbols/ParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/ParameterSymbol.cs index cf6023d30f4..2c89021e750 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ParameterSymbol.cs @@ -412,7 +412,7 @@ ITypeSymbol IParameterSymbol.Type get { return this.Type; } } - CodeAnalysis.NullableAnnotation IParameterSymbol.NullableAnnotation => TypeWithAnnotations.NullableAnnotation.ToPublicAnnotation(); + CodeAnalysis.NullableAnnotation IParameterSymbol.NullableAnnotation => TypeWithAnnotations.ToPublicAnnotation(); ImmutableArray IParameterSymbol.CustomModifiers { diff --git a/src/Compilers/CSharp/Portable/Symbols/PropertySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/PropertySymbol.cs index e8e10325daf..772eefb565e 100644 --- a/src/Compilers/CSharp/Portable/Symbols/PropertySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/PropertySymbol.cs @@ -433,7 +433,7 @@ ITypeSymbol IPropertySymbol.Type get { return this.Type; } } - CodeAnalysis.NullableAnnotation IPropertySymbol.NullableAnnotation => TypeWithAnnotations.NullableAnnotation.ToPublicAnnotation(); + CodeAnalysis.NullableAnnotation IPropertySymbol.NullableAnnotation => TypeWithAnnotations.ToPublicAnnotation(); ImmutableArray IPropertySymbol.Parameters { diff --git a/src/Compilers/CSharp/Portable/Symbols/ReducedExtensionMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/ReducedExtensionMethodSymbol.cs index 67629af917f..24a320c4d06 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ReducedExtensionMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ReducedExtensionMethodSymbol.cs @@ -183,7 +183,7 @@ public override TypeSymbol ReceiverType } protected override CodeAnalysis.NullableAnnotation ReceiverNullableAnnotation => - _reducedFrom.Parameters[0].TypeWithAnnotations.NullableAnnotation.ToPublicAnnotation(); + _reducedFrom.Parameters[0].TypeWithAnnotations.ToPublicAnnotation(); public override TypeSymbol GetTypeInferredDuringReduction(TypeParameterSymbol reducedFromTypeParameter) { diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/TypeParameterSymbol.cs index 761e8abfc3d..15f0ed0ea58 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeParameterSymbol.cs @@ -716,10 +716,8 @@ ImmutableArray ITypeParameterSymbol.ConstraintTypes } } - ImmutableArray ITypeParameterSymbol.ConstraintNullableAnnotations - { - get => this.ConstraintTypesNoUseSiteDiagnostics.SelectAsArray(c => c.NullableAnnotation.ToPublicAnnotation()); - } + ImmutableArray ITypeParameterSymbol.ConstraintNullableAnnotations => + ConstraintTypesNoUseSiteDiagnostics.SelectAsArray(c => c.ToPublicAnnotation()); ITypeParameterSymbol ITypeParameterSymbol.OriginalDefinition { diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeWithAnnotations.cs b/src/Compilers/CSharp/Portable/Symbols/TypeWithAnnotations.cs index 89b598977a0..9fd8cdf87d6 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeWithAnnotations.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeWithAnnotations.cs @@ -43,7 +43,7 @@ internal Boxed(TypeWithAnnotations value) private TypeWithAnnotations(TypeSymbol defaultType, NullableAnnotation nullableAnnotation, Extensions extensions) { - Debug.Assert(defaultType?.IsNullableType() != true || (nullableAnnotation != NullableAnnotation.Oblivious && nullableAnnotation != NullableAnnotation.NotAnnotated)); + Debug.Assert(defaultType?.IsNullableType() != true || nullableAnnotation == NullableAnnotation.Annotated); Debug.Assert(extensions != null); DefaultType = defaultType; @@ -53,7 +53,7 @@ private TypeWithAnnotations(TypeSymbol defaultType, NullableAnnotation nullableA public override string ToString() => Type.ToString(); - internal static readonly SymbolDisplayFormat DebuggerDisplayFormat = new SymbolDisplayFormat( + private static readonly SymbolDisplayFormat DebuggerDisplayFormat = new SymbolDisplayFormat( typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces, genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters, miscellaneousOptions: SymbolDisplayMiscellaneousOptions.UseSpecialTypes | SymbolDisplayMiscellaneousOptions.IncludeNullableReferenceTypeModifier); @@ -64,15 +64,14 @@ private TypeWithAnnotations(TypeSymbol defaultType, NullableAnnotation nullableA miscellaneousOptions: SymbolDisplayMiscellaneousOptions.UseSpecialTypes | SymbolDisplayMiscellaneousOptions.IncludeNullableReferenceTypeModifier, compilerInternalOptions: SymbolDisplayCompilerInternalOptions.IncludeNonNullableTypeModifier); - internal static TypeWithAnnotations Create(bool isNullableEnabled, TypeSymbol typeSymbol, bool isAnnotated = false, ImmutableArray customModifiers = default) + internal static TypeWithAnnotations Create(bool isNullableEnabled, TypeSymbol typeSymbol, bool isAnnotated = false) { if (typeSymbol is null) { return default; } - return Create(typeSymbol, nullableAnnotation: isAnnotated ? NullableAnnotation.Annotated : isNullableEnabled ? NullableAnnotation.NotAnnotated : NullableAnnotation.Oblivious, - customModifiers.NullToEmpty()); + return Create(typeSymbol, nullableAnnotation: isAnnotated ? NullableAnnotation.Annotated : isNullableEnabled ? NullableAnnotation.NotAnnotated : NullableAnnotation.Oblivious); } internal static TypeWithAnnotations Create(TypeSymbol typeSymbol, NullableAnnotation nullableAnnotation = NullableAnnotation.Oblivious, ImmutableArray customModifiers = default) @@ -580,7 +579,7 @@ public bool NeedsNullableAttribute() /// If the type is a non-generic value type or Nullable<>, and /// is not a type parameter, the nullability is not included in the byte[]. /// - private static bool SkipNullableTransform(TypeSymbol type) + private static bool IsNonGenericValueType(TypeSymbol type) { var namedType = type as NamedTypeSymbol; if (namedType is null) @@ -605,7 +604,7 @@ private static void AddNullableTransforms(TypeWithAnnotations typeWithAnnotation { var type = typeWithAnnotations.Type; - if (!SkipNullableTransform(type)) + if (!IsNonGenericValueType(type)) { var annotation = typeWithAnnotations.NullableAnnotation; byte flag; @@ -642,9 +641,9 @@ public bool ApplyNullableTransforms(byte defaultTransformFlag, ImmutableArray + where U : class + where V : struct +{ + T F1(); + U F2(); + U? F3(); + V F4(); + V? F5(); +#nullable disable + T F6(); + U F7(); + U? F8(); + V F9(); + V? F10(); +}"; + var comp = CreateCompilation(source); + var expected = +@"I where U : class! where V : struct + [Nullable(2)] T + [Nullable(1)] U + [NullableContext(1)] T F1() + [NullableContext(1)] U! F2() + [NullableContext(2)] U? F3() + [NullableContext(2)] U? F8() +"; + AssertNullableAttributes(comp, expected); + } + [Fact] public void EmitAttribute_Constraint_Nullable() { diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/NullablePublicAPITests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/NullablePublicAPITests.cs index 8ec74022649..46591fccde7 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/NullablePublicAPITests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/NullablePublicAPITests.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Immutable; using System.Linq; -using System.Reflection; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; @@ -87,7 +86,8 @@ void M1(C? c) Assert.Equal(PublicNullableAnnotation.NotAnnotated, expressionTypes[2].TypeArgumentNullableAnnotations.Single()); } - [Fact(Skip = "https://github.com/dotnet/roslyn/issues/34412")] + [Fact] + [WorkItem(34412, "https://github.com/dotnet/roslyn/issues/34412")] public void FieldDeclarations() { var source = @" @@ -143,11 +143,12 @@ public class C PublicNullableAnnotation.Annotated, PublicNullableAnnotation.Disabled, PublicNullableAnnotation.Annotated, - PublicNullableAnnotation.Disabled, + PublicNullableAnnotation.NotAnnotated, PublicNullableAnnotation.Annotated); } - [Fact(Skip = "https://github.com/dotnet/roslyn/issues/34412")] + [Fact] + [WorkItem(34412, "https://github.com/dotnet/roslyn/issues/34412")] public void PropertyDeclarations() { var source = @" @@ -204,11 +205,12 @@ public class C PublicNullableAnnotation.Annotated, PublicNullableAnnotation.Disabled, PublicNullableAnnotation.Annotated, - PublicNullableAnnotation.Disabled, + PublicNullableAnnotation.NotAnnotated, PublicNullableAnnotation.Annotated); } - [Fact(Skip = "https://github.com/dotnet/roslyn/issues/34412")] + [Fact] + [WorkItem(34412, "https://github.com/dotnet/roslyn/issues/34412")] public void MethodReturnDeclarations() { var source = @" @@ -272,12 +274,12 @@ public class C PublicNullableAnnotation.Annotated, PublicNullableAnnotation.NotAnnotated, PublicNullableAnnotation.Annotated, - PublicNullableAnnotation.Disabled, + PublicNullableAnnotation.NotAnnotated, PublicNullableAnnotation.Annotated); - } - [Fact(Skip = "https://github.com/dotnet/roslyn/issues/34412")] + [Fact] + [WorkItem(34412, "https://github.com/dotnet/roslyn/issues/34412")] public void ParameterDeclarations() { var source = @" @@ -355,7 +357,7 @@ public class C PublicNullableAnnotation.Annotated, PublicNullableAnnotation.Annotated, PublicNullableAnnotation.NotAnnotated, - PublicNullableAnnotation.Disabled, + PublicNullableAnnotation.NotAnnotated, PublicNullableAnnotation.Annotated, PublicNullableAnnotation.Annotated); } @@ -375,52 +377,56 @@ public class C public static void M3() {} public void M4() {} } -public static class CExt +public static class Ext { #nullable enable public static void M5(this C c) {} public static void M6(this C? c) {} + public static void M7(this int i) {} + public static void M8(this int? i) {} #nullable disable - public static void M7(this C c) {} - public static void M8(this C? c) {} + public static void M9(this C c) {} + public static void M10(this C? c) {} + public static void M11(this int i) {} + public static void M12(this int? i) {} } "; var comp1 = CreateCompilation(source, options: WithNonNullTypesTrue()); comp1.VerifyDiagnostics( - // (20,33): warning CS8632: The annotation for nullable reference types should only be used in code within a '#nullable' context. - // public static void M8(this C? c) {} - Diagnostic(ErrorCode.WRN_MissingNonNullTypesContextForAnnotation, "?").WithLocation(20, 33)); + // (22,34): warning CS8632: The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. + // public static void M10(this C? c) {} + Diagnostic(ErrorCode.WRN_MissingNonNullTypesContextForAnnotation, "?").WithLocation(22, 34)); verifyCompilation(comp1); var comp2 = CreateCompilation(source, options: WithNonNullTypesFalse()); comp2.VerifyDiagnostics( - // (20,33): warning CS8632: The annotation for nullable reference types should only be used in code within a '#nullable' context. - // public static void M8(this C? c) {} - Diagnostic(ErrorCode.WRN_MissingNonNullTypesContextForAnnotation, "?").WithLocation(20, 33)); + // (22,34): warning CS8632: The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. + // public static void M10(this C? c) {} + Diagnostic(ErrorCode.WRN_MissingNonNullTypesContextForAnnotation, "?").WithLocation(22, 34)); verifyCompilation(comp2); var comp3 = CreateCompilation(source, parseOptions: TestOptions.Regular7_3, skipUsesIsNullable: true); comp3.VerifyDiagnostics( - // (4,2): error CS8652: The feature 'nullable reference types' is not available in C# 7.3. Please use language version 8.0 or greater. + // (4,2): error CS8370: Feature 'nullable reference types' is not available in C# 7.3. Please use language version 8.0 or greater. // #nullable enable Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "nullable").WithArguments("nullable reference types", "8.0").WithLocation(4, 2), - // (8,2): error CS8652: The feature 'nullable reference types' is not available in C# 7.3. Please use language version 8.0 or greater. + // (8,2): error CS8370: Feature 'nullable reference types' is not available in C# 7.3. Please use language version 8.0 or greater. // #nullable disable Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "nullable").WithArguments("nullable reference types", "8.0").WithLocation(8, 2), - // (14,2): error CS8652: The feature 'nullable reference types' is not available in C# 7.3. Please use language version 8.0 or greater. + // (14,2): error CS8370: Feature 'nullable reference types' is not available in C# 7.3. Please use language version 8.0 or greater. // #nullable enable Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "nullable").WithArguments("nullable reference types", "8.0").WithLocation(14, 2), - // (16,33): error CS8652: The feature 'nullable reference types' is not available in C# 7.3. Please use language version 8.0 or greater. + // (16,33): error CS8370: Feature 'nullable reference types' is not available in C# 7.3. Please use language version 8.0 or greater. // public static void M6(this C? c) {} Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "?").WithArguments("nullable reference types", "8.0").WithLocation(16, 33), - // (18,2): error CS8652: The feature 'nullable reference types' is not available in C# 7.3. Please use language version 8.0 or greater. + // (20,2): error CS8370: Feature 'nullable reference types' is not available in C# 7.3. Please use language version 8.0 or greater. // #nullable disable - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "nullable").WithArguments("nullable reference types", "8.0").WithLocation(18, 2), - // (20,33): error CS8652: The feature 'nullable reference types' is not available in C# 7.3. Please use language version 8.0 or greater. - // public static void M8(this C? c) {} - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "?").WithArguments("nullable reference types", "8.0").WithLocation(20, 33)); + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "nullable").WithArguments("nullable reference types", "8.0").WithLocation(20, 2), + // (22,34): error CS8370: Feature 'nullable reference types' is not available in C# 7.3. Please use language version 8.0 or greater. + // public static void M10(this C? c) {} + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "?").WithArguments("nullable reference types", "8.0").WithLocation(22, 34)); verifyCompilation(comp3); var comp1Emit = comp1.EmitToImageReference(); @@ -441,22 +447,28 @@ void verifyCompilation(CSharpCompilation compilation) { var c = compilation.GetTypeByMetadataName("C"); var members = c.GetMembers().OfType().Where(m => m.Name.StartsWith("M")).ToArray(); - - assertNullability(PublicNullableAnnotation.NotApplicable, 0); - assertNullability(PublicNullableAnnotation.NotAnnotated, 1); - assertNullability(PublicNullableAnnotation.NotApplicable, 2); - assertNullability(PublicNullableAnnotation.NotAnnotated, 3); - - var cExt = compilation.GetTypeByMetadataName("CExt"); - members = cExt.GetMembers().OfType().Where(m => m.Name.StartsWith("M")).Select(m => m.ReduceExtensionMethod(c)).ToArray(); - assertNullability(PublicNullableAnnotation.NotAnnotated, 0); - assertNullability(PublicNullableAnnotation.Annotated, 1); - assertNullability(PublicNullableAnnotation.Disabled, 2); - assertNullability(PublicNullableAnnotation.Annotated, 3); - - void assertNullability(PublicNullableAnnotation nullability, int index) + assertNullability(members, + PublicNullableAnnotation.NotApplicable, + PublicNullableAnnotation.NotAnnotated, + PublicNullableAnnotation.NotApplicable, + PublicNullableAnnotation.NotAnnotated); + + var e = compilation.GetTypeByMetadataName("Ext"); + members = e.GetMembers().OfType().Where(m => m.Name.StartsWith("M")).Select(m => m.ReduceExtensionMethod(m.Parameters[0].Type)).ToArray(); + assertNullability(members, + PublicNullableAnnotation.NotAnnotated, + PublicNullableAnnotation.Annotated, + PublicNullableAnnotation.NotAnnotated, + PublicNullableAnnotation.Annotated, + PublicNullableAnnotation.Disabled, + PublicNullableAnnotation.Annotated, + PublicNullableAnnotation.NotAnnotated, + PublicNullableAnnotation.Annotated); + + static void assertNullability(IMethodSymbol[] methods, params PublicNullableAnnotation[] expectedAnnotations) { - Assert.Equal(nullability, members[index].ReceiverNullableAnnotation); + var actualAnnotations = methods.Select(m => m.ReceiverNullableAnnotation); + AssertEx.Equal(expectedAnnotations, actualAnnotations); } } } @@ -534,7 +546,7 @@ void M1() PublicNullableAnnotation.Annotated, PublicNullableAnnotation.Disabled, PublicNullableAnnotation.Annotated, - PublicNullableAnnotation.Disabled, + PublicNullableAnnotation.NotAnnotated, PublicNullableAnnotation.Annotated); } @@ -592,6 +604,345 @@ public class C PublicNullableAnnotation.Annotated); } + [Fact] + [WorkItem(34412, "https://github.com/dotnet/roslyn/issues/34412")] + public void ArrayElements() + { + var source = +@"public interface I +{ +#nullable enable + object[] F1(); + object?[] F2(); + int[] F3(); + int?[] F4(); +#nullable disable + object[] F5(); + object?[] F6(); + int[] F7(); + int?[] F8(); +}"; + VerifyAcrossCompilations( + source, + new[] + { + // (10,11): warning CS8632: The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. + // object?[] F6(); + Diagnostic(ErrorCode.WRN_MissingNonNullTypesContextForAnnotation, "?").WithLocation(10, 11) + }, + new[] + { + // (3,2): error CS8370: Feature 'nullable reference types' is not available in C# 7.3. Please use language version 8.0 or greater. + // #nullable enable + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "nullable").WithArguments("nullable reference types", "8.0").WithLocation(3, 2), + // (5,11): error CS8370: Feature 'nullable reference types' is not available in C# 7.3. Please use language version 8.0 or greater. + // object?[] F2(); + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "?").WithArguments("nullable reference types", "8.0").WithLocation(5, 11), + // (8,2): error CS8370: Feature 'nullable reference types' is not available in C# 7.3. Please use language version 8.0 or greater. + // #nullable disable + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "nullable").WithArguments("nullable reference types", "8.0").WithLocation(8, 2), + // (10,11): error CS8370: Feature 'nullable reference types' is not available in C# 7.3. Please use language version 8.0 or greater. + // object?[] F6(); + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "?").WithArguments("nullable reference types", "8.0").WithLocation(10, 11) + }, + comp => ((NamedTypeSymbol)comp.GetMember("I")).GetMembers().OfType().Where(m => m.Name.StartsWith("F")).ToArray(), + method => ((IArrayTypeSymbol)method.ReturnType).ElementNullableAnnotation, + testMetadata: true, + PublicNullableAnnotation.NotAnnotated, + PublicNullableAnnotation.Annotated, + PublicNullableAnnotation.NotAnnotated, + PublicNullableAnnotation.Annotated, + PublicNullableAnnotation.Disabled, + PublicNullableAnnotation.Annotated, + PublicNullableAnnotation.NotAnnotated, + PublicNullableAnnotation.Annotated); + } + + [Fact] + [WorkItem(34412, "https://github.com/dotnet/roslyn/issues/34412")] + public void TypeParameters() + { + var source = +@"#nullable enable +public interface I + where U : class + where V : struct +{ + T F1(); + U F2(); + U? F3(); + V F4(); + V? F5(); +#nullable disable + T F6(); + U F7(); + U? F8(); + V F9(); + V? F10(); +}"; + VerifyAcrossCompilations( + source, + new[] + { + // (14,6): warning CS8632: The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. + // U? F8(); + Diagnostic(ErrorCode.WRN_MissingNonNullTypesContextForAnnotation, "?").WithLocation(14, 6) + }, + new[] + { + // (1,2): error CS8370: Feature 'nullable reference types' is not available in C# 7.3. Please use language version 8.0 or greater. + // #nullable enable + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "nullable").WithArguments("nullable reference types", "8.0").WithLocation(1, 2), + // (8,6): error CS8370: Feature 'nullable reference types' is not available in C# 7.3. Please use language version 8.0 or greater. + // U? F3(); + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "?").WithArguments("nullable reference types", "8.0").WithLocation(8, 6), + // (11,2): error CS8370: Feature 'nullable reference types' is not available in C# 7.3. Please use language version 8.0 or greater. + // #nullable disable + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "nullable").WithArguments("nullable reference types", "8.0").WithLocation(11, 2), + // (14,6): error CS8370: Feature 'nullable reference types' is not available in C# 7.3. Please use language version 8.0 or greater. + // U? F8(); + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "?").WithArguments("nullable reference types", "8.0").WithLocation(14, 6) + }, + comp => ((NamedTypeSymbol)comp.GetMember("I")).GetMembers().OfType().Where(m => m.Name.StartsWith("F")).ToArray(), + method => method.ReturnNullableAnnotation, + testMetadata: true, + PublicNullableAnnotation.NotAnnotated, + PublicNullableAnnotation.NotAnnotated, + PublicNullableAnnotation.Annotated, + PublicNullableAnnotation.NotAnnotated, + PublicNullableAnnotation.Annotated, + PublicNullableAnnotation.Disabled, + PublicNullableAnnotation.Disabled, + PublicNullableAnnotation.Annotated, + PublicNullableAnnotation.NotAnnotated, + PublicNullableAnnotation.Annotated); + } + + [Fact] + [WorkItem(34412, "https://github.com/dotnet/roslyn/issues/34412")] + public void Constraints() + { + var source = +@"public class A +{ + public class B where U : T { } +} +public interface I +{ +#nullable enable + A F1(); + A F2(); + A F3(); + A F4(); +#nullable disable + A F5(); + A F6(); + A F7(); + A F8(); +}"; + VerifyAcrossCompilations( + source, + new[] + { + // (14,13): warning CS8632: The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. + // A F6(); + Diagnostic(ErrorCode.WRN_MissingNonNullTypesContextForAnnotation, "?").WithLocation(14, 13) + }, + new[] + { + // (7,2): error CS8370: Feature 'nullable reference types' is not available in C# 7.3. Please use language version 8.0 or greater. + // #nullable enable + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "nullable").WithArguments("nullable reference types", "8.0").WithLocation(7, 2), + // (9,13): error CS8370: Feature 'nullable reference types' is not available in C# 7.3. Please use language version 8.0 or greater. + // A F2(); + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "?").WithArguments("nullable reference types", "8.0").WithLocation(9, 13), + // (12,2): error CS8370: Feature 'nullable reference types' is not available in C# 7.3. Please use language version 8.0 or greater. + // #nullable disable + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "nullable").WithArguments("nullable reference types", "8.0").WithLocation(12, 2), + // (14,13): error CS8370: Feature 'nullable reference types' is not available in C# 7.3. Please use language version 8.0 or greater. + // A F6(); + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "?").WithArguments("nullable reference types", "8.0").WithLocation(14, 13) + }, + comp => ((NamedTypeSymbol)comp.GetMember("I")).GetMembers().OfType().Where(m => m.Name.StartsWith("F")).ToArray(), + method => ((INamedTypeSymbol)((INamedTypeSymbol)method.ReturnType).GetMembers("B").Single()).TypeParameters.Single().ConstraintNullableAnnotations.Single(), + testMetadata: true, + PublicNullableAnnotation.NotAnnotated, + PublicNullableAnnotation.Annotated, + PublicNullableAnnotation.NotAnnotated, + PublicNullableAnnotation.Annotated, + PublicNullableAnnotation.Disabled, + PublicNullableAnnotation.Annotated, + PublicNullableAnnotation.NotAnnotated, + PublicNullableAnnotation.Annotated); + } + + [Fact] + [WorkItem(34412, "https://github.com/dotnet/roslyn/issues/34412")] + public void TypeArguments_01() + { + var source = +@"public interface IA +{ +} +#nullable enable +public interface IB + where U : class + where V : struct +{ + IA F1(); + IA F2(); + IA F3(); + IA F4(); + IA F5(); +#nullable disable + IA F6(); + IA F7(); + IA F8(); + IA F9(); + IA F10(); +}"; + VerifyAcrossCompilations( + source, + new[] + { + // (17,9): warning CS8632: The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. + // IA F8(); + Diagnostic(ErrorCode.WRN_MissingNonNullTypesContextForAnnotation, "?").WithLocation(17, 9) + }, + new[] + { + // (4,2): error CS8370: Feature 'nullable reference types' is not available in C# 7.3. Please use language version 8.0 or greater. + // #nullable enable + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "nullable").WithArguments("nullable reference types", "8.0").WithLocation(4, 2), + // (11,9): error CS8370: Feature 'nullable reference types' is not available in C# 7.3. Please use language version 8.0 or greater. + // IA F3(); + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "?").WithArguments("nullable reference types", "8.0").WithLocation(11, 9), + // (14,2): error CS8370: Feature 'nullable reference types' is not available in C# 7.3. Please use language version 8.0 or greater. + // #nullable disable + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "nullable").WithArguments("nullable reference types", "8.0").WithLocation(14, 2), + // (17,9): error CS8370: Feature 'nullable reference types' is not available in C# 7.3. Please use language version 8.0 or greater. + // IA F8(); + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "?").WithArguments("nullable reference types", "8.0").WithLocation(17, 9) + }, + comp => ((NamedTypeSymbol)comp.GetMember("IB")).GetMembers().OfType().Where(m => m.Name.StartsWith("F")).ToArray(), + method => ((INamedTypeSymbol)method.ReturnType).TypeArgumentNullableAnnotations.Single(), + testMetadata: true, + PublicNullableAnnotation.NotAnnotated, + PublicNullableAnnotation.NotAnnotated, + PublicNullableAnnotation.Annotated, + PublicNullableAnnotation.NotAnnotated, + PublicNullableAnnotation.Annotated, + PublicNullableAnnotation.Disabled, + PublicNullableAnnotation.Disabled, + PublicNullableAnnotation.Annotated, + PublicNullableAnnotation.NotAnnotated, + PublicNullableAnnotation.Annotated); + } + + [Fact] + [WorkItem(34412, "https://github.com/dotnet/roslyn/issues/34412")] + public void TypeArguments_02() + { + var source = +@"class C +{ + static void F() + { + } +#nullable enable + static void M() + where U : class + where V : struct + { + F(); + F(); + F(); + F(); + F(); +#nullable disable + F(); + F(); + F(); + F(); + F(); + } +}"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (19,12): warning CS8632: The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. + // F(); + Diagnostic(ErrorCode.WRN_MissingNonNullTypesContextForAnnotation, "?").WithLocation(19, 12)); + var syntaxTree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(syntaxTree); + var invocations = syntaxTree.GetRoot().DescendantNodes().OfType(); + var actualAnnotations = invocations.Select(inv => ((IMethodSymbol)model.GetSymbolInfo(inv).Symbol).TypeArgumentNullableAnnotations.Single()).ToArray(); + var expectedAnnotations = new[] + { + PublicNullableAnnotation.NotAnnotated, + PublicNullableAnnotation.NotAnnotated, + PublicNullableAnnotation.Annotated, + PublicNullableAnnotation.NotAnnotated, + PublicNullableAnnotation.Annotated, + PublicNullableAnnotation.Disabled, + PublicNullableAnnotation.Disabled, + PublicNullableAnnotation.Annotated, + PublicNullableAnnotation.NotAnnotated, + PublicNullableAnnotation.Annotated + }; + AssertEx.Equal(expectedAnnotations, actualAnnotations); + } + + [Fact] + [WorkItem(34412, "https://github.com/dotnet/roslyn/issues/34412")] + public void Locals() + { + var source = +@"#pragma warning disable 168 +class C +{ +#nullable enable + static void M() + where U : class + where V : struct + { + T x1; + U x2; + U? x3; + V x4; + V? x5; +#nullable disable + T x6; + U x7; + U? x8; + V x9; + V? x10; + } +}"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (17,10): warning CS8632: The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. + // U? x8; + Diagnostic(ErrorCode.WRN_MissingNonNullTypesContextForAnnotation, "?").WithLocation(17, 10)); + var syntaxTree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(syntaxTree); + var variables = syntaxTree.GetRoot().DescendantNodes().OfType(); + var actualAnnotations = variables.Select(v => ((ILocalSymbol)model.GetDeclaredSymbol(v)).NullableAnnotation).ToArray(); + var expectedAnnotations = new[] + { + PublicNullableAnnotation.NotAnnotated, + PublicNullableAnnotation.NotAnnotated, + PublicNullableAnnotation.Annotated, + PublicNullableAnnotation.NotAnnotated, + PublicNullableAnnotation.Annotated, + PublicNullableAnnotation.Disabled, + PublicNullableAnnotation.Disabled, + PublicNullableAnnotation.Annotated, + PublicNullableAnnotation.NotAnnotated, + PublicNullableAnnotation.Annotated + }; + AssertEx.Equal(expectedAnnotations, actualAnnotations); + } + private static void VerifyAcrossCompilations(string source, DiagnosticDescription[] nullableEnabledErrors, DiagnosticDescription[] nullableDisabledErrors, @@ -600,7 +951,6 @@ public class C bool testMetadata, params PublicNullableAnnotation[] expectedNullabilities) { - var comp1 = CreateCompilation(source, options: WithNonNullTypesTrue()); comp1.VerifyDiagnostics(nullableEnabledErrors); verifyCompilation(comp1); diff --git a/src/Compilers/Test/Utilities/CSharp/CompilationTestUtils.cs b/src/Compilers/Test/Utilities/CSharp/CompilationTestUtils.cs index ccd53c572b9..5129a30f944 100644 --- a/src/Compilers/Test/Utilities/CSharp/CompilationTestUtils.cs +++ b/src/Compilers/Test/Utilities/CSharp/CompilationTestUtils.cs @@ -319,14 +319,10 @@ internal static void VerifyTypes(this CSharpCompilation compilation, SyntaxTree Assert.NotEqual(CodeAnalysis.NullableAnnotation.NotApplicable, typeInfo.Nullability.Annotation); Assert.NotEqual(CodeAnalysis.NullableFlowState.NotApplicable, typeInfo.Nullability.FlowState); // https://github.com/dotnet/roslyn/issues/35035: After refactoring symboldisplay, we should be able to just call something like typeInfo.Type.ToDisplayString(typeInfo.Nullability.FlowState, TypeWithState.TestDisplayFormat) - if (annotation.IsConverted) - { - return TypeWithState.Create((TypeSymbol)typeInfo.ConvertedType, typeInfo.ConvertedNullability.FlowState.ToInternalFlowState()).ToTypeWithAnnotations().ToDisplayString(TypeWithAnnotations.TestDisplayFormat); - } - else - { - return TypeWithState.Create((TypeSymbol)typeInfo.Type, typeInfo.Nullability.FlowState.ToInternalFlowState()).ToTypeWithAnnotations().ToDisplayString(TypeWithAnnotations.TestDisplayFormat); - } + var type = TypeWithState.Create( + (TypeSymbol)(annotation.IsConverted ? typeInfo.ConvertedType : typeInfo.Type), + (annotation.IsConverted ? typeInfo.ConvertedNullability : typeInfo.Nullability).FlowState.ToInternalFlowState()).ToTypeWithAnnotations(); + return type.ToDisplayString(TypeWithAnnotations.TestDisplayFormat); }); // Consider reporting the correct source with annotations on mismatch. AssertEx.Equal(expectedTypes, actualTypes, message: method.ToTestDisplayString()); -- GitLab