diff --git a/docs/features/nullable-metadata.md b/docs/features/nullable-metadata.md index ab2498757a0a0..e99e4e204e835 100644 --- a/docs/features/nullable-metadata.md +++ b/docs/features/nullable-metadata.md @@ -148,18 +148,27 @@ for members that are inaccessible outside the assembly (`private` members, and a if the assembly does not contain `InternalsVisibleToAttribute` attributes). The compiler behavior is configured from a command-line flag. -For now a feature flag is used: `-feature:nullablePublicOnly`. +For now a feature flag is used: `-features:nullablePublicOnly`. If private member attributes are dropped, the compiler will emit a `[module: NullablePublicOnly]` attribute. The presence or absence of the `NullablePublicOnlyAttribute` can be used by tools to interpret the nullability of private members that do not have an associated `NullableAttribute` attribute. +For members that do not have explicit accessibility in metadata +(specifically for parameters, type parameters, events, and properties), +the compiler uses the accessibility of the container to determine whether to emit nullable attributes. + ```C# namespace System.Runtime.CompilerServices { [System.AttributeUsage(AttributeTargets.Module, AllowMultiple = false)] public sealed class NullablePublicOnlyAttribute : Attribute { + public readonly bool IncludesInternals; + public NullablePublicOnlyAttribute(bool includesInternals) + { + IncludesInternals = includesInternals; + } } } ``` @@ -167,6 +176,8 @@ namespace System.Runtime.CompilerServices The `NullablePublicOnlyAttribute` type is for compiler use only - it is not permitted in source. The type declaration is synthesized by the compiler if not already included in the compilation. +`IncludesInternal` is true if `internal` members are annotated in addition to `public` and `protected` members. + ## Compatibility The nullable metadata does not include an explicit version number. diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs index 909f1b9590ddd..c2bbfa9b47a02 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs @@ -3305,12 +3305,34 @@ internal bool ShouldEmitNullableAttributes(Symbol symbol) return true; } + // For symbols that do not have explicit accessibility in metadata, + // use the accessibility of the container. + symbol = getExplicitAccessibilitySymbol(symbol); + if (!AccessCheck.IsEffectivelyPublicOrInternal(symbol, out bool isInternal)) { return false; } return !isInternal || SourceAssembly.InternalsAreVisible; + + static Symbol getExplicitAccessibilitySymbol(Symbol symbol) + { + while (true) + { + switch (symbol.Kind) + { + case SymbolKind.Parameter: + case SymbolKind.TypeParameter: + case SymbolKind.Property: + case SymbolKind.Event: + symbol = symbol.ContainingSymbol; + break; + default: + return symbol; + } + } + } } internal override AnalyzerDriver AnalyzerForLanguage(ImmutableArray analyzers, AnalyzerManager analyzerManager) diff --git a/src/Compilers/CSharp/Portable/Emitter/Model/MostCommonNullableValueBuilder.cs b/src/Compilers/CSharp/Portable/Emitter/Model/MostCommonNullableValueBuilder.cs new file mode 100644 index 0000000000000..81e2d783c2238 --- /dev/null +++ b/src/Compilers/CSharp/Portable/Emitter/Model/MostCommonNullableValueBuilder.cs @@ -0,0 +1,95 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.CodeAnalysis.CSharp.Symbols; +using Microsoft.CodeAnalysis.PooledObjects; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.CSharp +{ + internal struct MostCommonNullableValueBuilder + { + private int _value0; + private int _value1; + private int _value2; + + internal byte? MostCommonValue + { + get + { + int max; + byte b; + if (_value1 > _value0) + { + max = _value1; + b = 1; + } + else + { + max = _value0; + b = 0; + } + if (_value2 > max) + { + return 2; + } + return max == 0 ? (byte?)null : b; + } + } + + internal void AddValue(byte value) + { + switch (value) + { + case 0: + _value0++; + break; + case 1: + _value1++; + break; + case 2: + _value2++; + break; + default: + throw ExceptionUtilities.UnexpectedValue(value); + } + } + + internal void AddValue(byte? value) + { + if (value != null) + { + AddValue(value.GetValueOrDefault()); + } + } + + internal void AddValue(TypeWithAnnotations type) + { + var builder = ArrayBuilder.GetInstance(); + type.AddNullableTransforms(builder); + AddValue(GetCommonValue(builder)); + builder.Free(); + } + + /// + /// Returns the common value if all bytes are the same value. + /// Otherwise returns null. + /// + internal static byte? GetCommonValue(ArrayBuilder builder) + { + int n = builder.Count; + if (n == 0) + { + return null; + } + byte b = builder[0]; + for (int i = 1; i < n; i++) + { + if (builder[i] != b) + { + return null; + } + } + return b; + } + } +} diff --git a/src/Compilers/CSharp/Portable/Emitter/Model/PEAssemblyBuilder.cs b/src/Compilers/CSharp/Portable/Emitter/Model/PEAssemblyBuilder.cs index 95743684c5001..bd3923de178d6 100644 --- a/src/Compilers/CSharp/Portable/Emitter/Model/PEAssemblyBuilder.cs +++ b/src/Compilers/CSharp/Portable/Emitter/Model/PEAssemblyBuilder.cs @@ -26,7 +26,8 @@ internal abstract class PEAssemblyBuilderBase : PEModuleBuilder, Cci.IAssemblyRe private SynthesizedEmbeddedAttributeSymbol _lazyIsByRefLikeAttribute; private SynthesizedEmbeddedAttributeSymbol _lazyIsUnmanagedAttribute; private SynthesizedEmbeddedNullableAttributeSymbol _lazyNullableAttribute; - private SynthesizedEmbeddedAttributeSymbol _lazyNullablePublicOnlyAttribute; + private SynthesizedEmbeddedNullableContextAttributeSymbol _lazyNullableContextAttribute; + private SynthesizedEmbeddedNullablePublicOnlyAttributeSymbol _lazyNullablePublicOnlyAttribute; /// /// The behavior of the C# command-line compiler is as follows: @@ -83,6 +84,7 @@ internal override ImmutableArray GetEmbeddedTypes(DiagnosticBag builder.AddIfNotNull(_lazyIsUnmanagedAttribute); builder.AddIfNotNull(_lazyIsByRefLikeAttribute); builder.AddIfNotNull(_lazyNullableAttribute); + builder.AddIfNotNull(_lazyNullableContextAttribute); builder.AddIfNotNull(_lazyNullablePublicOnlyAttribute); return builder.ToImmutableAndFree(); @@ -195,17 +197,30 @@ internal override SynthesizedAttributeData SynthesizeNullableAttribute(WellKnown return base.SynthesizeNullableAttribute(member, arguments); } - internal override SynthesizedAttributeData SynthesizeNullablePublicOnlyAttribute() + internal override SynthesizedAttributeData SynthesizeNullableContextAttribute(ImmutableArray arguments) + { + if ((object)_lazyNullableContextAttribute != null) + { + return new SynthesizedAttributeData( + _lazyNullableContextAttribute.Constructors[0], + arguments, + ImmutableArray>.Empty); + } + + return base.SynthesizeNullableContextAttribute(arguments); + } + + internal override SynthesizedAttributeData SynthesizeNullablePublicOnlyAttribute(ImmutableArray arguments) { if ((object)_lazyNullablePublicOnlyAttribute != null) { return new SynthesizedAttributeData( _lazyNullablePublicOnlyAttribute.Constructors[0], - ImmutableArray.Empty, + arguments, ImmutableArray>.Empty); } - return base.SynthesizeNullablePublicOnlyAttribute(); + return base.SynthesizeNullablePublicOnlyAttribute(arguments); } protected override SynthesizedAttributeData TrySynthesizeIsReadOnlyAttribute() @@ -292,12 +307,18 @@ private void CreateEmbeddedAttributesIfNeeded(DiagnosticBag diagnostics) diagnostics); } + if ((needsAttributes & EmbeddableAttributes.NullableContextAttribute) != 0) + { + CreateEmbeddedNullableContextAttributeIfNeeded( + ref _lazyNullableContextAttribute, + diagnostics); + } + if ((needsAttributes & EmbeddableAttributes.NullablePublicOnlyAttribute) != 0) { - CreateEmbeddedAttributeIfNeeded( + CreateEmbeddedNullablePublicOnlyAttributeIfNeeded( ref _lazyNullablePublicOnlyAttribute, - diagnostics, - AttributeDescription.NullablePublicOnlyAttribute); + diagnostics); } } @@ -324,6 +345,28 @@ private void CreateEmbeddedNullableAttributeIfNeeded( } } + private void CreateEmbeddedNullableContextAttributeIfNeeded( + ref SynthesizedEmbeddedNullableContextAttributeSymbol symbol, + DiagnosticBag diagnostics) + { + if (symbol is null) + { + AddDiagnosticsForExistingAttribute(AttributeDescription.NullableContextAttribute, diagnostics); + symbol = new SynthesizedEmbeddedNullableContextAttributeSymbol(_sourceAssembly.DeclaringCompilation, diagnostics); + } + } + + private void CreateEmbeddedNullablePublicOnlyAttributeIfNeeded( + ref SynthesizedEmbeddedNullablePublicOnlyAttributeSymbol symbol, + DiagnosticBag diagnostics) + { + if (symbol is null) + { + AddDiagnosticsForExistingAttribute(AttributeDescription.NullablePublicOnlyAttribute, diagnostics); + symbol = new SynthesizedEmbeddedNullablePublicOnlyAttributeSymbol(_sourceAssembly.DeclaringCompilation, diagnostics); + } + } + private void AddDiagnosticsForExistingAttribute(AttributeDescription description, DiagnosticBag diagnostics) { var attributeMetadataName = MetadataTypeName.FromFullName(description.FullName); diff --git a/src/Compilers/CSharp/Portable/Emitter/Model/PEModuleBuilder.cs b/src/Compilers/CSharp/Portable/Emitter/Model/PEModuleBuilder.cs index 0dcdd0151c126..47c389c71b578 100644 --- a/src/Compilers/CSharp/Portable/Emitter/Model/PEModuleBuilder.cs +++ b/src/Compilers/CSharp/Portable/Emitter/Model/PEModuleBuilder.cs @@ -1462,7 +1462,7 @@ internal SynthesizedAttributeData SynthesizeIsByRefLikeAttribute(Symbol symbol) /// is a constructed type with a nullable reference type present in its type argument tree, /// returns a synthesized NullableAttribute with encoded nullable transforms array. /// - internal SynthesizedAttributeData SynthesizeNullableAttribute(Symbol symbol, TypeWithAnnotations type) + internal SynthesizedAttributeData SynthesizeNullableAttributeIfNecessary(Symbol symbol, byte? nullableContextValue, TypeWithAnnotations type) { if ((object)Compilation.SourceModule != symbol.ContainingModule) { @@ -1473,36 +1473,46 @@ internal SynthesizedAttributeData SynthesizeNullableAttribute(Symbol symbol, Typ var flagsBuilder = ArrayBuilder.GetInstance(); type.AddNullableTransforms(flagsBuilder); - Debug.Assert(flagsBuilder.Any()); - Debug.Assert(flagsBuilder.Contains(NullableAnnotationExtensions.NotAnnotatedAttributeValue) || flagsBuilder.Contains(NullableAnnotationExtensions.AnnotatedAttributeValue)); - - WellKnownMember constructor; - ImmutableArray arguments; - NamedTypeSymbol byteType = Compilation.GetSpecialType(SpecialType.System_Byte); - Debug.Assert((object)byteType != null); - - if (flagsBuilder.All(flag => flag == NullableAnnotationExtensions.NotAnnotatedAttributeValue) || flagsBuilder.All(flag => flag == NullableAnnotationExtensions.AnnotatedAttributeValue)) + SynthesizedAttributeData attribute; + if (!flagsBuilder.Any()) { - constructor = WellKnownMember.System_Runtime_CompilerServices_NullableAttribute__ctorByte; - arguments = ImmutableArray.Create(new TypedConstant(byteType, TypedConstantKind.Primitive, flagsBuilder[0])); + attribute = null; } else { - var constantsBuilder = ArrayBuilder.GetInstance(flagsBuilder.Count); - - foreach (byte flag in flagsBuilder) + Debug.Assert(flagsBuilder.All(f => f <= 2)); + byte? commonValue = MostCommonNullableValueBuilder.GetCommonValue(flagsBuilder); + if (commonValue != null) { - Debug.Assert(flag == NullableAnnotationExtensions.ObliviousAttributeValue || flag == NullableAnnotationExtensions.NotAnnotatedAttributeValue || flag == NullableAnnotationExtensions.AnnotatedAttributeValue); - constantsBuilder.Add(new TypedConstant(byteType, TypedConstantKind.Primitive, flag)); + attribute = SynthesizeNullableAttributeIfNecessary(nullableContextValue, commonValue.GetValueOrDefault()); + } + else + { + NamedTypeSymbol byteType = Compilation.GetSpecialType(SpecialType.System_Byte); + var byteArrayType = ArrayTypeSymbol.CreateSZArray(byteType.ContainingAssembly, TypeWithAnnotations.Create(byteType)); + var value = flagsBuilder.SelectAsArray((flag, byteType) => new TypedConstant(byteType, TypedConstantKind.Primitive, flag), byteType); + attribute = SynthesizeNullableAttribute( + WellKnownMember.System_Runtime_CompilerServices_NullableAttribute__ctorTransformFlags, + ImmutableArray.Create(new TypedConstant(byteArrayType, value))); } - - var byteArray = ArrayTypeSymbol.CreateSZArray(byteType.ContainingAssembly, TypeWithAnnotations.Create(byteType)); - constructor = WellKnownMember.System_Runtime_CompilerServices_NullableAttribute__ctorTransformFlags; - arguments = ImmutableArray.Create(new TypedConstant(byteArray, constantsBuilder.ToImmutableAndFree())); } flagsBuilder.Free(); - return SynthesizeNullableAttribute(constructor, arguments); + return attribute; + } + + internal SynthesizedAttributeData SynthesizeNullableAttributeIfNecessary(byte? nullableContextValue, byte nullableValue) + { + if (nullableValue == nullableContextValue || + (nullableContextValue == null && nullableValue == 0)) + { + return null; + } + + NamedTypeSymbol byteType = Compilation.GetSpecialType(SpecialType.System_Byte); + return SynthesizeNullableAttribute( + WellKnownMember.System_Runtime_CompilerServices_NullableAttribute__ctorByte, + ImmutableArray.Create(new TypedConstant(byteType, TypedConstantKind.Primitive, nullableValue))); } internal virtual SynthesizedAttributeData SynthesizeNullableAttribute(WellKnownMember member, ImmutableArray arguments) @@ -1512,10 +1522,30 @@ internal virtual SynthesizedAttributeData SynthesizeNullableAttribute(WellKnownM return Compilation.TrySynthesizeAttribute(member, arguments, isOptionalUse: true); } - internal virtual SynthesizedAttributeData SynthesizeNullablePublicOnlyAttribute() + internal SynthesizedAttributeData SynthesizeNullableContextAttribute(Symbol symbol, byte value) + { + var module = Compilation.SourceModule; + if ((object)module != symbol && (object)module != symbol.ContainingModule) + { + // For symbols that are not defined in the same compilation (like NoPia), don't synthesize this attribute. + return null; + } + + return SynthesizeNullableContextAttribute( + ImmutableArray.Create(new TypedConstant(Compilation.GetSpecialType(SpecialType.System_Byte), TypedConstantKind.Primitive, value))); + } + + internal virtual SynthesizedAttributeData SynthesizeNullableContextAttribute(ImmutableArray arguments) { // For modules, this attribute should be present. Only assemblies generate and embed this type. - return Compilation.TrySynthesizeAttribute(WellKnownMember.System_Runtime_CompilerServices_NullablePublicOnlyAttribute__ctor); + // https://github.com/dotnet/roslyn/issues/30062 Should not be optional. + return Compilation.TrySynthesizeAttribute(WellKnownMember.System_Runtime_CompilerServices_NullableContextAttribute__ctor, arguments, isOptionalUse: true); + } + + internal virtual SynthesizedAttributeData SynthesizeNullablePublicOnlyAttribute(ImmutableArray arguments) + { + // For modules, this attribute should be present. Only assemblies generate and embed this type. + return Compilation.TrySynthesizeAttribute(WellKnownMember.System_Runtime_CompilerServices_NullablePublicOnlyAttribute__ctor, arguments); } protected virtual SynthesizedAttributeData TrySynthesizeIsReadOnlyAttribute() @@ -1566,10 +1596,5 @@ internal void EnsureNullableAttributeExists() { EnsureEmbeddableAttributeExists(EmbeddableAttributes.NullableAttribute); } - - internal void EnsureNullablePublicOnlyAttributeExists() - { - EnsureEmbeddableAttributeExists(EmbeddableAttributes.NullablePublicOnlyAttribute); - } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Compilation_WellKnownMembers.cs b/src/Compilers/CSharp/Portable/Symbols/Compilation_WellKnownMembers.cs index 9ba580e3269f8..365e1c270f0ad 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Compilation_WellKnownMembers.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Compilation_WellKnownMembers.cs @@ -478,6 +478,11 @@ internal void EnsureNullableAttributeExists(DiagnosticBag diagnostics, Location EnsureEmbeddableAttributeExists(EmbeddableAttributes.NullableAttribute, diagnostics, location, modifyCompilation); } + internal void EnsureNullableContextAttributeExists(DiagnosticBag diagnostics, Location location, bool modifyCompilation) + { + EnsureEmbeddableAttributeExists(EmbeddableAttributes.NullableContextAttribute, diagnostics, location, modifyCompilation); + } + internal void EnsureNullablePublicOnlyAttributeExists(DiagnosticBag diagnostics, Location location, bool modifyCompilation) { EnsureEmbeddableAttributeExists(EmbeddableAttributes.NullablePublicOnlyAttribute, diagnostics, location, modifyCompilation); @@ -517,6 +522,13 @@ internal bool CheckIfAttributeShouldBeEmbedded(EmbeddableAttributes attribute, D WellKnownMember.System_Runtime_CompilerServices_NullableAttribute__ctorByte, WellKnownMember.System_Runtime_CompilerServices_NullableAttribute__ctorTransformFlags); + case EmbeddableAttributes.NullableContextAttribute: + return CheckIfAttributeShouldBeEmbedded( + diagnosticsOpt, + locationOpt, + WellKnownType.System_Runtime_CompilerServices_NullableContextAttribute, + WellKnownMember.System_Runtime_CompilerServices_NullableContextAttribute__ctor); + case EmbeddableAttributes.NullablePublicOnlyAttribute: return CheckIfAttributeShouldBeEmbedded( diagnosticsOpt, diff --git a/src/Compilers/CSharp/Portable/Symbols/EmbeddableAttributes.cs b/src/Compilers/CSharp/Portable/Symbols/EmbeddableAttributes.cs index cdb7082f6bed6..0da953299bab2 100644 --- a/src/Compilers/CSharp/Portable/Symbols/EmbeddableAttributes.cs +++ b/src/Compilers/CSharp/Portable/Symbols/EmbeddableAttributes.cs @@ -11,6 +11,7 @@ internal enum EmbeddableAttributes IsByRefLikeAttribute = 0x02, IsUnmanagedAttribute = 0x04, NullableAttribute = 0x08, - NullablePublicOnlyAttribute = 0x10, + NullableContextAttribute = 0x10, + NullablePublicOnlyAttribute = 0x20, } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/NullableTypeDecoder.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/NullableTypeDecoder.cs index 53cf625f75877..d9f3413bad3ac 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/NullableTypeDecoder.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/NullableTypeDecoder.cs @@ -16,13 +16,35 @@ internal static class NullableTypeDecoder internal static TypeWithAnnotations TransformType( TypeWithAnnotations metadataType, EntityHandle targetSymbolToken, - PEModuleSymbol containingModule) + PEModuleSymbol containingModule, + Symbol accessSymbol, + Symbol nullableContext) { Debug.Assert(metadataType.HasType); + Debug.Assert(accessSymbol.IsDefinition); + Debug.Assert((object)accessSymbol.ContainingModule == containingModule); +#if DEBUG + // Ensure we could check accessibility at this point if we had to in ShouldDecodeNullableAttributes(). + // That is, ensure the accessibility of the symbol (and containing symbols) is available. + _ = AccessCheck.IsEffectivelyPublicOrInternal(accessSymbol, out _); +#endif byte defaultTransformFlag; ImmutableArray nullableTransformFlags; - containingModule.Module.HasNullableAttribute(targetSymbolToken, out defaultTransformFlag, out nullableTransformFlags); + if (!containingModule.Module.HasNullableAttribute(targetSymbolToken, out defaultTransformFlag, out nullableTransformFlags)) + { + byte? value = nullableContext.GetNullableContextValue(); + if (value == null) + { + return metadataType; + } + defaultTransformFlag = value.GetValueOrDefault(); + } + + if (!containingModule.ShouldDecodeNullableAttributes(accessSymbol)) + { + return metadataType; + } return TransformType(metadataType, defaultTransformFlag, nullableTransformFlags); } @@ -51,11 +73,13 @@ internal static TypeWithAnnotations TransformType( TypeWithAnnotations metadataType, EntityHandle targetSymbolToken, PEModuleSymbol containingModule, + Symbol accessSymbol, + Symbol nullableContext, ImmutableArray extraAnnotations) { if (extraAnnotations.IsDefault) { - return NullableTypeDecoder.TransformType(metadataType, targetSymbolToken, containingModule); + return NullableTypeDecoder.TransformType(metadataType, targetSymbolToken, containingModule, accessSymbol, nullableContext); } else { diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEEventSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEEventSymbol.cs index 87f917335aac9..f199e62c46110 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEEventSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEEventSymbol.cs @@ -102,7 +102,10 @@ internal PEEventSymbol( // Decode nullable before tuple types to avoid converting between // NamedTypeSymbol and TupleTypeSymbol unnecessarily. - type = NullableTypeDecoder.TransformType(type, handle, moduleSymbol); + + // The containing type is passed to NullableTypeDecoder.TransformType to determine access + // because the event does not have explicit accessibility in metadata. + type = NullableTypeDecoder.TransformType(type, handle, moduleSymbol, accessSymbol: _containingType, nullableContext: _containingType); type = TupleTypeDecoder.DecodeTupleTypesIfApplicable(type, handle, moduleSymbol); _eventTypeWithAnnotations = type; } diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEFieldSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEFieldSymbol.cs index 7d78f0e7bd55e..89eadca9cd524 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEFieldSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEFieldSymbol.cs @@ -217,7 +217,7 @@ private void EnsureSignatureIsLoaded() // Decode nullable before tuple types to avoid converting between // NamedTypeSymbol and TupleTypeSymbol unnecessarily. - type = NullableTypeDecoder.TransformType(type, _handle, moduleSymbol); + type = NullableTypeDecoder.TransformType(type, _handle, moduleSymbol, accessSymbol: this, nullableContext: _containingType); type = TupleTypeDecoder.DecodeTupleTypesIfApplicable(type, _handle, moduleSymbol); _lazyIsVolatile = isVolatile; diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEMethodSymbol.cs index 1d7d874072caf..59f4bb4bbc316 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEMethodSymbol.cs @@ -42,7 +42,7 @@ private struct PackedFlags { // We currently pack everything into a 32-bit int with the following layout: // - // | n|m|l|k|j|i|h|g|f|e|d|c|b|aaaaa| + // | ooo|n|m|l|k|j|i|h|g|f|e|d|c|b|aaaaa| // // a = method kind. 5 bits. // b = method kind populated. 1 bit. @@ -60,10 +60,10 @@ private struct PackedFlags // l = isOverriddenOrHiddenMembers populated. 1 bit // m = isReadOnly. 1 bit. // n = isReadOnlyPopulated. 1 bit. - // 14 bits remain for future purposes. + // o = NullableContext. 3 bits. + // 11 bits remain for future purposes. private const int MethodKindOffset = 0; - private const int MethodKindMask = 0x1F; private const int MethodKindIsPopulatedBit = 0x1 << 5; @@ -79,6 +79,8 @@ private struct PackedFlags private const int IsOverriddenOrHiddenMembersPopulatedBit = 0x1 << 15; private const int IsReadOnlyBit = 0x1 << 16; private const int IsReadOnlyPopulatedBit = 0x1 << 17; + private const int NullableContextOffset = 18; + private const int NullableContextMask = 0x7; private int _bits; @@ -113,12 +115,9 @@ public MethodKind MethodKind #if DEBUG static PackedFlags() { - // Verify a few things about the values we combine into flags. This way, if they ever - // change, this will get hit and you will know you have to update this type as well. - - // 1) Verify that the range of method kinds doesn't fall outside the bounds of the - // method kind mask. + // Verify masks are sufficient for values. Debug.Assert(EnumUtilities.ContainsAllValues(MethodKindMask)); + Debug.Assert(EnumUtilities.ContainsAllValues(NullableContextMask)); } #endif @@ -183,6 +182,16 @@ public void SetIsOverriddenOrHiddenMembersPopulated() { ThreadSafeFlagOperations.Set(ref _bits, IsOverriddenOrHiddenMembersPopulatedBit); } + + public bool TryGetNullableContext(out byte? value) + { + return ((NullableContextKind)((_bits >> NullableContextOffset) & NullableContextMask)).TryGetByte(out value); + } + + public bool SetNullableContext(byte? value) + { + return ThreadSafeFlagOperations.Set(ref _bits, (((int)value.ToNullableContextFlags() & NullableContextMask) << NullableContextOffset)); + } } /// @@ -613,7 +622,7 @@ private SignatureData LoadSignature() builder.Add(PEParameterSymbol.Create( moduleSymbol, this, this.IsMetadataVirtual(), i, - paramInfo[i + 1], extraAnnotations, isReturn: false, out isBadParameter)); + paramInfo[i + 1], nullableContext: this, extraAnnotations, isReturn: false, out isBadParameter)); if (isBadParameter) { @@ -636,7 +645,7 @@ private SignatureData LoadSignature() ImmutableArray extraReturnAnnotations = extraMethodAnnotations.IsDefault ? default : extraMethodAnnotations[0]; var returnParam = PEParameterSymbol.Create( moduleSymbol, this, this.IsMetadataVirtual(), 0, - paramInfo[0], extraReturnAnnotations, isReturn: true, out isBadParameter); + paramInfo[0], nullableContext: this, extraReturnAnnotations, isReturn: true, out isBadParameter); if (makeBad || isBadParameter) { @@ -809,6 +818,24 @@ public override ImmutableArray GetAttributes() public override ImmutableArray GetReturnTypeAttributes() => Signature.ReturnParam.GetAttributes(); + internal override byte? GetNullableContextValue() + { + byte? value; + if (!_packedFlags.TryGetNullableContext(out value)) + { + value = _containingType.ContainingPEModule.Module.HasNullableContextAttribute(_handle, out byte arg) ? + arg : + _containingType.GetNullableContextValue(); + _packedFlags.SetNullableContext(value); + } + return value; + } + + internal override byte? GetLocalNullableContextValue() + { + throw ExceptionUtilities.Unreachable; + } + public override MethodKind MethodKind { get diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEModuleSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEModuleSymbol.cs index 8aae48bb82f0e..cf1ba82d11a63 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEModuleSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEModuleSymbol.cs @@ -4,16 +4,15 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.Immutable; -using System.Collections.ObjectModel; using System.Diagnostics; +using System.Reflection; +using System.Reflection.Metadata; +using System.Reflection.PortableExecutable; using System.Runtime.InteropServices; using System.Threading; -using System.Reflection.Metadata; using Microsoft.CodeAnalysis.CSharp.Symbols.Retargeting; using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; -using System.Reflection.PortableExecutable; -using System.Reflection; namespace Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE { @@ -89,6 +88,16 @@ internal sealed class PEModuleSymbol : NonMissingModuleSymbol // Namespace names from module private ICollection _lazyNamespaceNames; + private enum NullableMemberMetadata + { + Unknown = 0, + Public, + Internal, + All, + } + + private NullableMemberMetadata _lazyNullableMemberMetadata; + internal PEModuleSymbol(PEAssemblySymbol assemblySymbol, PEModule module, MetadataImportOptions importOptions, int ordinal) : this((AssemblySymbol)assemblySymbol, module, importOptions, ordinal) { @@ -372,7 +381,6 @@ internal ImmutableArray GetCustomAttributesForToken(EntityH return GetCustomAttributesForToken(token, out paramArrayAttribute, AttributeDescription.ParamArrayAttribute); } - internal bool HasAnyCustomAttributes(EntityHandle token) { try @@ -491,7 +499,6 @@ internal DocumentationProvider DocumentationProvider } } - internal NamedTypeSymbol EventRegistrationToken { get @@ -509,7 +516,6 @@ internal NamedTypeSymbol EventRegistrationToken } } - internal NamedTypeSymbol EventRegistrationTokenTable_T { get @@ -702,5 +708,40 @@ internal IEnumerable GetForwardedTypes() } public override ModuleMetadata GetMetadata() => _module.GetNonDisposableMetadata(); + + internal bool ShouldDecodeNullableAttributes(Symbol symbol) + { + Debug.Assert(!(symbol is null)); + Debug.Assert(symbol.IsDefinition); + Debug.Assert((object)symbol.ContainingModule == this); + + if (_lazyNullableMemberMetadata == NullableMemberMetadata.Unknown) + { + _lazyNullableMemberMetadata = _module.HasNullablePublicOnlyAttribute(Token, out bool includesInternals) ? + (includesInternals ? NullableMemberMetadata.Internal : NullableMemberMetadata.Public) : + NullableMemberMetadata.All; + } + + NullableMemberMetadata nullableMemberMetadata = _lazyNullableMemberMetadata; + if (nullableMemberMetadata == NullableMemberMetadata.All) + { + return true; + } + + if (AccessCheck.IsEffectivelyPublicOrInternal(symbol, out bool isInternal)) + { + switch (nullableMemberMetadata) + { + case NullableMemberMetadata.Public: + return !isInternal; + case NullableMemberMetadata.Internal: + return true; + default: + throw ExceptionUtilities.UnexpectedValue(nullableMemberMetadata); + } + } + + return false; + } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PENamedTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PENamedTypeSymbol.cs index 80a8383dafed3..6915c6d2e4850 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PENamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PENamedTypeSymbol.cs @@ -62,6 +62,8 @@ internal abstract class PENamedTypeSymbol : NamedTypeSymbol /// private TypeKind _lazyKind; + private NullableContextKind _lazyNullableContextValue; + private NamedTypeSymbol _lazyBaseType = ErrorTypeSymbol.UnknownResultType; private ImmutableArray _lazyInterfaces = default(ImmutableArray); private NamedTypeSymbol _lazyDeclaredBaseType = ErrorTypeSymbol.UnknownResultType; @@ -71,7 +73,6 @@ internal abstract class PENamedTypeSymbol : NamedTypeSymbol private DiagnosticInfo _lazyUseSiteDiagnostic = CSDiagnosticInfo.EmptyErrorInfo; // Indicates unknown state. - // There is a bunch of type properties relevant only for enums or types with custom attributes. // It is fairly easy to check whether a type s is not "uncommon". So we store uncommon properties in // a separate class with a noUncommonProperties singleton used for cases when type is "common". @@ -445,7 +446,7 @@ private NamedTypeSymbol GetDeclaredBaseType(bool skipTransformsIfNecessary) var moduleSymbol = ContainingPEModule; TypeSymbol decodedType = DynamicTypeDecoder.TransformType(baseType, 0, _handle, moduleSymbol); decodedType = TupleTypeDecoder.DecodeTupleTypesIfApplicable(decodedType, _handle, moduleSymbol); - baseType = (NamedTypeSymbol)NullableTypeDecoder.TransformType(TypeWithAnnotations.Create(decodedType), _handle, moduleSymbol).Type; + baseType = (NamedTypeSymbol)NullableTypeDecoder.TransformType(TypeWithAnnotations.Create(decodedType), _handle, moduleSymbol, accessSymbol: this, nullableContext: this).Type; } Interlocked.CompareExchange(ref _lazyDeclaredBaseType, baseType, ErrorTypeSymbol.UnknownResultType); @@ -504,7 +505,7 @@ private ImmutableArray MakeDeclaredInterfaces() TypeSymbol typeSymbol = tokenDecoder.GetTypeOfToken(interfaceHandle); typeSymbol = TupleTypeDecoder.DecodeTupleTypesIfApplicable(typeSymbol, interfaceImpl, moduleSymbol); - typeSymbol = NullableTypeDecoder.TransformType(TypeWithAnnotations.Create(typeSymbol), interfaceImpl, moduleSymbol).Type; + typeSymbol = NullableTypeDecoder.TransformType(TypeWithAnnotations.Create(typeSymbol), interfaceImpl, moduleSymbol, accessSymbol: this, nullableContext: this).Type; var namedTypeSymbol = typeSymbol as NamedTypeSymbol ?? new UnsupportedMetadataTypeSymbol(); // interface list contains a bad type symbols.Add(namedTypeSymbol); @@ -641,6 +642,24 @@ internal override IEnumerable GetCustomAttributesToEmit(PEM return GetAttributes(); } + internal override byte? GetNullableContextValue() + { + byte? value; + if (!_lazyNullableContextValue.TryGetByte(out value)) + { + value = ContainingPEModule.Module.HasNullableContextAttribute(_handle, out byte arg) ? + arg : + _container.GetNullableContextValue(); + _lazyNullableContextValue = value.ToNullableContextFlags(); + } + return value; + } + + internal override byte? GetLocalNullableContextValue() + { + throw ExceptionUtilities.Unreachable; + } + public override IEnumerable MemberNames { get diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEParameterSymbol.cs index 8126f1de7823c..fc59b31b423e0 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEParameterSymbol.cs @@ -73,17 +73,9 @@ public bool HasNameInMetadata #if DEBUG static PackedFlags() { - // Verify a few things about the values we combine into flags. This way, if they ever - // change, this will get hit and you will know you have to update this type as well. - - // 1) Verify that the range of well known attributes doesn't fall outside the bounds of - // the attribute completion and data mask. + // Verify masks are sufficient for values. Debug.Assert(EnumUtilities.ContainsAllValues(WellKnownAttributeDataMask)); - - // 2) Verify that the range of ref kinds doesn't fall outside the bounds of - // the ref kind mask. Debug.Assert(EnumUtilities.ContainsAllValues(RefKindMask)); - Debug.Assert(EnumUtilities.ContainsAllValues(FlowAnalysisAnnotationsMask)); } #endif @@ -160,6 +152,7 @@ internal static PEParameterSymbol Create( bool isContainingSymbolVirtual, int ordinal, ParamInfo parameterInfo, + Symbol nullableContext, ImmutableArray extraAnnotations, bool isReturn, out bool isBad) @@ -167,7 +160,7 @@ internal static PEParameterSymbol Create( return Create( moduleSymbol, containingSymbol, isContainingSymbolVirtual, ordinal, parameterInfo.IsByRef, parameterInfo.RefCustomModifiers, parameterInfo.Type, extraAnnotations, - parameterInfo.Handle, parameterInfo.CustomModifiers, isReturn, out isBad); + parameterInfo.Handle, nullableContext, parameterInfo.CustomModifiers, isReturn, out isBad); } /// @@ -178,7 +171,7 @@ internal static PEParameterSymbol Create( /// /// The property parameter doesn't have a name in metadata, /// so this is the handle of a corresponding accessor parameter, if there is one, - /// or of the ParamInfo passed in, otherwise). + /// or of the ParamInfo passed in, otherwise. /// /// internal static PEParameterSymbol Create( @@ -188,13 +181,14 @@ internal static PEParameterSymbol Create( int ordinal, ParameterHandle handle, ParamInfo parameterInfo, + Symbol nullableContext, ImmutableArray extraAnnotations, out bool isBad) { return Create( moduleSymbol, containingSymbol, isContainingSymbolVirtual, ordinal, parameterInfo.IsByRef, parameterInfo.RefCustomModifiers, parameterInfo.Type, extraAnnotations, - handle, parameterInfo.CustomModifiers, isReturn: false, out isBad); + handle, nullableContext, parameterInfo.CustomModifiers, isReturn: false, out isBad); } private PEParameterSymbol( @@ -205,6 +199,7 @@ private PEParameterSymbol( TypeWithAnnotations typeWithAnnotations, ImmutableArray extraAnnotations, ParameterHandle handle, + Symbol nullableContext, int countOfCustomModifiers, out bool isBad) { @@ -233,6 +228,14 @@ private PEParameterSymbol( { typeWithAnnotations = NullableTypeDecoder.TransformType(typeWithAnnotations, defaultTransformFlag: 0, extraAnnotations); } + else + { + byte? value = nullableContext.GetNullableContextValue(); + if (value.HasValue) + { + typeWithAnnotations = NullableTypeDecoder.TransformType(typeWithAnnotations, value.GetValueOrDefault(), default); + } + } _lazyCustomAttributes = ImmutableArray.Empty; _lazyHiddenAttributes = ImmutableArray.Empty; @@ -268,12 +271,15 @@ private PEParameterSymbol( } } - // CONSIDER: Can we make parameter type computation lazy? var typeSymbol = DynamicTypeDecoder.TransformType(typeWithAnnotations.Type, countOfCustomModifiers, handle, moduleSymbol, refKind); typeWithAnnotations = typeWithAnnotations.WithTypeAndModifiers(typeSymbol, typeWithAnnotations.CustomModifiers); // Decode nullable before tuple types to avoid converting between // NamedTypeSymbol and TupleTypeSymbol unnecessarily. - typeWithAnnotations = NullableTypeDecoder.TransformType(typeWithAnnotations, handle, moduleSymbol, extraAnnotations); + + // The containing type is passed to NullableTypeDecoder.TransformType to determine access + // for property parameters because the property does not have explicit accessibility in metadata. + var accessSymbol = containingSymbol.Kind == SymbolKind.Property ? containingSymbol.ContainingSymbol : containingSymbol; + typeWithAnnotations = NullableTypeDecoder.TransformType(typeWithAnnotations, handle, moduleSymbol, accessSymbol: accessSymbol, nullableContext: nullableContext, extraAnnotations); typeWithAnnotations = TupleTypeDecoder.DecodeTupleTypesIfApplicable(typeWithAnnotations, handle, moduleSymbol); } @@ -310,6 +316,7 @@ private static PEParameterSymbol Create( TypeSymbol type, ImmutableArray extraAnnotations, ParameterHandle handle, + Symbol nullableContext, ImmutableArray> customModifiers, bool isReturn, out bool isBad) @@ -318,8 +325,8 @@ private static PEParameterSymbol Create( var typeWithModifiers = TypeWithAnnotations.Create(type, customModifiers: CSharpCustomModifier.Convert(customModifiers)); PEParameterSymbol parameter = customModifiers.IsDefaultOrEmpty && refCustomModifiers.IsDefaultOrEmpty - ? new PEParameterSymbol(moduleSymbol, containingSymbol, ordinal, isByRef, typeWithModifiers, extraAnnotations, handle, 0, out isBad) - : new PEParameterSymbolWithCustomModifiers(moduleSymbol, containingSymbol, ordinal, isByRef, refCustomModifiers, typeWithModifiers, extraAnnotations, handle, out isBad); + ? new PEParameterSymbol(moduleSymbol, containingSymbol, ordinal, isByRef, typeWithModifiers, extraAnnotations, handle, nullableContext, 0, out isBad) + : new PEParameterSymbolWithCustomModifiers(moduleSymbol, containingSymbol, ordinal, isByRef, refCustomModifiers, typeWithModifiers, extraAnnotations, handle, nullableContext, out isBad); bool hasInAttributeModifier = parameter.RefCustomModifiers.HasInAttributeModifier(); @@ -355,8 +362,9 @@ public PEParameterSymbolWithCustomModifiers( TypeWithAnnotations type, ImmutableArray extraAnnotations, ParameterHandle handle, + Symbol nullableContext, out bool isBad) : - base(moduleSymbol, containingSymbol, ordinal, isByRef, type, extraAnnotations, handle, + base(moduleSymbol, containingSymbol, ordinal, isByRef, type, extraAnnotations, handle, nullableContext, refCustomModifiers.NullToEmpty().Length + type.CustomModifiers.Length, out isBad) { diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEPropertySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEPropertySymbol.cs index 846cdbefdf316..6a559ddff2819 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEPropertySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEPropertySymbol.cs @@ -172,7 +172,10 @@ private PEPropertySymbol( // Decode nullable before tuple types to avoid converting between // NamedTypeSymbol and TupleTypeSymbol unnecessarily. - propertyTypeWithAnnotations = NullableTypeDecoder.TransformType(propertyTypeWithAnnotations, handle, moduleSymbol); + + // The containing type is passed to NullableTypeDecoder.TransformType to determine access + // because the property does not have explicit accessibility in metadata. + propertyTypeWithAnnotations = NullableTypeDecoder.TransformType(propertyTypeWithAnnotations, handle, moduleSymbol, accessSymbol: _containingType, nullableContext: _containingType); propertyTypeWithAnnotations = TupleTypeDecoder.DecodeTupleTypesIfApplicable(propertyTypeWithAnnotations, handle, moduleSymbol); _propertyTypeWithAnnotations = propertyTypeWithAnnotations; @@ -685,7 +688,7 @@ private static ImmutableArray GetParameters( bool isBad; // https://github.com/dotnet/roslyn/issues/29821: handle extra annotations - parameters[ordinal] = PEParameterSymbol.Create(moduleSymbol, property, isPropertyVirtual, ordinal, paramHandle, propertyParam, extraAnnotations: default, out isBad); + parameters[ordinal] = PEParameterSymbol.Create(moduleSymbol, property, isPropertyVirtual, ordinal, paramHandle, propertyParam, nullableContext: property, extraAnnotations: default, out isBad); if (isBad) { diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PETypeParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PETypeParameterSymbol.cs index e8bc78b115f3f..bafbefccfd8a2 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PETypeParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PETypeParameterSymbol.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using System.Reflection; @@ -220,7 +219,7 @@ private ImmutableArray GetDeclaredConstraintTypes(ConsList< } // - presence of unmanaged pattern has to be matched with `valuetype` - // - IsUnmanagedAttribute is allowed iif there is an unmanaged pattern + // - IsUnmanagedAttribute is allowed iff there is an unmanaged pattern if (hasUnmanagedModreqPattern && (_flags & GenericParameterAttributes.NotNullableValueTypeConstraint) == 0 || hasUnmanagedModreqPattern != peModule.HasIsUnmanagedAttribute(_handle)) { @@ -272,7 +271,7 @@ private TypeWithAnnotations GetConstraintTypeOrDefault(PEModuleSymbol moduleSymb } var type = TypeWithAnnotations.Create(typeSymbol); - type = NullableTypeDecoder.TransformType(type, constraintHandle, moduleSymbol); + type = NullableTypeDecoder.TransformType(type, constraintHandle, moduleSymbol, accessSymbol: _containingSymbol, nullableContext: _containingSymbol); type = TupleTypeDecoder.DecodeTupleTypesIfApplicable(type, constraintHandle, moduleSymbol); return type; } @@ -431,6 +430,19 @@ public override bool HasReferenceTypeConstraint } } + /// + /// Returns the byte value from the (single byte) NullableAttribute or nearest + /// NullableContextAttribute. Returns 0 if neither attribute is specified. + /// + private byte GetNullableAttributeValue() + { + if (((PEModuleSymbol)this.ContainingModule).Module.HasNullableAttribute(_handle, out byte value, out _)) + { + return value; + } + return _containingSymbol.GetNullableContextValue() ?? 0; + } + internal override bool? ReferenceTypeConstraintIsNullable { get @@ -441,15 +453,12 @@ internal override bool? ReferenceTypeConstraintIsNullable return false; } - if (((PEModuleSymbol)this.ContainingModule).Module.HasNullableAttribute(_handle, out byte transformFlag, out _)) + switch (GetNullableAttributeValue()) { - switch (transformFlag) - { - case NullableAnnotationExtensions.AnnotatedAttributeValue: - return true; - case NullableAnnotationExtensions.NotAnnotatedAttributeValue: - return false; - } + case NullableAnnotationExtensions.AnnotatedAttributeValue: + return true; + case NullableAnnotationExtensions.NotAnnotatedAttributeValue: + return false; } return null; @@ -461,8 +470,7 @@ public override bool HasNotNullConstraint get { return (_flags & (GenericParameterAttributes.NotNullableValueTypeConstraint | GenericParameterAttributes.ReferenceTypeConstraint)) == 0 && - ((PEModuleSymbol)this.ContainingModule).Module.HasNullableAttribute(_handle, out byte transformFlag, out _) && - transformFlag == NullableAnnotationExtensions.NotAnnotatedAttributeValue; + GetNullableAttributeValue() == NullableAnnotationExtensions.NotAnnotatedAttributeValue; } } @@ -479,7 +487,7 @@ internal override bool? IsNotNullable if (constraints.Count == 0) { - if (module.HasNullableAttribute(_handle, out byte transformFlag, out _) && transformFlag == NullableAnnotationExtensions.AnnotatedAttributeValue) + if (GetNullableAttributeValue() == NullableAnnotationExtensions.AnnotatedAttributeValue) { return false; } diff --git a/src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs index e9fa0cfc9789c..0dde98511f793 100644 --- a/src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs @@ -1206,9 +1206,9 @@ internal virtual void AddSynthesizedReturnTypeAttributes(PEModuleBuilder moduleB AddSynthesizedAttribute(ref attributes, compilation.SynthesizeTupleNamesAttribute(type.Type)); } - if (compilation.ShouldEmitNullableAttributes(this) && type.NeedsNullableAttribute()) + if (compilation.ShouldEmitNullableAttributes(this)) { - AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeNullableAttribute(this, type)); + AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeNullableAttributeIfNecessary(this, GetNullableContextValue(), type)); } } diff --git a/src/Compilers/CSharp/Portable/Symbols/NullableContextKind.cs b/src/Compilers/CSharp/Portable/Symbols/NullableContextKind.cs new file mode 100644 index 0000000000000..f89e5766f6e79 --- /dev/null +++ b/src/Compilers/CSharp/Portable/Symbols/NullableContextKind.cs @@ -0,0 +1,82 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.CSharp.Symbols +{ + /// + /// Used by symbol implementations (source and metadata) to represent the value + /// that was mapped from, or will be mapped to a [NullableContext] attribute. + /// + internal enum NullableContextKind : byte + { + /// + /// Uninitialized state + /// + Unknown = 0, + + /// + /// No [NullableContext] attribute + /// + None, + + /// + /// [NullableContext(0)] + /// + Oblivious, + + /// + /// [NullableContext(1)] + /// + NotAnnotated, + + /// + /// [NullableContext(2)] + /// + Annotated, + } + + internal static class NullableContextExtensions + { + internal static bool TryGetByte(this NullableContextKind kind, out byte? value) + { + switch (kind) + { + case NullableContextKind.Unknown: + value = null; + return false; + case NullableContextKind.None: + value = null; + return true; + case NullableContextKind.Oblivious: + value = NullableAnnotationExtensions.ObliviousAttributeValue; + return true; + case NullableContextKind.NotAnnotated: + value = NullableAnnotationExtensions.NotAnnotatedAttributeValue; + return true; + case NullableContextKind.Annotated: + value = NullableAnnotationExtensions.AnnotatedAttributeValue; + return true; + default: + throw ExceptionUtilities.UnexpectedValue(kind); + } + } + + internal static NullableContextKind ToNullableContextFlags(this byte? value) + { + switch (value) + { + case null: + return NullableContextKind.None; + case NullableAnnotationExtensions.ObliviousAttributeValue: + return NullableContextKind.Oblivious; + case NullableAnnotationExtensions.NotAnnotatedAttributeValue: + return NullableContextKind.NotAnnotated; + case NullableAnnotationExtensions.AnnotatedAttributeValue: + return NullableContextKind.Annotated; + default: + throw ExceptionUtilities.UnexpectedValue(value); + } + } + } +} diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/FieldSymbolWithAttributesAndModifiers.cs b/src/Compilers/CSharp/Portable/Symbols/Source/FieldSymbolWithAttributesAndModifiers.cs old mode 100755 new mode 100644 index 78c93e5c9ae9e..a2aa9fe66455f --- a/src/Compilers/CSharp/Portable/Symbols/Source/FieldSymbolWithAttributesAndModifiers.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/FieldSymbolWithAttributesAndModifiers.cs @@ -6,7 +6,6 @@ using System.Linq; using System.Runtime.InteropServices; using Microsoft.CodeAnalysis.CSharp.Emit; -using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; @@ -382,9 +381,9 @@ internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, r compilation.SynthesizeTupleNamesAttribute(type.Type)); } - if (compilation.ShouldEmitNullableAttributes(this) && type.NeedsNullableAttribute()) + if (compilation.ShouldEmitNullableAttributes(this)) { - AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeNullableAttribute(this, type)); + AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeNullableAttributeIfNecessary(this, ContainingType.GetNullableContextValue(), type)); } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/ImplicitNamedTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/ImplicitNamedTypeSymbol.cs index f433a35bafe7c..50ce6977f2c81 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/ImplicitNamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/ImplicitNamedTypeSymbol.cs @@ -4,13 +4,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; -using System.Linq; using System.Runtime.InteropServices; -using System.Text; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CSharp.Symbols; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Symbols diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventSymbol.cs index 9dd0e0034d5a1..de15bc06cf4ec 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventSymbol.cs @@ -322,9 +322,9 @@ internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, r DeclaringCompilation.SynthesizeTupleNamesAttribute(type.Type)); } - if (compilation.ShouldEmitNullableAttributes(this) && type.NeedsNullableAttribute()) + if (compilation.ShouldEmitNullableAttributes(this)) { - AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeNullableAttribute(this, type)); + AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeNullableAttributeIfNecessary(this, containingType.GetNullableContextValue(), type)); } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs index 7c94a1eb32685..c2e1712cbb1be 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs @@ -8,6 +8,7 @@ using System.Runtime.InteropServices; using System.Threading; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Emit; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Text; @@ -55,16 +56,19 @@ private struct Flags // More flags. // - // | |zzzz|f| + // | |vvv|zzzz|f| // // f = FlattenedMembersIsSorted. 1 bit. // z = TypeKind. 4 bits. + // v = NullableContext. 3 bits. private const int TypeKindOffset = 1; - private const int TypeKindMask = 0xF; private const int FlattenedMembersIsSortedBit = 1 << 0; + private const int NullableContextOffset = 5; + private const int NullableContextMask = 0x7; + private int _flags2; public SpecialType SpecialType @@ -98,20 +102,13 @@ public TypeKind TypeKind get { return (TypeKind)((_flags2 >> TypeKindOffset) & TypeKindMask); } } - #if DEBUG static Flags() { - // Verify a few things about the values we combine into flags. This way, if they ever - // change, this will get hit and you will know you have to update this type as well. - - // 1) Verify that the range of special types doesn't fall outside the bounds of the - // special type mask. + // Verify masks are sufficient for values. Debug.Assert(EnumUtilities.ContainsAllValues(SpecialTypeMask)); - - // 2) Verify that the range of declaration modifiers doesn't fall outside the bounds of - // the declaration modifier mask. Debug.Assert(EnumUtilities.ContainsAllValues(DeclarationModifiersMask)); + Debug.Assert(EnumUtilities.ContainsAllValues(NullableContextMask)); } #endif @@ -146,6 +143,16 @@ public void SetManagedKind(ManagedKind managedKind) Debug.Assert(BitsAreUnsetOrSame(_flags, bitsToSet)); ThreadSafeFlagOperations.Set(ref _flags, bitsToSet); } + + public bool TryGetNullableContext(out byte? value) + { + return ((NullableContextKind)((_flags2 >> NullableContextOffset) & NullableContextMask)).TryGetByte(out value); + } + + public bool SetNullableContext(byte? value) + { + return ThreadSafeFlagOperations.Set(ref _flags2, (((int)value.ToNullableContextFlags() & NullableContextMask) << NullableContextOffset)); + } } protected SymbolCompletionState state; @@ -1422,6 +1429,11 @@ protected void AfterMembersChecks(DiagnosticBag diagnostics) if (compilation.ShouldEmitNullableAttributes(this)) { + if (ShouldEmitNullableContextValue(out _)) + { + compilation.EnsureNullableContextAttributeExists(diagnostics, location, modifyCompilation: true); + } + // https://github.com/dotnet/roslyn/issues/30080: Report diagnostics for base type and interfaces at more specific locations. var baseType = BaseTypeNoUseSiteDiagnostics; var interfaces = InterfacesNoUseSiteDiagnostics(); @@ -3306,6 +3318,81 @@ private void AddAccessorIfAvailable(ArrayBuilder symbols, MethodSymbol a } } + internal override byte? GetLocalNullableContextValue() + { + byte? value; + if (!_flags.TryGetNullableContext(out value)) + { + value = ComputeNullableContextValue(); + _flags.SetNullableContext(value); + } + return value; + } + + private byte? ComputeNullableContextValue() + { + var compilation = DeclaringCompilation; + if (!compilation.ShouldEmitNullableAttributes(this)) + { + return null; + } + + var builder = new MostCommonNullableValueBuilder(); + var baseType = BaseTypeNoUseSiteDiagnostics; + if (!(baseType is null)) + { + builder.AddValue(TypeWithAnnotations.Create(baseType)); + } + foreach (var @interface in GetInterfacesToEmit()) + { + builder.AddValue(TypeWithAnnotations.Create(@interface)); + } + foreach (var typeParameter in TypeParameters) + { + typeParameter.GetCommonNullableValues(compilation, ref builder); + } + foreach (var member in GetMembersUnordered()) + { + member.GetCommonNullableValues(compilation, ref builder); + } + // Not including lambdas or local functions. + return builder.MostCommonValue; + } + + internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, ref ArrayBuilder attributes) + { + base.AddSynthesizedAttributes(moduleBuilder, ref attributes); + + var compilation = DeclaringCompilation; + NamedTypeSymbol baseType = this.BaseTypeNoUseSiteDiagnostics; + + if (!(baseType is null)) + { + if (baseType.ContainsDynamic()) + { + AddSynthesizedAttribute(ref attributes, compilation.SynthesizeDynamicAttribute(baseType, customModifiersCount: 0)); + } + + if (baseType.ContainsTupleNames()) + { + AddSynthesizedAttribute(ref attributes, compilation.SynthesizeTupleNamesAttribute(baseType)); + } + } + + if (compilation.ShouldEmitNullableAttributes(this)) + { + if (ShouldEmitNullableContextValue(out byte nullableContextValue)) + { + AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeNullableContextAttribute(this, nullableContextValue)); + } + + if (!(baseType is null)) + { + AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeNullableAttributeIfNecessary(this, nullableContextValue, TypeWithAnnotations.Create(baseType))); + } + } + } + #endregion #region Extension Methods diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberMethodSymbol.cs index bdf463a9cc978..d530bc0651754 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberMethodSymbol.cs @@ -47,12 +47,31 @@ protected struct Flags private const int IsMetadataVirtualLockedBit = 1 << 31; private int _flags; - private bool _returnsVoid; - public bool ReturnsVoid + // More flags. + // + // | |vvv|yy| + // + // y = ReturnsVoid. 2 bits. + // v = NullableContext. 3 bits. + private const int ReturnsVoidBit = 1 << 0; + private const int ReturnsVoidIsSetBit = 1 << 1; + + private const int NullableContextOffset = 2; + private const int NullableContextMask = 0x7; + + private int _flags2; + + public bool TryGetReturnsVoid(out bool value) + { + int bits = _flags2; + value = (bits & ReturnsVoidBit) != 0; + return (bits & ReturnsVoidIsSetBit) != 0; + } + + public void SetReturnsVoid(bool value) { - get { return _returnsVoid; } - set { _returnsVoid = value; } + ThreadSafeFlagOperations.Set(ref _flags2, (int)(ReturnsVoidIsSetBit | (value ? ReturnsVoidBit : 0))); } public MethodKind MethodKind @@ -78,16 +97,10 @@ public DeclarationModifiers DeclarationModifiers #if DEBUG static Flags() { - // Verify a few things about the values we combine into flags. This way, if they ever - // change, this will get hit and you will know you have to update this type as well. - - // 1) Verify that the range of method kinds doesn't fall outside the bounds of the - // method kind mask. + // Verify masks are sufficient for values. Debug.Assert(EnumUtilities.ContainsAllValues(MethodKindMask)); - - // 2) Verify that the range of declaration modifiers doesn't fall outside the bounds of - // the declaration modifier mask. Debug.Assert(EnumUtilities.ContainsAllValues(DeclarationModifiersMask)); + Debug.Assert(EnumUtilities.ContainsAllValues(NullableContextMask)); } #endif @@ -112,7 +125,7 @@ public Flags( int isMetadataVirtualInt = isMetadataVirtual ? IsMetadataVirtualBit : 0; _flags = methodKindInt | declarationModifiersInt | isExtensionMethodInt | isMetadataVirtualIgnoringInterfaceImplementationChangesInt | isMetadataVirtualInt; - _returnsVoid = returnsVoid; + _flags2 = (returnsVoid ? ReturnsVoidBit : 0) | ReturnsVoidIsSetBit; } public bool IsMetadataVirtual(bool ignoreInterfaceImplementationChanges = false) @@ -144,6 +157,16 @@ public void EnsureMetadataVirtual() ThreadSafeFlagOperations.Set(ref _flags, IsMetadataVirtualBit); } } + + public bool TryGetNullableContext(out byte? value) + { + return ((NullableContextKind)((_flags2 >> NullableContextOffset) & NullableContextMask)).TryGetByte(out value); + } + + public bool SetNullableContext(byte? value) + { + return ThreadSafeFlagOperations.Set(ref _flags2, (((int)value.ToNullableContextFlags() & NullableContextMask) << NullableContextOffset)); + } } protected SymbolCompletionState state; @@ -243,7 +266,7 @@ protected void MakeFlags( protected void SetReturnsVoid(bool returnsVoid) { - this.flags.ReturnsVoid = returnsVoid; + this.flags.SetReturnsVoid(returnsVoid); } /// @@ -354,7 +377,8 @@ public override bool ReturnsVoid { get { - return this.flags.ReturnsVoid; + flags.TryGetReturnsVoid(out bool value); + return value; } } @@ -1202,6 +1226,10 @@ private void DecodeWellKnownAttributeAppliedToMethod(ref DecodeWellKnownAttribut // [Extension] attribute should not be set explicitly. arguments.Diagnostics.Add(ErrorCode.ERR_ExplicitExtension, arguments.AttributeSyntaxOpt.Location); } + else if (attribute.IsTargetAttribute(this, AttributeDescription.NullableContextAttribute)) + { + ReportExplicitUseOfNullabilityAttribute(in arguments, AttributeDescription.NullableContextAttribute); + } else if (attribute.IsTargetAttribute(this, AttributeDescription.SecurityCriticalAttribute) || attribute.IsTargetAttribute(this, AttributeDescription.SecuritySafeCriticalAttribute)) { @@ -1654,12 +1682,55 @@ internal override void AfterAddingTypeMembersChecks(ConversionsBase conversions, { base.AfterAddingTypeMembersChecks(conversions, diagnostics); + var compilation = this.DeclaringCompilation; + var location = locations[0]; + if (IsDeclaredReadOnly && !ContainingType.IsReadOnly) { - this.DeclaringCompilation.EnsureIsReadOnlyAttributeExists(diagnostics, locations[0], modifyCompilation: true); + compilation.EnsureIsReadOnlyAttributeExists(diagnostics, location, modifyCompilation: true); + } + + if (compilation.ShouldEmitNullableAttributes(this) && + ShouldEmitNullableContextValue(out _)) + { + compilation.EnsureNullableContextAttributeExists(diagnostics, location, modifyCompilation: true); } } + // Consider moving this state to SourceMethodSymbol to emit NullableContextAttributes + // on lambdas and local functions (see https://github.com/dotnet/roslyn/issues/36736). + internal override byte? GetLocalNullableContextValue() + { + byte? value; + if (!flags.TryGetNullableContext(out value)) + { + value = ComputeNullableContextValue(); + flags.SetNullableContext(value); + } + return value; + } + + private byte? ComputeNullableContextValue() + { + var compilation = DeclaringCompilation; + if (!compilation.ShouldEmitNullableAttributes(this)) + { + return null; + } + + var builder = new MostCommonNullableValueBuilder(); + foreach (var typeParameter in TypeParameters) + { + typeParameter.GetCommonNullableValues(compilation, ref builder); + } + builder.AddValue(ReturnTypeWithAnnotations); + foreach (var parameter in Parameters) + { + parameter.GetCommonNullableValues(compilation, ref builder); + } + return builder.MostCommonValue; + } + internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, ref ArrayBuilder attributes) { base.AddSynthesizedAttributes(moduleBuilder, ref attributes); @@ -1669,6 +1740,14 @@ internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, r AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeIsReadOnlyAttribute(this)); } + var compilation = this.DeclaringCompilation; + + if (compilation.ShouldEmitNullableAttributes(this) && + ShouldEmitNullableContextValue(out byte nullableContextValue)) + { + AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeNullableContextAttribute(this, nullableContextValue)); + } + bool isAsync = this.IsAsync; bool isIterator = this.IsIterator; @@ -1677,8 +1756,6 @@ internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, r return; } - var compilation = this.DeclaringCompilation; - // The async state machine type is not synthesized until the async method body is rewritten. If we are // only emitting metadata the method body will not have been rewritten, and the async state machine // type will not have been created. In this case, omit the attribute. diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceModuleSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceModuleSymbol.cs index aa143e6c90bf8..4318e458024ac 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceModuleSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceModuleSymbol.cs @@ -523,6 +523,14 @@ internal override void DecodeWellKnownAttribute(ref DecodeWellKnownAttributeArgu arguments.GetOrCreateData().DefaultCharacterSet = charSet; } } + else if (attribute.IsTargetAttribute(this, AttributeDescription.NullableContextAttribute)) + { + ReportExplicitUseOfNullabilityAttribute(in arguments, AttributeDescription.NullableContextAttribute); + } + else if (attribute.IsTargetAttribute(this, AttributeDescription.NullablePublicOnlyAttribute)) + { + ReportExplicitUseOfNullabilityAttribute(in arguments, AttributeDescription.NullablePublicOnlyAttribute); + } } private bool EmitNullablePublicOnlyAttribute @@ -560,7 +568,9 @@ internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, r if (EmitNullablePublicOnlyAttribute) { - AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeNullablePublicOnlyAttribute()); + var includesInternals = ImmutableArray.Create( + new TypedConstant(compilation.GetSpecialType(SpecialType.System_Boolean), TypedConstantKind.Primitive, _assemblySymbol.InternalsAreVisible)); + AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeNullablePublicOnlyAttribute(includesInternals)); } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs index f28bdd3d549ca..e595b216fc2a4 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs @@ -920,6 +920,10 @@ internal sealed override void DecodeWellKnownAttribute(ref DecodeWellKnownAttrib // NullableAttribute should not be set explicitly. arguments.Diagnostics.Add(ErrorCode.ERR_ExplicitNullableAttribute, arguments.AttributeSyntaxOpt.Location); } + else if (attribute.IsTargetAttribute(this, AttributeDescription.NullableContextAttribute)) + { + ReportExplicitUseOfNullabilityAttribute(in arguments, AttributeDescription.NullableContextAttribute); + } else { var compilation = this.DeclaringCompilation; @@ -1369,26 +1373,6 @@ internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, r WellKnownMember.System_Reflection_DefaultMemberAttribute__ctor, ImmutableArray.Create(defaultMemberNameConstant))); } - - NamedTypeSymbol baseType = this.BaseTypeNoUseSiteDiagnostics; - if ((object)baseType != null) - { - if (baseType.ContainsDynamic()) - { - AddSynthesizedAttribute(ref attributes, compilation.SynthesizeDynamicAttribute(baseType, customModifiersCount: 0)); - } - - if (baseType.ContainsTupleNames()) - { - AddSynthesizedAttribute(ref attributes, compilation.SynthesizeTupleNamesAttribute(baseType)); - } - - if (compilation.ShouldEmitNullableAttributes(this) && - baseType.NeedsNullableAttribute()) - { - AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeNullableAttribute(this, TypeWithAnnotations.Create(baseType))); - } - } } #endregion diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceParameterSymbolBase.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceParameterSymbolBase.cs index 1adebec15abf3..7082f5c2efefb 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceParameterSymbolBase.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceParameterSymbolBase.cs @@ -96,9 +96,9 @@ internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, r AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeIsReadOnlyAttribute(this)); } - if (compilation.ShouldEmitNullableAttributes(_containingSymbol) && type.NeedsNullableAttribute()) + if (compilation.ShouldEmitNullableAttributes(this)) { - AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeNullableAttribute(this, type)); + AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeNullableAttributeIfNecessary(this, GetNullableContextValue(), type)); } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs index c9b738002927e..4e2f8d16d7807 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs @@ -1246,9 +1246,9 @@ internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, r compilation.SynthesizeTupleNamesAttribute(type.Type)); } - if (compilation.ShouldEmitNullableAttributes(this) && type.NeedsNullableAttribute()) + if (compilation.ShouldEmitNullableAttributes(this)) { - AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeNullableAttribute(this, type)); + AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeNullableAttributeIfNecessary(this, ContainingType.GetNullableContextValue(), type)); } if (this.ReturnsByRefReadonly) diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceTypeParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceTypeParameterSymbol.cs index fa975f0dd87b2..cd19e9bd949ce 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceTypeParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceTypeParameterSymbol.cs @@ -309,7 +309,7 @@ private void CheckNullableAnnotationsInConstraints(DiagnosticBag diagnostics) // See https://github.com/dotnet/roslyn/blob/master/docs/features/nullable-metadata.md internal bool ConstraintsNeedNullableAttribute() { - if (!DeclaringCompilation.ShouldEmitNullableAttributes(ContainingSymbol)) + if (!DeclaringCompilation.ShouldEmitNullableAttributes(this)) { return false; } @@ -381,24 +381,15 @@ internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, r } var compilation = DeclaringCompilation; - if (compilation.ShouldEmitNullableAttributes(ContainingSymbol)) + if (compilation.ShouldEmitNullableAttributes(this)) { - byte nullableAttributeValue = GetSynthesizedNullableAttributeValue(); - if (nullableAttributeValue != NullableAnnotationExtensions.ObliviousAttributeValue) - { - NamedTypeSymbol byteType = compilation.GetSpecialType(SpecialType.System_Byte); - Debug.Assert((object)byteType != null); - - AddSynthesizedAttribute( - ref attributes, - moduleBuilder.SynthesizeNullableAttribute(WellKnownMember.System_Runtime_CompilerServices_NullableAttribute__ctorByte, - ImmutableArray.Create(new TypedConstant(byteType, TypedConstantKind.Primitive, - nullableAttributeValue)))); - } + AddSynthesizedAttribute( + ref attributes, + moduleBuilder.SynthesizeNullableAttributeIfNecessary(GetNullableContextValue(), GetSynthesizedNullableAttributeValue())); } } - private byte GetSynthesizedNullableAttributeValue() + internal byte GetSynthesizedNullableAttributeValue() { if (this.HasReferenceTypeConstraint) { diff --git a/src/Compilers/CSharp/Portable/Symbols/Symbol.cs b/src/Compilers/CSharp/Portable/Symbols/Symbol.cs index 112b4c58874af..26ef743f94b5b 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Symbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Symbol.cs @@ -8,7 +8,6 @@ using System.Runtime.InteropServices; using System.Text; using System.Threading; -using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.CSharp.Emit; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -1157,6 +1156,87 @@ internal static void CheckForBlockAndExpressionBody( } } + internal void ReportExplicitUseOfNullabilityAttribute(in DecodeWellKnownAttributeArguments arguments, AttributeDescription attributeDescription) + { + // Attribute should not be set explicitly. + arguments.Diagnostics.Add(ErrorCode.ERR_ExplicitReservedAttr, arguments.AttributeSyntaxOpt.Location, attributeDescription.FullName); + } + + internal virtual byte? GetNullableContextValue() + { + return GetLocalNullableContextValue() ?? ContainingSymbol?.GetNullableContextValue(); + } + + internal virtual byte? GetLocalNullableContextValue() + { + return null; + } + + internal void GetCommonNullableValues(CSharpCompilation compilation, ref MostCommonNullableValueBuilder builder) + { + switch (this.Kind) + { + case SymbolKind.NamedType: + if (compilation.ShouldEmitNullableAttributes(this)) + { + builder.AddValue(this.GetLocalNullableContextValue()); + } + break; + case SymbolKind.Event: + if (compilation.ShouldEmitNullableAttributes(this)) + { + builder.AddValue(((EventSymbol)this).TypeWithAnnotations); + } + break; + case SymbolKind.Field: + if (compilation.ShouldEmitNullableAttributes(this)) + { + builder.AddValue(((FieldSymbol)this).TypeWithAnnotations); + } + break; + case SymbolKind.Method: + if (compilation.ShouldEmitNullableAttributes(this)) + { + builder.AddValue(this.GetLocalNullableContextValue()); + } + break; + case SymbolKind.Property: + if (compilation.ShouldEmitNullableAttributes(this)) + { + builder.AddValue(((PropertySymbol)this).TypeWithAnnotations); + // Attributes are not emitted for property parameters. + } + break; + case SymbolKind.Parameter: + builder.AddValue(((ParameterSymbol)this).TypeWithAnnotations); + break; + case SymbolKind.TypeParameter: + if (this is SourceTypeParameterSymbolBase typeParameter) + { + builder.AddValue(typeParameter.GetSynthesizedNullableAttributeValue()); + foreach (var constraintType in typeParameter.ConstraintTypesNoUseSiteDiagnostics) + { + builder.AddValue(constraintType); + } + } + break; + } + } + + internal bool ShouldEmitNullableContextValue(out byte value) + { + byte? localValue = GetLocalNullableContextValue(); + if (localValue == null) + { + value = 0; + return false; + } + + value = localValue.GetValueOrDefault(); + byte containingValue = ContainingSymbol?.GetNullableContextValue() ?? 0; + return value != containingValue; + } + #region ISymbol Members public string Language diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEmbeddedNullableAttributeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEmbeddedNullableAttributeSymbol.cs index 7fdce9a74358d..e4db998f9412b 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEmbeddedNullableAttributeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEmbeddedNullableAttributeSymbol.cs @@ -95,32 +95,32 @@ private void GenerateSingleByteConstructorBody(SyntheticBoundNodeFactory factory ) ); } + } - private sealed class SynthesizedEmbeddedAttributeConstructorWithBodySymbol : SynthesizedInstanceConstructor - { - private readonly ImmutableArray _parameters; - - private readonly Action, ImmutableArray> _getConstructorBody; + internal sealed class SynthesizedEmbeddedAttributeConstructorWithBodySymbol : SynthesizedInstanceConstructor + { + private readonly ImmutableArray _parameters; - internal SynthesizedEmbeddedAttributeConstructorWithBodySymbol( - NamedTypeSymbol containingType, - Func> getParameters, - Action, ImmutableArray> getConstructorBody) : - base(containingType) - { - _parameters = getParameters(this); - _getConstructorBody = getConstructorBody; - } + private readonly Action, ImmutableArray> _getConstructorBody; - public override ImmutableArray Parameters => _parameters; + internal SynthesizedEmbeddedAttributeConstructorWithBodySymbol( + NamedTypeSymbol containingType, + Func> getParameters, + Action, ImmutableArray> getConstructorBody) : + base(containingType) + { + _parameters = getParameters(this); + _getConstructorBody = getConstructorBody; + } - internal override void GenerateMethodBody(TypeCompilationState compilationState, DiagnosticBag diagnostics) - { - GenerateMethodBodyCore(compilationState, diagnostics); - } + public override ImmutableArray Parameters => _parameters; - protected override void GenerateMethodBodyStatements(SyntheticBoundNodeFactory factory, ArrayBuilder statements, DiagnosticBag diagnostics) => _getConstructorBody(factory, statements, _parameters); + internal override void GenerateMethodBody(TypeCompilationState compilationState, DiagnosticBag diagnostics) + { + GenerateMethodBodyCore(compilationState, diagnostics); } + + protected override void GenerateMethodBodyStatements(SyntheticBoundNodeFactory factory, ArrayBuilder statements, DiagnosticBag diagnostics) => _getConstructorBody(factory, statements, _parameters); } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEmbeddedNullableContextAttributeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEmbeddedNullableContextAttributeSymbol.cs new file mode 100644 index 0000000000000..e856844c52ef9 --- /dev/null +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEmbeddedNullableContextAttributeSymbol.cs @@ -0,0 +1,56 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Linq; +using Microsoft.CodeAnalysis.PooledObjects; + +namespace Microsoft.CodeAnalysis.CSharp.Symbols +{ + internal sealed class SynthesizedEmbeddedNullableContextAttributeSymbol : SynthesizedEmbeddedAttributeSymbolBase + { + private readonly ImmutableArray _fields; + private readonly ImmutableArray _constructors; + + public SynthesizedEmbeddedNullableContextAttributeSymbol( + CSharpCompilation compilation, + DiagnosticBag diagnostics) + : base(AttributeDescription.NullableContextAttribute, compilation, diagnostics) + { + var byteType = compilation.GetSpecialType(SpecialType.System_Byte); + Binder.ReportUseSiteDiagnostics(byteType, diagnostics, Location.None); + + _fields = ImmutableArray.Create( + new SynthesizedFieldSymbol( + this, + byteType, + "Flag", + isPublic: true, + isReadOnly: true, + isStatic: false)); + + _constructors = ImmutableArray.Create( + new SynthesizedEmbeddedAttributeConstructorWithBodySymbol( + this, + m => ImmutableArray.Create(SynthesizedParameterSymbol.Create(m, TypeWithAnnotations.Create(byteType), 0, RefKind.None)), + GenerateConstructorBody)); + + // Ensure we never get out of sync with the description + Debug.Assert(_constructors.Length == AttributeDescription.NullableContextAttribute.Signatures.Length); + } + + internal override IEnumerable GetFieldsToEmit() => _fields; + + public override ImmutableArray Constructors => _constructors; + + private void GenerateConstructorBody(SyntheticBoundNodeFactory factory, ArrayBuilder statements, ImmutableArray parameters) + { + statements.Add( + factory.ExpressionStatement( + factory.AssignmentExpression( + factory.Field(factory.This(), _fields.Single()), + factory.Parameter(parameters.Single())))); + } + } +} diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEmbeddedNullablePublicOnlyAttributeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEmbeddedNullablePublicOnlyAttributeSymbol.cs new file mode 100644 index 0000000000000..185eeb6818d98 --- /dev/null +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEmbeddedNullablePublicOnlyAttributeSymbol.cs @@ -0,0 +1,56 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Linq; +using Microsoft.CodeAnalysis.PooledObjects; + +namespace Microsoft.CodeAnalysis.CSharp.Symbols +{ + internal sealed class SynthesizedEmbeddedNullablePublicOnlyAttributeSymbol : SynthesizedEmbeddedAttributeSymbolBase + { + private readonly ImmutableArray _fields; + private readonly ImmutableArray _constructors; + + public SynthesizedEmbeddedNullablePublicOnlyAttributeSymbol( + CSharpCompilation compilation, + DiagnosticBag diagnostics) + : base(AttributeDescription.NullablePublicOnlyAttribute, compilation, diagnostics) + { + var boolType = compilation.GetSpecialType(SpecialType.System_Boolean); + Binder.ReportUseSiteDiagnostics(boolType, diagnostics, Location.None); + + _fields = ImmutableArray.Create( + new SynthesizedFieldSymbol( + this, + boolType, + "IncludesInternals", + isPublic: true, + isReadOnly: true, + isStatic: false)); + + _constructors = ImmutableArray.Create( + new SynthesizedEmbeddedAttributeConstructorWithBodySymbol( + this, + m => ImmutableArray.Create(SynthesizedParameterSymbol.Create(m, TypeWithAnnotations.Create(boolType), 0, RefKind.None)), + GenerateConstructorBody)); + + // Ensure we never get out of sync with the description + Debug.Assert(_constructors.Length == AttributeDescription.NullablePublicOnlyAttribute.Signatures.Length); + } + + internal override IEnumerable GetFieldsToEmit() => _fields; + + public override ImmutableArray Constructors => _constructors; + + private void GenerateConstructorBody(SyntheticBoundNodeFactory factory, ArrayBuilder statements, ImmutableArray parameters) + { + statements.Add( + factory.ExpressionStatement( + factory.AssignmentExpression( + factory.Field(factory.This(), _fields.Single()), + factory.Parameter(parameters.Single())))); + } + } +} diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedFieldSymbolBase.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedFieldSymbolBase.cs index 011d44c61bfaf..b4edc4f78c384 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedFieldSymbolBase.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedFieldSymbolBase.cs @@ -69,9 +69,9 @@ internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, r compilation.SynthesizeTupleNamesAttribute(Type)); } - if (compilation.ShouldEmitNullableAttributes(this) && typeWithAnnotations.NeedsNullableAttribute()) + if (compilation.ShouldEmitNullableAttributes(this)) { - AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeNullableAttribute(this, typeWithAnnotations)); + AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeNullableAttributeIfNecessary(this, ContainingType.GetNullableContextValue(), typeWithAnnotations)); } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedParameterSymbol.cs index 7e09d022eb765..ebd56aad43257 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedParameterSymbol.cs @@ -157,9 +157,9 @@ internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, r compilation.SynthesizeTupleNamesAttribute(type.Type)); } - if (compilation.ShouldEmitNullableAttributes(_container) && type.NeedsNullableAttribute()) + if (compilation.ShouldEmitNullableAttributes(this)) { - AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeNullableAttribute(this, type)); + AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeNullableAttributeIfNecessary(this, GetNullableContextValue(), type)); } if (this.RefKind == RefKind.RefReadOnly) diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs index 1ac7e6b380d74..14ff53f6f9300 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs @@ -1665,8 +1665,8 @@ internal static Cci.TypeReferenceWithAttributes GetTypeRefWithAttributes( Cci.ITypeReference typeRef) { var builder = ArrayBuilder.GetInstance(); - var compilation = declaringSymbol.DeclaringCompilation; + if (compilation != null) { if (type.Type.ContainsTupleNames()) @@ -1678,10 +1678,9 @@ internal static Cci.TypeReferenceWithAttributes GetTypeRefWithAttributes( } } - if (compilation.ShouldEmitNullableAttributes(declaringSymbol) && - type.NeedsNullableAttribute()) + if (compilation.ShouldEmitNullableAttributes(declaringSymbol)) { - SynthesizedAttributeData attr = moduleBuilder.SynthesizeNullableAttribute(declaringSymbol, type); + SynthesizedAttributeData attr = moduleBuilder.SynthesizeNullableAttributeIfNecessary(declaringSymbol, declaringSymbol.GetNullableContextValue(), type); if (attr != null) { builder.Add(attr); @@ -1689,6 +1688,7 @@ internal static Cci.TypeReferenceWithAttributes GetTypeRefWithAttributes( } } + return new Cci.TypeReferenceWithAttributes(typeRef, builder.ToImmutableAndFree()); } diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeWithAnnotations.cs b/src/Compilers/CSharp/Portable/Symbols/TypeWithAnnotations.cs index 8607f9887e69e..c4375ba8c7293 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeWithAnnotations.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeWithAnnotations.cs @@ -547,6 +547,17 @@ public bool Is(TypeParameterSymbol other) public TypeWithAnnotations WithTypeAndModifiers(TypeSymbol typeSymbol, ImmutableArray customModifiers) => _extensions.WithTypeAndModifiers(this, typeSymbol, customModifiers); + /// + /// Used by callers before calling CSharpCompilation.EnsureNullableAttributeExists(). + /// + /// + /// This method ignores any [NullableContext]. For example, if there is a [NullableContext(1)] + /// at the containing type, and this type reference is oblivious, NeedsNullableAttribute() + /// will return false even though a [Nullable(0)] will be emitted for this type reference. + /// In practice, this shouldn't be an issue though since EnsuresNullableAttributeExists() + /// will have returned true for at least some of other type references that required + /// [Nullable(1)] and were subsequently aggregated to the [NullableContext(1)]. + /// public bool NeedsNullableAttribute() { return NeedsNullableAttribute(this, typeOpt: null); @@ -585,27 +596,43 @@ private static bool SkipNullableTransform(TypeSymbol type) public void AddNullableTransforms(ArrayBuilder transforms) { - var type = Type; + AddNullableTransforms(this, transforms); + } - if (!SkipNullableTransform(type)) + private static void AddNullableTransforms(TypeWithAnnotations typeWithAnnotations, ArrayBuilder transforms) + { + while (true) { - byte flag; - if (NullableAnnotation.IsOblivious() || type.IsValueType) - { - flag = NullableAnnotationExtensions.ObliviousAttributeValue; - } - else if (NullableAnnotation.IsAnnotated()) + var type = typeWithAnnotations.Type; + + if (!SkipNullableTransform(type)) { - flag = NullableAnnotationExtensions.AnnotatedAttributeValue; + var annotation = typeWithAnnotations.NullableAnnotation; + byte flag; + if (annotation.IsOblivious() || type.IsValueType) + { + flag = NullableAnnotationExtensions.ObliviousAttributeValue; + } + else if (annotation.IsAnnotated()) + { + flag = NullableAnnotationExtensions.AnnotatedAttributeValue; + } + else + { + flag = NullableAnnotationExtensions.NotAnnotatedAttributeValue; + } + transforms.Add(flag); } - else + + if (type.TypeKind != TypeKind.Array) { - flag = NullableAnnotationExtensions.NotAnnotatedAttributeValue; + type.AddNullableTransforms(transforms); + return; } - transforms.Add(flag); - } - type.AddNullableTransforms(transforms); + // Avoid recursion to allow for deeply-nested arrays. + typeWithAnnotations = ((ArrayTypeSymbol)type).ElementTypeWithAnnotations; + } } public bool ApplyNullableTransforms(byte defaultTransformFlag, ImmutableArray transforms, ref int position, out TypeWithAnnotations result) diff --git a/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs b/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs index 38d2fabb306f2..712a2a68f39bf 100644 --- a/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs +++ b/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs @@ -9261,7 +9261,7 @@ public sealed class NullableAttribute : Attribute { } // missing constructor } public class Program { - private object? P => null; + private object? F = null; }"; string errorMessage = "error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.NullableAttribute..ctor'"; diff --git a/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_Nullable.cs b/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_Nullable.cs index 5a0e7f94672ba..b037da56171c3 100644 --- a/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_Nullable.cs +++ b/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_Nullable.cs @@ -6,6 +6,7 @@ using System.Reflection.Metadata; using System.Reflection.PortableExecutable; using Microsoft.CodeAnalysis.CSharp.Symbols; +using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Test.Utilities; @@ -179,14 +180,16 @@ public class Attribute references: new[] { ref0 }, parseOptions: TestOptions.Regular8); comp.VerifyEmitDiagnostics( - // (3,11): warning CS8632: The annotation for nullable reference types should only be used in code within a '#nullable' context. + // (3,11): warning CS8632: The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. // object? F() => null; Diagnostic(ErrorCode.WRN_MissingNonNullTypesContextForAnnotation, "?").WithLocation(3, 11), // error CS0518: Predefined type 'System.Byte' is not defined or imported Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound).WithArguments("System.Byte").WithLocation(1, 1), + // error CS0518: Predefined type 'System.Byte' is not defined or imported + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound).WithArguments("System.Byte").WithLocation(1, 1), // error CS0518: Predefined type 'System.Int32' is not defined or imported - Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "").WithArguments("System.Int32").WithLocation(1, 1) - ); + // + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "").WithArguments("System.Int32").WithLocation(1, 1)); } [Fact] @@ -219,6 +222,8 @@ public struct Byte { } // error CS0518: Predefined type 'System.Attribute' is not defined or imported Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound).WithArguments("System.Attribute").WithLocation(1, 1), // error CS0518: Predefined type 'System.Attribute' is not defined or imported + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound).WithArguments("System.Attribute").WithLocation(1, 1), + // error CS0518: Predefined type 'System.Attribute' is not defined or imported Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound).WithArguments("System.Attribute").WithLocation(1, 1)); } @@ -260,6 +265,8 @@ public Attribute(object o) { } // error CS1729: 'Attribute' does not contain a constructor that takes 0 arguments Diagnostic(ErrorCode.ERR_BadCtorArgCount).WithArguments("System.Attribute", "0").WithLocation(1, 1), // error CS1729: 'Attribute' does not contain a constructor that takes 0 arguments + Diagnostic(ErrorCode.ERR_BadCtorArgCount).WithArguments("System.Attribute", "0").WithLocation(1, 1), + // error CS1729: 'Attribute' does not contain a constructor that takes 0 arguments Diagnostic(ErrorCode.ERR_BadCtorArgCount).WithArguments("System.Attribute", "0").WithLocation(1, 1)); } @@ -486,6 +493,64 @@ public void EmitAttribute_NetModuleNoDeclarations() }); } + [Fact] + public void EmitAttribute_01() + { + var source = +@"public class Program +{ + public object? F; + public object?[]? G; +}"; + var comp = CreateCompilation(source, options: WithNonNullTypesTrue()); + var expected = +@"[NullableContext(2)] [Nullable(0)] Program + System.Object? F + System.Object?[]? G + Program() +"; + AssertNullableAttributes(comp, expected); + } + + [Fact] + public void EmitAttribute_02() + { + var source = +@"public class Program +{ + public object? F(object?[]? args) => null; + public object G(object[] args) => null!; +}"; + var comp = CreateCompilation(source, options: WithNonNullTypesTrue()); + var expected = +@"Program + [NullableContext(2)] System.Object? F(System.Object?[]? args) + System.Object?[]? args + [NullableContext(1)] System.Object! G(System.Object![]! args) + System.Object![]! args +"; + AssertNullableAttributes(comp, expected); + } + + [Fact] + public void EmitAttribute_03() + { + var source = +@"public class Program +{ + public static void F(string x, string y, string z) { } +}"; + var comp = CreateCompilation(source, options: WithNonNullTypesTrue()); + var expected = +@"Program + [NullableContext(1)] void F(System.String! x, System.String! y, System.String! z) + System.String! x + System.String! y + System.String! z +"; + AssertNullableAttributes(comp, expected); + } + [Fact] public void EmitAttribute_BaseClass() { @@ -500,17 +565,14 @@ public class B2 : A { }"; var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesTrue(), parseOptions: TestOptions.Regular8); - CompileAndVerify(comp, symbolValidator: module => - { - var type = module.ContainingAssembly.GetTypeByMetadataName("A`1"); - AssertNoNullableAttribute(type.GetAttributes()); - type = module.ContainingAssembly.GetTypeByMetadataName("B1"); - AssertNoNullableAttribute(type.BaseType().GetAttributes()); - AssertNullableAttribute(type.GetAttributes()); - type = module.ContainingAssembly.GetTypeByMetadataName("B2"); - AssertNoNullableAttribute(type.BaseType().GetAttributes()); - AssertNullableAttribute(type.GetAttributes()); - }); + var expected = +@"A + [Nullable(2)] T +[Nullable({ 0, 1 })] B1 +[Nullable({ 0, 2 })] B2 +"; + AssertNullableAttributes(comp, expected); + var source2 = @"class C { @@ -640,6 +702,81 @@ static void G(A a, B b) Assert.Equal("I<(System.Object X, System.Object? Y)>", type.Interfaces()[0].ToTestDisplayString()); } + [Fact] + public void EmitAttribute_ImplementedInterfaces_01() + { + var source = +@"#nullable enable +public interface I { } +public class A : + I, + I +{ + public object FA1; + public object FA2; +} +public class B : +#nullable disable + I, +#nullable enable + I +{ + public object FB1; + public object FB2; +}"; + var comp = CreateCompilation(source); + var expected = +@"[NullableContext(2)] I + T +[NullableContext(1)] [Nullable(0)] A + System.Object! FA1 + System.Object! FA2 + A() +B + [Nullable(1)] System.Object! FB1 + [Nullable(1)] System.Object! FB2 +"; + AssertNullableAttributes(comp, expected); + } + + [Fact] + public void EmitAttribute_ImplementedInterfaces_02() + { + var source = +@"#nullable enable +public interface IA { } +public interface IB : IA { } +public interface IC : IB< +#nullable disable + object +#nullable enable + > +{ +} +public class C : IC< +#nullable disable + string +#nullable enable + > +{ + public object F1; + public object F2; + public object F3; +}"; + var comp = CreateCompilation(source); + var expected = +@"IB + [Nullable(2)] T +IC + [Nullable(2)] T +C + [Nullable(1)] System.Object! F1 + [Nullable(1)] System.Object! F2 + [Nullable(1)] System.Object! F3 +"; + AssertNullableAttributes(comp, expected); + } + [Fact] public void EmitAttribute_Constraint_Nullable() { @@ -815,7 +952,6 @@ static void Main() Assert.Equal("A!", type.TypeParameters[0].ConstraintTypesNoUseSiteDiagnostics[0].ToTestDisplayString(true)); } - // https://github.com/dotnet/roslyn/issues/29976: Test `class C where T : class? { }`. [Fact] public void EmitAttribute_Constraint_TypeParameter() { @@ -865,6 +1001,347 @@ static void Main() Assert.Equal("T?", type.TypeParameters[1].ConstraintTypesNoUseSiteDiagnostics[0].ToTestDisplayString(true)); } + [Fact] + public void EmitAttribute_Constraints() + { + var source = +@"#nullable enable +public abstract class Program +{ +#nullable disable + public abstract void M0() + where T1 : class + where T2 : class + where T3 : class +#nullable enable + where T4 : class?; + public abstract void M1() + where T1 : class + where T2 : class + where T3 : class +#nullable disable + where T4 : class; +#nullable enable + public abstract void M2() + where T1 : class? + where T2 : class? + where T3 : class? + where T4 : class; + private object _f1; + private object _f2; + private object _f3; +}"; + var comp = CreateCompilation(source); + var expected = +@"[NullableContext(1)] [Nullable(0)] Program + [NullableContext(0)] void M0() where T1 : class where T2 : class where T3 : class where T4 : class? + T1 + T2 + T3 + [Nullable(2)] T4 + void M1() where T1 : class! where T2 : class! where T3 : class! where T4 : class + T1 + T2 + T3 + [Nullable(0)] T4 + [NullableContext(2)] void M2() where T1 : class? where T2 : class? where T3 : class? where T4 : class! + T1 + T2 + T3 + [Nullable(1)] T4 + Program() +"; + AssertNullableAttributes(comp, expected); + } + + [Fact] + public void EmitAttribute_ClassConstraint_SameAsContext() + { + var source = +@"#nullable enable +public class Program +{ + public class C0 +#nullable disable + where T0 : class +#nullable enable + { +#nullable disable + public object F01; + public object F02; +#nullable enable + } + public class C1 + where T1 : class + { + public object F11; + public object F12; + } + public class C2 + where T2 : class? + { + public object? F21; + public object? F22; + } + public object F31; + public object F32; + public object F33; +}"; + var comp = CreateCompilation(source); + var expected = +@"[NullableContext(1)] [Nullable(0)] Program + System.Object! F31 + System.Object! F32 + System.Object! F33 + Program() + [NullableContext(0)] Program.C0 where T0 : class + T0 + System.Object F01 + System.Object F02 + C0() + [Nullable(0)] Program.C1 where T1 : class! + T1 + System.Object! F11 + System.Object! F12 + C1() + [NullableContext(2)] [Nullable(0)] Program.C2 where T2 : class? + T2 + System.Object? F21 + System.Object? F22 + C2() +"; + CompileAndVerify(comp, symbolValidator: module => + { + AssertNullableAttributes(module, expected); + verifyTypeParameterConstraint("Program.C0", null); + verifyTypeParameterConstraint("Program.C1", false); + verifyTypeParameterConstraint("Program.C2", true); + + void verifyTypeParameterConstraint(string typeName, bool? expectedConstraintIsNullable) + { + var typeParameter = module.GlobalNamespace.GetMember(typeName).TypeParameters.Single(); + Assert.True(typeParameter.HasReferenceTypeConstraint); + Assert.Equal(expectedConstraintIsNullable, typeParameter.ReferenceTypeConstraintIsNullable); + } + }); + } + + [Fact] + public void EmitAttribute_ClassConstraint_DifferentFromContext() + { + var source = +@"#nullable enable +public class Program +{ +#nullable enable + public class C0 +#nullable disable + where T0 : class +#nullable enable + { + public object F01; + public object F02; + } + public class C1 + where T1 : class + { + public object? F11; + public object? F12; + } + public class C2 + where T2 : class? + { + public object F21; + public object F22; + } + public object F31; + public object F32; + public object F33; +}"; + var comp = CreateCompilation(source); + var expected = +@"[NullableContext(1)] [Nullable(0)] Program + System.Object! F31 + System.Object! F32 + System.Object! F33 + Program() + [NullableContext(0)] Program.C0 where T0 : class + T0 + [Nullable(1)] System.Object! F01 + [Nullable(1)] System.Object! F02 + C0() + [NullableContext(2)] [Nullable(0)] Program.C1 where T1 : class! + [Nullable(1)] T1 + System.Object? F11 + System.Object? F12 + C1() + [Nullable(0)] Program.C2 where T2 : class? + [Nullable(2)] T2 + System.Object! F21 + System.Object! F22 + C2() +"; + CompileAndVerify(comp, symbolValidator: module => + { + AssertNullableAttributes(module, expected); + verifyTypeParameterConstraint("Program.C0", null); + verifyTypeParameterConstraint("Program.C1", false); + verifyTypeParameterConstraint("Program.C2", true); + + void verifyTypeParameterConstraint(string typeName, bool? expectedConstraintIsNullable) + { + var typeParameter = module.GlobalNamespace.GetMember(typeName).TypeParameters.Single(); + Assert.True(typeParameter.HasReferenceTypeConstraint); + Assert.Equal(expectedConstraintIsNullable, typeParameter.ReferenceTypeConstraintIsNullable); + } + }); + } + + [Fact] + public void EmitAttribute_NotNullConstraint() + { + var source = +@"#nullable enable +public class C0 + where T0 : notnull +{ +#nullable disable + public object F01; + public object F02; +#nullable enable +} +public class C1 + where T1 : notnull +{ + public object F11; + public object F12; +} +public class C2 + where T2 : notnull +{ + public object? F21; + public object? F22; +}"; + var comp = CreateCompilation(source); + var expected = +@"C0 where T0 : notnull + [Nullable(1)] T0 +[NullableContext(1)] [Nullable(0)] C1 where T1 : notnull + T1 + System.Object! F11 + System.Object! F12 + C1() +[NullableContext(2)] [Nullable(0)] C2 where T2 : notnull + [Nullable(1)] T2 + System.Object? F21 + System.Object? F22 + C2() +"; + CompileAndVerify(comp, symbolValidator: module => + { + AssertNullableAttributes(module, expected); + verifyTypeParameterConstraint("C0"); + verifyTypeParameterConstraint("C1"); + verifyTypeParameterConstraint("C2"); + + void verifyTypeParameterConstraint(string typeName) + { + var typeParameter = module.GlobalNamespace.GetMember(typeName).TypeParameters.Single(); + Assert.True(typeParameter.HasNotNullConstraint); + } + }); + } + + [Fact] + public void EmitAttribute_ConstraintTypes_01() + { + var source = +@"#nullable enable +public interface IA { } +public interface IB { } +public interface I0 +#nullable disable + where T : IA, IB +#nullable enable +{ + object F01(); + object F02(); +} +public interface I1 + where T : IA, IB +{ + object? F11(); + object? F12(); +} +public interface I2 + where T : IA?, IB? +{ + object F21(); + object F22(); +}"; + var comp = CreateCompilation(source); + var expected = +@"[NullableContext(2)] IB + T +I0 where T : IA, IB + [NullableContext(1)] System.Object! F01() + [NullableContext(1)] System.Object! F02() +[NullableContext(1)] I1 where T : IA!, IB! + [Nullable(0)] T + [NullableContext(2)] System.Object? F11() + [NullableContext(2)] System.Object? F12() +[NullableContext(1)] I2 where T : IA?, IB? + [Nullable(0)] T + System.Object! F21() + System.Object! F22() +"; + AssertNullableAttributes(comp, expected); + } + + [Fact] + public void EmitAttribute_ConstraintTypes_02() + { + var source = +@"#nullable enable +public interface IA { } +public interface IB { } +public class Program +{ + public static void M0(object x, object y) +#nullable disable + where T : IA, IB +#nullable enable + { + } + public static void M1(object? x, object? y) + where T : IA, IB + { + } + public static void M2(object x, object y) + where T : IA?, IB? + { + } +}"; + var comp = CreateCompilation(source); + var expected = +@"[NullableContext(2)] IB + T +Program + void M0(System.Object! x, System.Object! y) where T : IA, IB + [Nullable(1)] System.Object! x + [Nullable(1)] System.Object! y + [NullableContext(1)] void M1(System.Object? x, System.Object? y) where T : IA!, IB! + [Nullable(0)] T + [Nullable(2)] System.Object? x + [Nullable(2)] System.Object? y + [NullableContext(1)] void M2(System.Object! x, System.Object! y) where T : IA?, IB? + [Nullable(0)] T + System.Object! x + System.Object! y +"; + AssertNullableAttributes(comp, expected); + } + [Fact] public void EmitAttribute_MethodReturnType() { @@ -874,29 +1351,37 @@ public void EmitAttribute_MethodReturnType() public object? F() => null; }"; var comp = CreateCompilation(source, parseOptions: TestOptions.Regular8); - CompileAndVerify(comp, symbolValidator: module => - { - var type = module.ContainingAssembly.GetTypeByMetadataName("C"); - var method = (MethodSymbol)type.GetMembers("F").Single(); - AssertNullableAttribute(method.GetReturnTypeAttributes()); - }); + var expected = +@"C + [NullableContext(2)] System.Object? F() +"; + AssertNullableAttributes(comp, expected); } [Fact] public void EmitAttribute_MethodParameters() { var source = -@"public class C +@"public class A { public void F(object?[] c) { } +} +#nullable enable +public class B +{ + public void F(object x, object y) { } }"; var comp = CreateCompilation(source, parseOptions: TestOptions.Regular8); - CompileAndVerify(comp, symbolValidator: module => - { - var type = module.ContainingAssembly.GetTypeByMetadataName("C"); - var method = (MethodSymbol)type.GetMembers("F").Single(); - AssertNullableAttribute(method.Parameters.Single().GetAttributes()); - }); + var expected = +@"A + void F(System.Object?[] c) + [Nullable({ 0, 2 })] System.Object?[] c +B + [NullableContext(1)] void F(System.Object! x, System.Object! y) + System.Object! x + System.Object! y +"; + AssertNullableAttributes(comp, expected); } [Fact] @@ -908,12 +1393,12 @@ public void EmitAttribute_ConstructorParameters() public C(object?[] c) { } }"; var comp = CreateCompilation(source, parseOptions: TestOptions.Regular8); - CompileAndVerify(comp, symbolValidator: module => - { - var type = module.ContainingAssembly.GetTypeByMetadataName("C"); - var method = type.Constructors.Single(); - AssertNullableAttribute(method.Parameters.Single().GetAttributes()); - }); + var expected = +@"C + C(System.Object?[] c) + [Nullable({ 0, 2 })] System.Object?[] c +"; + AssertNullableAttributes(comp, expected); } [Fact] @@ -925,30 +1410,74 @@ public void EmitAttribute_PropertyType() public object? P => null; }"; var comp = CreateCompilation(source, parseOptions: TestOptions.Regular8); - CompileAndVerify(comp, symbolValidator: module => - { - var type = module.ContainingAssembly.GetTypeByMetadataName("C"); - var property = (PropertySymbol)type.GetMembers("P").Single(); - AssertNullableAttribute(property.GetAttributes()); - }); + var expected = +@"[NullableContext(2)] [Nullable(0)] C + C() + System.Object? P { get; } + System.Object? P.get +"; + AssertNullableAttributes(comp, expected); } [Fact] public void EmitAttribute_PropertyParameters() { var source = -@"public class C +@"public class A { public object this[object x, object? y] => throw new System.NotImplementedException(); +} +#nullable enable +public class B +{ + public object this[object x, object y] => throw new System.NotImplementedException(); }"; var comp = CreateCompilation(source, parseOptions: TestOptions.Regular8); - CompileAndVerify(comp, symbolValidator: module => - { - var type = module.ContainingAssembly.GetTypeByMetadataName("C"); - var property = (PropertySymbol)type.GetMembers("this[]").Single(); - AssertNoNullableAttribute(property.Parameters[0].GetAttributes()); - AssertNullableAttribute(property.Parameters[1].GetAttributes()); - }); + var expected = +@"A + System.Object this[System.Object x, System.Object? y] { get; } + [Nullable(2)] System.Object? y + System.Object this[System.Object x, System.Object? y].get + [Nullable(2)] System.Object? y +[NullableContext(1)] [Nullable(0)] B + B() + System.Object! this[System.Object! x, System.Object! y] { get; } + System.Object! x + System.Object! y + System.Object! this[System.Object! x, System.Object! y].get + System.Object! x + System.Object! y +"; + AssertNullableAttributes(comp, expected); + } + + [Fact] + public void EmitAttribute_Indexers() + { + var source = +@"#nullable enable +public class Program +{ + public object this[object? x, object? y] => throw new System.NotImplementedException(); + public object this[object? z] { set { } } +}"; + var comp = CreateCompilation(source); + var expected = +@"[NullableContext(1)] [Nullable(0)] Program + Program() + System.Object! this[System.Object! x, System.Object! y] { get; } + System.Object! x + System.Object! y + [NullableContext(2)] [Nullable(1)] System.Object! this[System.Object! x, System.Object! y].get + System.Object? x + System.Object? y + System.Object! this[System.Object? z] { set; } + [Nullable(2)] System.Object? z + void this[System.Object? z].set + [Nullable(2)] System.Object? z + System.Object! value +"; + AssertNullableAttributes(comp, expected); } [Fact] @@ -960,13 +1489,11 @@ public void EmitAttribute_OperatorReturnType() public static object? operator+(C a, C b) => null; }"; var comp = CreateCompilation(source, parseOptions: TestOptions.Regular8); - CompileAndVerify(comp, symbolValidator: module => - { - var type = module.ContainingAssembly.GetTypeByMetadataName("C"); - var method = (MethodSymbol)type.GetMembers("op_Addition").Single(); - AssertNullableAttribute(method.GetReturnTypeAttributes()); - AssertNoNullableAttribute(method.GetAttributes()); - }); + var expected = +@"C + [Nullable(2)] System.Object? operator +(C a, C b) +"; + AssertNullableAttributes(comp, expected); } [Fact] @@ -978,13 +1505,12 @@ public void EmitAttribute_OperatorParameters() public static object operator+(C a, object?[] b) => a; }"; var comp = CreateCompilation(source, parseOptions: TestOptions.Regular8); - CompileAndVerify(comp, symbolValidator: module => - { - var type = module.ContainingAssembly.GetTypeByMetadataName("C"); - var method = (MethodSymbol)type.GetMembers("op_Addition").Single(); - AssertNoNullableAttribute(method.Parameters[0].GetAttributes()); - AssertNullableAttribute(method.Parameters[1].GetAttributes()); - }); + var expected = +@"C + System.Object operator +(C a, System.Object?[] b) + [Nullable({ 0, 2 })] System.Object?[] b +"; + AssertNullableAttributes(comp, expected); } [Fact] @@ -993,12 +1519,12 @@ public void EmitAttribute_DelegateReturnType() var source = @"public delegate object? D();"; var comp = CreateCompilation(source, parseOptions: TestOptions.Regular8); - CompileAndVerify(comp, symbolValidator: module => - { - var method = module.ContainingAssembly.GetTypeByMetadataName("D").DelegateInvokeMethod; - AssertNullableAttribute(method.GetReturnTypeAttributes()); - AssertNoNullableAttribute(method.GetAttributes()); - }); + var expected = +@"D + [NullableContext(2)] System.Object? Invoke() + [Nullable(2)] System.Object? EndInvoke(System.IAsyncResult result) +"; + AssertNullableAttributes(comp, expected); } [Fact] @@ -1007,11 +1533,14 @@ public void EmitAttribute_DelegateParameters() var source = @"public delegate void D(object?[] o);"; var comp = CreateCompilation(source, parseOptions: TestOptions.Regular8); - CompileAndVerify(comp, symbolValidator: module => - { - var method = module.ContainingAssembly.GetTypeByMetadataName("D").DelegateInvokeMethod; - AssertNullableAttribute(method.Parameters[0].GetAttributes()); - }); + var expected = +@"D + void Invoke(System.Object?[] o) + [Nullable({ 0, 2 })] System.Object?[] o + System.IAsyncResult BeginInvoke(System.Object?[] o, System.AsyncCallback callback, System.Object @object) + [Nullable({ 0, 2 })] System.Object?[] o +"; + AssertNullableAttributes(comp, expected); } [Fact] @@ -1034,7 +1563,7 @@ static void G(object o) } }"; var comp = CreateCompilation(source, parseOptions: TestOptions.Regular8); - AssertNoNullableAttribute(comp); + AssertNoNullableAttributes(comp); } [Fact] @@ -1053,7 +1582,7 @@ static void G() } }"; var comp = CreateCompilation(source, parseOptions: TestOptions.Regular8); - AssertNoNullableAttribute(comp); + AssertNoNullableAttributes(comp); } // See https://github.com/dotnet/roslyn/issues/28862. @@ -1081,7 +1610,7 @@ static void M(object[] c) } }"; var comp = CreateCompilation(source, parseOptions: TestOptions.Regular8, references: new[] { ref0 }); - AssertNoNullableAttribute(comp); + AssertNoNullableAttributes(comp); } [Fact] @@ -1217,7 +1746,36 @@ class C } [Fact] - public void EmitPrivateMetadata_Types() + public void EmitAttribute_Byte0() + { + var source = +@"#nullable enable +public class Program +{ +#nullable disable + public object +#nullable enable + F1(object x, object y) => null; +#nullable disable + public object +#nullable enable + F2(object? x, object? y) => null; +}"; + var comp = CreateCompilation(source); + var expected = +@"Program + [NullableContext(1)] [Nullable(0)] System.Object F1(System.Object! x, System.Object! y) + System.Object! x + System.Object! y + [NullableContext(2)] [Nullable(0)] System.Object F2(System.Object? x, System.Object? y) + System.Object? x + System.Object? y +"; + AssertNullableAttributes(comp, expected); + } + + [Fact] + public void EmitPrivateMetadata_BaseTypes() { var source = @"public class Base { } @@ -1245,9 +1803,10 @@ private protected class PrivateProtected : Base { } private class Private : Base { } }"; var expectedPublicOnly = @" -Base - [Nullable(2)] T - [Nullable(2)] U +[NullableContext(2)] [Nullable(0)] Base + T + U + Base() PublicTypes [Nullable({ 0, 1, 2 })] PublicTypes.Public [Nullable({ 0, 1, 2 })] PublicTypes.Protected @@ -1255,9 +1814,10 @@ private class Private : Base { } [Nullable({ 0, 1, 2 })] Namespace.Public "; var expectedPublicAndInternal = @" -Base - [Nullable(2)] T - [Nullable(2)] U +[NullableContext(2)] [Nullable(0)] Base + T + U + Base() PublicTypes [Nullable({ 0, 1, 2 })] PublicTypes.Public [Nullable({ 0, 1, 2 })] PublicTypes.Internal @@ -1274,9 +1834,10 @@ private class Private : Base { } [Nullable({ 0, 1, 2 })] Namespace.Internal "; var expectedAll = @" -Base - [Nullable(2)] T - [Nullable(2)] U +[NullableContext(2)] [Nullable(0)] Base + T + U + Base() PublicTypes [Nullable({ 0, 1, 2 })] PublicTypes.Public [Nullable({ 0, 1, 2 })] PublicTypes.Internal @@ -1310,7 +1871,7 @@ public void EmitPrivateMetadata_Delegates() var expectedPublicOnly = @" Program Program.ProtectedDelegate - [Nullable(1)] System.Object! Invoke(System.Object? arg) + [NullableContext(1)] System.Object! Invoke(System.Object? arg) [Nullable(2)] System.Object? arg System.IAsyncResult BeginInvoke(System.Object? arg, System.AsyncCallback callback, System.Object @object) [Nullable(2)] System.Object? arg @@ -1319,13 +1880,13 @@ System.IAsyncResult BeginInvoke(System.Object? arg, System.AsyncCallback callbac var expectedPublicAndInternal = @" Program Program.ProtectedDelegate - [Nullable(1)] System.Object! Invoke(System.Object? arg) + [NullableContext(1)] System.Object! Invoke(System.Object? arg) [Nullable(2)] System.Object? arg System.IAsyncResult BeginInvoke(System.Object? arg, System.AsyncCallback callback, System.Object @object) [Nullable(2)] System.Object? arg [Nullable(1)] System.Object! EndInvoke(System.IAsyncResult result) Program.InternalDelegate - [Nullable(1)] System.Object! Invoke(System.Object? arg) + [NullableContext(1)] System.Object! Invoke(System.Object? arg) [Nullable(2)] System.Object? arg System.IAsyncResult BeginInvoke(System.Object? arg, System.AsyncCallback callback, System.Object @object) [Nullable(2)] System.Object? arg @@ -1334,19 +1895,19 @@ System.IAsyncResult BeginInvoke(System.Object? arg, System.AsyncCallback callbac var expectedAll = @" Program Program.ProtectedDelegate - [Nullable(1)] System.Object! Invoke(System.Object? arg) + [NullableContext(1)] System.Object! Invoke(System.Object? arg) [Nullable(2)] System.Object? arg System.IAsyncResult BeginInvoke(System.Object? arg, System.AsyncCallback callback, System.Object @object) [Nullable(2)] System.Object? arg [Nullable(1)] System.Object! EndInvoke(System.IAsyncResult result) Program.InternalDelegate - [Nullable(1)] System.Object! Invoke(System.Object? arg) + [NullableContext(1)] System.Object! Invoke(System.Object? arg) [Nullable(2)] System.Object? arg System.IAsyncResult BeginInvoke(System.Object? arg, System.AsyncCallback callback, System.Object @object) [Nullable(2)] System.Object? arg [Nullable(1)] System.Object! EndInvoke(System.IAsyncResult result) Program.PrivateDelegate - [Nullable(1)] System.Object! Invoke(System.Object? arg) + [NullableContext(1)] System.Object! Invoke(System.Object? arg) [Nullable(2)] System.Object? arg System.IAsyncResult BeginInvoke(System.Object? arg, System.AsyncCallback callback, System.Object @object) [Nullable(2)] System.Object? arg @@ -1364,24 +1925,96 @@ public void EmitPrivateMetadata_Events() #nullable enable public class Program { + public event D? PublicEvent { add { } remove { } } + internal event D InternalEvent { add { } remove { } } protected event D ProtectedEvent { add { } remove { } } - internal event D InternalEvent { add { } remove { } } - private event D PrivateEvent { add { } remove { } } + protected internal event D ProtectedInternalEvent { add { } remove { } } + private protected event D? PrivateProtectedEvent { add { } remove { } } + private event D? PrivateEvent { add { } remove { } } }"; var expectedPublicOnly = @" -Program +[NullableContext(2)] [Nullable(0)] Program + Program() + event D? PublicEvent + void PublicEvent.add + D? value + void PublicEvent.remove + D? value + [Nullable(1)] event D! InternalEvent [Nullable({ 1, 2 })] event D! ProtectedEvent + void ProtectedEvent.add + [Nullable({ 1, 2 })] D! value + void ProtectedEvent.remove + [Nullable({ 1, 2 })] D! value + [Nullable({ 1, 2 })] event D! ProtectedInternalEvent + void ProtectedInternalEvent.add + [Nullable({ 1, 2 })] D! value + void ProtectedInternalEvent.remove + [Nullable({ 1, 2 })] D! value + [Nullable({ 2, 1 })] event D? PrivateProtectedEvent "; var expectedPublicAndInternal = @" -Program +[NullableContext(2)] [Nullable(0)] Program + Program() + event D? PublicEvent + void PublicEvent.add + D? value + void PublicEvent.remove + D? value + [Nullable(1)] event D! InternalEvent + [NullableContext(1)] void InternalEvent.add + D! value + [NullableContext(1)] void InternalEvent.remove + D! value [Nullable({ 1, 2 })] event D! ProtectedEvent - [Nullable({ 1, 2 })] event D! InternalEvent + void ProtectedEvent.add + [Nullable({ 1, 2 })] D! value + void ProtectedEvent.remove + [Nullable({ 1, 2 })] D! value + [Nullable({ 1, 2 })] event D! ProtectedInternalEvent + void ProtectedInternalEvent.add + [Nullable({ 1, 2 })] D! value + void ProtectedInternalEvent.remove + [Nullable({ 1, 2 })] D! value + [Nullable({ 2, 1 })] event D? PrivateProtectedEvent + void PrivateProtectedEvent.add + [Nullable({ 2, 1 })] D? value + void PrivateProtectedEvent.remove + [Nullable({ 2, 1 })] D? value "; var expectedAll = @" -Program +[NullableContext(2)] [Nullable(0)] Program + Program() + event D? PublicEvent + void PublicEvent.add + D? value + void PublicEvent.remove + D? value + [Nullable(1)] event D! InternalEvent + [NullableContext(1)] void InternalEvent.add + D! value + [NullableContext(1)] void InternalEvent.remove + D! value [Nullable({ 1, 2 })] event D! ProtectedEvent - [Nullable({ 1, 2 })] event D! InternalEvent - [Nullable({ 1, 2 })] event D! PrivateEvent + void ProtectedEvent.add + [Nullable({ 1, 2 })] D! value + void ProtectedEvent.remove + [Nullable({ 1, 2 })] D! value + [Nullable({ 1, 2 })] event D! ProtectedInternalEvent + void ProtectedInternalEvent.add + [Nullable({ 1, 2 })] D! value + void ProtectedInternalEvent.remove + [Nullable({ 1, 2 })] D! value + [Nullable({ 2, 1 })] event D? PrivateProtectedEvent + void PrivateProtectedEvent.add + [Nullable({ 2, 1 })] D? value + void PrivateProtectedEvent.remove + [Nullable({ 2, 1 })] D? value + event D? PrivateEvent + void PrivateEvent.add + D? value + void PrivateEvent.remove + D? value "; EmitPrivateMetadata(source, expectedPublicOnly, expectedPublicAndInternal, expectedAll); } @@ -1392,24 +2025,38 @@ public void EmitPrivateMetadata_Fields() var source = @"public class Program { - protected object? ProtectedField; + public object PublicField; internal object? InternalField; + protected object ProtectedField; + protected internal object? ProtectedInternalField; + private protected object? PrivateProtectedField; private object? PrivateField; }"; var expectedPublicOnly = @" -Program - [Nullable(2)] System.Object? ProtectedField +[NullableContext(1)] [Nullable(0)] Program + System.Object! PublicField + System.Object! ProtectedField + [Nullable(2)] System.Object? ProtectedInternalField + Program() "; var expectedPublicAndInternal = @" -Program - [Nullable(2)] System.Object? ProtectedField - [Nullable(2)] System.Object? InternalField +[NullableContext(2)] [Nullable(0)] Program + [Nullable(1)] System.Object! PublicField + System.Object? InternalField + [Nullable(1)] System.Object! ProtectedField + System.Object? ProtectedInternalField + System.Object? PrivateProtectedField + Program() "; var expectedAll = @" -Program - [Nullable(2)] System.Object? ProtectedField - [Nullable(2)] System.Object? InternalField - [Nullable(2)] System.Object? PrivateField +[NullableContext(2)] [Nullable(0)] Program + [Nullable(1)] System.Object! PublicField + System.Object? InternalField + [Nullable(1)] System.Object! ProtectedField + System.Object? ProtectedInternalField + System.Object? PrivateProtectedField + System.Object? PrivateField + Program() "; EmitPrivateMetadata(source, expectedPublicOnly, expectedPublicAndInternal, expectedAll); } @@ -1420,30 +2067,52 @@ public void EmitPrivateMetadata_Methods() var source = @"public class Program { - protected object? ProtectedMethod(object arg) => null; - internal object? InternalMethod(object arg) => null; - private object? PrivateMethod(object arg) => null; + public void PublicMethod(object arg) { } + internal object? InternalMethod(object? arg) => null; + protected object ProtectedMethod(object? arg) => null; + protected internal object? ProtectedInternalMethod(object? arg) => null; + private protected void PrivateProtectedMethod(object? arg) { } + private object? PrivateMethod(object? arg) => null; }"; var expectedPublicOnly = @" -Program - [Nullable(2)] System.Object? ProtectedMethod(System.Object! arg) - [Nullable(1)] System.Object! arg +[NullableContext(1)] [Nullable(0)] Program + void PublicMethod(System.Object! arg) + System.Object! arg + System.Object! ProtectedMethod(System.Object? arg) + [Nullable(2)] System.Object? arg + [NullableContext(2)] System.Object? ProtectedInternalMethod(System.Object? arg) + System.Object? arg + Program() "; var expectedPublicAndInternal = @" -Program - [Nullable(2)] System.Object? ProtectedMethod(System.Object! arg) - [Nullable(1)] System.Object! arg - [Nullable(2)] System.Object? InternalMethod(System.Object! arg) - [Nullable(1)] System.Object! arg +[NullableContext(2)] [Nullable(0)] Program + [NullableContext(1)] void PublicMethod(System.Object! arg) + System.Object! arg + System.Object? InternalMethod(System.Object? arg) + System.Object? arg + [NullableContext(1)] System.Object! ProtectedMethod(System.Object? arg) + [Nullable(2)] System.Object? arg + System.Object? ProtectedInternalMethod(System.Object? arg) + System.Object? arg + void PrivateProtectedMethod(System.Object? arg) + System.Object? arg + Program() "; var expectedAll = @" -Program - [Nullable(2)] System.Object? ProtectedMethod(System.Object! arg) - [Nullable(1)] System.Object! arg - [Nullable(2)] System.Object? InternalMethod(System.Object! arg) - [Nullable(1)] System.Object! arg - [Nullable(2)] System.Object? PrivateMethod(System.Object! arg) - [Nullable(1)] System.Object! arg +[NullableContext(2)] [Nullable(0)] Program + [NullableContext(1)] void PublicMethod(System.Object! arg) + System.Object! arg + System.Object? InternalMethod(System.Object? arg) + System.Object? arg + [NullableContext(1)] System.Object! ProtectedMethod(System.Object? arg) + [Nullable(2)] System.Object? arg + System.Object? ProtectedInternalMethod(System.Object? arg) + System.Object? arg + void PrivateProtectedMethod(System.Object? arg) + System.Object? arg + System.Object? PrivateMethod(System.Object? arg) + System.Object? arg + Program() "; EmitPrivateMetadata(source, expectedPublicOnly, expectedPublicAndInternal, expectedAll); } @@ -1454,24 +2123,233 @@ public void EmitPrivateMetadata_Properties() var source = @"public class Program { - protected object? ProtectedProperty => null; + public object PublicProperty => null; internal object? InternalProperty => null; + protected object ProtectedProperty => null; + protected internal object? ProtectedInternalProperty => null; + private protected object? PrivateProtectedProperty => null; private object? PrivateProperty => null; }"; var expectedPublicOnly = @" -Program - [Nullable(2)] System.Object? ProtectedProperty { get; } +[NullableContext(2)] [Nullable(0)] Program + Program() + [Nullable(1)] System.Object! PublicProperty { get; } + [NullableContext(1)] System.Object! PublicProperty.get + [Nullable(1)] System.Object! ProtectedProperty { get; } + [NullableContext(1)] System.Object! ProtectedProperty.get + System.Object? ProtectedInternalProperty { get; } + System.Object? ProtectedInternalProperty.get "; var expectedPublicAndInternal = @" -Program - [Nullable(2)] System.Object? ProtectedProperty { get; } - [Nullable(2)] System.Object? InternalProperty { get; } +[NullableContext(2)] [Nullable(0)] Program + Program() + [Nullable(1)] System.Object! PublicProperty { get; } + [NullableContext(1)] System.Object! PublicProperty.get + System.Object? InternalProperty { get; } + System.Object? InternalProperty.get + [Nullable(1)] System.Object! ProtectedProperty { get; } + [NullableContext(1)] System.Object! ProtectedProperty.get + System.Object? ProtectedInternalProperty { get; } + System.Object? ProtectedInternalProperty.get + System.Object? PrivateProtectedProperty { get; } + System.Object? PrivateProtectedProperty.get "; var expectedAll = @" -Program - [Nullable(2)] System.Object? ProtectedProperty { get; } - [Nullable(2)] System.Object? InternalProperty { get; } - [Nullable(2)] System.Object? PrivateProperty { get; } +[NullableContext(2)] [Nullable(0)] Program + Program() + [Nullable(1)] System.Object! PublicProperty { get; } + [NullableContext(1)] System.Object! PublicProperty.get + System.Object? InternalProperty { get; } + System.Object? InternalProperty.get + [Nullable(1)] System.Object! ProtectedProperty { get; } + [NullableContext(1)] System.Object! ProtectedProperty.get + System.Object? ProtectedInternalProperty { get; } + System.Object? ProtectedInternalProperty.get + System.Object? PrivateProtectedProperty { get; } + System.Object? PrivateProtectedProperty.get + System.Object? PrivateProperty { get; } + System.Object? PrivateProperty.get +"; + EmitPrivateMetadata(source, expectedPublicOnly, expectedPublicAndInternal, expectedAll); + } + + [Fact] + public void EmitPrivateMetadata_Indexers() + { + var source = +@"public class Program +{ + public class PublicType + { + public object? this[object? x, object y] => null; + } + internal class InternalType + { + public object this[object x, object y] { get => null; set { } } + } + protected class ProtectedType + { + public object? this[object x, object? y] { get => null; set { } } + } + protected internal class ProtectedInternalType + { + public object this[object x, object y] { set { } } + } + private protected class PrivateProtectedType + { + public object this[object x, object y] => null; + } + private class PrivateType + { + public object this[object x, object y] => null; + } +}"; + var expectedPublicOnly = @" +[NullableContext(2)] [Nullable(0)] Program + Program() + [Nullable(0)] Program.PublicType + PublicType() + System.Object? this[System.Object? x, System.Object! y] { get; } + System.Object? x + [Nullable(1)] System.Object! y + System.Object? this[System.Object? x, System.Object! y].get + System.Object? x + [Nullable(1)] System.Object! y + [Nullable(0)] Program.ProtectedType + ProtectedType() + System.Object? this[System.Object! x, System.Object? y] { get; set; } + [Nullable(1)] System.Object! x + System.Object? y + System.Object? this[System.Object! x, System.Object? y].get + [Nullable(1)] System.Object! x + System.Object? y + void this[System.Object! x, System.Object? y].set + [Nullable(1)] System.Object! x + System.Object? y + System.Object? value + [NullableContext(1)] [Nullable(0)] Program.ProtectedInternalType + ProtectedInternalType() + System.Object! this[System.Object! x, System.Object! y] { set; } + System.Object! x + System.Object! y + void this[System.Object! x, System.Object! y].set + System.Object! x + System.Object! y + System.Object! value +"; + var expectedPublicAndInternal = @" +[NullableContext(1)] [Nullable(0)] Program + Program() + [NullableContext(2)] [Nullable(0)] Program.PublicType + PublicType() + System.Object? this[System.Object? x, System.Object! y] { get; } + System.Object? x + [Nullable(1)] System.Object! y + System.Object? this[System.Object? x, System.Object! y].get + System.Object? x + [Nullable(1)] System.Object! y + [Nullable(0)] Program.InternalType + InternalType() + System.Object! this[System.Object! x, System.Object! y] { get; set; } + System.Object! x + System.Object! y + System.Object! this[System.Object! x, System.Object! y].get + System.Object! x + System.Object! y + void this[System.Object! x, System.Object! y].set + System.Object! x + System.Object! y + System.Object! value + [NullableContext(2)] [Nullable(0)] Program.ProtectedType + ProtectedType() + System.Object? this[System.Object! x, System.Object? y] { get; set; } + [Nullable(1)] System.Object! x + System.Object? y + System.Object? this[System.Object! x, System.Object? y].get + [Nullable(1)] System.Object! x + System.Object? y + void this[System.Object! x, System.Object? y].set + [Nullable(1)] System.Object! x + System.Object? y + System.Object? value + [Nullable(0)] Program.ProtectedInternalType + ProtectedInternalType() + System.Object! this[System.Object! x, System.Object! y] { set; } + System.Object! x + System.Object! y + void this[System.Object! x, System.Object! y].set + System.Object! x + System.Object! y + System.Object! value + [Nullable(0)] Program.PrivateProtectedType + PrivateProtectedType() + System.Object! this[System.Object! x, System.Object! y] { get; } + System.Object! x + System.Object! y + System.Object! this[System.Object! x, System.Object! y].get + System.Object! x + System.Object! y +"; + var expectedAll = @" +[NullableContext(1)] [Nullable(0)] Program + Program() + [NullableContext(2)] [Nullable(0)] Program.PublicType + PublicType() + System.Object? this[System.Object? x, System.Object! y] { get; } + System.Object? x + [Nullable(1)] System.Object! y + System.Object? this[System.Object? x, System.Object! y].get + System.Object? x + [Nullable(1)] System.Object! y + [Nullable(0)] Program.InternalType + InternalType() + System.Object! this[System.Object! x, System.Object! y] { get; set; } + System.Object! x + System.Object! y + System.Object! this[System.Object! x, System.Object! y].get + System.Object! x + System.Object! y + void this[System.Object! x, System.Object! y].set + System.Object! x + System.Object! y + System.Object! value + [NullableContext(2)] [Nullable(0)] Program.ProtectedType + ProtectedType() + System.Object? this[System.Object! x, System.Object? y] { get; set; } + [Nullable(1)] System.Object! x + System.Object? y + System.Object? this[System.Object! x, System.Object? y].get + [Nullable(1)] System.Object! x + System.Object? y + void this[System.Object! x, System.Object? y].set + [Nullable(1)] System.Object! x + System.Object? y + System.Object? value + [Nullable(0)] Program.ProtectedInternalType + ProtectedInternalType() + System.Object! this[System.Object! x, System.Object! y] { set; } + System.Object! x + System.Object! y + void this[System.Object! x, System.Object! y].set + System.Object! x + System.Object! y + System.Object! value + [Nullable(0)] Program.PrivateProtectedType + PrivateProtectedType() + System.Object! this[System.Object! x, System.Object! y] { get; } + System.Object! x + System.Object! y + System.Object! this[System.Object! x, System.Object! y].get + System.Object! x + System.Object! y + [Nullable(0)] Program.PrivateType + PrivateType() + System.Object! this[System.Object! x, System.Object! y] { get; } + System.Object! x + System.Object! y + System.Object! this[System.Object! x, System.Object! y].get + System.Object! x + System.Object! y "; EmitPrivateMetadata(source, expectedPublicOnly, expectedPublicAndInternal, expectedAll); } @@ -1501,30 +2379,32 @@ private static void PrivateMethod() }"; var expectedPublicOnly = @" Program - void ProtectedMethod() where T : notnull where U : class! - [Nullable(1)] T - [Nullable(1)] U + [NullableContext(1)] void ProtectedMethod() where T : notnull where U : class! + T + U "; var expectedPublicAndInternal = @" -Program +[NullableContext(1)] [Nullable(0)] Program void ProtectedMethod() where T : notnull where U : class! - [Nullable(1)] T - [Nullable(1)] U + T + U void InternalMethod() where T : notnull where U : class! - [Nullable(1)] T - [Nullable(1)] U + T + U + Program() "; var expectedAll = @" -Program +[NullableContext(1)] [Nullable(0)] Program void ProtectedMethod() where T : notnull where U : class! - [Nullable(1)] T - [Nullable(1)] U + T + U void InternalMethod() where T : notnull where U : class! - [Nullable(1)] T - [Nullable(1)] U + T + U void PrivateMethod() where T : notnull where U : class! - [Nullable(1)] T - [Nullable(1)] U + T + U + Program() "; EmitPrivateMetadata(source, expectedPublicOnly, expectedPublicAndInternal, expectedAll); } @@ -1575,12 +2455,12 @@ private static void PrivateMethod(string x) var expectedPublicAndInternal = @""; var expectedAll = @" Public - void PrivateMethod(System.String! x) - [Nullable(1)] System.String! x + [NullableContext(1)] void PrivateMethod(System.String! x) + System.String! x Public.<>c [Nullable({ 0, 2 })] System.Action <>9__0_0 - void b__0_0(System.String! y) - [Nullable(1)] System.String! y + [NullableContext(1)] void b__0_0(System.String! y) + System.String! y "; EmitPrivateMetadata(source, expectedPublicOnly, expectedPublicAndInternal, expectedAll); } @@ -1627,6 +2507,8 @@ public class Program [Nullable({ 1, 2 })] System.Collections.Generic.IEnumerable! F() Program.d__0 [Nullable(2)] System.Object? <>2__current + System.Object System.Collections.Generic.IEnumerator.Current { get; } + [Nullable(2)] System.Object? System.Collections.Generic.IEnumerator.Current.get "; EmitPrivateMetadata(source, expectedPublicOnly, expectedPublicAndInternal, expectedAll); } @@ -1639,10 +2521,10 @@ private void EmitPrivateMetadata(string source, string expectedPublicOnly, strin var options = WithNonNullTypesTrue().WithMetadataImportOptions(MetadataImportOptions.All); var parseOptions = TestOptions.Regular8; - VerifyNullableAttributes(CreateCompilation(source, options: options, parseOptions: parseOptions), expectedAll); - VerifyNullableAttributes(CreateCompilation(source, options: options, parseOptions: parseOptions.WithFeature("nullablePublicOnly")), expectedPublicOnly); - VerifyNullableAttributes(CreateCompilation(new[] { source, sourceIVTs }, options: options, parseOptions: parseOptions), expectedAll); - VerifyNullableAttributes(CreateCompilation(new[] { source, sourceIVTs }, options: options, parseOptions: parseOptions.WithFeature("nullablePublicOnly")), expectedPublicAndInternal); + AssertNullableAttributes(CreateCompilation(source, options: options, parseOptions: parseOptions), expectedAll); + AssertNullableAttributes(CreateCompilation(source, options: options, parseOptions: parseOptions.WithFeature("nullablePublicOnly")), expectedPublicOnly); + AssertNullableAttributes(CreateCompilation(new[] { source, sourceIVTs }, options: options, parseOptions: parseOptions), expectedAll); + AssertNullableAttributes(CreateCompilation(new[] { source, sourceIVTs }, options: options, parseOptions: parseOptions.WithFeature("nullablePublicOnly")), expectedPublicAndInternal); } /// @@ -1666,6 +2548,7 @@ public class A private static object? M(object arg) => null; private object? P => null; private object? this[object x, object? y] => null; + private event D E; public static void M() { object? f(object arg) => arg; @@ -1707,48 +2590,66 @@ internal class B : I // (9,36): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.NullableAttribute..ctor' // private object? this[object x, object? y] => null; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "object? y").WithArguments("System.Runtime.CompilerServices.NullableAttribute", ".ctor").WithLocation(9, 36), - // (12,9): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.NullableAttribute..ctor' - // object? f(object arg) => arg; - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "object?").WithArguments("System.Runtime.CompilerServices.NullableAttribute", ".ctor").WithLocation(12, 9), - // (12,19): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.NullableAttribute..ctor' - // object? f(object arg) => arg; - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "object arg").WithArguments("System.Runtime.CompilerServices.NullableAttribute", ".ctor").WithLocation(12, 19), + // (10,30): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.NullableAttribute..ctor' + // private event D E; + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "E").WithArguments("System.Runtime.CompilerServices.NullableAttribute", ".ctor").WithLocation(10, 30), // (13,9): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.NullableAttribute..ctor' - // object? l(object arg) { return arg; } + // object? f(object arg) => arg; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "object?").WithArguments("System.Runtime.CompilerServices.NullableAttribute", ".ctor").WithLocation(13, 9), // (13,19): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.NullableAttribute..ctor' - // object? l(object arg) { return arg; } + // object? f(object arg) => arg; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "object arg").WithArguments("System.Runtime.CompilerServices.NullableAttribute", ".ctor").WithLocation(13, 19), - // (14,26): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.NullableAttribute..ctor' + // (14,9): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.NullableAttribute..ctor' + // object? l(object arg) { return arg; } + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "object?").WithArguments("System.Runtime.CompilerServices.NullableAttribute", ".ctor").WithLocation(14, 9), + // (14,19): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.NullableAttribute..ctor' + // object? l(object arg) { return arg; } + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "object arg").WithArguments("System.Runtime.CompilerServices.NullableAttribute", ".ctor").WithLocation(14, 19), + // (15,26): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.NullableAttribute..ctor' // D d = () => new object(); - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "=>").WithArguments("System.Runtime.CompilerServices.NullableAttribute", ".ctor").WithLocation(14, 26), - // (17,19): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.NullableAttribute..ctor' + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "=>").WithArguments("System.Runtime.CompilerServices.NullableAttribute", ".ctor").WithLocation(15, 26), + // (18,19): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.NullableAttribute..ctor' // internal delegate T D(); - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "T").WithArguments("System.Runtime.CompilerServices.NullableAttribute", ".ctor").WithLocation(17, 19), - // (17,23): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.NullableAttribute..ctor' + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "T").WithArguments("System.Runtime.CompilerServices.NullableAttribute", ".ctor").WithLocation(18, 19), + // (18,23): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.NullableAttribute..ctor' // internal delegate T D(); - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "T").WithArguments("System.Runtime.CompilerServices.NullableAttribute", ".ctor").WithLocation(17, 23), - // (18,22): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.NullableAttribute..ctor' + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "T").WithArguments("System.Runtime.CompilerServices.NullableAttribute", ".ctor").WithLocation(18, 23), + // (19,22): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.NullableAttribute..ctor' // internal interface I { } - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "T").WithArguments("System.Runtime.CompilerServices.NullableAttribute", ".ctor").WithLocation(18, 22), - // (19,16): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.NullableAttribute..ctor' + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "T").WithArguments("System.Runtime.CompilerServices.NullableAttribute", ".ctor").WithLocation(19, 22), + // (20,16): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.NullableAttribute..ctor' // internal class B : I - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "B").WithArguments("System.Runtime.CompilerServices.NullableAttribute", ".ctor").WithLocation(19, 16), - // (21,19): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.NullableAttribute..ctor' + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "B").WithArguments("System.Runtime.CompilerServices.NullableAttribute", ".ctor").WithLocation(20, 16), + // (22,19): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.NullableAttribute..ctor' // public static object operator!(B b) => b; - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "object").WithArguments("System.Runtime.CompilerServices.NullableAttribute", ".ctor").WithLocation(21, 19), - // (21,36): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.NullableAttribute..ctor' + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "object").WithArguments("System.Runtime.CompilerServices.NullableAttribute", ".ctor").WithLocation(22, 19), + // (22,36): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.NullableAttribute..ctor' // public static object operator!(B b) => b; - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "B b").WithArguments("System.Runtime.CompilerServices.NullableAttribute", ".ctor").WithLocation(21, 36), - // (22,29): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.NullableAttribute..ctor' + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "B b").WithArguments("System.Runtime.CompilerServices.NullableAttribute", ".ctor").WithLocation(22, 36), + // (23,29): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.NullableAttribute..ctor' // public event D E; - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "E").WithArguments("System.Runtime.CompilerServices.NullableAttribute", ".ctor").WithLocation(22, 29), - // (23,31): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.NullableAttribute..ctor' + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "E").WithArguments("System.Runtime.CompilerServices.NullableAttribute", ".ctor").WithLocation(23, 29), + // (24,31): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.NullableAttribute..ctor' // private (object, object?) F; - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "F").WithArguments("System.Runtime.CompilerServices.NullableAttribute", ".ctor").WithLocation(23, 31)); + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "F").WithArguments("System.Runtime.CompilerServices.NullableAttribute", ".ctor").WithLocation(24, 31)); comp = CreateCompilation(new[] { sourceAttribute, source }, options: options, parseOptions: parseOptions.WithFeature("nullablePublicOnly")); - comp.VerifyEmitDiagnostics(); + comp.VerifyEmitDiagnostics( + // (8,13): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.NullableAttribute..ctor' + // private object? P => null; + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "object?").WithArguments("System.Runtime.CompilerServices.NullableAttribute", ".ctor").WithLocation(8, 13), + // (9,13): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.NullableAttribute..ctor' + // private object? this[object x, object? y] => null; + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "object?").WithArguments("System.Runtime.CompilerServices.NullableAttribute", ".ctor").WithLocation(9, 13), + // (9,26): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.NullableAttribute..ctor' + // private object? this[object x, object? y] => null; + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "object x").WithArguments("System.Runtime.CompilerServices.NullableAttribute", ".ctor").WithLocation(9, 26), + // (9,36): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.NullableAttribute..ctor' + // private object? this[object x, object? y] => null; + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "object? y").WithArguments("System.Runtime.CompilerServices.NullableAttribute", ".ctor").WithLocation(9, 36), + // (10,30): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.NullableAttribute..ctor' + // private event D E; + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "E").WithArguments("System.Runtime.CompilerServices.NullableAttribute", ".ctor").WithLocation(10, 30)); } [Fact] @@ -1760,8 +2661,10 @@ public void EmitPrivateMetadata_MissingAttributeConstructor_NullableDisabled() public sealed class NullableAttribute : Attribute { } }"; var source = -@"public class Program +@"#pragma warning disable 414 +public class Program { + private object? F = null; private object? P => null; }"; var options = TestOptions.ReleaseDll; @@ -1769,18 +2672,30 @@ public sealed class NullableAttribute : Attribute { } var comp = CreateCompilation(new[] { sourceAttribute, source }, options: options, parseOptions: parseOptions); comp.VerifyEmitDiagnostics( - // (3,13): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.NullableAttribute..ctor' + // (4,19): warning CS8632: The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. + // private object? F = null; + Diagnostic(ErrorCode.WRN_MissingNonNullTypesContextForAnnotation, "?").WithLocation(4, 19), + // (4,21): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.NullableAttribute..ctor' + // private object? F = null; + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "F").WithArguments("System.Runtime.CompilerServices.NullableAttribute", ".ctor").WithLocation(4, 21), + // (5,13): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.NullableAttribute..ctor' // private object? P => null; - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "object?").WithArguments("System.Runtime.CompilerServices.NullableAttribute", ".ctor").WithLocation(3, 13), - // (3,19): warning CS8632: The annotation for nullable reference types should only be used in code within a '#nullable' context. + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "object?").WithArguments("System.Runtime.CompilerServices.NullableAttribute", ".ctor").WithLocation(5, 13), + // (5,19): warning CS8632: The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. // private object? P => null; - Diagnostic(ErrorCode.WRN_MissingNonNullTypesContextForAnnotation, "?").WithLocation(3, 19)); + Diagnostic(ErrorCode.WRN_MissingNonNullTypesContextForAnnotation, "?").WithLocation(5, 19)); comp = CreateCompilation(new[] { sourceAttribute, source }, options: options, parseOptions: parseOptions.WithFeature("nullablePublicOnly")); comp.VerifyEmitDiagnostics( - // (3,19): warning CS8632: The annotation for nullable reference types should only be used in code within a '#nullable' context. + // (4,19): warning CS8632: The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. + // private object? F = null; + Diagnostic(ErrorCode.WRN_MissingNonNullTypesContextForAnnotation, "?").WithLocation(4, 19), + // (5,13): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.NullableAttribute..ctor' + // private object? P => null; + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "object?").WithArguments("System.Runtime.CompilerServices.NullableAttribute", ".ctor").WithLocation(5, 13), + // (5,19): warning CS8632: The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. // private object? P => null; - Diagnostic(ErrorCode.WRN_MissingNonNullTypesContextForAnnotation, "?").WithLocation(3, 19)); + Diagnostic(ErrorCode.WRN_MissingNonNullTypesContextForAnnotation, "?").WithLocation(5, 19)); } [Fact] @@ -1970,6 +2885,7 @@ public void EmitAttribute_ValueTypes_03() @"#nullable enable class Program { + System.ValueTuple F0; (int, int) F1; (int?, int?)? F2; #nullable disable @@ -1993,6 +2909,7 @@ class Program static void validate(ModuleSymbol module) { var globalNamespace = module.GlobalNamespace; + VerifyBytes(globalNamespace.GetMember("Program.F0").TypeWithAnnotations, new byte[] { 0 }, new byte[] { }, "System.ValueTuple"); VerifyBytes(globalNamespace.GetMember("Program.F1").TypeWithAnnotations, new byte[] { 0, 0, 0 }, new byte[] { 0 }, "(int, int)"); VerifyBytes(globalNamespace.GetMember("Program.F2").TypeWithAnnotations, new byte[] { 0, 0, 0, 0, 0, 0 }, new byte[] { 0 }, "(int?, int?)?"); VerifyBytes(globalNamespace.GetMember("Program.F3").TypeWithAnnotations, new byte[] { 0, 0, 0 }, new byte[] { 0, 0 }, "(int, object)"); @@ -2252,7 +3169,7 @@ static void validate(ModuleSymbol module) VerifyBytes(globalNamespace.GetMember("Program.F31").TypeWithAnnotations, new byte[] { 0 }, new byte[] { 0 }, "V"); VerifyBytes(globalNamespace.GetMember("Program.F32").TypeWithAnnotations, new byte[] { 0, 0 }, new byte[] { 0 }, "V?"); VerifyBytes(globalNamespace.GetMember("Program.F33").TypeWithAnnotations, new byte[] { 1, 0 }, new byte[] { 1, 0 }, "V[]!"); - VerifyBytes(globalNamespace.GetMember("Program.F34").TypeWithAnnotations, new byte[] { 1, 0 }, new byte[] { 1 , 0}, "C!"); + VerifyBytes(globalNamespace.GetMember("Program.F34").TypeWithAnnotations, new byte[] { 1, 0 }, new byte[] { 1, 0 }, "C!"); VerifyBytes(globalNamespace.GetMember("Program.F35").TypeWithAnnotations, new byte[] { 0, 0 }, new byte[] { 0, 0 }, "S"); } } @@ -2266,21 +3183,23 @@ public struct S2 { }"; var comp = CreateCompilation(source0); var ref0 = comp.EmitToImageReference(); - var source = + var source1 = @"#nullable enable -class C2 { } -class Program +public class C2 { } +public class Program { - C2 F1; - C2? F2; - S2 F3; - S2 F4; - (S0, object) F5; - (object?, S0) F6; + public C2 F1; + public C2? F2; + public S2 F3; + public S2 F4; + public (S0, object) F5; + public (object?, S0) F6; }"; // With reference assembly. - comp = CreateCompilation(source, references: new[] { ref0 }); + comp = CreateCompilation(source1, references: new[] { ref0 }); + var ref1 = comp.EmitToImageReference(); + var globalNamespace = comp.GlobalNamespace; VerifyBytes(globalNamespace.GetMember("Program.F1").TypeWithAnnotations, new byte[] { 1, 0, 2 }, new byte[] { 1, 2 }, "C2!"); VerifyBytes(globalNamespace.GetMember("Program.F2").TypeWithAnnotations, new byte[] { 2, 1, 0 }, new byte[] { 2, 1 }, "C2?"); @@ -2290,7 +3209,7 @@ class Program VerifyBytes(globalNamespace.GetMember("Program.F6").TypeWithAnnotations, new byte[] { 0, 2, 0 }, new byte[] { 0, 2 }, "(object?, S0)"); // Without reference assembly. - comp = CreateCompilation(source); + comp = CreateCompilation(source1); globalNamespace = comp.GlobalNamespace; VerifyBytes(globalNamespace.GetMember("Program.F1").TypeWithAnnotations, new byte[] { 1, 0, 2 }, new byte[] { 1, 1, 2 }, "C2!"); VerifyBytes(globalNamespace.GetMember("Program.F2").TypeWithAnnotations, new byte[] { 2, 1, 0 }, new byte[] { 2, 1, 1 }, "C2?"); @@ -2298,6 +3217,19 @@ class Program VerifyBytes(globalNamespace.GetMember("Program.F4").TypeWithAnnotations, new byte[] { 0, 2, 0 }, new byte[] { 1, 2, 1 }, "S2!"); VerifyBytes(globalNamespace.GetMember("Program.F5").TypeWithAnnotations, new byte[] { 0, 0, 1 }, new byte[] { 0, 1, 1 }, "(S0!, object!)"); VerifyBytes(globalNamespace.GetMember("Program.F6").TypeWithAnnotations, new byte[] { 0, 2, 0 }, new byte[] { 0, 2, 1 }, "(object?, S0!)"); + + var source2 = +@""; + + // Without reference assembly. + comp = CreateCompilation(source2, references: new[] { ref1 }); + globalNamespace = comp.GlobalNamespace; + VerifyBytes(globalNamespace.GetMember("Program.F1").TypeWithAnnotations, new byte[] { 1, 0, 2 }, new byte[] { 0, 0, 0 }, "C2"); + VerifyBytes(globalNamespace.GetMember("Program.F2").TypeWithAnnotations, new byte[] { 2, 1, 0 }, new byte[] { 0, 0, 0 }, "C2"); + VerifyBytes(globalNamespace.GetMember("Program.F3").TypeWithAnnotations, new byte[] { 0, 0, 1 }, new byte[] { 0, 0, 0 }, "S2"); + VerifyBytes(globalNamespace.GetMember("Program.F4").TypeWithAnnotations, new byte[] { 0, 2, 0 }, new byte[] { 0, 0, 0 }, "S2"); + VerifyBytes(globalNamespace.GetMember("Program.F5").TypeWithAnnotations, new byte[] { 0, 0, 1 }, new byte[] { 0, 0, 0 }, "(S0, object)"); + VerifyBytes(globalNamespace.GetMember("Program.F6").TypeWithAnnotations, new byte[] { 0, 2, 0 }, new byte[] { 0, 0, 0 }, "(object, S0)"); } private static readonly SymbolDisplayFormat _displayFormat = SymbolDisplayFormat.TestFormat. @@ -2402,6 +3334,9 @@ class C : I<(object X, object? Y)> }"; var comp = CreateCompilation(new[] { source }, parseOptions: TestOptions.Regular8, options: WithNonNullTypesTrue(TestOptions.ReleaseModule)); comp.VerifyEmitDiagnostics( + // (1,11): error CS0518: Predefined type 'System.Runtime.CompilerServices.NullableContextAttribute' is not defined or imported + // interface I + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "I").WithArguments("System.Runtime.CompilerServices.NullableContextAttribute").WithLocation(1, 11), // (1,13): error CS0518: Predefined type 'System.Runtime.CompilerServices.NullableAttribute' is not defined or imported // interface I Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "T").WithArguments("System.Runtime.CompilerServices.NullableAttribute").WithLocation(1, 13), @@ -2422,7 +3357,10 @@ public void ModuleMissingAttribute_MethodReturnType() comp.VerifyEmitDiagnostics( // (3,5): error CS0518: Predefined type 'System.Runtime.CompilerServices.NullableAttribute' is not defined or imported // object? F() => null; - Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "object?").WithArguments("System.Runtime.CompilerServices.NullableAttribute").WithLocation(3, 5)); + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "object?").WithArguments("System.Runtime.CompilerServices.NullableAttribute").WithLocation(3, 5), + // (3,13): error CS0518: Predefined type 'System.Runtime.CompilerServices.NullableContextAttribute' is not defined or imported + // object? F() => null; + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "F").WithArguments("System.Runtime.CompilerServices.NullableContextAttribute").WithLocation(3, 13)); } [Fact] @@ -2465,6 +3403,9 @@ public void ModuleMissingAttribute_PropertyType() }"; var comp = CreateCompilation(new[] { source }, parseOptions: TestOptions.Regular8, options: WithNonNullTypesTrue(TestOptions.ReleaseModule)); comp.VerifyEmitDiagnostics( + // (1,7): error CS0518: Predefined type 'System.Runtime.CompilerServices.NullableContextAttribute' is not defined or imported + // class C + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "C").WithArguments("System.Runtime.CompilerServices.NullableContextAttribute").WithLocation(1, 7), // (3,5): error CS0518: Predefined type 'System.Runtime.CompilerServices.NullableAttribute' is not defined or imported // object? P => null; Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "object?").WithArguments("System.Runtime.CompilerServices.NullableAttribute").WithLocation(3, 5)); @@ -2480,6 +3421,9 @@ public void ModuleMissingAttribute_PropertyParameters() }"; var comp = CreateCompilation(new[] { source }, parseOptions: TestOptions.Regular8, options: WithNonNullTypesTrue(TestOptions.ReleaseModule)); comp.VerifyEmitDiagnostics( + // (1,7): error CS0518: Predefined type 'System.Runtime.CompilerServices.NullableContextAttribute' is not defined or imported + // class C + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "C").WithArguments("System.Runtime.CompilerServices.NullableContextAttribute").WithLocation(1, 7), // (3,5): error CS0518: Predefined type 'System.Runtime.CompilerServices.NullableAttribute' is not defined or imported // object this[object x, object? y] => throw new System.NotImplementedException(); Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "object").WithArguments("System.Runtime.CompilerServices.NullableAttribute").WithLocation(3, 5), @@ -2543,7 +3487,10 @@ public void ModuleMissingAttribute_DelegateReturnType() comp.VerifyEmitDiagnostics( // (1,10): error CS0518: Predefined type 'System.Runtime.CompilerServices.NullableAttribute' is not defined or imported // delegate object? D(); - Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "object?").WithArguments("System.Runtime.CompilerServices.NullableAttribute").WithLocation(1, 10)); + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "object?").WithArguments("System.Runtime.CompilerServices.NullableAttribute").WithLocation(1, 10), + // (1,18): error CS0518: Predefined type 'System.Runtime.CompilerServices.NullableContextAttribute' is not defined or imported + // delegate object? D(); + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "D").WithArguments("System.Runtime.CompilerServices.NullableContextAttribute").WithLocation(1, 18)); } [Fact] @@ -2601,12 +3548,18 @@ static void G() }"; var comp = CreateCompilation(new[] { source }, parseOptions: TestOptions.Regular8, options: WithNonNullTypesTrue(TestOptions.ReleaseModule)); comp.VerifyEmitDiagnostics( + // (1,15): error CS0518: Predefined type 'System.Runtime.CompilerServices.NullableContextAttribute' is not defined or imported + // delegate void D(T t); + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "D").WithArguments("System.Runtime.CompilerServices.NullableContextAttribute").WithLocation(1, 15), // (1,17): error CS0518: Predefined type 'System.Runtime.CompilerServices.NullableAttribute' is not defined or imported // delegate void D(T t); Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "T").WithArguments("System.Runtime.CompilerServices.NullableAttribute").WithLocation(1, 17), // (1,20): error CS0518: Predefined type 'System.Runtime.CompilerServices.NullableAttribute' is not defined or imported // delegate void D(T t); Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "T t").WithArguments("System.Runtime.CompilerServices.NullableAttribute").WithLocation(1, 20), + // (4,17): error CS0518: Predefined type 'System.Runtime.CompilerServices.NullableContextAttribute' is not defined or imported + // static void F(D d) + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "F").WithArguments("System.Runtime.CompilerServices.NullableContextAttribute").WithLocation(4, 17), // (4,19): error CS0518: Predefined type 'System.Runtime.CompilerServices.NullableAttribute' is not defined or imported // static void F(D d) Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "T").WithArguments("System.Runtime.CompilerServices.NullableAttribute").WithLocation(4, 19), @@ -2681,7 +3634,7 @@ public void Tuples() AssertAttributes(reader, customAttributes, "MemberReference:Void System.Runtime.CompilerServices.TupleElementNamesAttribute..ctor(String[])", "MethodDefinition:Void System.Runtime.CompilerServices.NullableAttribute..ctor(Byte[])"); - var customAttribute = reader.GetCustomAttribute(customAttributes.ElementAt(1)); + var customAttribute = GetAttributeByConstructorName(reader, customAttributes, "MethodDefinition:Void System.Runtime.CompilerServices.NullableAttribute..ctor(Byte[])"); AssertEx.Equal(ImmutableArray.Create(0, 0, 2, 0, 2, 0, 0, 0, 0, 2, 0, 2), reader.ReadByteArray(customAttribute.Value)); // Long tuple @@ -2690,7 +3643,7 @@ public void Tuples() AssertAttributes(reader, customAttributes, "MemberReference:Void System.Runtime.CompilerServices.TupleElementNamesAttribute..ctor(String[])", "MethodDefinition:Void System.Runtime.CompilerServices.NullableAttribute..ctor(Byte[])"); - customAttribute = reader.GetCustomAttribute(customAttributes.ElementAt(1)); + customAttribute = GetAttributeByConstructorName(reader, customAttributes, "MethodDefinition:Void System.Runtime.CompilerServices.NullableAttribute..ctor(Byte[])"); AssertEx.Equal(ImmutableArray.Create(0, 2, 0, 2, 0, 2, 0, 2, 0, 0, 2), reader.ReadByteArray(customAttribute.Value)); }); @@ -2812,7 +3765,7 @@ void checkAttributes(CustomAttributeHandleCollection customAttributes, byte? add "MemberReference:Void System.Runtime.CompilerServices.DynamicAttribute..ctor(Boolean[])", "MemberReference:Void System.Runtime.CompilerServices.TupleElementNamesAttribute..ctor(String[])", "MethodDefinition:Void System.Runtime.CompilerServices.NullableAttribute..ctor(Byte[])"); - checkAttribute(reader.GetCustomAttribute(customAttributes.ElementAt(2)), addOne); + checkNullableAttribute(customAttributes, addOne); } void checkAttributesNoDynamic(CustomAttributeHandleCollection customAttributes, byte? addOne = null) @@ -2820,11 +3773,12 @@ void checkAttributesNoDynamic(CustomAttributeHandleCollection customAttributes, AssertAttributes(reader, customAttributes, "MemberReference:Void System.Runtime.CompilerServices.TupleElementNamesAttribute..ctor(String[])", "MethodDefinition:Void System.Runtime.CompilerServices.NullableAttribute..ctor(Byte[])"); - checkAttribute(reader.GetCustomAttribute(customAttributes.ElementAt(1)), addOne); + checkNullableAttribute(customAttributes, addOne); } - void checkAttribute(CustomAttribute customAttribute, byte? addOne) + void checkNullableAttribute(CustomAttributeHandleCollection customAttributes, byte? addOne) { + var customAttribute = GetAttributeByConstructorName(reader, customAttributes, "MethodDefinition:Void System.Runtime.CompilerServices.NullableAttribute..ctor(Byte[])"); var expectedBits = ImmutableArray.Create(0, 2, 0, 1, 2, 1, 2, 1, 2, 1, 0, 2); if (addOne.HasValue) { @@ -2889,14 +3843,14 @@ public void NullableFlags_Field_Exists() var source = @"public class C { - public void F(object? c) { } + public void F(object? x, object y, object z) { } }"; var comp = CreateCompilation(source, parseOptions: TestOptions.Regular8); CompileAndVerify(comp, symbolValidator: module => { var type = module.ContainingAssembly.GetTypeByMetadataName("C"); var method = (MethodSymbol)type.GetMembers("F").Single(); - var attributes = method.Parameters.Single().GetAttributes(); + var attributes = method.Parameters[0].GetAttributes(); AssertNullableAttribute(attributes); var nullable = GetNullableAttribute(attributes); @@ -2917,7 +3871,7 @@ public void NullableFlags_Field_Contains_ConstructorArguments_SingleByteConstruc using System.Linq; public class C { - public void F(object? c) { } + public void F(object? x, object y, object z) { } public static void Main() { @@ -2931,15 +3885,14 @@ public static void Main() var comp = CreateCompilation(source, parseOptions: TestOptions.Regular8, options: TestOptions.DebugExe); CompileAndVerify(comp, expectedOutput: "{ 2 }", symbolValidator: module => { - var type = module.ContainingAssembly.GetTypeByMetadataName("C"); - var method = (MethodSymbol)type.GetMembers("F").Single(); - var attributes = method.Parameters.Single().GetAttributes(); - AssertNullableAttribute(attributes); - - var nullable = GetNullableAttribute(attributes); - var args = nullable.ConstructorArguments; - Assert.Single(args); - Assert.Equal((byte)2, args.First().Value); + var expected = +@"C + [NullableContext(1)] void F(System.Object? x, System.Object! y, System.Object! z) + [Nullable(2)] System.Object? x + System.Object! y + System.Object! z +"; + AssertNullableAttributes(module, expected); }); } @@ -2967,20 +3920,12 @@ public static void Main() var comp = CreateCompilation(source, parseOptions: TestOptions.Regular8, options: TestOptions.DebugExe); CompileAndVerify(comp, expectedOutput: "{ 1,2,2,1,2 }", symbolValidator: module => { - var type = module.ContainingAssembly.GetTypeByMetadataName("C"); - var method = (MethodSymbol)type.GetMembers("F").Single(); - var attributes = method.Parameters.Single().GetAttributes(); - AssertNullableAttribute(attributes); - - var nullable = GetNullableAttribute(attributes); - var args = nullable.ConstructorArguments; - Assert.Single(args); - var byteargs = args.First().Values; - Assert.Equal((byte)1, byteargs[0].Value); - Assert.Equal((byte)2, byteargs[1].Value); - Assert.Equal((byte)2, byteargs[2].Value); - Assert.Equal((byte)1, byteargs[3].Value); - Assert.Equal((byte)2, byteargs[4].Value); + var expected = +@"C + void F(System.Action?>! c) + [Nullable({ 1, 2, 2, 1, 2 })] System.Action?>! c +"; + AssertNullableAttributes(module, expected); }); } @@ -3000,15 +3945,15 @@ private static void AssertAttributes(ImmutableArray attribu AssertEx.SetEqual(actualNames, expectedNames); } - private static void AssertNoNullableAttribute(CSharpCompilation comp) + private static void AssertNoNullableAttributes(CSharpCompilation comp) { - string attributeName = "NullableAttribute"; var image = comp.EmitToArray(); using (var reader = new PEReader(image)) { var metadataReader = reader.GetMetadataReader(); var attributes = metadataReader.GetCustomAttributeRows().Select(metadataReader.GetCustomAttributeName).ToArray(); - Assert.False(attributes.Contains(attributeName)); + Assert.False(attributes.Contains("NullableContextAttribute")); + Assert.False(attributes.Contains("NullableAttribute")); } } @@ -3022,19 +3967,31 @@ private static TypeDefinition GetTypeDefinitionByName(MetadataReader reader, str return reader.GetTypeDefinition(reader.TypeDefinitions.Single(h => reader.StringComparer.Equals(reader.GetTypeDefinition(h).Name, name))); } + private static string GetAttributeConstructorName(MetadataReader reader, CustomAttributeHandle handle) + { + return reader.Dump(reader.GetCustomAttribute(handle).Constructor); + } + + private static CustomAttribute GetAttributeByConstructorName(MetadataReader reader, CustomAttributeHandleCollection handles, string name) + { + return reader.GetCustomAttribute(handles.FirstOrDefault(h => GetAttributeConstructorName(reader, h) == name)); + } + private static void AssertAttributes(MetadataReader reader, CustomAttributeHandleCollection handles, params string[] expectedNames) { - var actualNames = handles.Select(h => reader.Dump(reader.GetCustomAttribute(h).Constructor)).ToArray(); + var actualNames = handles.Select(h => GetAttributeConstructorName(reader, h)).ToArray(); AssertEx.SetEqual(actualNames, expectedNames); } - private void VerifyNullableAttributes(CSharpCompilation comp, string expected) + private void AssertNullableAttributes(CSharpCompilation comp, string expected) { - CompileAndVerify(comp, symbolValidator: module => - { - var actual = NullableAttributesVisitor.GetString(module); - AssertEx.AssertEqualToleratingWhitespaceDifferences(expected, actual); - }); + CompileAndVerify(comp, symbolValidator: module => AssertNullableAttributes(module, expected)); + } + + private static void AssertNullableAttributes(ModuleSymbol module, string expected) + { + var actual = NullableAttributesVisitor.GetString((PEModuleSymbol)module); + AssertEx.AssertEqualToleratingWhitespaceDifferences(expected, actual); } } } diff --git a/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_NullableContext.cs b/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_NullableContext.cs new file mode 100644 index 0000000000000..e5211234d45d0 --- /dev/null +++ b/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_NullableContext.cs @@ -0,0 +1,322 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.CodeAnalysis.CSharp.Symbols; +using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE; +using Microsoft.CodeAnalysis.CSharp.Test.Utilities; +using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Test.Utilities; +using Roslyn.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.CSharp.UnitTests +{ + public class AttributeTests_NullableContext : CSharpTestBase + { + [Fact] + public void EmptyProject() + { + var source = @""; + var comp = CreateCompilation(source); + var expected = +@""; + AssertNullableAttributes(comp, expected); + } + + [Fact] + public void ExplicitAttribute_FromSource() + { + var source = +@"#nullable enable +public class Program +{ + public object F(object arg) => arg; +}"; + var comp = CreateCompilation(new[] { NullableContextAttributeDefinition, source }); + var expected = +@"Program + [NullableContext(1)] System.Object! F(System.Object! arg) + System.Object! arg +"; + AssertNullableAttributes(comp, expected); + } + + [Fact] + public void ExplicitAttribute_FromMetadata() + { + var comp = CreateCompilation(NullableContextAttributeDefinition); + comp.VerifyDiagnostics(); + var ref0 = comp.EmitToImageReference(); + + var source = +@"#nullable enable +public class Program +{ + public object F(object arg) => arg; +}"; + comp = CreateCompilation(source, references: new[] { ref0 }); + var expected = +@"Program + [NullableContext(1)] System.Object! F(System.Object! arg) + System.Object! arg +"; + AssertNullableAttributes(comp, expected); + } + + [Fact] + public void ExplicitAttribute_MissingSingleByteConstructor() + { + var source1 = +@"namespace System.Runtime.CompilerServices +{ + public sealed class NullableContextAttribute : Attribute + { + } +}"; + var source2 = +@"public class Program +{ + public object F(object arg) => arg; +}"; + + // C#7 + var comp = CreateCompilation(new[] { source1, source2 }, parseOptions: TestOptions.Regular7); + comp.VerifyEmitDiagnostics(); + + // C#8, nullable disabled + comp = CreateCompilation(new[] { source1, source2 }, options: WithNonNullTypesFalse()); + comp.VerifyEmitDiagnostics(); + + // C#8, nullable enabled + comp = CreateCompilation(new[] { source1, source2 }, options: WithNonNullTypesTrue()); + comp.VerifyEmitDiagnostics( + // (3,19): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.NullableContextAttribute..ctor' + // public object F(object arg) => arg; + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "F").WithArguments("System.Runtime.CompilerServices.NullableContextAttribute", ".ctor").WithLocation(3, 19)); + } + + [Fact] + public void ExplicitAttribute_ReferencedInSource() + { + var sourceAttribute = +@"namespace System.Runtime.CompilerServices +{ + internal class NullableContextAttribute : System.Attribute + { + internal NullableContextAttribute(byte b) { } + } +}"; + var source = +@"#pragma warning disable 169 +using System.Runtime.CompilerServices; +[assembly: NullableContext(0)] +[module: NullableContext(0)] +[NullableContext(0)] +class Program +{ + [NullableContext(0)]object F; + [NullableContext(0)]static object M1() => throw null; + [return: NullableContext(0)]static object M2() => throw null; + static void M3([NullableContext(0)]object arg) { } +}"; + + // C#7 + var comp = CreateCompilation(new[] { sourceAttribute, source }, parseOptions: TestOptions.Regular7); + verifyDiagnostics(comp); + + // C#8 + comp = CreateCompilation(new[] { sourceAttribute, source }); + verifyDiagnostics(comp); + + static void verifyDiagnostics(CSharpCompilation comp) + { + comp.VerifyDiagnostics( + // (4,10): error CS8335: Do not use 'System.Runtime.CompilerServices.NullableContextAttribute'. This is reserved for compiler usage. + // [module: NullableContext(0)] + Diagnostic(ErrorCode.ERR_ExplicitReservedAttr, "NullableContext(0)").WithArguments("System.Runtime.CompilerServices.NullableContextAttribute").WithLocation(4, 10), + // (5,2): error CS8335: Do not use 'System.Runtime.CompilerServices.NullableContextAttribute'. This is reserved for compiler usage. + // [NullableContext(0)] + Diagnostic(ErrorCode.ERR_ExplicitReservedAttr, "NullableContext(0)").WithArguments("System.Runtime.CompilerServices.NullableContextAttribute").WithLocation(5, 2), + // (9,6): error CS8335: Do not use 'System.Runtime.CompilerServices.NullableContextAttribute'. This is reserved for compiler usage. + // [NullableContext(0)]static object M1() => throw null; + Diagnostic(ErrorCode.ERR_ExplicitReservedAttr, "NullableContext(0)").WithArguments("System.Runtime.CompilerServices.NullableContextAttribute").WithLocation(9, 6)); + } + } + + [Fact] + public void ExplicitAttribute_WithNullableContext() + { + var sourceAttribute = +@"#nullable enable +namespace System.Runtime.CompilerServices +{ + public sealed class NullableContextAttribute : Attribute + { + private object _f1; + private object _f2; + private object _f3; + public NullableContextAttribute(byte b) + { + } + } +}"; + var comp = CreateCompilation(sourceAttribute); + var ref0 = comp.EmitToImageReference(); + var expected = +@"[NullableContext(1)] [Nullable(0)] System.Runtime.CompilerServices.NullableContextAttribute + NullableContextAttribute(System.Byte b) + System.Byte b +"; + AssertNullableAttributes(comp, expected); + + var source = +@"#nullable enable +public class Program +{ + private object _f1; + private object _f2; + private object _f3; +}"; + comp = CreateCompilation(source, references: new[] { ref0 }); + expected = +@"[NullableContext(1)] [Nullable(0)] Program + Program() +"; + AssertNullableAttributes(comp, expected); + } + + [Fact] + public void AttributeField() + { + var source = +@"#nullable enable +using System; +using System.Linq; +public class A +{ + private object _f1; + private object _f2; + private object _f3; +} +public class B +{ + private object? _f1; + private object? _f2; + private object? _f3; +} +class Program +{ + static void Main() + { + Console.WriteLine(GetAttributeValue(typeof(A))); + Console.WriteLine(GetAttributeValue(typeof(B))); + } + static byte GetAttributeValue(Type type) + { + var attribute = type.GetCustomAttributes(false).Single(a => a.GetType().Name == ""NullableContextAttribute""); + var field = attribute.GetType().GetField(""Flag""); + return (byte)field.GetValue(attribute); + } +}"; + var expectedOutput = +@"1 +2"; + var expectedAttributes = +@"[NullableContext(1)] [Nullable(0)] A + A() +[NullableContext(2)] [Nullable(0)] B + B() +"; + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular8, options: TestOptions.DebugExe); + CompileAndVerify(comp, expectedOutput: expectedOutput, symbolValidator: module => AssertNullableAttributes(module, expectedAttributes)); + } + + [Fact] + public void MostCommonNullableValue() + { + Assert.Equal(null, getMostCommonValue()); + Assert.Equal(null, getMostCommonValue((byte?)null)); + Assert.Equal(null, getMostCommonValue(null, null)); + Assert.Equal((byte)0, getMostCommonValue(0)); + Assert.Equal((byte)1, getMostCommonValue(1)); + Assert.Equal((byte)2, getMostCommonValue(2)); +#if !DEBUG + Assert.Throws(() => getMostCommonValue(3)); +#endif + Assert.Equal((byte)0, getMostCommonValue(0, 0)); + Assert.Equal((byte)0, getMostCommonValue(0, 1)); + Assert.Equal((byte)0, getMostCommonValue(1, 0)); + Assert.Equal((byte)1, getMostCommonValue(1, 1)); + Assert.Equal((byte)1, getMostCommonValue(1, 2)); + Assert.Equal((byte)1, getMostCommonValue(2, 1)); + Assert.Equal((byte)2, getMostCommonValue(2, 2)); +#if !DEBUG + Assert.Throws(() => getMostCommonValue(2, 3)); +#endif + Assert.Equal((byte)0, getMostCommonValue(0, null)); + Assert.Equal((byte)0, getMostCommonValue(null, 0)); + Assert.Equal((byte)1, getMostCommonValue(1, null)); + Assert.Equal((byte)1, getMostCommonValue(null, 1)); + Assert.Equal((byte)0, getMostCommonValue(0, 1, 2, null)); + Assert.Equal((byte)0, getMostCommonValue(null, 2, 1, 0)); + Assert.Equal((byte)1, getMostCommonValue(1, 2, null)); + Assert.Equal((byte)1, getMostCommonValue(null, 2, 1)); + Assert.Equal((byte)2, getMostCommonValue(null, 2, null)); + Assert.Equal((byte)0, getMostCommonValue(0, 1, 0)); + Assert.Equal((byte)0, getMostCommonValue(1, 0, 0)); + Assert.Equal((byte)1, getMostCommonValue(1, 0, 1)); + Assert.Equal((byte)1, getMostCommonValue(1, 2, 1)); + Assert.Equal((byte)2, getMostCommonValue(2, 2, 1)); + + static byte? getMostCommonValue(params byte?[] values) + { + var builder = new MostCommonNullableValueBuilder(); + foreach (var value in values) + { + builder.AddValue(value); + } + return builder.MostCommonValue; + } + } + + [Fact] + public void GetCommonNullableValue() + { + Assert.Equal(null, getCommonValue()); + Assert.Equal((byte)0, getCommonValue(0)); + Assert.Equal((byte)1, getCommonValue(1)); + Assert.Equal((byte)2, getCommonValue(2)); + Assert.Equal((byte)3, getCommonValue(3)); + Assert.Equal((byte)0, getCommonValue(0, 0)); + Assert.Equal(null, getCommonValue(0, 1)); + Assert.Equal(null, getCommonValue(1, 0)); + Assert.Equal((byte)1, getCommonValue(1, 1)); + Assert.Equal(null, getCommonValue(1, 2)); + Assert.Equal((byte)2, getCommonValue(2, 2)); + Assert.Equal(null, getCommonValue(0, 1, 0)); + Assert.Equal(null, getCommonValue(1, 0, 1)); + Assert.Equal(null, getCommonValue(2, 2, 1)); + Assert.Equal((byte)3, getCommonValue(3, 3, 3)); + + static byte? getCommonValue(params byte[] values) + { + var builder = ArrayBuilder.GetInstance(); + builder.AddRange(values); + var result = MostCommonNullableValueBuilder.GetCommonValue(builder); + builder.Free(); + return result; + } + } + + private void AssertNullableAttributes(CSharpCompilation comp, string expected) + { + CompileAndVerify(comp, symbolValidator: module => AssertNullableAttributes(module, expected)); + } + + private static void AssertNullableAttributes(ModuleSymbol module, string expected) + { + var actual = NullableAttributesVisitor.GetString((PEModuleSymbol)module); + AssertEx.AssertEqualToleratingWhitespaceDifferences(expected, actual); + } + } +} diff --git a/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_NullablePublicOnly.cs b/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_NullablePublicOnly.cs index ea56845373dae..07b9a02617f5b 100644 --- a/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_NullablePublicOnly.cs +++ b/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_NullablePublicOnly.cs @@ -3,6 +3,7 @@ using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis.CSharp.Symbols; +using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; @@ -57,14 +58,13 @@ public class B : A } [Fact] - public void ExplicitAttribute_MissingParameterlessConstructor() + public void ExplicitAttribute_MissingSingleValueConstructor() { var source1 = @"namespace System.Runtime.CompilerServices { public sealed class NullablePublicOnlyAttribute : Attribute { - public NullablePublicOnlyAttribute(bool b) { } } }"; var source2 = @@ -195,5 +195,154 @@ private static void AssertAttributes(ImmutableArray attribu var actualNames = attributes.Select(a => a.AttributeClass.ToTestDisplayString()).ToArray(); AssertEx.SetEqual(actualNames, expectedNames); } + + [Fact] + [WorkItem(36457, "https://github.com/dotnet/roslyn/issues/36457")] + public void ExplicitAttribute_ReferencedInSource_Assembly() + { + var sourceAttribute = +@"namespace System.Runtime.CompilerServices +{ + internal class NullablePublicOnlyAttribute : System.Attribute + { + internal NullablePublicOnlyAttribute(bool b) { } + } +}"; + var source = +@"using System.Runtime.CompilerServices; +[assembly: NullablePublicOnly(false)] +[module: NullablePublicOnly(false)] +"; + + // C#7 + var comp = CreateCompilation(new[] { sourceAttribute, source }, parseOptions: TestOptions.Regular7); + verifyDiagnostics(comp); + + // C#8 + comp = CreateCompilation(new[] { sourceAttribute, source }); + verifyDiagnostics(comp); + + static void verifyDiagnostics(CSharpCompilation comp) + { + comp.VerifyDiagnostics( + // (3,10): error CS8335: Do not use 'System.Runtime.CompilerServices.NullablePublicOnlyAttribute'. This is reserved for compiler usage. + // [module: NullablePublicOnly(false)] + Diagnostic(ErrorCode.ERR_ExplicitReservedAttr, "NullablePublicOnly(false)").WithArguments("System.Runtime.CompilerServices.NullablePublicOnlyAttribute").WithLocation(3, 10)); + } + } + + [Fact] + [WorkItem(36457, "https://github.com/dotnet/roslyn/issues/36457")] + public void ExplicitAttribute_ReferencedInSource_Other() + { + var sourceAttribute = +@"namespace System.Runtime.CompilerServices +{ + internal class NullablePublicOnlyAttribute : System.Attribute + { + internal NullablePublicOnlyAttribute(bool b) { } + } +}"; + var source = +@"using System.Runtime.CompilerServices; +[NullablePublicOnly(false)] +class Program +{ +}"; + var comp = CreateCompilation(new[] { sourceAttribute, source }); + comp.VerifyDiagnostics(); + } + + [Fact] + public void ExplicitAttribute_WithNullableAttribute() + { + var sourceAttribute = +@"#nullable enable +namespace System.Runtime.CompilerServices +{ + public class NullablePublicOnlyAttribute : System.Attribute + { + public NullablePublicOnlyAttribute(bool b) { } + public NullablePublicOnlyAttribute( + object x, + object? y, +#nullable disable + object z) + { + } + } +}"; + var comp = CreateCompilation(sourceAttribute); + var ref0 = comp.EmitToImageReference(); + var expected = +@"System.Runtime.CompilerServices.NullablePublicOnlyAttribute + NullablePublicOnlyAttribute(System.Object! x, System.Object? y, System.Object z) + [Nullable(1)] System.Object! x + [Nullable(2)] System.Object? y +"; + AssertNullableAttributes(comp, expected); + + var source = +@"#nullable enable +public class Program +{ + public object _f1; + public object? _f2; +#nullable disable + public object _f3; +}"; + comp = CreateCompilation(source, references: new[] { ref0 }); + expected = +@"Program + [Nullable(1)] System.Object! _f1 + [Nullable(2)] System.Object? _f2 +"; + AssertNullableAttributes(comp, expected); + } + + [Fact] + public void AttributeField() + { + var source = +@"#nullable enable +using System; +using System.Linq; +using System.Reflection; +class Program +{ + static void Main() + { + var value = GetAttributeValue(typeof(Program).Assembly.Modules.First()); + Console.WriteLine(value == null ? """" : value.ToString()); + } + static bool? GetAttributeValue(Module module) + { + var attribute = module.GetCustomAttributes(false).SingleOrDefault(a => a.GetType().Name == ""NullablePublicOnlyAttribute""); + if (attribute == null) return null; + var field = attribute.GetType().GetField(""IncludesInternals""); + return (bool)field.GetValue(attribute); + } +}"; + var sourceIVTs = +@"using System.Runtime.CompilerServices; +[assembly: InternalsVisibleTo(""Other"")]"; + + var parseOptions = TestOptions.Regular8; + CompileAndVerify(source, parseOptions: parseOptions, expectedOutput: ""); + CompileAndVerify(source, parseOptions: parseOptions.WithFeature("nullablePublicOnly"), expectedOutput: "False"); + CompileAndVerify(new[] { source, sourceIVTs }, parseOptions: parseOptions, expectedOutput: ""); + CompileAndVerify(new[] { source, sourceIVTs }, parseOptions: parseOptions.WithFeature("nullablePublicOnly"), expectedOutput: "True"); + } + + private void AssertNullableAttributes(CSharpCompilation comp, string expected) + { + CompileAndVerify(comp, symbolValidator: module => AssertNullableAttributes(module, expected)); + } + + private static void AssertNullableAttributes(ModuleSymbol module, string expected) + { + var actual = NullableAttributesVisitor.GetString((PEModuleSymbol)module); + AssertEx.AssertEqualToleratingWhitespaceDifferences(expected, actual); + } } } diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenOverridingAndHiding.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenOverridingAndHiding.cs index 20ee86e333d27..4b778a7c1c901 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenOverridingAndHiding.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenOverridingAndHiding.cs @@ -3421,7 +3421,7 @@ static void Main() expectedOutput: @"Hello 3", expectedSignatures: new[] { - // The ILDASM output is following,and Roslyn handles it correctly. + // The ILDASM output is following, and Roslyn handles it correctly. // Verifier tool gives different output due to the limitation of Reflection // @".method public hidebysig virtual instance System.Void Method(" + // @"System.String modopt([mscorlib]System.Runtime.CompilerServices.IsConst)[] modopt([mscorlib]System.Runtime.CompilerServices.IsConst) x," + diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs index bc9039f53d8b4..e2fc3b71429a9 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs @@ -20381,16 +20381,25 @@ void symbolValidator(ModuleSymbol m) { bool isSource = !(m is PEModuleSymbol); var copen = (NamedTypeSymbol)m.GlobalNamespace.GetMember("COpen"); - var property = copen.GetMembers().OfType().Single(); + var copenAttributes = copen.GetAttributes().Select(a => a.ToString()); + if (isSource) + { + AssertEx.Empty(copenAttributes); + } + else + { + AssertEx.Equal(new[] { "System.Runtime.CompilerServices.NullableContextAttribute(1)", "System.Runtime.CompilerServices.NullableAttribute(0)" }, copenAttributes); + } - var attributes = property.GetAttributes().Select(a => a.ToString()); + var property = copen.GetMembers().OfType().Single(); + var propertyAttributes = property.GetAttributes().Select(a => a.ToString()); if (isSource) { - AssertEx.Equal(new[] { "System.Diagnostics.CodeAnalysis.MaybeNullAttribute" }, attributes); + AssertEx.Equal(new[] { "System.Diagnostics.CodeAnalysis.MaybeNullAttribute" }, propertyAttributes); } else { - AssertEx.Equal(new[] { "System.Runtime.CompilerServices.NullableAttribute(1)" }, attributes); + AssertEx.Empty(propertyAttributes); } var getter = property.GetMethod; @@ -20412,7 +20421,7 @@ void symbolValidator(ModuleSymbol m) } else { - AssertEx.Equal(new[] { "System.Runtime.CompilerServices.NullableAttribute(1)", "System.Diagnostics.CodeAnalysis.MaybeNullAttribute" }, getterReturnAttributes); + AssertEx.Equal(new[] { "System.Diagnostics.CodeAnalysis.MaybeNullAttribute" }, getterReturnAttributes); } var setter = property.SetMethod; @@ -20474,8 +20483,17 @@ void symbolValidator(ModuleSymbol m) { bool isSource = !(m is PEModuleSymbol); var copen = (NamedTypeSymbol)m.GlobalNamespace.GetMember("COpen"); - var property = copen.GetMembers().OfType().Single(); + var copenAttributes = copen.GetAttributes().Select(a => a.ToString()); + if (isSource) + { + AssertEx.Empty(copenAttributes); + } + else + { + AssertEx.Equal(new[] { "System.Runtime.CompilerServices.NullableContextAttribute(1)", "System.Runtime.CompilerServices.NullableAttribute(0)" }, copenAttributes); + } + var property = copen.GetMembers().OfType().Single(); var propertyAttributes = property.GetAttributes().Select(a => a.ToString()); if (isSource) { @@ -20483,7 +20501,7 @@ void symbolValidator(ModuleSymbol m) } else { - AssertEx.Equal(new[] { "System.Runtime.CompilerServices.NullableAttribute(1)" }, propertyAttributes); + AssertEx.Empty(propertyAttributes); } var getter = property.GetMethod; @@ -20505,7 +20523,7 @@ void symbolValidator(ModuleSymbol m) } else { - AssertEx.Equal(new[] { "System.Runtime.CompilerServices.NullableAttribute(1)", + AssertEx.Equal(new[] { "System.Diagnostics.CodeAnalysis.MaybeNullAttribute", "System.Diagnostics.CodeAnalysis.NotNullAttribute" }, getterReturnAttributes); } @@ -26458,8 +26476,17 @@ void symbolValidator(ModuleSymbol m) { bool isSource = !(m is PEModuleSymbol); var copen = (NamedTypeSymbol)m.GlobalNamespace.GetMember("COpen"); - var property = copen.GetMembers().OfType().Single(); + var copenAttributes = copen.GetAttributes().Select(a => a.ToString()); + if (isSource) + { + AssertEx.Empty(copenAttributes); + } + else + { + AssertEx.Equal(new[] { "System.Runtime.CompilerServices.NullableContextAttribute(1)", "System.Runtime.CompilerServices.NullableAttribute(0)", "System.Reflection.DefaultMemberAttribute(\"Item\")" }, copenAttributes); + } + var property = copen.GetMembers().OfType().Single(); var propertyAttributes = property.GetAttributes().Select(a => a.ToString()); if (isSource) { @@ -26467,7 +26494,7 @@ void symbolValidator(ModuleSymbol m) } else { - AssertEx.Equal(new[] { "System.Runtime.CompilerServices.NullableAttribute(1)" }, propertyAttributes); + AssertEx.Empty(propertyAttributes); } var setter = property.SetMethod; @@ -26482,8 +26509,7 @@ void symbolValidator(ModuleSymbol m) } else { - AssertEx.Equal(new[] { "System.Runtime.CompilerServices.NullableAttribute(1)", - "System.Diagnostics.CodeAnalysis.AllowNullAttribute" }, setterValueAttributes); + AssertEx.Equal(new[] { "System.Diagnostics.CodeAnalysis.AllowNullAttribute" }, setterValueAttributes); } Assert.Equal(FlowAnalysisAnnotations.None, setter.ReturnTypeFlowAnalysisAnnotations); @@ -26511,8 +26537,17 @@ void symbolValidator(ModuleSymbol m) { bool isSource = !(m is PEModuleSymbol); var copen = (NamedTypeSymbol)m.GlobalNamespace.GetMember("COpen"); - var property = copen.GetMembers().OfType().Single(); + var copenAttributes = copen.GetAttributes().Select(a => a.ToString()); + if (isSource) + { + AssertEx.Empty(copenAttributes); + } + else + { + AssertEx.Equal(new[] { "System.Runtime.CompilerServices.NullableContextAttribute(1)", "System.Runtime.CompilerServices.NullableAttribute(0)", "System.Reflection.DefaultMemberAttribute(\"Item\")" }, copenAttributes); + } + var property = copen.GetMembers().OfType().Single(); var propertyAttributes = property.GetAttributes().Select(a => a.ToString()); if (isSource) { @@ -26520,7 +26555,7 @@ void symbolValidator(ModuleSymbol m) } else { - AssertEx.Equal(new[] { "System.Runtime.CompilerServices.NullableAttribute(1)" }, propertyAttributes); + AssertEx.Empty(propertyAttributes); } var setter = property.SetMethod; @@ -26535,7 +26570,7 @@ void symbolValidator(ModuleSymbol m) } else { - AssertEx.Equal(new[] { "System.Runtime.CompilerServices.NullableAttribute(1)", + AssertEx.Equal(new[] { "System.Diagnostics.CodeAnalysis.DisallowNullAttribute", "System.Diagnostics.CodeAnalysis.AllowNullAttribute" }, setterValueAttributes); } @@ -27436,8 +27471,17 @@ void symbolValidator(ModuleSymbol m) { bool isSource = !(m is PEModuleSymbol); var copen = (NamedTypeSymbol)m.GlobalNamespace.GetMember("COpen"); - var property = copen.GetMembers().OfType().Single(); + var copenAttributes = copen.GetAttributes().Select(a => a.ToString()); + if (isSource) + { + AssertEx.Empty(copenAttributes); + } + else + { + AssertEx.Equal(new[] { "System.Runtime.CompilerServices.NullableContextAttribute(1)", "System.Runtime.CompilerServices.NullableAttribute(0)" }, copenAttributes); + } + var property = copen.GetMembers().OfType().Single(); var propertyAttributes = property.GetAttributes().Select(a => a.ToString()); if (isSource) { @@ -27445,7 +27489,7 @@ void symbolValidator(ModuleSymbol m) } else { - AssertEx.Equal(new[] { "System.Runtime.CompilerServices.NullableAttribute(1)" }, propertyAttributes); + AssertEx.Empty(propertyAttributes); } var setter = property.SetMethod; @@ -27467,8 +27511,7 @@ void symbolValidator(ModuleSymbol m) } else { - AssertEx.Equal(new[] { "System.Runtime.CompilerServices.NullableAttribute(1)", - "System.Diagnostics.CodeAnalysis.DisallowNullAttribute" }, setterValueAttributes); + AssertEx.Equal(new[] { "System.Diagnostics.CodeAnalysis.DisallowNullAttribute" }, setterValueAttributes); } } } @@ -30178,16 +30221,25 @@ void symbolValidator(ModuleSymbol m) { bool isSource = !(m is PEModuleSymbol); var copen = (NamedTypeSymbol)m.GlobalNamespace.GetMember("COpen"); - var property = copen.GetMembers().OfType().Single(); + var copenAttributes = copen.GetAttributes().Select(a => a.ToString()); + if (isSource) + { + AssertEx.Empty(copenAttributes); + } + else + { + AssertEx.Equal(new[] { "System.Runtime.CompilerServices.NullableContextAttribute(1)", "System.Runtime.CompilerServices.NullableAttribute(0)", "System.Reflection.DefaultMemberAttribute(\"Item\")" }, copenAttributes); + } - var attributes = property.GetAttributes().Select(a => a.ToString()); + var property = copen.GetMembers().OfType().Single(); + var propertyAttributes = property.GetAttributes().Select(a => a.ToString()); if (isSource) { - AssertEx.Equal(new[] { "System.Diagnostics.CodeAnalysis.NotNullAttribute" }, attributes); + AssertEx.Equal(new[] { "System.Diagnostics.CodeAnalysis.NotNullAttribute" }, propertyAttributes); } else { - AssertEx.Equal(new[] { "System.Runtime.CompilerServices.NullableAttribute(1)" }, attributes); + AssertEx.Empty(propertyAttributes); } var getter = property.GetMethod; @@ -30200,7 +30252,7 @@ void symbolValidator(ModuleSymbol m) } else { - AssertEx.Equal(new[] { "System.Runtime.CompilerServices.NullableAttribute(1)", "System.Diagnostics.CodeAnalysis.NotNullAttribute" }, getterReturnAttributes); + AssertEx.Equal(new[] { "System.Diagnostics.CodeAnalysis.NotNullAttribute" }, getterReturnAttributes); } } } @@ -66937,18 +66989,20 @@ void symbolValidator(ModuleSymbol m) { bool isSource = !(m is PEModuleSymbol); - var f1 = (MethodSymbol)m.GlobalNamespace.GetMember("B.F1"); - Assert.Equal("void B.F1(T1? t1) where T1 : class!", f1.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints)); - TypeParameterSymbol t1 = f1.TypeParameters[0]; - Assert.False(t1.ReferenceTypeConstraintIsNullable); + var b = m.GlobalNamespace.GetMember("B"); if (isSource) { - Assert.Empty(t1.GetAttributes()); + Assert.Empty(b.GetAttributes()); } else { - Assert.Equal("System.Runtime.CompilerServices.NullableAttribute(1)", t1.GetAttributes().Single().ToString()); + Assert.Equal("System.Runtime.CompilerServices.NullableContextAttribute(1)", b.GetAttributes().First().ToString()); } + var f1 = (MethodSymbol)m.GlobalNamespace.GetMember("B.F1"); + Assert.Equal("void B.F1(T1? t1) where T1 : class!", f1.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints)); + TypeParameterSymbol t1 = f1.TypeParameters[0]; + Assert.False(t1.ReferenceTypeConstraintIsNullable); + Assert.Empty(t1.GetAttributes()); var f2 = (MethodSymbol)m.GlobalNamespace.GetMember("B.F2"); Assert.Equal("void B.F2(T2 t2) where T2 : class?", f2.ToDisplayString(SymbolDisplayFormat.TestFormat.WithGenericsOptions(SymbolDisplayFormat.TestFormat.GenericsOptions | SymbolDisplayGenericsOptions.IncludeTypeConstraints))); @@ -67725,30 +67779,32 @@ void symbolValidator(ModuleSymbol m) var bf1 = (MethodSymbol)m.GlobalNamespace.GetMember("B.F1"); Assert.Equal("void B.F1() where T11 : class!", bf1.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints)); - TypeParameterSymbol t11 = bf1.TypeParameters[0]; - Assert.False(t11.ReferenceTypeConstraintIsNullable); if (isSource) { - Assert.Empty(t11.GetAttributes()); + Assert.Empty(bf1.GetAttributes()); } else { - Assert.Equal("System.Runtime.CompilerServices.NullableAttribute(1)", t11.GetAttributes().Single().ToString()); + Assert.Equal("System.Runtime.CompilerServices.NullableContextAttribute(1)", bf1.GetAttributes().Single().ToString()); } + TypeParameterSymbol t11 = bf1.TypeParameters[0]; + Assert.False(t11.ReferenceTypeConstraintIsNullable); + Assert.Empty(t11.GetAttributes()); var bf2 = (MethodSymbol)m.GlobalNamespace.GetMember("B.F2"); Assert.Equal("void B.F2() where T22 : class?", bf2.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints)); - - TypeParameterSymbol t22 = bf2.TypeParameters[0]; - Assert.True(t22.ReferenceTypeConstraintIsNullable); if (isSource) { - Assert.Empty(t22.GetAttributes()); + Assert.Empty(bf2.GetAttributes()); } else { - Assert.Equal("System.Runtime.CompilerServices.NullableAttribute(2)", t22.GetAttributes().Single().ToString()); + Assert.Equal("System.Runtime.CompilerServices.NullableContextAttribute(2)", bf2.GetAttributes().Single().ToString()); } + + TypeParameterSymbol t22 = bf2.TypeParameters[0]; + Assert.True(t22.ReferenceTypeConstraintIsNullable); + Assert.Empty(t22.GetAttributes()); } } @@ -67914,31 +67970,33 @@ void symbolValidator(ModuleSymbol m) var bf1 = (MethodSymbol)m.GlobalNamespace.GetMember("B.F1"); Assert.Equal("void B.F1() where T11 : class!", bf1.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints)); - TypeParameterSymbol t11 = bf1.TypeParameters[0]; - Assert.False(t11.ReferenceTypeConstraintIsNullable); if (isSource) { - Assert.Empty(t11.GetAttributes()); + Assert.Empty(bf1.GetAttributes()); } else { - Assert.Equal("System.Runtime.CompilerServices.NullableAttribute(1)", t11.GetAttributes().Single().ToString()); + Assert.Equal("System.Runtime.CompilerServices.NullableContextAttribute(1)", bf1.GetAttributes().Single().ToString()); } + TypeParameterSymbol t11 = bf1.TypeParameters[0]; + Assert.False(t11.ReferenceTypeConstraintIsNullable); + Assert.Empty(t11.GetAttributes()); var bf2 = (MethodSymbol)m.GlobalNamespace.GetMember("B.F2"); Assert.Equal("void B.F2() where T22 : class?", bf2.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints)); - - TypeParameterSymbol t22 = bf2.TypeParameters[0]; - Assert.True(t22.ReferenceTypeConstraintIsNullable); if (isSource) { - Assert.Empty(t22.GetAttributes()); + Assert.Empty(bf2.GetAttributes()); } else { - Assert.Equal("System.Runtime.CompilerServices.NullableAttribute(2)", t22.GetAttributes().Single().ToString()); + Assert.Equal("System.Runtime.CompilerServices.NullableContextAttribute(2)", bf2.GetAttributes().Single().ToString()); } + TypeParameterSymbol t22 = bf2.TypeParameters[0]; + Assert.True(t22.ReferenceTypeConstraintIsNullable); + Assert.Empty(t22.GetAttributes()); + var bf3 = (MethodSymbol)m.GlobalNamespace.GetMember("B.F3"); Assert.Equal("void B.F3() where T33 : C1!", bf3.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints)); @@ -68019,30 +68077,32 @@ void symbolValidator(ModuleSymbol m) var bf1 = (MethodSymbol)m.GlobalNamespace.GetMember("B.IA.F1"); Assert.Equal("void B.IA.F1() where T11 : class!", bf1.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints)); - TypeParameterSymbol t11 = bf1.TypeParameters[0]; - Assert.False(t11.ReferenceTypeConstraintIsNullable); if (isSource) { - Assert.Empty(t11.GetAttributes()); + Assert.Empty(bf1.GetAttributes()); } else { - Assert.Equal("System.Runtime.CompilerServices.NullableAttribute(1)", t11.GetAttributes().Single().ToString()); + Assert.Equal("System.Runtime.CompilerServices.NullableContextAttribute(1)", bf1.GetAttributes().Single().ToString()); } + TypeParameterSymbol t11 = bf1.TypeParameters[0]; + Assert.False(t11.ReferenceTypeConstraintIsNullable); + Assert.Empty(t11.GetAttributes()); var bf2 = (MethodSymbol)m.GlobalNamespace.GetMember("B.IA.F2"); Assert.Equal("void B.IA.F2() where T22 : class?", bf2.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints)); - - TypeParameterSymbol t22 = bf2.TypeParameters[0]; - Assert.True(t22.ReferenceTypeConstraintIsNullable); if (isSource) { - Assert.Empty(t22.GetAttributes()); + Assert.Empty(bf2.GetAttributes()); } else { - Assert.Equal("System.Runtime.CompilerServices.NullableAttribute(2)", t22.GetAttributes().Single().ToString()); + Assert.Equal("System.Runtime.CompilerServices.NullableContextAttribute(2)", bf2.GetAttributes().Single().ToString()); } + + TypeParameterSymbol t22 = bf2.TypeParameters[0]; + Assert.True(t22.ReferenceTypeConstraintIsNullable); + Assert.Empty(t22.GetAttributes()); } } @@ -68083,33 +68143,35 @@ void symbolValidator(ModuleSymbol m) var bf1 = (MethodSymbol)m.GlobalNamespace.GetMember("B.IA.F1"); Assert.Equal("void B.IA.F1() where T11 : class!", bf1.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints)); - TypeParameterSymbol t11 = bf1.TypeParameters[0]; - Assert.False(t11.ReferenceTypeConstraintIsNullable); if (isSource) { - Assert.Empty(t11.GetAttributes()); + Assert.Empty(bf1.GetAttributes()); } else { - Assert.Equal("System.Runtime.CompilerServices.NullableAttribute(1)", t11.GetAttributes().Single().ToString()); + Assert.Equal("System.Runtime.CompilerServices.NullableContextAttribute(1)", bf1.GetAttributes().Single().ToString()); } + TypeParameterSymbol t11 = bf1.TypeParameters[0]; + Assert.False(t11.ReferenceTypeConstraintIsNullable); + Assert.Empty(t11.GetAttributes()); var bf2 = (MethodSymbol)m.GlobalNamespace.GetMember("B.IA.F2"); Assert.Equal("void B.IA.F2() where T22 : class?", bf2.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints)); - - TypeParameterSymbol t22 = bf2.TypeParameters[0]; - Assert.True(t22.ReferenceTypeConstraintIsNullable); if (isSource) { - Assert.Empty(t22.GetAttributes()); + Assert.Empty(bf2.GetAttributes()); } else { - CSharpAttributeData nullableAttribute = t22.GetAttributes().Single(); - Assert.Equal("System.Runtime.CompilerServices.NullableAttribute(2)", nullableAttribute.ToString()); + CSharpAttributeData nullableAttribute = bf2.GetAttributes().Single(); + Assert.Equal("System.Runtime.CompilerServices.NullableContextAttribute(2)", nullableAttribute.ToString()); Assert.Same(m, nullableAttribute.AttributeClass.ContainingModule); Assert.Equal(Accessibility.Internal, nullableAttribute.AttributeClass.DeclaredAccessibility); } + + TypeParameterSymbol t22 = bf2.TypeParameters[0]; + Assert.True(t22.ReferenceTypeConstraintIsNullable); + Assert.Empty(t22.GetAttributes()); } } @@ -68126,7 +68188,7 @@ public interface IA "; var comp1 = CreateCompilation(new[] { source1 }, options: WithNonNullTypesTrue()); - var comp2 = CreateCompilation(NullableAttributeDefinition); + var comp2 = CreateCompilation(NullableContextAttributeDefinition); var source3 = @" @@ -68153,33 +68215,35 @@ void symbolValidator(ModuleSymbol m) bool isSource = !(m is PEModuleSymbol); var bf1 = (MethodSymbol)m.GlobalNamespace.GetMember("B.IA.F1"); - Assert.Equal("void B.IA.F1() where T11 : class!", bf1.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints)); - TypeParameterSymbol t11 = bf1.TypeParameters[0]; - Assert.False(t11.ReferenceTypeConstraintIsNullable); if (isSource) { - Assert.Empty(t11.GetAttributes()); + Assert.Empty(bf1.GetAttributes()); } else { - Assert.Equal("System.Runtime.CompilerServices.NullableAttribute(1)", t11.GetAttributes().Single().ToString()); + Assert.Equal("System.Runtime.CompilerServices.NullableContextAttribute(1)", bf1.GetAttributes().Single().ToString()); } + Assert.Equal("void B.IA.F1() where T11 : class!", bf1.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints)); + TypeParameterSymbol t11 = bf1.TypeParameters[0]; + Assert.False(t11.ReferenceTypeConstraintIsNullable); + Assert.Empty(t11.GetAttributes()); var bf2 = (MethodSymbol)m.GlobalNamespace.GetMember("B.IA.F2"); Assert.Equal("void B.IA.F2() where T22 : class?", bf2.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints)); - - TypeParameterSymbol t22 = bf2.TypeParameters[0]; - Assert.True(t22.ReferenceTypeConstraintIsNullable); if (isSource) { - Assert.Empty(t22.GetAttributes()); + Assert.Empty(bf2.GetAttributes()); } else { - CSharpAttributeData nullableAttribute = t22.GetAttributes().Single(); - Assert.Equal("System.Runtime.CompilerServices.NullableAttribute(2)", nullableAttribute.ToString()); + CSharpAttributeData nullableAttribute = bf2.GetAttributes().Single(); + Assert.Equal("System.Runtime.CompilerServices.NullableContextAttribute(2)", nullableAttribute.ToString()); Assert.NotEqual(m, nullableAttribute.AttributeClass.ContainingModule); } + + TypeParameterSymbol t22 = bf2.TypeParameters[0]; + Assert.True(t22.ReferenceTypeConstraintIsNullable); + Assert.Empty(t22.GetAttributes()); } } @@ -68211,7 +68275,6 @@ void IA.F2() "; var comp2 = CreateCompilation(new[] { source2 }, references: new[] { comp1.EmitToImageReference() }); - comp2.VerifyDiagnostics(); CompileAndVerify(comp2, sourceSymbolValidator: symbolValidator, symbolValidator: symbolValidator); @@ -68221,30 +68284,32 @@ void symbolValidator(ModuleSymbol m) var bf1 = (MethodSymbol)m.GlobalNamespace.GetMember("B.IA.F1"); Assert.Equal("void B.IA.F1() where T11 : class!", bf1.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints)); - TypeParameterSymbol t11 = bf1.TypeParameters[0]; - Assert.False(t11.ReferenceTypeConstraintIsNullable); if (isSource) { - Assert.Empty(t11.GetAttributes()); + Assert.Empty(bf1.GetAttributes()); } else { - Assert.Equal("System.Runtime.CompilerServices.NullableAttribute(1)", t11.GetAttributes().Single().ToString()); + Assert.Equal("System.Runtime.CompilerServices.NullableContextAttribute(1)", bf1.GetAttributes().Single().ToString()); } + TypeParameterSymbol t11 = bf1.TypeParameters[0]; + Assert.False(t11.ReferenceTypeConstraintIsNullable); + Assert.Empty(t11.GetAttributes()); var bf2 = (MethodSymbol)m.GlobalNamespace.GetMember("B.IA.F2"); Assert.Equal("void B.IA.F2() where T22 : class?", bf2.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints)); - - TypeParameterSymbol t22 = bf2.TypeParameters[0]; - Assert.True(t22.ReferenceTypeConstraintIsNullable); if (isSource) { - Assert.Empty(t22.GetAttributes()); + Assert.Empty(bf2.GetAttributes()); } else { - Assert.Equal("System.Runtime.CompilerServices.NullableAttribute(2)", t22.GetAttributes().Single().ToString()); + Assert.Equal("System.Runtime.CompilerServices.NullableContextAttribute(2)", bf2.GetAttributes().Single().ToString()); } + + TypeParameterSymbol t22 = bf2.TypeParameters[0]; + Assert.True(t22.ReferenceTypeConstraintIsNullable); + Assert.Empty(t22.GetAttributes()); } } @@ -68309,16 +68374,17 @@ void symbolValidator(ModuleSymbol m) var f2 = (MethodSymbol)m.GlobalNamespace.GetMember("B.F2"); Assert.Equal("void B.F2() where T2 : class?", f2.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints)); - TypeParameterSymbol t2 = f2.TypeParameters[0]; - Assert.True(t2.ReferenceTypeConstraintIsNullable); if (isSource) { - Assert.Empty(t2.GetAttributes()); + Assert.Empty(f2.GetAttributes()); } else { - Assert.Equal("System.Runtime.CompilerServices.NullableAttribute(2)", t2.GetAttributes().Single().ToString()); + Assert.Equal("System.Runtime.CompilerServices.NullableContextAttribute(2)", f2.GetAttributes().Single().ToString()); } + TypeParameterSymbol t2 = f2.TypeParameters[0]; + Assert.True(t2.ReferenceTypeConstraintIsNullable); + Assert.Empty(t2.GetAttributes()); } } @@ -68592,31 +68658,33 @@ void symbolValidator(ModuleSymbol m) var bf1 = (MethodSymbol)m.GlobalNamespace.GetMember("B.F1"); Assert.Equal("void B.F1() where T11 : class!", bf1.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints)); - TypeParameterSymbol t11 = bf1.TypeParameters[0]; - Assert.False(t11.ReferenceTypeConstraintIsNullable); if (isSource) { - Assert.Empty(t11.GetAttributes()); + Assert.Empty(bf1.GetAttributes()); } else { - Assert.Equal("System.Runtime.CompilerServices.NullableAttribute(1)", t11.GetAttributes().Single().ToString()); + Assert.Equal("System.Runtime.CompilerServices.NullableContextAttribute(1)", bf1.GetAttributes().Single().ToString()); } + TypeParameterSymbol t11 = bf1.TypeParameters[0]; + Assert.False(t11.ReferenceTypeConstraintIsNullable); + Assert.Empty(t11.GetAttributes()); var bf2 = (MethodSymbol)m.GlobalNamespace.GetMember("B.F2"); Assert.Equal("void B.F2() where T22 : class?", bf2.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints)); - - TypeParameterSymbol t22 = bf2.TypeParameters[0]; - Assert.True(t22.ReferenceTypeConstraintIsNullable); if (isSource) { - Assert.Empty(t22.GetAttributes()); + Assert.Empty(bf2.GetAttributes()); } else { - Assert.Equal("System.Runtime.CompilerServices.NullableAttribute(2)", t22.GetAttributes().Single().ToString()); + Assert.Equal("System.Runtime.CompilerServices.NullableContextAttribute(2)", bf2.GetAttributes().Single().ToString()); } + TypeParameterSymbol t22 = bf2.TypeParameters[0]; + Assert.True(t22.ReferenceTypeConstraintIsNullable); + Assert.Empty(t22.GetAttributes()); + var bf3 = (MethodSymbol)m.GlobalNamespace.GetMember("B.F3"); Assert.Equal("void B.F3() where T33 : C1!", bf3.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints)); @@ -69584,19 +69652,19 @@ void symbolValidator(ModuleSymbol m) var f1 = (MethodSymbol)m.GlobalNamespace.GetMember("B.F1"); Assert.Equal("void B.F1() where T1 : notnull", f1.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints)); - TypeParameterSymbol t1 = f1.TypeParameters[0]; - Assert.False(t1.IsReferenceType); - Assert.True(t1.IsNotNullable); - var attributes = t1.GetAttributes(); - if (isSource) { - Assert.Empty(attributes); + Assert.Empty(f1.GetAttributes()); } else { - Assert.Equal("System.Runtime.CompilerServices.NullableAttribute(1)", attributes[0].ToString()); + Assert.Equal("System.Runtime.CompilerServices.NullableContextAttribute(1)", f1.GetAttributes().Single().ToString()); } + TypeParameterSymbol t1 = f1.TypeParameters[0]; + Assert.False(t1.IsReferenceType); + Assert.True(t1.IsNotNullable); + var attributes = t1.GetAttributes(); + Assert.Empty(attributes); } } @@ -69622,20 +69690,20 @@ void symbolValidator(ModuleSymbol m) var f1 = (MethodSymbol)m.GlobalNamespace.GetMember("B.F1"); Assert.Equal("void B.F1() where T1 : notnull", f1.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints)); - TypeParameterSymbol t1 = f1.TypeParameters[0]; - Assert.False(t1.IsReferenceType); - Assert.True(t1.HasNotNullConstraint); - Assert.True(t1.IsNotNullable); - var attributes = t1.GetAttributes(); - if (isSource) { - Assert.Empty(attributes); + Assert.Empty(f1.GetAttributes()); } else { - Assert.Equal("System.Runtime.CompilerServices.NullableAttribute(1)", attributes[0].ToString()); + Assert.Equal("System.Runtime.CompilerServices.NullableContextAttribute(1)", f1.GetAttributes().Single().ToString()); } + TypeParameterSymbol t1 = f1.TypeParameters[0]; + Assert.False(t1.IsReferenceType); + Assert.True(t1.HasNotNullConstraint); + Assert.True(t1.IsNotNullable); + var attributes = t1.GetAttributes(); + Assert.Empty(attributes); } } @@ -69963,19 +70031,21 @@ void symbolValidator(ModuleSymbol m) else { Assert.Equal("void A.I.F1(TF1A x) where TF1A : System.Object!", af1.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints)); + Assert.Equal("System.Runtime.CompilerServices.NullableContextAttribute(1)", af1.GetAttributes().Single().ToString()); } TypeParameterSymbol at1 = af1.TypeParameters[0]; Assert.False(at1.IsReferenceType); Assert.True(at1.IsNotNullable); - Assert.Empty(at1.GetAttributes()); if (isSource) { + Assert.Empty(at1.GetAttributes()); Assert.Equal("void I.F1(TF1 x) where TF1 : System.Object!", af1.ExplicitInterfaceImplementations.Single().ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints)); } else { + Assert.Equal("System.Runtime.CompilerServices.NullableAttribute(0)", at1.GetAttributes().Single().ToString()); var impl = af1.ExplicitInterfaceImplementations.Single(); Assert.Equal("void I.F1(TF1 x)", impl.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints)); Assert.Null(impl.TypeParameters[0].IsNotNullable); @@ -69991,6 +70061,7 @@ void symbolValidator(ModuleSymbol m) else { Assert.Equal("void B.I.F1(TF1B x)", bf1.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints)); + Assert.Equal("System.Runtime.CompilerServices.NullableContextAttribute(1)", bf1.GetAttributes().Single().ToString()); } TypeParameterSymbol tf1 = bf1.TypeParameters[0]; @@ -70055,19 +70126,21 @@ void symbolValidator(ModuleSymbol m) else { Assert.Equal("void A.I.F1(TF1A! x) where TF1A : A!", af1.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints)); + Assert.Equal("System.Runtime.CompilerServices.NullableContextAttribute(1)", af1.GetAttributes().Single().ToString()); } TypeParameterSymbol at1 = af1.TypeParameters[0]; Assert.True(at1.IsReferenceType); Assert.True(at1.IsNotNullable); - Assert.Empty(at1.GetAttributes()); if (isSource) { + Assert.Empty(at1.GetAttributes()); Assert.Equal("void I.F1(TF1 x) where TF1 : A!", af1.ExplicitInterfaceImplementations.Single().ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints)); } else { + Assert.Equal("System.Runtime.CompilerServices.NullableAttribute(0)", at1.GetAttributes().Single().ToString()); Assert.Equal("void I.F1(TF1 x) where TF1 : A", af1.ExplicitInterfaceImplementations.Single().ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints)); } @@ -70081,18 +70154,20 @@ void symbolValidator(ModuleSymbol m) else { Assert.Equal("void B.I.F1(TF1B x) where TF1B : A?", bf1.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints)); + Assert.Empty(bf1.GetAttributes()); } TypeParameterSymbol tf1 = bf1.TypeParameters[0]; Assert.True(tf1.IsReferenceType); Assert.False(tf1.IsNotNullable); - Assert.Empty(tf1.GetAttributes()); if (isSource) { + Assert.Empty(tf1.GetAttributes()); Assert.Equal("void I.F1(TF1 x) where TF1 : A?", bf1.ExplicitInterfaceImplementations.Single().ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints)); } else { + Assert.Empty(tf1.GetAttributes()); Assert.Equal("void I.F1(TF1 x) where TF1 : A", bf1.ExplicitInterfaceImplementations.Single().ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints)); } } @@ -71402,22 +71477,21 @@ void symbolValidator(ModuleSymbol m) { bool isSource = !(m is PEModuleSymbol); - var f1 = (MethodSymbol)m.GlobalNamespace.GetMember("I1.F1"); - Assert.Equal("void I1.F1() where TF1 : class?", f1.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints)); - - TypeParameterSymbol tf1 = f1.TypeParameters[0]; - Assert.False(tf1.IsNotNullable); - var attributes = tf1.GetAttributes(); - + var i1 = m.GlobalNamespace.GetMember("I1"); if (isSource) { - Assert.Empty(attributes); + Assert.Empty(i1.GetAttributes()); } else { - Assert.Equal(1, attributes.Length); - Assert.Equal("System.Runtime.CompilerServices.NullableAttribute(2)", attributes[0].ToString()); + Assert.Equal("System.Runtime.CompilerServices.NullableContextAttribute(2)", i1.GetAttributes().Single().ToString()); } + var f1 = (MethodSymbol)m.GlobalNamespace.GetMember("I1.F1"); + Assert.Equal("void I1.F1() where TF1 : class?", f1.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints)); + + TypeParameterSymbol tf1 = f1.TypeParameters[0]; + Assert.False(tf1.IsNotNullable); + Assert.Empty(tf1.GetAttributes()); } } @@ -71472,22 +71546,21 @@ void symbolValidator1(ModuleSymbol m) { bool isSource = !(m is PEModuleSymbol); - var f1 = (MethodSymbol)m.GlobalNamespace.GetMember("I1.F1"); - Assert.Equal("void I1.F1()", f1.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints)); - - TypeParameterSymbol tf1 = f1.TypeParameters[0]; - Assert.False(tf1.IsNotNullable); - var attributes = tf1.GetAttributes(); - + var i1 = m.GlobalNamespace.GetMember("I1"); if (isSource) { - Assert.Empty(attributes); + Assert.Empty(i1.GetAttributes()); } else { - Assert.Equal(1, attributes.Length); - Assert.Equal("System.Runtime.CompilerServices.NullableAttribute(2)", attributes[0].ToString()); + Assert.Equal("System.Runtime.CompilerServices.NullableContextAttribute(2)", i1.GetAttributes().Single().ToString()); } + var f1 = (MethodSymbol)m.GlobalNamespace.GetMember("I1.F1"); + Assert.Equal("void I1.F1()", f1.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints)); + + TypeParameterSymbol tf1 = f1.TypeParameters[0]; + Assert.False(tf1.IsNotNullable); + Assert.Empty(tf1.GetAttributes()); } var source2 = @@ -71507,23 +71580,22 @@ void symbolValidator2(ModuleSymbol m) { bool isSource = !(m is PEModuleSymbol); + var i1 = m.GlobalNamespace.GetMember("I1"); + if (isSource) + { + Assert.Empty(i1.GetAttributes()); + } + else + { + Assert.Equal("System.Runtime.CompilerServices.NullableContextAttribute(2)", i1.GetAttributes().Single().ToString()); + } var f1 = (MethodSymbol)m.GlobalNamespace.GetMember("I1.F1"); Assert.Equal("void I1.F1() where TF1 : class?", f1.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints)); foreach (TypeParameterSymbol tf1 in f1.TypeParameters) { Assert.False(tf1.IsNotNullable); - var attributes = tf1.GetAttributes(); - - if (isSource) - { - Assert.Empty(attributes); - } - else - { - Assert.Equal(1, attributes.Length); - Assert.Equal("System.Runtime.CompilerServices.NullableAttribute(2)", attributes[0].ToString()); - } + Assert.Empty(tf1.GetAttributes()); } } } @@ -71548,22 +71620,21 @@ void symbolValidator(ModuleSymbol m) { bool isSource = !(m is PEModuleSymbol); - var f1 = (MethodSymbol)m.GlobalNamespace.GetMember("I1.F1"); - Assert.Equal("void I1.F1() where TF1 : new()", f1.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints)); - - TypeParameterSymbol tf1 = f1.TypeParameters[0]; - Assert.False(tf1.IsNotNullable); - var attributes = tf1.GetAttributes(); - + var i1 = m.GlobalNamespace.GetMember("I1"); if (isSource) { - Assert.Empty(attributes); + Assert.Empty(i1.GetAttributes()); } else { - Assert.Equal(1, attributes.Length); - Assert.Equal("System.Runtime.CompilerServices.NullableAttribute(2)", attributes[0].ToString()); + Assert.Equal("System.Runtime.CompilerServices.NullableContextAttribute(2)", i1.GetAttributes().Single().ToString()); } + var f1 = (MethodSymbol)m.GlobalNamespace.GetMember("I1.F1"); + Assert.Equal("void I1.F1() where TF1 : new()", f1.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints)); + + TypeParameterSymbol tf1 = f1.TypeParameters[0]; + Assert.False(tf1.IsNotNullable); + Assert.Empty(tf1.GetAttributes()); } } @@ -71587,22 +71658,21 @@ void symbolValidator(ModuleSymbol m) { bool isSource = !(m is PEModuleSymbol); - var f1 = (MethodSymbol)m.GlobalNamespace.GetMember("I1.F1"); - Assert.Equal("void I1.F1() where TF1 : class!", f1.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints)); - - TypeParameterSymbol tf1 = f1.TypeParameters[0]; - Assert.True(tf1.IsNotNullable); - var attributes = tf1.GetAttributes(); - + var i1 = m.GlobalNamespace.GetMember("I1"); if (isSource) { - Assert.Empty(attributes); + Assert.Empty(i1.GetAttributes()); } else { - Assert.Equal(1, attributes.Length); - Assert.Equal("System.Runtime.CompilerServices.NullableAttribute(1)", attributes[0].ToString()); + Assert.Equal("System.Runtime.CompilerServices.NullableContextAttribute(1)", i1.GetAttributes().Single().ToString()); } + var f1 = (MethodSymbol)m.GlobalNamespace.GetMember("I1.F1"); + Assert.Equal("void I1.F1() where TF1 : class!", f1.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints)); + + TypeParameterSymbol tf1 = f1.TypeParameters[0]; + Assert.True(tf1.IsNotNullable); + Assert.Empty(tf1.GetAttributes()); } } @@ -71710,22 +71780,22 @@ void symbolValidator(ModuleSymbol m) { bool isSource = !(m is PEModuleSymbol); - var f1 = (MethodSymbol)m.GlobalNamespace.GetMember("I1.F1"); - Assert.Equal("void I1.F1() where TF1 : class?", f1.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints)); - - TypeParameterSymbol tf1 = f1.TypeParameters[0]; - Assert.False(tf1.IsNotNullable); - var attributes = tf1.GetAttributes(); - + var i1 = m.GlobalNamespace.GetMember("I1"); if (isSource) { - Assert.Empty(attributes); + Assert.Empty(i1.GetAttributes()); } else { - Assert.Equal(1, attributes.Length); - Assert.Equal("System.Runtime.CompilerServices.NullableAttribute(2)", attributes[0].ToString()); + Assert.Equal("System.Runtime.CompilerServices.NullableContextAttribute(2)", i1.GetAttributes().Single().ToString()); } + var f1 = (MethodSymbol)m.GlobalNamespace.GetMember("I1.F1"); + Assert.Equal("void I1.F1() where TF1 : class?", f1.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints)); + + TypeParameterSymbol tf1 = f1.TypeParameters[0]; + Assert.False(tf1.IsNotNullable); + var attributes = tf1.GetAttributes(); + Assert.Empty(attributes); } } @@ -71962,20 +72032,19 @@ void symbolValidator(ModuleSymbol m) var i1 = m.GlobalNamespace.GetTypeMember("I1"); Assert.Equal("I1 where TF1 : class?", i1.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints)); - - TypeParameterSymbol tf1 = i1.TypeParameters[0]; - Assert.False(tf1.IsNotNullable); - var attributes = tf1.GetAttributes(); - if (isSource) { - Assert.Empty(attributes); + Assert.Empty(i1.GetAttributes()); } else { - Assert.Equal(1, attributes.Length); - Assert.Equal("System.Runtime.CompilerServices.NullableAttribute(2)", attributes[0].ToString()); + Assert.Equal("System.Runtime.CompilerServices.NullableContextAttribute(2)", i1.GetAttributes().Single().ToString()); } + + TypeParameterSymbol tf1 = i1.TypeParameters[0]; + Assert.False(tf1.IsNotNullable); + var attributes = tf1.GetAttributes(); + Assert.Empty(attributes); } } @@ -72030,20 +72099,19 @@ void symbolValidator1(ModuleSymbol m) var i1 = m.GlobalNamespace.GetTypeMember("I1"); Assert.Equal("I1", i1.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints)); - - TypeParameterSymbol tf1 = i1.TypeParameters[0]; - Assert.False(tf1.IsNotNullable); - var attributes = tf1.GetAttributes(); - if (isSource) { - Assert.Empty(attributes); + Assert.Empty(i1.GetAttributes()); } else { - Assert.Equal(1, attributes.Length); - Assert.Equal("System.Runtime.CompilerServices.NullableAttribute(2)", attributes[0].ToString()); + Assert.Equal("System.Runtime.CompilerServices.NullableContextAttribute(2)", i1.GetAttributes().Single().ToString()); } + + TypeParameterSymbol tf1 = i1.TypeParameters[0]; + Assert.False(tf1.IsNotNullable); + var attributes = tf1.GetAttributes(); + Assert.Empty(attributes); } var source2 = @@ -72064,21 +72132,20 @@ void symbolValidator2(ModuleSymbol m) var i1 = m.GlobalNamespace.GetTypeMember("I1"); Assert.Equal("I1 where TF1 : class?", i1.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints)); + if (isSource) + { + Assert.Empty(i1.GetAttributes()); + } + else + { + Assert.Equal("System.Runtime.CompilerServices.NullableContextAttribute(2)", i1.GetAttributes().Single().ToString()); + } foreach (TypeParameterSymbol tf1 in i1.TypeParameters) { Assert.False(tf1.IsNotNullable); var attributes = tf1.GetAttributes(); - - if (isSource) - { - Assert.Empty(attributes); - } - else - { - Assert.Equal(1, attributes.Length); - Assert.Equal("System.Runtime.CompilerServices.NullableAttribute(2)", attributes[0].ToString()); - } + Assert.Empty(attributes); } } } @@ -72104,20 +72171,19 @@ void symbolValidator(ModuleSymbol m) var i1 = m.GlobalNamespace.GetTypeMember("I1"); Assert.Equal("I1 where TF1 : new()", i1.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints)); - - TypeParameterSymbol tf1 = i1.TypeParameters[0]; - Assert.False(tf1.IsNotNullable); - var attributes = tf1.GetAttributes(); - if (isSource) { - Assert.Empty(attributes); + Assert.Empty(i1.GetAttributes()); } else { - Assert.Equal(1, attributes.Length); - Assert.Equal("System.Runtime.CompilerServices.NullableAttribute(2)", attributes[0].ToString()); + Assert.Equal("System.Runtime.CompilerServices.NullableContextAttribute(2)", i1.GetAttributes().Single().ToString()); } + + TypeParameterSymbol tf1 = i1.TypeParameters[0]; + Assert.False(tf1.IsNotNullable); + var attributes = tf1.GetAttributes(); + Assert.Empty(attributes); } } @@ -72142,20 +72208,19 @@ void symbolValidator(ModuleSymbol m) var i1 = m.GlobalNamespace.GetTypeMember("I1"); Assert.Equal("I1 where TF1 : class!", i1.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints)); - - TypeParameterSymbol tf1 = i1.TypeParameters[0]; - Assert.True(tf1.IsNotNullable); - var attributes = tf1.GetAttributes(); - if (isSource) { - Assert.Empty(attributes); + Assert.Empty(i1.GetAttributes()); } else { - Assert.Equal(1, attributes.Length); - Assert.Equal("System.Runtime.CompilerServices.NullableAttribute(1)", attributes[0].ToString()); + Assert.Equal("System.Runtime.CompilerServices.NullableContextAttribute(1)", i1.GetAttributes().Single().ToString()); } + + TypeParameterSymbol tf1 = i1.TypeParameters[0]; + Assert.True(tf1.IsNotNullable); + var attributes = tf1.GetAttributes(); + Assert.Empty(attributes); } } @@ -72261,20 +72326,19 @@ void symbolValidator(ModuleSymbol m) var i1 = m.GlobalNamespace.GetTypeMember("I1"); Assert.Equal("I1 where TF1 : class?", i1.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints)); - - TypeParameterSymbol tf1 = i1.TypeParameters[0]; - Assert.False(tf1.IsNotNullable); - var attributes = tf1.GetAttributes(); - if (isSource) { - Assert.Empty(attributes); + Assert.Empty(i1.GetAttributes()); } else { - Assert.Equal(1, attributes.Length); - Assert.Equal("System.Runtime.CompilerServices.NullableAttribute(2)", attributes[0].ToString()); + Assert.Equal("System.Runtime.CompilerServices.NullableContextAttribute(2)", i1.GetAttributes().Single().ToString()); } + + TypeParameterSymbol tf1 = i1.TypeParameters[0]; + Assert.False(tf1.IsNotNullable); + var attributes = tf1.GetAttributes(); + Assert.Empty(attributes); } } @@ -72362,20 +72426,19 @@ void symbolValidator1(ModuleSymbol m) var i1 = m.GlobalNamespace.GetTypeMember("I1"); Assert.Equal("I1", i1.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints)); - - TypeParameterSymbol tf1 = i1.TypeParameters[0]; - Assert.False(tf1.IsNotNullable); - var attributes = tf1.GetAttributes(); - if (isSource) { - Assert.Empty(attributes); + Assert.Empty(i1.GetAttributes()); } else { - Assert.Equal(1, attributes.Length); - Assert.Equal("System.Runtime.CompilerServices.NullableAttribute(2)", attributes[0].ToString()); + Assert.Equal("System.Runtime.CompilerServices.NullableContextAttribute(2)", i1.GetAttributes().Single().ToString()); } + + TypeParameterSymbol tf1 = i1.TypeParameters[0]; + Assert.False(tf1.IsNotNullable); + var attributes = tf1.GetAttributes(); + Assert.Empty(attributes); } } @@ -72406,20 +72469,19 @@ void symbolValidator1(ModuleSymbol m) var i1 = m.GlobalNamespace.GetTypeMember("I1"); Assert.Equal("I1", i1.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints)); - - TypeParameterSymbol tf1 = i1.TypeParameters[0]; - Assert.False(tf1.IsNotNullable); - var attributes = tf1.GetAttributes(); - if (isSource) { - Assert.Empty(attributes); + Assert.Empty(i1.GetAttributes()); } else { - Assert.Equal(1, attributes.Length); - Assert.Equal("System.Runtime.CompilerServices.NullableAttribute(2)", attributes[0].ToString()); + Assert.Equal("System.Runtime.CompilerServices.NullableContextAttribute(2)", i1.GetAttributes().Single().ToString()); } + + TypeParameterSymbol tf1 = i1.TypeParameters[0]; + Assert.False(tf1.IsNotNullable); + var attributes = tf1.GetAttributes(); + Assert.Empty(attributes); } } @@ -72451,20 +72513,19 @@ void symbolValidator1(ModuleSymbol m) var i1 = m.GlobalNamespace.GetTypeMember("I1"); Assert.Equal("I1", i1.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints)); - - TypeParameterSymbol tf1 = i1.TypeParameters[0]; - Assert.False(tf1.IsNotNullable); - var attributes = tf1.GetAttributes(); - if (isSource) { - Assert.Empty(attributes); + Assert.Empty(i1.GetAttributes()); } else { - Assert.Equal(1, attributes.Length); - Assert.Equal("System.Runtime.CompilerServices.NullableAttribute(2)", attributes[0].ToString()); + Assert.Equal("System.Runtime.CompilerServices.NullableContextAttribute(2)", i1.GetAttributes().Single().ToString()); } + + TypeParameterSymbol tf1 = i1.TypeParameters[0]; + Assert.False(tf1.IsNotNullable); + var attributes = tf1.GetAttributes(); + Assert.Empty(attributes); } } @@ -74201,18 +74262,20 @@ void symbolValidator(ModuleSymbol m) { bool isSource = !(m is PEModuleSymbol); - var bf1 = (MethodSymbol)m.GlobalNamespace.GetMember("B.F1"); - Assert.Equal("void B.F1(T11? t1) where T11 : class!", bf1.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints)); - TypeParameterSymbol t11 = bf1.TypeParameters[0]; - Assert.False(t11.ReferenceTypeConstraintIsNullable); + var b = m.GlobalNamespace.GetMember("B"); if (isSource) { - Assert.Empty(t11.GetAttributes()); + Assert.Empty(b.GetAttributes()); } else { - Assert.Equal("System.Runtime.CompilerServices.NullableAttribute(1)", t11.GetAttributes().Single().ToString()); + Assert.Equal("System.Runtime.CompilerServices.NullableContextAttribute(1)", b.GetAttributes().First().ToString()); } + var bf1 = (MethodSymbol)m.GlobalNamespace.GetMember("B.F1"); + Assert.Equal("void B.F1(T11? t1) where T11 : class!", bf1.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints)); + TypeParameterSymbol t11 = bf1.TypeParameters[0]; + Assert.False(t11.ReferenceTypeConstraintIsNullable); + Assert.Empty(t11.GetAttributes()); var bf2 = (MethodSymbol)m.GlobalNamespace.GetMember("B.F2"); Assert.Equal("void B.F2(T22 t2) where T22 : class?", bf2.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints)); @@ -74276,18 +74339,20 @@ void symbolValidator(ModuleSymbol m) { bool isSource = !(m is PEModuleSymbol); - var bf1 = (MethodSymbol)m.GlobalNamespace.GetMember("B.F1"); - Assert.Equal("void B.F1(T11? t1) where T11 : class!", bf1.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints)); - TypeParameterSymbol t11 = bf1.TypeParameters[0]; - Assert.False(t11.ReferenceTypeConstraintIsNullable); + var b = m.GlobalNamespace.GetMember("B"); if (isSource) { - Assert.Empty(t11.GetAttributes()); + Assert.Empty(b.GetAttributes()); } else { - Assert.Equal("System.Runtime.CompilerServices.NullableAttribute(1)", t11.GetAttributes().Single().ToString()); + Assert.Equal("System.Runtime.CompilerServices.NullableContextAttribute(1)", b.GetAttributes().First().ToString()); } + var bf1 = (MethodSymbol)m.GlobalNamespace.GetMember("B.F1"); + Assert.Equal("void B.F1(T11? t1) where T11 : class!", bf1.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints)); + TypeParameterSymbol t11 = bf1.TypeParameters[0]; + Assert.False(t11.ReferenceTypeConstraintIsNullable); + Assert.Empty(t11.GetAttributes()); var bf2 = (MethodSymbol)m.GlobalNamespace.GetMember("B.F2"); Assert.Equal("void B.F2(T22 t2) where T22 : class?", bf2.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints)); @@ -85554,7 +85619,7 @@ static void Main() expectedOutput: @"Hello 3", expectedSignatures: new[] { - // The ILDASM output is following,and Roslyn handles it correctly. + // The ILDASM output is following, and Roslyn handles it correctly. // Verifier tool gives different output due to the limitation of Reflection // @".method public hidebysig virtual instance System.Void Method(" + // @"System.String modopt([mscorlib]System.Runtime.CompilerServices.IsConst)[] modopt([mscorlib]System.Runtime.CompilerServices.IsConst) x," + @@ -85562,12 +85627,24 @@ static void Main() // @"!!X modopt([mscorlib]System.Runtime.CompilerServices.IsConst)[] modopt([mscorlib]System.Runtime.CompilerServices.IsConst) z) cil managed") Signature("Metadata.GD", "Method", @".method public hidebysig virtual instance System.Void Method(" + - @"[System.Runtime.CompilerServices.NullableAttribute(1)] " + @"modopt(System.Runtime.CompilerServices.IsConst) System.String[] x, " + - @"[System.Runtime.CompilerServices.NullableAttribute(1)] " + @"modopt(System.Runtime.CompilerServices.IsConst) System.UInt64[] y, "+ - @"[System.Runtime.CompilerServices.NullableAttribute(1)] " + @"modopt(System.Runtime.CompilerServices.IsConst) X[] z) cil managed"), + }, + symbolValidator: module => + { + var expected = +@"[NullableContext(1)] [Nullable({ 0, 1 })] Metadata.GD + void Method(System.String![]! x, System.UInt64[]! y, X[]! z) + [Nullable(0)] X + System.String![]! x + System.UInt64[]! y + X[]! z + GD() + +"; + var actual = NullableAttributesVisitor.GetString((PEModuleSymbol)module); + AssertEx.AssertEqualToleratingWhitespaceDifferences(expected, actual); }); } @@ -85665,6 +85742,10 @@ interface I2 // error CS0518: Predefined type 'System.Attribute' is not defined or imported Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound).WithArguments("System.Attribute").WithLocation(1, 1), // error CS0518: Predefined type 'System.Byte' is not defined or imported + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound).WithArguments("System.Byte").WithLocation(1, 1), + // error CS0518: Predefined type 'System.Attribute' is not defined or imported + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound).WithArguments("System.Attribute").WithLocation(1, 1), + // error CS0518: Predefined type 'System.Byte' is not defined or imported Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound).WithArguments("System.Byte").WithLocation(1, 1)); var compilation6 = CreateEmptyCompilation(source2, options: WithNonNullTypesTrue(TestOptions.ReleaseDll), references: new[] { compilation1.EmitToImageReference(), MscorlibRef }); diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs index bd253e6013321..2c84852163e5e 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs @@ -574,6 +574,7 @@ public void AllWellKnownTypes() case WellKnownType.System_FormattableString: case WellKnownType.System_Runtime_CompilerServices_FormattableStringFactory: case WellKnownType.System_Runtime_CompilerServices_NullableAttribute: + case WellKnownType.System_Runtime_CompilerServices_NullableContextAttribute: case WellKnownType.System_Runtime_CompilerServices_NullablePublicOnlyAttribute: case WellKnownType.System_Runtime_CompilerServices_IsReadOnlyAttribute: case WellKnownType.System_Runtime_CompilerServices_IsByRefLikeAttribute: @@ -900,6 +901,7 @@ public void AllWellKnownTypeMembers() case WellKnownMember.System_Array__Empty: case WellKnownMember.System_Runtime_CompilerServices_NullableAttribute__ctorByte: case WellKnownMember.System_Runtime_CompilerServices_NullableAttribute__ctorTransformFlags: + case WellKnownMember.System_Runtime_CompilerServices_NullableContextAttribute__ctor: case WellKnownMember.System_Runtime_CompilerServices_NullablePublicOnlyAttribute__ctor: case WellKnownMember.System_Span_T__ctor: case WellKnownMember.System_Span_T__get_Item: diff --git a/src/Compilers/Core/Portable/Emit/CommonPEModuleBuilder.cs b/src/Compilers/Core/Portable/Emit/CommonPEModuleBuilder.cs index 3f87479ec9a7f..c7ab5b825cbdf 100644 --- a/src/Compilers/Core/Portable/Emit/CommonPEModuleBuilder.cs +++ b/src/Compilers/Core/Portable/Emit/CommonPEModuleBuilder.cs @@ -7,13 +7,12 @@ using System.Diagnostics; using System.IO; using System.Linq; +using System.Security.Cryptography; using System.Threading; using Microsoft.CodeAnalysis.CodeGen; using Microsoft.CodeAnalysis.Emit.NoPia; using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; -using Microsoft.CodeAnalysis.Text; -using System.Security.Cryptography; namespace Microsoft.CodeAnalysis.Emit { diff --git a/src/Compilers/Core/Portable/MetadataReader/PEModule.cs b/src/Compilers/Core/Portable/MetadataReader/PEModule.cs index 7c08204587701..c9c37b1961b75 100644 --- a/src/Compilers/Core/Portable/MetadataReader/PEModule.cs +++ b/src/Compilers/Core/Portable/MetadataReader/PEModule.cs @@ -1164,6 +1164,22 @@ internal bool HasDecimalConstantAttribute(EntityHandle token, out ConstantValue return false; } + internal bool HasNullablePublicOnlyAttribute(EntityHandle token, out bool includesInternals) + { + AttributeInfo info = FindTargetAttribute(token, AttributeDescription.NullablePublicOnlyAttribute); + if (info.HasValue) + { + Debug.Assert(info.SignatureIndex == 0); + if (TryExtractValueFromAttribute(info.Handle, out bool value, s_attributeBooleanValueExtractor)) + { + includesInternals = value; + return true; + } + } + includesInternals = false; + return false; + } + internal ImmutableArray GetInternalsVisibleToAttributeValues(EntityHandle token) { List attrInfos = FindTargetAttributes(token, AttributeDescription.InternalsVisibleToAttribute); @@ -2435,6 +2451,20 @@ internal bool ContainsNoPiaLocalTypes() return _lazyContainsNoPiaLocalTypes == ThreeState.True; } + internal bool HasNullableContextAttribute(EntityHandle token, out byte value) + { + AttributeInfo info = FindTargetAttribute(token, AttributeDescription.NullableContextAttribute); + Debug.Assert(!info.HasValue || info.SignatureIndex == 0); + + if (!info.HasValue) + { + value = 0; + return false; + } + + return TryExtractValueFromAttribute(info.Handle, out value, s_attributeByteValueExtractor); + } + internal bool HasNullableAttribute(EntityHandle token, out byte defaultTransform, out ImmutableArray nullableTransforms) { AttributeInfo info = FindTargetAttribute(token, AttributeDescription.NullableAttribute); diff --git a/src/Compilers/Core/Portable/Symbols/Attributes/AttributeDescription.cs b/src/Compilers/Core/Portable/Symbols/Attributes/AttributeDescription.cs index cc08ec834924a..f4462fa93b35a 100644 --- a/src/Compilers/Core/Portable/Symbols/Attributes/AttributeDescription.cs +++ b/src/Compilers/Core/Portable/Symbols/Attributes/AttributeDescription.cs @@ -411,7 +411,8 @@ static AttributeDescription() }; private static readonly byte[][] s_signaturesOfNullableAttribute = { s_signature_HasThis_Void_Byte, s_signature_HasThis_Void_SzArray_Byte }; - private static readonly byte[][] s_signaturesOfNullablePublicOnlyAttribute = { s_signature_HasThis_Void }; + private static readonly byte[][] s_signaturesOfNullableContextAttribute = { s_signature_HasThis_Void_Byte }; + private static readonly byte[][] s_signaturesOfNullablePublicOnlyAttribute = { s_signature_HasThis_Void_Boolean }; private static readonly byte[][] s_signaturesOfExperimentalAttribute = { s_signature_HasThis_Void }; private static readonly byte[][] s_signaturesOfExcludeFromCodeCoverageAttribute = { s_signature_HasThis_Void }; @@ -539,6 +540,7 @@ static AttributeDescription() internal static readonly AttributeDescription AssemblyAlgorithmIdAttribute = new AttributeDescription("System.Reflection", "AssemblyAlgorithmIdAttribute", s_signaturesOfAssemblyAlgorithmIdAttribute); internal static readonly AttributeDescription DeprecatedAttribute = new AttributeDescription("Windows.Foundation.Metadata", "DeprecatedAttribute", s_signaturesOfDeprecatedAttribute); internal static readonly AttributeDescription NullableAttribute = new AttributeDescription("System.Runtime.CompilerServices", "NullableAttribute", s_signaturesOfNullableAttribute); + internal static readonly AttributeDescription NullableContextAttribute = new AttributeDescription("System.Runtime.CompilerServices", "NullableContextAttribute", s_signaturesOfNullableContextAttribute); internal static readonly AttributeDescription NullablePublicOnlyAttribute = new AttributeDescription("System.Runtime.CompilerServices", "NullablePublicOnlyAttribute", s_signaturesOfNullablePublicOnlyAttribute); internal static readonly AttributeDescription ExperimentalAttribute = new AttributeDescription("Windows.Foundation.Metadata", "ExperimentalAttribute", s_signaturesOfExperimentalAttribute); internal static readonly AttributeDescription ExcludeFromCodeCoverageAttribute = new AttributeDescription("System.Diagnostics.CodeAnalysis", "ExcludeFromCodeCoverageAttribute", s_signaturesOfExcludeFromCodeCoverageAttribute); diff --git a/src/Compilers/Core/Portable/WellKnownMember.cs b/src/Compilers/Core/Portable/WellKnownMember.cs index d0514fd21cf10..7e067350bd570 100644 --- a/src/Compilers/Core/Portable/WellKnownMember.cs +++ b/src/Compilers/Core/Portable/WellKnownMember.cs @@ -420,6 +420,7 @@ internal enum WellKnownMember System_Runtime_CompilerServices_NullableAttribute__ctorByte, System_Runtime_CompilerServices_NullableAttribute__ctorTransformFlags, + System_Runtime_CompilerServices_NullableContextAttribute__ctor, System_Runtime_CompilerServices_NullablePublicOnlyAttribute__ctor, System_Runtime_CompilerServices_ReferenceAssemblyAttribute__ctor, System_Runtime_CompilerServices_IsReadOnlyAttribute__ctor, diff --git a/src/Compilers/Core/Portable/WellKnownMembers.cs b/src/Compilers/Core/Portable/WellKnownMembers.cs index 1168db951e2e2..10552a0313da7 100644 --- a/src/Compilers/Core/Portable/WellKnownMembers.cs +++ b/src/Compilers/Core/Portable/WellKnownMembers.cs @@ -2915,12 +2915,22 @@ static WellKnownMembers() (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Void, (byte)SignatureTypeCode.SZArray, (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Byte, + // System_Runtime_CompilerServices_NullableContextAttribute__ctor + (byte)MemberFlags.Constructor, // Flags + (byte)WellKnownType.ExtSentinel, (byte)(WellKnownType.System_Runtime_CompilerServices_NullableContextAttribute - WellKnownType.ExtSentinel), // DeclaringTypeId + 0, // Arity + 1, // Method Signature + (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Void, + (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Byte, + + // System_Runtime_CompilerServices_NullablePublicOnlyAttribute__ctor (byte)MemberFlags.Constructor, // Flags (byte)WellKnownType.ExtSentinel, (byte)(WellKnownType.System_Runtime_CompilerServices_NullablePublicOnlyAttribute - WellKnownType.ExtSentinel), // DeclaringTypeId 0, // Arity - 0, // Method Signature + 1, // Method Signature (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Void, + (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Boolean, // System_Runtime_CompilerServices_ReferenceAssemblyAttribute__ctor (byte)MemberFlags.Constructor, // Flags @@ -3790,6 +3800,7 @@ static WellKnownMembers() ".ctor", // System_Runtime_CompilerServices_NullableAttribute__ctorByte ".ctor", // System_Runtime_CompilerServices_NullableAttribute__ctorTransformFlags + ".ctor", // System_Runtime_CompilerServices_NullableContextAttribute__ctor ".ctor", // System_Runtime_CompilerServices_NullablePublicOnlyAttribute__ctor ".ctor", // System_Runtime_CompilerServices_ReferenceAssemblyAttribute__ctor ".ctor", // System_Runtime_CompilerServices_IsReadOnlyAttribute__ctor diff --git a/src/Compilers/Core/Portable/WellKnownTypes.cs b/src/Compilers/Core/Portable/WellKnownTypes.cs index 9b8c212fb3236..b7067725c333e 100644 --- a/src/Compilers/Core/Portable/WellKnownTypes.cs +++ b/src/Compilers/Core/Portable/WellKnownTypes.cs @@ -264,6 +264,7 @@ internal enum WellKnownType Microsoft_CodeAnalysis_Runtime_Instrumentation, System_Runtime_CompilerServices_NullableAttribute, + System_Runtime_CompilerServices_NullableContextAttribute, System_Runtime_CompilerServices_NullablePublicOnlyAttribute, System_Runtime_CompilerServices_ReferenceAssemblyAttribute, @@ -563,6 +564,7 @@ internal static class WellKnownTypes "Microsoft.CodeAnalysis.Runtime.Instrumentation", "System.Runtime.CompilerServices.NullableAttribute", + "System.Runtime.CompilerServices.NullableContextAttribute", "System.Runtime.CompilerServices.NullablePublicOnlyAttribute", "System.Runtime.CompilerServices.ReferenceAssemblyAttribute", diff --git a/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs b/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs index 60b01f43c95ec..631c76a2be885 100644 --- a/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs +++ b/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs @@ -51,12 +51,39 @@ public NullableAttribute(byte[] transformFlags) } "; + protected const string NullableContextAttributeDefinition = @" +namespace System.Runtime.CompilerServices +{ + [System.AttributeUsage( + AttributeTargets.Module | + AttributeTargets.Class | + AttributeTargets.Delegate | + AttributeTargets.Interface | + AttributeTargets.Method | + AttributeTargets.Struct, + AllowMultiple = false, + Inherited = false)] + public sealed class NullableContextAttribute : Attribute + { + public readonly byte Flag; + public NullableContextAttribute(byte flag) + { + Flag = flag; + } + } +}"; + protected const string NullablePublicOnlyAttributeDefinition = @" namespace System.Runtime.CompilerServices { [System.AttributeUsage(AttributeTargets.Module, AllowMultiple = false)] public sealed class NullablePublicOnlyAttribute : Attribute { + public readonly bool IncludesInternals; + public NullablePublicOnlyAttribute(bool includesInternals) + { + IncludesInternals = includesInternals; + } } }"; diff --git a/src/Compilers/Test/Utilities/CSharp/NullableAttributesVisitor.cs b/src/Compilers/Test/Utilities/CSharp/NullableAttributesVisitor.cs index 75fc7f4b2cae1..05be558e7a052 100644 --- a/src/Compilers/Test/Utilities/CSharp/NullableAttributesVisitor.cs +++ b/src/Compilers/Test/Utilities/CSharp/NullableAttributesVisitor.cs @@ -4,6 +4,7 @@ using System.Collections.Immutable; using System.Text; using Microsoft.CodeAnalysis.CSharp.Symbols; +using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE; namespace Microsoft.CodeAnalysis.CSharp.Test.Utilities { @@ -12,19 +13,22 @@ namespace Microsoft.CodeAnalysis.CSharp.Test.Utilities /// internal sealed class NullableAttributesVisitor : CSharpSymbolVisitor { - internal static string GetString(Symbol symbol) + internal static string GetString(PEModuleSymbol module) { var builder = new StringBuilder(); - var visitor = new NullableAttributesVisitor(builder); - visitor.Visit(symbol); + var visitor = new NullableAttributesVisitor(module, builder); + visitor.Visit(module); return builder.ToString(); } + private readonly PEModuleSymbol _module; private readonly StringBuilder _builder; private readonly HashSet _reported; + private CSharpAttributeData _nullableContext; - private NullableAttributesVisitor(StringBuilder builder) + private NullableAttributesVisitor(PEModuleSymbol module, StringBuilder builder) { + _module = module; _builder = builder; _reported = new HashSet(); } @@ -46,19 +50,47 @@ public override void VisitNamespace(NamespaceSymbol @namespace) public override void VisitNamedType(NamedTypeSymbol type) { + var previousContext = _nullableContext; + _nullableContext = GetNullableContextAttribute(type.GetAttributes()) ?? _nullableContext; + ReportSymbol(type); VisitList(type.TypeParameters); - VisitList(type.GetMembers()); + + foreach (var member in type.GetMembers()) + { + // Skip accessors since those are covered by associated symbol. + if (member.IsAccessor()) continue; + Visit(member); + } + + _nullableContext = previousContext; } public override void VisitMethod(MethodSymbol method) { - // Skip accessors since those are covered by associated symbol. - if (method.IsAccessor()) return; + var previousContext = _nullableContext; + _nullableContext = GetNullableContextAttribute(method.GetAttributes()) ?? _nullableContext; ReportSymbol(method); VisitList(method.TypeParameters); VisitList(method.Parameters); + + _nullableContext = previousContext; + } + + public override void VisitEvent(EventSymbol @event) + { + ReportSymbol(@event); + Visit(@event.AddMethod); + Visit(@event.RemoveMethod); + } + + public override void VisitProperty(PropertySymbol property) + { + ReportSymbol(property); + VisitList(property.Parameters); + Visit(property.GetMethod); + Visit(property.SetMethod); } public override void VisitTypeParameter(TypeParameterSymbol typeParameter) @@ -74,13 +106,28 @@ private void VisitList(ImmutableArray symbols) where TSymbol : } } + /// + /// Return the containing symbol used in the hierarchy here. Specifically, the + /// hierarchy contains types, members, and parameters only, and accessors are + /// considered members of the associated symbol rather than the type. + /// + private static Symbol GetContainingSymbol(Symbol symbol) + { + if (symbol.IsAccessor()) + { + return ((MethodSymbol)symbol).AssociatedSymbol; + } + var containingSymbol = symbol.ContainingSymbol; + return containingSymbol?.Kind == SymbolKind.Namespace ? null : containingSymbol; + } + private static string GetIndentString(Symbol symbol) { int level = 0; while (true) { - symbol = symbol.ContainingSymbol; - if (symbol.Kind == SymbolKind.Namespace) + symbol = GetContainingSymbol(symbol); + if (symbol is null) { break; } @@ -98,7 +145,8 @@ private static string GetIndentString(Symbol symbol) private void ReportContainingSymbols(Symbol symbol) { - if (symbol.Kind == SymbolKind.Namespace) + symbol = GetContainingSymbol(symbol); + if (symbol is null) { return; } @@ -106,28 +154,64 @@ private void ReportContainingSymbols(Symbol symbol) { return; } - ReportContainingSymbols(symbol.ContainingSymbol); - ReportSymbol(symbol, includeAlways: true); + ReportContainingSymbols(symbol); + _builder.Append(GetIndentString(symbol)); + _builder.AppendLine(symbol.ToDisplayString(_displayFormat)); + _reported.Add(symbol); } - private void ReportSymbol(Symbol symbol, bool includeAlways = false) + private void ReportSymbol(Symbol symbol) { - var attributes = (symbol is MethodSymbol method) ? method.GetReturnTypeAttributes() : symbol.GetAttributes(); - var nullableAttribute = GetAttribute(attributes, "System.Runtime.CompilerServices", "NullableAttribute"); - if (!includeAlways && nullableAttribute == null) + var nullableContextAttribute = GetNullableContextAttribute(symbol.GetAttributes()); + var nullableAttribute = GetNullableAttribute((symbol is MethodSymbol method) ? method.GetReturnTypeAttributes() : symbol.GetAttributes()); + + if (nullableContextAttribute == null && nullableAttribute == null) { - return; + if (_nullableContext == null) + { + // No explicit attributes on this symbol or above. + return; + } + // No explicit attributes on this symbol. Check if nullability metadata was dropped. + if (!_module.ShouldDecodeNullableAttributes(GetAccessSymbol(symbol))) + { + return; + } } - ReportContainingSymbols(symbol.ContainingSymbol); + + ReportContainingSymbols(symbol); _builder.Append(GetIndentString(symbol)); + + if (nullableContextAttribute != null) + { + _builder.Append($"{ReportAttribute(nullableContextAttribute)} "); + } + if (nullableAttribute != null) { _builder.Append($"{ReportAttribute(nullableAttribute)} "); } + _builder.AppendLine(symbol.ToDisplayString(_displayFormat)); _reported.Add(symbol); } + private static Symbol GetAccessSymbol(Symbol symbol) + { + while (true) + { + switch (symbol.Kind) + { + case SymbolKind.Parameter: + case SymbolKind.TypeParameter: + symbol = symbol.ContainingSymbol; + break; + default: + return symbol; + } + } + } + private static string ReportAttribute(CSharpAttributeData attribute) { var builder = new StringBuilder(); @@ -175,6 +259,12 @@ static void printValue(StringBuilder builder, TypedConstant value) } } + private static CSharpAttributeData GetNullableContextAttribute(ImmutableArray attributes) => + GetAttribute(attributes, "System.Runtime.CompilerServices", "NullableContextAttribute"); + + private static CSharpAttributeData GetNullableAttribute(ImmutableArray attributes) => + GetAttribute(attributes, "System.Runtime.CompilerServices", "NullableAttribute"); + private static CSharpAttributeData GetAttribute(ImmutableArray attributes, string namespaceName, string name) { foreach (var attribute in attributes) diff --git a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/WellKnownTypeValidationTests.vb b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/WellKnownTypeValidationTests.vb index b52215bed63da..d96ec2d735ae6 100644 --- a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/WellKnownTypeValidationTests.vb +++ b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/WellKnownTypeValidationTests.vb @@ -513,6 +513,7 @@ End Namespace Case WellKnownType.System_FormattableString, WellKnownType.System_Runtime_CompilerServices_FormattableStringFactory, WellKnownType.System_Runtime_CompilerServices_NullableAttribute, + WellKnownType.System_Runtime_CompilerServices_NullableContextAttribute, WellKnownType.System_Runtime_CompilerServices_NullablePublicOnlyAttribute, WellKnownType.System_Span_T, WellKnownType.System_ReadOnlySpan_T, @@ -575,6 +576,7 @@ End Namespace Case WellKnownType.System_FormattableString, WellKnownType.System_Runtime_CompilerServices_FormattableStringFactory, WellKnownType.System_Runtime_CompilerServices_NullableAttribute, + WellKnownType.System_Runtime_CompilerServices_NullableContextAttribute, WellKnownType.System_Runtime_CompilerServices_NullablePublicOnlyAttribute, WellKnownType.System_Span_T, WellKnownType.System_ReadOnlySpan_T, @@ -643,6 +645,7 @@ End Namespace Case WellKnownMember.System_Array__Empty, WellKnownMember.System_Runtime_CompilerServices_NullableAttribute__ctorByte, WellKnownMember.System_Runtime_CompilerServices_NullableAttribute__ctorTransformFlags, + WellKnownMember.System_Runtime_CompilerServices_NullableContextAttribute__ctor, WellKnownMember.System_Runtime_CompilerServices_NullablePublicOnlyAttribute__ctor, WellKnownMember.System_Span_T__ctor, WellKnownMember.System_Span_T__get_Item, @@ -781,6 +784,7 @@ End Namespace Case WellKnownMember.System_Array__Empty, WellKnownMember.System_Runtime_CompilerServices_NullableAttribute__ctorByte, WellKnownMember.System_Runtime_CompilerServices_NullableAttribute__ctorTransformFlags, + WellKnownMember.System_Runtime_CompilerServices_NullableContextAttribute__ctor, WellKnownMember.System_Runtime_CompilerServices_NullablePublicOnlyAttribute__ctor, WellKnownMember.System_Span_T__ctor, WellKnownMember.System_Span_T__get_Item, diff --git a/src/EditorFeatures/Test/MetadataAsSource/MetadataAsSourceTests.cs b/src/EditorFeatures/Test/MetadataAsSource/MetadataAsSourceTests.cs index cfd73056f9630..d6a3f8501a0c0 100644 --- a/src/EditorFeatures/Test/MetadataAsSource/MetadataAsSourceTests.cs +++ b/src/EditorFeatures/Test/MetadataAsSource/MetadataAsSourceTests.cs @@ -2131,8 +2131,8 @@ public class TestType {{ public TestType(); - public void [|M|]<[NullableAttribute(1)] - T>() where T : notnull; + [NullableContextAttribute(1)] + public void [|M|]() where T : notnull; }}"; using (var context = TestContext.Create(