diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index 30dabe45853e7f0c5dd19b4df79e3f6c9b820b26..a404736e25c8427087a1ad97e131799aaddb0333 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 5a9be0f8348c96c8fc0bad97d0241db2ffb67f9e..163efdf9f89b6d79b46efdff4f436c4154be68fe 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 ead0ba7d021e1eccf2be0f88b1de37bf196b7b4d..02ccc5dec1a7c4361f38e1571c874f14be74212d 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 e843bd2937775316280a26ee1c791edc61e811ab..f498b99ede7d415d924ad3c86a6596a0271ff022 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 54e07754a904508993af378b5cf2d53ae91e1521..f70d2365b0285a83ea93c6e2ca180332ac4b8878 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 889a5e5afe5c043f5a7d5597726ef108a09226ce..029f768acbc203bc3430adb4cd2c81d7044735ce 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 bc8759562524945c1f90c51f4aded83ac1f1db0a..9c5d475468a216eb57ab0eaf57e471e60f9638e1 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 99ebbc333b9423a692555d2ef2f0f6e5eef6fc6a..67ad31a269de9c547b7ff12ca7788a49ba6d1d6c 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 3848c09f8f5994522945f1da5e8240a21d730193..afb61f55004187ff40309e5624264b2d1e705400 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 cf6023d30f4aaeb0d9395878a86a57a0981a3c87..2c89021e7505cfe2bef83e6578c6ad092a17f385 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 e8e10325dafb4f5c08144a2cec4a6c982380710c..772eefb565e41e37228093abf2c372d8af768ec8 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 67629af917f0a0f881367aa27c4c62215ef2228c..24a320c4d06a7ca4c987c72ed2223b14d26a83e1 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 761e8abfc3de198faa81170c584c1040ab62f620..15f0ed0ea58772f2ca4d43aecdc7baa25b9cfd9f 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 89b598977a05284d5154d85fe13678f40c21cdc9..9fd8cdf87d6415d9b6ab57131b2f21438d43f568 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 8ec7402264937c356ac8f345a76ce5ee97e2820a..46591fccde7c3d01e89fd47a752b711304bc34b9 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 ccd53c572b961ec55a86a8620d0e95125e98c6e6..5129a30f94466ca75953150b49d758671f857abf 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());