From f1f48e7a4f90d6b93b76fd3b746ab5d5b6ec5e89 Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Tue, 11 Jun 2019 21:27:38 -0700 Subject: [PATCH 01/16] Add option to emit nullable metadata for public members only --- docs/features/nullable-metadata.md | 191 +++++++ docs/features/nullable-reference-types.md | 64 +-- .../Portable/Binder/Semantics/AccessCheck.cs | 47 +- .../Portable/BoundTree/UnboundLambda.cs | 24 +- .../Portable/CSharpCompilationOptions.cs | 34 +- .../Portable/Compilation/CSharpCompilation.cs | 19 + .../Lowering/LocalRewriter/LocalRewriter.cs | 25 +- .../CSharp/Portable/NullableContextOptions.cs | 2 - .../CSharp/Portable/Symbols/MethodSymbol.cs | 2 +- .../FieldSymbolWithAttributesAndModifiers.cs | 7 +- .../Symbols/Source/LocalFunctionSymbol.cs | 19 +- .../Symbols/Source/ParameterHelpers.cs | 16 +- .../Symbols/Source/SourceConstructorSymbol.cs | 7 +- .../Source/SourceDelegateMethodSymbol.cs | 12 +- .../Symbols/Source/SourceEventSymbol.cs | 12 +- .../Symbols/Source/SourceFieldSymbol.cs | 7 +- .../Source/SourceMemberContainerSymbol.cs | 21 +- .../Symbols/Source/SourceNamedTypeSymbol.cs | 3 +- .../Source/SourceOrdinaryMethodSymbol.cs | 17 +- .../Source/SourceParameterSymbolBase.cs | 2 +- .../Symbols/Source/SourcePropertySymbol.cs | 21 +- .../Source/SourceTypeParameterSymbol.cs | 68 ++- .../SourceUserDefinedOperatorSymbolBase.cs | 16 +- ...thesizedEmbeddedNullableAttributeSymbol.cs | 49 +- ...dEmbeddedNullableMembersAttributeSymbol.cs | 56 ++ .../Synthesized/SynthesizedFieldSymbolBase.cs | 9 +- .../Synthesized/SynthesizedParameterSymbol.cs | 6 +- .../Portable/Symbols/TypeSymbolExtensions.cs | 27 +- .../Attributes/AttributeTests_Nullable.cs | 487 ++++++++++++++++++ .../AttributeTests_NullableMembers.cs | 10 + .../CSharpCompilationOptionsTests.cs | 4 +- .../Symbol/Symbols/MissingSpecialMember.cs | 2 + .../Attributes/AttributeDescription.cs | 2 + .../Core/Portable/WellKnownMember.cs | 1 + .../Core/Portable/WellKnownMembers.cs | 9 + src/Compilers/Core/Portable/WellKnownTypes.cs | 2 + .../Test/Utilities/CSharp/CSharpTestBase.cs | 22 +- .../CSharp/NullableAttributesVisitor.cs | 213 ++++++++ .../WellKnownTypeValidationTests.vb | 3 + 39 files changed, 1302 insertions(+), 236 deletions(-) create mode 100644 docs/features/nullable-metadata.md create mode 100644 src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEmbeddedNullableMembersAttributeSymbol.cs create mode 100644 src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_NullableMembers.cs create mode 100644 src/Compilers/Test/Utilities/CSharp/NullableAttributesVisitor.cs diff --git a/docs/features/nullable-metadata.md b/docs/features/nullable-metadata.md new file mode 100644 index 0000000000000..acdedd34234f4 --- /dev/null +++ b/docs/features/nullable-metadata.md @@ -0,0 +1,191 @@ +Nullable Metadata +========= +The following describes the representation of nullable annotations in metadata. + +## NullableAttribute +Type references are annotated in metadata with a `NullableAttribute`. + +```C# +namespace System.Runtime.CompilerServices +{ + [AttributeUsage( + AttributeTargets.Class | + AttributeTargets.Event | + AttributeTargets.Field | + AttributeTargets.GenericParameter | + AttributeTargets.Parameter | + AttributeTargets.Property | + AttributeTargets.ReturnValue, + AllowMultiple = false)] + public sealed class NullableAttribute : Attribute + { + public readonly byte[] Flags; + public NullableAttribute(byte flag) + { + Flags = new byte[] { flag }; + } + public NullableAttribute(byte[] flags) + { + Flags = flags; + } + } +} +``` + +The `NullableAttribute` 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. + +Each type reference in metadata may have an associated `NullableAttribute` with a `byte[]` where each `byte` +represents nullability: 0 for oblivious, 1 for not annotated, and 2 for annotated. + +The `byte[]` is constructed as follows: +- Reference type: the nullability (0, 1, or 2), followed by the representation of the type arguments in order including containing types +- Nullable value type: the representation of the type argument only +- Value type: the representation of the type arguments in order including containing types +- Array: the nullability (0, 1, or 2), followed by the representation of the element type +- Tuple: the representation of the tuple elements in order +- Type parameter reference: the nullability (0, 1, or 2, with 0 for unconstrained type parameter) + +Note that certain type references are represented by an empty `byte[]`: +- Non-generic value types, and +- Nullable value types and tuples where all elements are represented by an empty `byte[]`. + +### Optimizations + +If the `byte[]` is empty, the `NullableAttribute` is omitted. + +If all values in the `byte[]` are the same, the `NullableAttribute` is constructed with that single `byte` value. (For instance, `NullableAttribute(1)` rather than `NullableAttribute(new byte[] { 1, 1 }))`.) + +### Type parameters +Each type parameter definition may have an associated `NullableAttribute` with a single `byte`: + +1. `notnull` constraint: `NullableAttribute(1)` +2. `class` constraint in `#nullable disable` context: `NullableAttribute(0)` +3. `class` constraint in `#nullable enable` context: `NullableAttribute(1)` +4. `class?` constraint: `NullableAttribute(2)` +5. No `notnull`, `class`, `struct`, `unmanaged`, or type constraints in `#nullable disable` context: `NullableAttribute(0)` +6. No `notnull`, `class`, `struct`, `unmanaged`, or type constraints in `#nullable enable` context +(equivalent to an `object?` constraint): `NullableAttribute(2)` + +## NullableContextAttribute +`NullableContextAttribute` can be used to indicate the nullability of type references that have no `NullableAttribute` annotations. + +```C# +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; + } + } +} +``` + +The `NullableContextAttribute` 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. + +The `NullableContextAttribute` is optional - nullable annotations can be represented in metadata with full fidelity using `NullableAttribute` only. + +`NullableContextAttribute` is valid in metadata at the module level and at type and method declarations. +The `byte` value represents the implicit `NullableAttribute` value for type references within that scope +that do not have an explicit `NullableAttribute` and would not otherwise be represented by an empty `byte[]`. +The nearest `NullableContextAttribute` in the metadata hierarchy applies. +If there are no `NullableContextAttribute` attributes in the hierarchy, +missing `NullableAttribute` attributes are treated as `NullableAttribute(0)`. + +The attribute is not inherited. + +The C#8 compiler uses the following algorithm to determine which `NullableAttribute` and +`NullableContextAttribute` attributes to emit. +First, `NullableAttribute` attributes are generated at each type reference and type parameter definition by: +calculating the `byte[]`, skipping empty `byte[]`, and collapsing `byte[]` to single `byte` where possible. +Then at each level in metadata hierarchy starting at methods: +The compiler finds the most common single `byte` value across all the `NullableAttribute` attributes at that level +and any `NullableContextAttribute` attributes on immediate children. +If there are no single `byte` values, there are no changes. +Otherwise, a `NullableContext(value)` attribute is created at that level where `value` is most common +value (preferring `0` over `1` and preferring `1` over `2`), and all `NullableAttribute` and `NullableContextAttribute` attributes with that value are removed. +That iterative process continues up to the module level. +If the common value at the module level is a value other than `0` (the default), a module level `NullableContext(value)` attribute is emitted. + +Note that an assembly compiled with C#8 where all reference types are oblivious will have no +`NullableContextAttribute` and no `NullableAttribute` attributes emitted. +That is equivalent to a legacy assembly. + +### Examples +```C# +// C# representation of metadata +[NullableContext(2)] +class Program +{ + string s; // string? + [Nullable({ 2, 1, 2 }] Dictionary d; // Dictionary? + [Nullable(1)] int[] a; // int[]! + int[] b; // int[]? + [Nullable({ 0, 2 })] object[] c; // object?[]~ +} +``` + +## NullableMembersAttribute + +`NullableMembersAttribute` can be used to indicate whether nullable attributes were included for `private` or `internal` members. + +```C# +namespace System.Runtime.CompilerServices +{ + public enum NullableMembers + { + Public = 0, // public and protected only + Internal = 1, // public, protected, internal + All = 2, + } + [System.AttributeUsage(AttributeTargets.Module, AllowMultiple = false)] + public sealed class NullableMembersAttribute : Attribute + { + public readonly NullableMembers Members; + public NullableMembersAttribute(NullableMembers members) + { + Members = members; + } + } +} +``` + +The `NullableMembersAttribute` 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. + +The `NullableMembersAttribute` must be emitted if nullable attributes are dropped for +`private` or `internal` members to allow tools to correctly interpret the nullability of members +without explicit `NullableAttribute` attributes. + +To reduce the size of metadata, the C#8 compiler will not emit attributes `private` members, +and the compiler will only emit attributes for `internal` members if the assembly contains +`InternalsVisibleToAttribute` attributes. +The compiler will emit a `[module: NullableMembers(...)]` attribute to indicate which members were included. + +_If necessary, a compiler option could be added in a future release to override the default behavior and +explicitly emit or drop nullable attributes for `private` or `internal` members._ + +## Compatibility + +The nullable metadata does not include an explicit version number. +Where possible, the compiler will silently ignore attribute forms that are unexpected. + +The metadata format described here is incompatible with the format used by earlier C#8 previews: +1. Concrete non-generic value types are no longer included in the `byte[]`, and +2. `NullableContextAttribute` attributes are used in place of explicit `NullableAttribute` attributes. + +Those differences mean that assemblies compiled with earlier previews may be read incorrectly by later previews, +and assemblies compiled with later previews may be read incorrectly by earlier previews. diff --git a/docs/features/nullable-reference-types.md b/docs/features/nullable-reference-types.md index 339c784a02b2b..bdf189e22b40e 100644 --- a/docs/features/nullable-reference-types.md +++ b/docs/features/nullable-reference-types.md @@ -22,70 +22,8 @@ Dictionary? OptDictionaryOptValues; // dictionary may be null, ``` A warning is reported when annotating a reference type with `?` outside a `#nullable` context. -In metadata, nullable reference types are annotated with a `[Nullable]` attribute. -```c# -namespace System.Runtime.CompilerServices -{ - [AttributeUsage( - AttributeTargets.Class | - AttributeTargets.GenericParameter | - AttributeTargets.Event | AttributeTargets.Field | AttributeTargets.Property | - AttributeTargets.Parameter | AttributeTargets.ReturnValue, - AllowMultiple = false)] - public sealed class NullableAttribute : Attribute - { - public readonly byte[] NullableFlags; - - public NullableAttribute(byte b) - { - NullableFlags = new byte[] { b }; - } - - public NullableAttribute(byte[] b) - { - NullableFlags = b; - } - } -} -``` - -Each type reference is accompanied by a NullableAttribute with an array of bytes, where 0 is Oblivious, 1 is NotAnnotated and 2 is Annotated. -All value types are marked with flag 0 (oblivious). - -To optimize trivial cases the attribute can be omitted, or instead can be replaced with an attribute that takes a single byte value rather than an array. +[Metadata representation](nullable-metadata.md) -Trivial/optimized cases: -1) All parts are NotAnnotated – a NullableAttribute with a single value 1 (rather than an array of 1s) -2) All parts are Annotated - a NullableAttribute with a single value 2 (rather than an array of 2s) -3) All parts are Oblivious – the attribute is omitted, this matches how we interpret the lack of an attribute in legacy assemblies. - For completeness, we would also recognize a NullableAttribute with a single value 0 (rather than an array of 0s), - but compiler will never emit an attribute like this. - -NullableAttribute(1) should be placed on a type parameter definition that has a `notnull` constraint. -NullableAttribute(1) should be placed on a type parameter definition that has a `class!` constraint. -NullableAttribute(2) should be placed on a type parameter definition that has a `class?` constraint. -NullableAttribute(2) should be placed on a type parameter definition that has no `notnull`, `class`, `struct`, `unmanaged` and type constraints and -is declared in a context where nullable type annotations are allowed, that is equivalent to having an `object?` constraint. -Other forms of NullableAttribute are not emitted on type parameter definitions and are not specially recognized on them. - -The `NullableAttribute` type declaration is synthesized by the compiler if it is not included in the compilation, but is needed to produce the output. - -```c# -// C# representation of metadata -[Nullable(2)] -string OptString; // string? -[Nullable(new[] { 2, 1, 2 })] -Dictionary OptDictionaryOptValues; // Dictionary? -string[] Oblivious1; // string~[]~ -[Nullable(0)] string[] Oblivious2; // string~[]~ -[Nullable(new[] { 0, 0 })] string[] Oblivious3; // string~[]~ -[Nullable(1)] string[] NotNull1; // string![]! -[Nullable(new[] { 1, 1 })] string[] NotNull2; // string![]! -[Nullable(new[] { 0, 2 })] string[] ObliviousMaybeNull; // string?[]~ -[Nullable(new[] { 1, 2 })] string[] NotNullMaybeNull; // string?[]! -int Int; // int -Nullable NullableInt1; // Nullable -``` ## Declaration warnings _Describe warnings reported for declarations in initial binding._ diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/AccessCheck.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/AccessCheck.cs index 48a8bb054a347..4c042ebe0409c 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/AccessCheck.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/AccessCheck.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; -using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; @@ -57,6 +56,52 @@ public static bool IsSymbolAccessible( return IsSymbolAccessibleCore(symbol, within, throughTypeOpt, out failedThroughTypeCheck, within.DeclaringCompilation, ref useSiteDiagnostics, basesBeingResolved); } + internal static bool IsPublicOrInternal(Symbol symbol, out bool isInternal) + { + Debug.Assert(!(symbol is null)); + + switch (symbol.Kind) + { + case SymbolKind.NamedType: + case SymbolKind.Event: + case SymbolKind.Field: + case SymbolKind.Method: + case SymbolKind.Property: + break; + case SymbolKind.TypeParameter: + symbol = symbol.ContainingSymbol; + break; + default: + throw ExceptionUtilities.UnexpectedValue(symbol.Kind); + } + + isInternal = false; + + do + { + switch (symbol.DeclaredAccessibility) + { + case Accessibility.Public: + case Accessibility.Protected: + case Accessibility.ProtectedOrInternal: + break; + case Accessibility.Internal: + case Accessibility.ProtectedAndInternal: + isInternal = true; + break; + case Accessibility.Private: + return false; + default: + throw ExceptionUtilities.UnexpectedValue(symbol.DeclaredAccessibility); + } + + symbol = symbol.ContainingType; + } + while (!(symbol is null)); + + return true; + } + /// /// Checks if 'symbol' is accessible from within 'within', which must be a NamedTypeSymbol /// or an AssemblySymbol. diff --git a/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs b/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs index 8cfc027f6ef13..937c5f41d3ec7 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs @@ -493,6 +493,7 @@ private BoundLambda ReallyBind(NamedTypeSymbol delegateType) BoundBlock block; var diagnostics = DiagnosticBag.GetInstance(); + var compilation = Binder.Compilation; // when binding for real (not for return inference), there is still // a good chance that we could reuse a body of a lambda previously bound for @@ -516,7 +517,7 @@ private BoundLambda ReallyBind(NamedTypeSymbol delegateType) } lambdaSymbol = new LambdaSymbol( - Binder.Compilation, + compilation, Binder.ContainingMemberOrLambda, _unboundLambda, cacheKey.ParameterTypes, @@ -528,23 +529,24 @@ private BoundLambda ReallyBind(NamedTypeSymbol delegateType) if (lambdaSymbol.RefKind == CodeAnalysis.RefKind.RefReadOnly) { - Binder.Compilation.EnsureIsReadOnlyAttributeExists(diagnostics, lambdaSymbol.DiagnosticLocation, modifyCompilation: false); + compilation.EnsureIsReadOnlyAttributeExists(diagnostics, lambdaSymbol.DiagnosticLocation, modifyCompilation: false); } var lambdaParameters = lambdaSymbol.Parameters; - ParameterHelpers.EnsureIsReadOnlyAttributeExists(lambdaParameters, diagnostics, modifyCompilation: false); + ParameterHelpers.EnsureIsReadOnlyAttributeExists(compilation, lambdaParameters, diagnostics, modifyCompilation: false); if (returnType.HasType) { - if (returnType.NeedsNullableAttribute()) + if (compilation.ShouldEmitNullableAttributes(lambdaSymbol) && + returnType.NeedsNullableAttribute()) { - Binder.Compilation.EnsureNullableAttributeExists(diagnostics, lambdaSymbol.DiagnosticLocation, modifyCompilation: false); - // Note: we don't need to warn on annotations used without NonNullTypes context for lambdas, as this is handled in binding already + compilation.EnsureNullableAttributeExists(diagnostics, lambdaSymbol.DiagnosticLocation, modifyCompilation: false); + // Note: we don't need to warn on annotations used in #nullable disable context for lambdas, as this is handled in binding already } } - ParameterHelpers.EnsureNullableAttributeExists(lambdaParameters, diagnostics, modifyCompilation: false); - // Note: we don't need to warn on annotations used without NonNullTypes context for lambdas, as this is handled in binding already + ParameterHelpers.EnsureNullableAttributeExists(compilation, lambdaSymbol, lambdaParameters, diagnostics, modifyCompilation: false); + // Note: we don't need to warn on annotations used in #nullable disable context for lambdas, as this is handled in binding already block = BindLambdaBody(lambdaSymbol, lambdaBodyBinder, diagnostics); @@ -553,7 +555,7 @@ private BoundLambda ReallyBind(NamedTypeSymbol delegateType) haveLambdaBodyAndBinders: - bool reachableEndpoint = ControlFlowPass.Analyze(Binder.Compilation, lambdaSymbol, block, diagnostics); + bool reachableEndpoint = ControlFlowPass.Analyze(compilation, lambdaSymbol, block, diagnostics); if (reachableEndpoint) { if (DelegateNeedsReturn(invokeMethod)) @@ -571,8 +573,8 @@ private BoundLambda ReallyBind(NamedTypeSymbol delegateType) { if (returnType.HasType && // Can be null if "delegateType" is not actually a delegate type. !returnType.IsVoidType() && - !returnType.Type.IsNonGenericTaskType(Binder.Compilation) && - !returnType.Type.IsGenericTaskType(Binder.Compilation)) + !returnType.Type.IsNonGenericTaskType(compilation) && + !returnType.Type.IsGenericTaskType(compilation)) { // Cannot convert async {0} to delegate type '{1}'. An async {0} may return void, Task or Task<T>, none of which are convertible to '{1}'. diagnostics.Add(ErrorCode.ERR_CantConvAsyncAnonFuncReturns, lambdaSymbol.DiagnosticLocation, lambdaSymbol.MessageID.Localize(), delegateType); diff --git a/src/Compilers/CSharp/Portable/CSharpCompilationOptions.cs b/src/Compilers/CSharp/Portable/CSharpCompilationOptions.cs index d37d2f7f45b6e..eeeb73223a9fc 100644 --- a/src/Compilers/CSharp/Portable/CSharpCompilationOptions.cs +++ b/src/Compilers/CSharp/Portable/CSharpCompilationOptions.cs @@ -3,12 +3,10 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.ComponentModel; using System.Linq; -using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; -using System.Diagnostics; -using System.ComponentModel; namespace Microsoft.CodeAnalysis.CSharp { @@ -40,6 +38,13 @@ public sealed class CSharpCompilationOptions : CompilationOptions, IEquatable public NullableContextOptions NullableContextOptions { get; private set; } + /// + /// Emit nullable attributes for only those members that are visible outside the assembly + /// (public, protected, and if any [InternalsVisibleTo] attributes, internal members). + /// If false, attributes are emitted for all members regardless of visibility. + /// + internal bool EmitPublicNullableMetadataOnly { get; private set; } + // Defaults correspond to the compiler's defaults or indicate that the user did not specify when that is significant. // That's significant when one option depends on another's setting. SubsystemVersion depends on Platform and Target. public CSharpCompilationOptions( @@ -86,7 +91,8 @@ public CSharpCompilationOptions( referencesSupersedeLowerVersions: false, publicSign: publicSign, topLevelBinderFlags: BinderFlags.None, - nullableContextOptions: nullableContextOptions) + nullableContextOptions: nullableContextOptions, + emitPublicNullableMetadataOnly: false) { } @@ -209,7 +215,8 @@ internal CSharpCompilationOptions( bool referencesSupersedeLowerVersions, bool publicSign, BinderFlags topLevelBinderFlags, - NullableContextOptions nullableContextOptions) + NullableContextOptions nullableContextOptions, + bool emitPublicNullableMetadataOnly) : base(outputKind, reportSuppressedDiagnostics, moduleName, mainTypeName, scriptClassName, cryptoKeyContainer, cryptoKeyFile, cryptoPublicKey, delaySign, publicSign, optimizationLevel, checkOverflow, platform, generalDiagnosticOption, warningLevel, specificDiagnosticOptions.ToImmutableDictionaryOrEmpty(), @@ -221,6 +228,7 @@ internal CSharpCompilationOptions( this.AllowUnsafe = allowUnsafe; this.TopLevelBinderFlags = topLevelBinderFlags; this.NullableContextOptions = nullableContextOptions; + this.EmitPublicNullableMetadataOnly = emitPublicNullableMetadataOnly; } private CSharpCompilationOptions(CSharpCompilationOptions other) : this( @@ -254,7 +262,8 @@ private CSharpCompilationOptions(CSharpCompilationOptions other) : this( reportSuppressedDiagnostics: other.ReportSuppressedDiagnostics, publicSign: other.PublicSign, topLevelBinderFlags: other.TopLevelBinderFlags, - nullableContextOptions: other.NullableContextOptions) + nullableContextOptions: other.NullableContextOptions, + emitPublicNullableMetadataOnly: other.EmitPublicNullableMetadataOnly) { } @@ -402,6 +411,16 @@ public CSharpCompilationOptions WithNullableContextOptions(NullableContextOption return new CSharpCompilationOptions(this) { NullableContextOptions = options }; } + internal CSharpCompilationOptions WithEmitPublicNullableMetadataOnly(bool emitPublicNullableMetadataOnly) + { + if (emitPublicNullableMetadataOnly == this.EmitPublicNullableMetadataOnly) + { + return this; + } + + return new CSharpCompilationOptions(this) { EmitPublicNullableMetadataOnly = emitPublicNullableMetadataOnly }; + } + public CSharpCompilationOptions WithAllowUnsafe(bool enabled) { if (enabled == this.AllowUnsafe) @@ -906,7 +925,8 @@ public CSharpCompilationOptions( referencesSupersedeLowerVersions: false, publicSign: false, topLevelBinderFlags: BinderFlags.None, - nullableContextOptions: NullableContextOptions.Disable) + nullableContextOptions: NullableContextOptions.Disable, + emitPublicNullableMetadataOnly: false) { } } diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs index 2e7c58608f48e..63aead06d8e2c 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs @@ -3270,6 +3270,25 @@ internal bool CanEmitSpecialType(SpecialType type) return (diagnostic == null) || (diagnostic.Severity != DiagnosticSeverity.Error); } + internal bool ShouldEmitNullableAttributes(Symbol symbol) + { + Debug.Assert(!(symbol is null)); + Debug.Assert(symbol.IsDefinition); + Debug.Assert(symbol.ContainingAssembly == SourceAssembly); + + if (!Options.EmitPublicNullableMetadataOnly) + { + return true; + } + + if (!AccessCheck.IsPublicOrInternal(symbol, out bool isInternal)) + { + return false; + } + + return !isInternal || SourceAssembly.InternalsAreVisible; + } + internal override AnalyzerDriver AnalyzerForLanguage(ImmutableArray analyzers, AnalyzerManager analyzerManager) { Func getKind = node => node.Kind(); diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs index 204f55a1f2323..85725cf4aefc4 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs @@ -248,29 +248,34 @@ public override BoundNode VisitLambda(BoundLambda node) public override BoundNode VisitLocalFunctionStatement(BoundLocalFunctionStatement node) { _sawLocalFunctions = true; - CheckRefReadOnlySymbols(node.Symbol); - var typeParameters = node.Symbol.TypeParameters; + var localFunction = node.Symbol; + CheckRefReadOnlySymbols(localFunction); + + var typeParameters = localFunction.TypeParameters; if (typeParameters.Any(typeParameter => typeParameter.HasUnmanagedTypeConstraint)) { _factory.CompilationState.ModuleBuilderOpt?.EnsureIsUnmanagedAttributeExists(); } - bool constraintsNeedNullableAttribute = typeParameters.Any( - typeParameter => ((SourceTypeParameterSymbolBase)typeParameter).ConstraintsNeedNullableAttribute()); + if (_factory.CompilationState.Compilation.ShouldEmitNullableAttributes(localFunction)) + { + bool constraintsNeedNullableAttribute = typeParameters.Any( + typeParameter => ((SourceTypeParameterSymbolBase)typeParameter).ConstraintsNeedNullableAttribute()); - bool returnTypeNeedsNullableAttribute = node.Symbol.ReturnTypeWithAnnotations.NeedsNullableAttribute(); - bool parametersNeedNullableAttribute = node.Symbol.ParameterTypesWithAnnotations.Any(parameter => parameter.NeedsNullableAttribute()); + bool returnTypeNeedsNullableAttribute = localFunction.ReturnTypeWithAnnotations.NeedsNullableAttribute(); + bool parametersNeedNullableAttribute = localFunction.ParameterTypesWithAnnotations.Any(parameter => parameter.NeedsNullableAttribute()); - if (constraintsNeedNullableAttribute || returnTypeNeedsNullableAttribute || parametersNeedNullableAttribute) - { - _factory.CompilationState.ModuleBuilderOpt?.EnsureNullableAttributeExists(); + if (constraintsNeedNullableAttribute || returnTypeNeedsNullableAttribute || parametersNeedNullableAttribute) + { + _factory.CompilationState.ModuleBuilderOpt?.EnsureNullableAttributeExists(); + } } var oldContainingSymbol = _factory.CurrentFunction; try { - _factory.CurrentFunction = node.Symbol; + _factory.CurrentFunction = localFunction; return base.VisitLocalFunctionStatement(node); } finally diff --git a/src/Compilers/CSharp/Portable/NullableContextOptions.cs b/src/Compilers/CSharp/Portable/NullableContextOptions.cs index 1673b0009fd30..54302268c5882 100644 --- a/src/Compilers/CSharp/Portable/NullableContextOptions.cs +++ b/src/Compilers/CSharp/Portable/NullableContextOptions.cs @@ -1,7 +1,5 @@ // 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.Text; - namespace Microsoft.CodeAnalysis.CSharp { /// diff --git a/src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs index ec3a653783c84..8e7a6b12af39d 100644 --- a/src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs @@ -1206,7 +1206,7 @@ internal virtual void AddSynthesizedReturnTypeAttributes(PEModuleBuilder moduleB AddSynthesizedAttribute(ref attributes, compilation.SynthesizeTupleNamesAttribute(type.Type)); } - if (type.NeedsNullableAttribute()) + if (compilation.ShouldEmitNullableAttributes(this) && type.NeedsNullableAttribute()) { AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeNullableAttribute(this, type)); } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/FieldSymbolWithAttributesAndModifiers.cs b/src/Compilers/CSharp/Portable/Symbols/Source/FieldSymbolWithAttributesAndModifiers.cs index 933ec0e7dfea8..78c93e5c9ae9e 100755 --- a/src/Compilers/CSharp/Portable/Symbols/Source/FieldSymbolWithAttributesAndModifiers.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/FieldSymbolWithAttributesAndModifiers.cs @@ -367,21 +367,22 @@ internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, r { base.AddSynthesizedAttributes(moduleBuilder, ref attributes); + var compilation = this.DeclaringCompilation; var type = this.TypeWithAnnotations; if (type.Type.ContainsDynamic()) { AddSynthesizedAttribute(ref attributes, - DeclaringCompilation.SynthesizeDynamicAttribute(type.Type, TypeWithAnnotations.CustomModifiers.Length)); + compilation.SynthesizeDynamicAttribute(type.Type, TypeWithAnnotations.CustomModifiers.Length)); } if (type.Type.ContainsTupleNames()) { AddSynthesizedAttribute(ref attributes, - DeclaringCompilation.SynthesizeTupleNamesAttribute(type.Type)); + compilation.SynthesizeTupleNamesAttribute(type.Type)); } - if (type.NeedsNullableAttribute()) + if (compilation.ShouldEmitNullableAttributes(this) && type.NeedsNullableAttribute()) { AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeNullableAttribute(this, type)); } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs index 88a49446288de..606a453fcc9e7 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs @@ -166,9 +166,10 @@ private void ComputeParameters() addRefReadOnlyModifier: false, diagnostics: diagnostics); - ParameterHelpers.EnsureIsReadOnlyAttributeExists(parameters, diagnostics, modifyCompilation: false); - ParameterHelpers.EnsureNullableAttributeExists(parameters, diagnostics, modifyCompilation: false); - // Note: we don't need to warn on annotations used without NonNullTypes context for local functions, as this is handled in binding already + var compilation = DeclaringCompilation; + ParameterHelpers.EnsureIsReadOnlyAttributeExists(compilation, parameters, diagnostics, modifyCompilation: false); + ParameterHelpers.EnsureNullableAttributeExists(compilation, this, parameters, diagnostics, modifyCompilation: false); + // Note: we don't need to warn on annotations used in #nullable disable context for local functions, as this is handled in binding already var isVararg = arglistToken.Kind() == SyntaxKind.ArgListKeyword; if (isVararg) @@ -216,6 +217,7 @@ internal void ComputeReturnType() } var diagnostics = DiagnosticBag.GetInstance(); + var compilation = DeclaringCompilation; TypeSyntax returnTypeSyntax = _syntax.ReturnType; TypeWithAnnotations returnType = _binder.BindType(returnTypeSyntax.SkipRef(), diagnostics); @@ -225,7 +227,7 @@ internal void ComputeReturnType() { ReportBadRefToken(returnTypeSyntax, diagnostics); } - else if (returnType.Type.IsBadAsyncReturn(this.DeclaringCompilation)) + else if (returnType.Type.IsBadAsyncReturn(compilation)) { diagnostics.Add(ErrorCode.ERR_BadAsyncReturn, this.Locations[0]); } @@ -234,13 +236,14 @@ internal void ComputeReturnType() var location = _syntax.ReturnType.Location; if (_refKind == RefKind.RefReadOnly) { - DeclaringCompilation.EnsureIsReadOnlyAttributeExists(diagnostics, location, modifyCompilation: false); + compilation.EnsureIsReadOnlyAttributeExists(diagnostics, location, modifyCompilation: false); } - if (returnType.NeedsNullableAttribute()) + if (compilation.ShouldEmitNullableAttributes(this) && + returnType.NeedsNullableAttribute()) { - DeclaringCompilation.EnsureNullableAttributeExists(diagnostics, location, modifyCompilation: false); - // Note: we don't need to warn on annotations used without NonNullTypes context for local functions, as this is handled in binding already + compilation.EnsureNullableAttributeExists(diagnostics, location, modifyCompilation: false); + // Note: we don't need to warn on annotations used in #nullable disable context for local functions, as this is handled in binding already } // span-like types are returnable in general diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs b/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs index 8182c68024533..b5a600cb53ec3 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs @@ -122,28 +122,26 @@ public static ImmutableArray MakeParameters( return parameters; } - internal static void EnsureIsReadOnlyAttributeExists(ImmutableArray parameters, DiagnosticBag diagnostics, bool modifyCompilation) + internal static void EnsureIsReadOnlyAttributeExists(CSharpCompilation compilation, ImmutableArray parameters, DiagnosticBag diagnostics, bool modifyCompilation) { + Debug.Assert(compilation != null); foreach (var parameter in parameters) { if (parameter.RefKind == RefKind.In) { - // These parameters might not come from a compilation (example: lambdas evaluated in EE). - // During rewriting, lowering will take care of flagging the appropriate PEModuleBuilder instead. - parameter.DeclaringCompilation?.EnsureIsReadOnlyAttributeExists(diagnostics, parameter.GetNonNullSyntaxNode().Location, modifyCompilation); + compilation.EnsureIsReadOnlyAttributeExists(diagnostics, parameter.GetNonNullSyntaxNode().Location, modifyCompilation); } } } - internal static void EnsureNullableAttributeExists(ImmutableArray parameters, DiagnosticBag diagnostics, bool modifyCompilation) + internal static void EnsureNullableAttributeExists(CSharpCompilation compilation, Symbol container, ImmutableArray parameters, DiagnosticBag diagnostics, bool modifyCompilation) { + Debug.Assert(compilation != null); foreach (var parameter in parameters) { - if (parameter.TypeWithAnnotations.NeedsNullableAttribute()) + if (compilation.ShouldEmitNullableAttributes(container) && parameter.TypeWithAnnotations.NeedsNullableAttribute()) { - // These parameters might not come from a compilation (example: lambdas evaluated in EE). - // During rewriting, lowering will take care of flagging the appropriate PEModuleBuilder instead. - parameter.DeclaringCompilation?.EnsureNullableAttributeExists(diagnostics, parameter.GetNonNullSyntaxNode().Location, modifyCompilation); + compilation.EnsureNullableAttributeExists(diagnostics, parameter.GetNonNullSyntaxNode().Location, modifyCompilation); } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceConstructorSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceConstructorSymbol.cs index 67e9bc77bf87f..85e79aef52991 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceConstructorSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceConstructorSymbol.cs @@ -126,12 +126,13 @@ internal override void AfterAddingTypeMembersChecks(ConversionsBase conversions, { base.AfterAddingTypeMembersChecks(conversions, diagnostics); - ParameterHelpers.EnsureIsReadOnlyAttributeExists(Parameters, diagnostics, modifyCompilation: true); - ParameterHelpers.EnsureNullableAttributeExists(Parameters, diagnostics, modifyCompilation: true); + var compilation = DeclaringCompilation; + ParameterHelpers.EnsureIsReadOnlyAttributeExists(compilation, Parameters, diagnostics, modifyCompilation: true); + ParameterHelpers.EnsureNullableAttributeExists(compilation, this, Parameters, diagnostics, modifyCompilation: true); foreach (var parameter in this.Parameters) { - parameter.Type.CheckAllConstraints(DeclaringCompilation, conversions, parameter.Locations[0], diagnostics); + parameter.Type.CheckAllConstraints(compilation, conversions, parameter.Locations[0], diagnostics); } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceDelegateMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceDelegateMethodSymbol.cs index 8b4cfd0b52cc2..857522a26653e 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceDelegateMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceDelegateMethodSymbol.cs @@ -305,6 +305,7 @@ internal override void AfterAddingTypeMembersChecks(ConversionsBase conversions, { var syntax = (DelegateDeclarationSyntax)SyntaxRef.GetSyntax(); var location = syntax.ReturnType.GetLocation(); + var compilation = DeclaringCompilation; Debug.Assert(location != null); @@ -312,17 +313,18 @@ internal override void AfterAddingTypeMembersChecks(ConversionsBase conversions, if (_refKind == RefKind.RefReadOnly) { - DeclaringCompilation.EnsureIsReadOnlyAttributeExists(diagnostics, location, modifyCompilation: true); + compilation.EnsureIsReadOnlyAttributeExists(diagnostics, location, modifyCompilation: true); } - ParameterHelpers.EnsureIsReadOnlyAttributeExists(Parameters, diagnostics, modifyCompilation: true); + ParameterHelpers.EnsureIsReadOnlyAttributeExists(compilation, Parameters, diagnostics, modifyCompilation: true); - if (ReturnTypeWithAnnotations.NeedsNullableAttribute()) + if (compilation.ShouldEmitNullableAttributes(this) && + ReturnTypeWithAnnotations.NeedsNullableAttribute()) { - this.DeclaringCompilation.EnsureNullableAttributeExists(diagnostics, location, modifyCompilation: true); + compilation.EnsureNullableAttributeExists(diagnostics, location, modifyCompilation: true); } - ParameterHelpers.EnsureNullableAttributeExists(Parameters, diagnostics, modifyCompilation: true); + ParameterHelpers.EnsureNullableAttributeExists(compilation, this, Parameters, diagnostics, modifyCompilation: true); } public override ImmutableArray RefCustomModifiers => _refCustomModifiers; diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventSymbol.cs index 0daa6a51f5231..9dd0e0034d5a1 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventSymbol.cs @@ -308,11 +308,11 @@ internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, r { base.AddSynthesizedAttributes(moduleBuilder, ref attributes); + var compilation = this.DeclaringCompilation; var type = this.TypeWithAnnotations; if (type.Type.ContainsDynamic()) { - var compilation = this.DeclaringCompilation; AddSynthesizedAttribute(ref attributes, compilation.SynthesizeDynamicAttribute(type.Type, type.CustomModifiers.Length)); } @@ -322,7 +322,7 @@ internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, r DeclaringCompilation.SynthesizeTupleNamesAttribute(type.Type)); } - if (type.NeedsNullableAttribute()) + if (compilation.ShouldEmitNullableAttributes(this) && type.NeedsNullableAttribute()) { AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeNullableAttribute(this, type)); } @@ -716,14 +716,16 @@ protected TypeWithAnnotations BindEventType(Binder binder, TypeSyntax typeSyntax internal override void AfterAddingTypeMembersChecks(ConversionsBase conversions, DiagnosticBag diagnostics) { + var compilation = DeclaringCompilation; var location = this.Locations[0]; this.CheckModifiersAndType(diagnostics); - this.Type.CheckAllConstraints(DeclaringCompilation, conversions, location, diagnostics); + this.Type.CheckAllConstraints(compilation, conversions, location, diagnostics); - if (this.TypeWithAnnotations.NeedsNullableAttribute()) + if (compilation.ShouldEmitNullableAttributes(this) && + TypeWithAnnotations.NeedsNullableAttribute()) { - this.DeclaringCompilation.EnsureNullableAttributeExists(diagnostics, location, modifyCompilation: true); + compilation.EnsureNullableAttributeExists(diagnostics, location, modifyCompilation: true); } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceFieldSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceFieldSymbol.cs index 717a8be100742..1531dce7459eb 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceFieldSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceFieldSymbol.cs @@ -127,10 +127,13 @@ internal sealed override void DecodeWellKnownAttribute(ref DecodeWellKnownAttrib internal override void AfterAddingTypeMembersChecks(ConversionsBase conversions, DiagnosticBag diagnostics) { + var compilation = DeclaringCompilation; var location = ErrorLocation; - if (this.TypeWithAnnotations.NeedsNullableAttribute()) + + if (compilation.ShouldEmitNullableAttributes(this) && + TypeWithAnnotations.NeedsNullableAttribute()) { - DeclaringCompilation.EnsureNullableAttributeExists(diagnostics, location, modifyCompilation: true); + compilation.EnsureNullableAttributeExists(diagnostics, location, modifyCompilation: true); } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs index 495dfe26c126f..7c94a1eb32685 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs @@ -1408,23 +1408,28 @@ protected void AfterMembersChecks(DiagnosticBag diagnostics) CheckForUnmatchedOperators(diagnostics); var location = Locations[0]; + var compilation = DeclaringCompilation; + if (this.IsRefLikeType) { - this.DeclaringCompilation.EnsureIsByRefLikeAttributeExists(diagnostics, location, modifyCompilation: true); + compilation.EnsureIsByRefLikeAttributeExists(diagnostics, location, modifyCompilation: true); } if (this.IsReadOnly) { - this.DeclaringCompilation.EnsureIsReadOnlyAttributeExists(diagnostics, location, modifyCompilation: true); + compilation.EnsureIsReadOnlyAttributeExists(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(); - if (baseType?.NeedsNullableAttribute() == true || - interfaces.Any(t => t.NeedsNullableAttribute())) + if (compilation.ShouldEmitNullableAttributes(this)) { - this.DeclaringCompilation.EnsureNullableAttributeExists(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(); + if (baseType?.NeedsNullableAttribute() == true || + interfaces.Any(t => t.NeedsNullableAttribute())) + { + compilation.EnsureNullableAttributeExists(diagnostics, location, modifyCompilation: true); + } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs index 389fe2fbfe035..f28bdd3d549ca 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs @@ -1383,7 +1383,8 @@ internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, r AddSynthesizedAttribute(ref attributes, compilation.SynthesizeTupleNamesAttribute(baseType)); } - if (baseType.NeedsNullableAttribute()) + if (compilation.ShouldEmitNullableAttributes(this) && + baseType.NeedsNullableAttribute()) { AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeNullableAttribute(this, TypeWithAnnotations.Create(baseType))); } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbol.cs index c41c42378831c..7d641f83ace3e 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbol.cs @@ -1132,6 +1132,7 @@ internal override void AfterAddingTypeMembersChecks(ConversionsBase conversions, base.AfterAddingTypeMembersChecks(conversions, diagnostics); var location = GetSyntax().ReturnType.Location; + var compilation = DeclaringCompilation; Debug.Assert(location != null); @@ -1143,14 +1144,14 @@ internal override void AfterAddingTypeMembersChecks(ConversionsBase conversions, { var syntax = this.GetSyntax(); Debug.Assert(syntax.ExplicitInterfaceSpecifier != null); - _explicitInterfaceType.CheckAllConstraints(DeclaringCompilation, conversions, new SourceLocation(syntax.ExplicitInterfaceSpecifier.Name), diagnostics); + _explicitInterfaceType.CheckAllConstraints(compilation, conversions, new SourceLocation(syntax.ExplicitInterfaceSpecifier.Name), diagnostics); } - this.ReturnType.CheckAllConstraints(DeclaringCompilation, conversions, this.Locations[0], diagnostics); + this.ReturnType.CheckAllConstraints(compilation, conversions, this.Locations[0], diagnostics); foreach (var parameter in this.Parameters) { - parameter.Type.CheckAllConstraints(DeclaringCompilation, conversions, parameter.Locations[0], diagnostics); + parameter.Type.CheckAllConstraints(compilation, conversions, parameter.Locations[0], diagnostics); } var implementingPart = this.SourcePartialImplementation; @@ -1161,17 +1162,17 @@ internal override void AfterAddingTypeMembersChecks(ConversionsBase conversions, if (_refKind == RefKind.RefReadOnly) { - this.DeclaringCompilation.EnsureIsReadOnlyAttributeExists(diagnostics, location, modifyCompilation: true); + compilation.EnsureIsReadOnlyAttributeExists(diagnostics, location, modifyCompilation: true); } - ParameterHelpers.EnsureIsReadOnlyAttributeExists(Parameters, diagnostics, modifyCompilation: true); + ParameterHelpers.EnsureIsReadOnlyAttributeExists(compilation, Parameters, diagnostics, modifyCompilation: true); - if (ReturnTypeWithAnnotations.NeedsNullableAttribute()) + if (compilation.ShouldEmitNullableAttributes(this) && ReturnTypeWithAnnotations.NeedsNullableAttribute()) { - this.DeclaringCompilation.EnsureNullableAttributeExists(diagnostics, location, modifyCompilation: true); + compilation.EnsureNullableAttributeExists(diagnostics, location, modifyCompilation: true); } - ParameterHelpers.EnsureNullableAttributeExists(Parameters, diagnostics, modifyCompilation: true); + ParameterHelpers.EnsureNullableAttributeExists(compilation, this, Parameters, diagnostics, modifyCompilation: true); } /// diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceParameterSymbolBase.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceParameterSymbolBase.cs index 3f16cfde268ae..1adebec15abf3 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceParameterSymbolBase.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceParameterSymbolBase.cs @@ -96,7 +96,7 @@ internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, r AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeIsReadOnlyAttribute(this)); } - if (type.NeedsNullableAttribute()) + if (compilation.ShouldEmitNullableAttributes(_containingSymbol) && type.NeedsNullableAttribute()) { AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeNullableAttribute(this, type)); } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs index cc60be9fd4a1c..532013c773119 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs @@ -773,6 +773,7 @@ internal SyntaxTree SyntaxTree internal override void AfterAddingTypeMembersChecks(ConversionsBase conversions, DiagnosticBag diagnostics) { Location location = CSharpSyntaxNode.Type.Location; + var compilation = DeclaringCompilation; Debug.Assert(location != null); @@ -784,7 +785,7 @@ internal override void AfterAddingTypeMembersChecks(ConversionsBase conversions, { var explicitInterfaceSpecifier = GetExplicitInterfaceSpecifier(this.CSharpSyntaxNode); Debug.Assert(explicitInterfaceSpecifier != null); - _explicitInterfaceType.CheckAllConstraints(DeclaringCompilation, conversions, new SourceLocation(explicitInterfaceSpecifier.Name), diagnostics); + _explicitInterfaceType.CheckAllConstraints(compilation, conversions, new SourceLocation(explicitInterfaceSpecifier.Name), diagnostics); // Note: we delayed nullable-related checks that could pull on NonNullTypes PropertySymbol overriddenOrImplementedProperty = null; @@ -805,17 +806,18 @@ internal override void AfterAddingTypeMembersChecks(ConversionsBase conversions, if (_refKind == RefKind.RefReadOnly) { - DeclaringCompilation.EnsureIsReadOnlyAttributeExists(diagnostics, location, modifyCompilation: true); + compilation.EnsureIsReadOnlyAttributeExists(diagnostics, location, modifyCompilation: true); } - ParameterHelpers.EnsureIsReadOnlyAttributeExists(Parameters, diagnostics, modifyCompilation: true); + ParameterHelpers.EnsureIsReadOnlyAttributeExists(compilation, Parameters, diagnostics, modifyCompilation: true); - if (this.TypeWithAnnotations.NeedsNullableAttribute()) + if (compilation.ShouldEmitNullableAttributes(this) && + this.TypeWithAnnotations.NeedsNullableAttribute()) { - DeclaringCompilation.EnsureNullableAttributeExists(diagnostics, location, modifyCompilation: true); + compilation.EnsureNullableAttributeExists(diagnostics, location, modifyCompilation: true); } - ParameterHelpers.EnsureNullableAttributeExists(this.Parameters, diagnostics, modifyCompilation: true); + ParameterHelpers.EnsureNullableAttributeExists(compilation, this, Parameters, diagnostics, modifyCompilation: true); } private void CheckAccessibility(Location location, DiagnosticBag diagnostics, bool isExplicitInterfaceImplementation) @@ -1239,21 +1241,22 @@ internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, r { base.AddSynthesizedAttributes(moduleBuilder, ref attributes); + var compilation = this.DeclaringCompilation; var type = this.TypeWithAnnotations; if (type.Type.ContainsDynamic()) { AddSynthesizedAttribute(ref attributes, - DeclaringCompilation.SynthesizeDynamicAttribute(type.Type, type.CustomModifiers.Length + RefCustomModifiers.Length, _refKind)); + compilation.SynthesizeDynamicAttribute(type.Type, type.CustomModifiers.Length + RefCustomModifiers.Length, _refKind)); } if (type.Type.ContainsTupleNames()) { AddSynthesizedAttribute(ref attributes, - DeclaringCompilation.SynthesizeTupleNamesAttribute(type.Type)); + compilation.SynthesizeTupleNamesAttribute(type.Type)); } - if (type.NeedsNullableAttribute()) + if (compilation.ShouldEmitNullableAttributes(this) && type.NeedsNullableAttribute()) { AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeNullableAttribute(this, type)); } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceTypeParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceTypeParameterSymbol.cs index 5d58e9dc792ee..b71d215717f55 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceTypeParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceTypeParameterSymbol.cs @@ -308,11 +308,26 @@ private void CheckNullableAnnotationsInConstraints(DiagnosticBag diagnostics) internal bool ConstraintsNeedNullableAttribute() { - return (this.HasReferenceTypeConstraint && this.ReferenceTypeConstraintIsNullable != null) || - this.ConstraintTypesNoUseSiteDiagnostics.Any(c => c.NeedsNullableAttribute()) || - this.HasNotNullConstraint || - (!this.HasReferenceTypeConstraint && !this.HasValueTypeConstraint && - this.ConstraintTypesNoUseSiteDiagnostics.IsEmpty && this.IsNotNullableIfReferenceType == false); + if (!DeclaringCompilation.ShouldEmitNullableAttributes(ContainingSymbol)) + { + return false; + } + if (this.HasReferenceTypeConstraint && this.ReferenceTypeConstraintIsNullable != null) + { + return true; + } + if (this.ConstraintTypesNoUseSiteDiagnostics.Any(c => c.NeedsNullableAttribute())) + { + return true; + } + if (this.HasNotNullConstraint) + { + return true; + } + return !this.HasReferenceTypeConstraint && + !this.HasValueTypeConstraint && + this.ConstraintTypesNoUseSiteDiagnostics.IsEmpty && + this.IsNotNullableIfReferenceType == false; } private NamedTypeSymbol GetDefaultBaseType() @@ -364,40 +379,45 @@ internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, r AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeIsUnmanagedAttribute(this)); } - byte nullableAttributeValue = NullableAnnotationExtensions.ObliviousAttributeValue; + var compilation = DeclaringCompilation; + if (compilation.ShouldEmitNullableAttributes(ContainingSymbol)) + { + 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)))); + } + } + } + private byte GetSynthesizedNullableAttributeValue() + { if (this.HasReferenceTypeConstraint) { switch (this.ReferenceTypeConstraintIsNullable) { case true: - nullableAttributeValue = NullableAnnotationExtensions.AnnotatedAttributeValue; - break; + return NullableAnnotationExtensions.AnnotatedAttributeValue; case false: - nullableAttributeValue = NullableAnnotationExtensions.NotAnnotatedAttributeValue; - break; + return NullableAnnotationExtensions.NotAnnotatedAttributeValue; } } else if (this.HasNotNullConstraint) { - nullableAttributeValue = NullableAnnotationExtensions.NotAnnotatedAttributeValue; + return NullableAnnotationExtensions.NotAnnotatedAttributeValue; } else if (!this.HasValueTypeConstraint && this.ConstraintTypesNoUseSiteDiagnostics.IsEmpty && this.IsNotNullableIfReferenceType == false) { - nullableAttributeValue = NullableAnnotationExtensions.AnnotatedAttributeValue; - } - - if (nullableAttributeValue != NullableAnnotationExtensions.ObliviousAttributeValue) - { - NamedTypeSymbol byteType = DeclaringCompilation.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)))); + return NullableAnnotationExtensions.AnnotatedAttributeValue; } + return NullableAnnotationExtensions.ObliviousAttributeValue; } internal override sealed void DecodeWellKnownAttribute(ref DecodeWellKnownAttributeArguments arguments) diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbolBase.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbolBase.cs index 2eb69ed0f2afb..b006cdc738c00 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbolBase.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbolBase.cs @@ -647,22 +647,24 @@ internal sealed override void AfterAddingTypeMembersChecks(ConversionsBase conve // method name location for any such errors. We'll do the same for return // type errors but for parameter errors, we'll use the parameter location. - this.ReturnType.CheckAllConstraints(DeclaringCompilation, conversions, this.Locations[0], diagnostics); + var compilation = DeclaringCompilation; + + this.ReturnType.CheckAllConstraints(compilation, conversions, this.Locations[0], diagnostics); foreach (var parameter in this.Parameters) { - parameter.Type.CheckAllConstraints(DeclaringCompilation, conversions, parameter.Locations[0], diagnostics); + parameter.Type.CheckAllConstraints(compilation, conversions, parameter.Locations[0], diagnostics); } - ParameterHelpers.EnsureIsReadOnlyAttributeExists(Parameters, diagnostics, modifyCompilation: true); + ParameterHelpers.EnsureIsReadOnlyAttributeExists(compilation, Parameters, diagnostics, modifyCompilation: true); - var location = ReturnTypeSyntax.Location; - if (ReturnTypeWithAnnotations.NeedsNullableAttribute()) + if (compilation.ShouldEmitNullableAttributes(this) && + ReturnTypeWithAnnotations.NeedsNullableAttribute()) { - this.DeclaringCompilation.EnsureNullableAttributeExists(diagnostics, location, modifyCompilation: true); + compilation.EnsureNullableAttributeExists(diagnostics, ReturnTypeSyntax.Location, modifyCompilation: true); } - ParameterHelpers.EnsureNullableAttributeExists(Parameters, diagnostics, modifyCompilation: true); + ParameterHelpers.EnsureNullableAttributeExists(compilation, this, Parameters, diagnostics, modifyCompilation: true); } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEmbeddedNullableAttributeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEmbeddedNullableAttributeSymbol.cs index e6a755eb4239d..e4db998f9412b 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEmbeddedNullableAttributeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEmbeddedNullableAttributeSymbol.cs @@ -1,19 +1,14 @@ // 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.Cci; -using Roslyn.Utilities; using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; -using System.Runtime.InteropServices; using System.Linq; -using Microsoft.CodeAnalysis.CSharp.Emit; using Microsoft.CodeAnalysis.PooledObjects; namespace Microsoft.CodeAnalysis.CSharp.Symbols { - internal sealed class SynthesizedEmbeddedNullableAttributeSymbol : SynthesizedEmbeddedAttributeSymbolBase { private readonly ImmutableArray _fields; @@ -25,8 +20,8 @@ internal sealed class SynthesizedEmbeddedNullableAttributeSymbol : SynthesizedEm private const string NullableFlagsFieldName = "NullableFlags"; public SynthesizedEmbeddedNullableAttributeSymbol( - CSharpCompilation compilation, - DiagnosticBag diagnostics) + CSharpCompilation compilation, + DiagnosticBag diagnostics) : base(AttributeDescription.NullableAttribute, compilation, diagnostics) { var byteType = TypeWithAnnotations.Create(compilation.GetSpecialType(SpecialType.System_Byte)); @@ -100,34 +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/SynthesizedEmbeddedNullableMembersAttributeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEmbeddedNullableMembersAttributeSymbol.cs new file mode 100644 index 0000000000000..9d6b6ebc4d7a1 --- /dev/null +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEmbeddedNullableMembersAttributeSymbol.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 SynthesizedEmbeddedNullableMembersAttributeSymbol : SynthesizedEmbeddedAttributeSymbolBase + { + private readonly ImmutableArray _fields; + private readonly ImmutableArray _constructors; + + public SynthesizedEmbeddedNullableMembersAttributeSymbol( + CSharpCompilation compilation, + DiagnosticBag diagnostics) + : base(AttributeDescription.NullableMembersAttribute, 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.NullableMembersAttribute.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 23b470831f591..6d3ce10a654ab 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedFieldSymbolBase.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedFieldSymbolBase.cs @@ -44,6 +44,7 @@ internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, r base.AddSynthesizedAttributes(moduleBuilder, ref attributes); CSharpCompilation compilation = this.DeclaringCompilation; + var type = this.TypeWithAnnotations; // do not emit CompilerGenerated attributes for fields inside compiler generated types: if (!_containingType.IsImplicitlyDeclared) @@ -56,10 +57,10 @@ internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, r compilation.HasDynamicEmitAttributes() && compilation.CanEmitBoolean()) { - AddSynthesizedAttribute(ref attributes, compilation.SynthesizeDynamicAttribute(this.Type, this.TypeWithAnnotations.CustomModifiers.Length)); + AddSynthesizedAttribute(ref attributes, compilation.SynthesizeDynamicAttribute(this.Type, type.CustomModifiers.Length)); } - if (TypeWithAnnotations.Type.ContainsTupleNames() && + if (this.Type.ContainsTupleNames() && compilation.HasTupleNamesAttributes && compilation.CanEmitSpecialType(SpecialType.System_String)) { @@ -67,9 +68,9 @@ internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, r compilation.SynthesizeTupleNamesAttribute(Type)); } - if (TypeWithAnnotations.NeedsNullableAttribute()) + if (compilation.ShouldEmitNullableAttributes(this) && type.NeedsNullableAttribute()) { - AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeNullableAttribute(this, this.TypeWithAnnotations)); + AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeNullableAttribute(this, type)); } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedParameterSymbol.cs index 498faa8cf7b1f..7e09d022eb765 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedParameterSymbol.cs @@ -4,8 +4,6 @@ using System.Collections.Immutable; using System.Diagnostics; using Microsoft.CodeAnalysis.CSharp.Emit; -using Microsoft.CodeAnalysis.CSharp.Symbols; -using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.PooledObjects; namespace Microsoft.CodeAnalysis.CSharp.Symbols @@ -159,9 +157,9 @@ internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, r compilation.SynthesizeTupleNamesAttribute(type.Type)); } - if (TypeWithAnnotations.NeedsNullableAttribute()) + if (compilation.ShouldEmitNullableAttributes(_container) && type.NeedsNullableAttribute()) { - AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeNullableAttribute(this, TypeWithAnnotations)); + AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeNullableAttribute(this, 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 06f7ffee99fbc..9cde1580fcb99 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs @@ -1665,23 +1665,30 @@ internal static Cci.TypeReferenceWithAttributes GetTypeRefWithAttributes( Cci.ITypeReference typeRef) { var builder = ArrayBuilder.GetInstance(); - if (type.Type.ContainsTupleNames()) + + var compilation = declaringSymbol.DeclaringCompilation; + if (compilation != null) { - SynthesizedAttributeData attr = declaringSymbol.DeclaringCompilation.SynthesizeTupleNamesAttribute(type.Type); - if (attr != null) + if (type.Type.ContainsTupleNames()) { - builder.Add(attr); + SynthesizedAttributeData attr = compilation.SynthesizeTupleNamesAttribute(type.Type); + if (attr != null) + { + builder.Add(attr); + } } - } - if (type.NeedsNullableAttribute()) - { - SynthesizedAttributeData attr = moduleBuilder.SynthesizeNullableAttribute(declaringSymbol, type); - if (attr != null) + if (compilation.ShouldEmitNullableAttributes(declaringSymbol) && + type.NeedsNullableAttribute()) { - builder.Add(attr); + SynthesizedAttributeData attr = moduleBuilder.SynthesizeNullableAttribute(declaringSymbol, type); + if (attr != null) + { + builder.Add(attr); + } } } + return new Cci.TypeReferenceWithAttributes(typeRef, builder.ToImmutableAndFree()); } diff --git a/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_Nullable.cs b/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_Nullable.cs index d7295e59427ca..c5c5e91d57023 100644 --- a/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_Nullable.cs +++ b/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_Nullable.cs @@ -1147,6 +1147,484 @@ class C }); } + [Fact] + public void EmitPrivateMetadata_Types() + { + var source = +@"public class Base { } +namespace Namespace +{ + public class Public : Base { } + internal class Internal : Base { } +} +public class PublicTypes +{ + public class Public : Base { } + internal class Internal : Base { } + protected class Protected : Base { } + protected internal class ProtectedInternal : Base { } + private protected class PrivateProtected : Base { } + private class Private : Base { } +} +internal class InternalTypes +{ + public class Public : Base { } + internal class Internal : Base { } + protected class Protected : Base { } + protected internal class ProtectedInternal : Base { } + private protected class PrivateProtected : Base { } + private class Private : Base { } +}"; + var expectedPublicOnly = @" +Base + [Nullable(2)] T + [Nullable(2)] U +PublicTypes + [Nullable({ 0, 1, 2 })] PublicTypes.Public + [Nullable({ 0, 1, 2 })] PublicTypes.Protected + [Nullable({ 0, 1, 2 })] PublicTypes.ProtectedInternal +[Nullable({ 0, 1, 2 })] Namespace.Public +"; + var expectedPublicAndInternal = @" +Base + [Nullable(2)] T + [Nullable(2)] U +PublicTypes + [Nullable({ 0, 1, 2 })] PublicTypes.Public + [Nullable({ 0, 1, 2 })] PublicTypes.Internal + [Nullable({ 0, 1, 2 })] PublicTypes.Protected + [Nullable({ 0, 1, 2 })] PublicTypes.ProtectedInternal + [Nullable({ 0, 1, 2 })] PublicTypes.PrivateProtected +InternalTypes + [Nullable({ 0, 1, 2 })] InternalTypes.Public + [Nullable({ 0, 1, 2 })] InternalTypes.Internal + [Nullable({ 0, 1, 2 })] InternalTypes.Protected + [Nullable({ 0, 1, 2 })] InternalTypes.ProtectedInternal + [Nullable({ 0, 1, 2 })] InternalTypes.PrivateProtected +[Nullable({ 0, 1, 2 })] Namespace.Public +[Nullable({ 0, 1, 2 })] Namespace.Internal +"; + var expectedAll = @" +Base + [Nullable(2)] T + [Nullable(2)] U +PublicTypes + [Nullable({ 0, 1, 2 })] PublicTypes.Public + [Nullable({ 0, 1, 2 })] PublicTypes.Internal + [Nullable({ 0, 1, 2 })] PublicTypes.Protected + [Nullable({ 0, 1, 2 })] PublicTypes.ProtectedInternal + [Nullable({ 0, 1, 2 })] PublicTypes.PrivateProtected + [Nullable({ 0, 1, 2 })] PublicTypes.Private +InternalTypes + [Nullable({ 0, 1, 2 })] InternalTypes.Public + [Nullable({ 0, 1, 2 })] InternalTypes.Internal + [Nullable({ 0, 1, 2 })] InternalTypes.Protected + [Nullable({ 0, 1, 2 })] InternalTypes.ProtectedInternal + [Nullable({ 0, 1, 2 })] InternalTypes.PrivateProtected + [Nullable({ 0, 1, 2 })] InternalTypes.Private +[Nullable({ 0, 1, 2 })] Namespace.Public +[Nullable({ 0, 1, 2 })] Namespace.Internal +"; + EmitPrivateMetadata(source, expectedPublicOnly, expectedPublicAndInternal, expectedAll); + } + + [Fact] + public void EmitPrivateMetadata_Delegates() + { + var source = +@"public class Program +{ + protected delegate object ProtectedDelegate(object? arg); + internal delegate object InternalDelegate(object? arg); + private delegate object PrivateDelegate(object? arg); +}"; + var expectedPublicOnly = @" +Program + Program.ProtectedDelegate + [Nullable(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) +"; + var expectedPublicAndInternal = @" +Program + Program.ProtectedDelegate + [Nullable(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) + [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) +"; + var expectedAll = @" +Program + Program.ProtectedDelegate + [Nullable(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) + [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) + [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) +"; + EmitPrivateMetadata(source, expectedPublicOnly, expectedPublicAndInternal, expectedAll); + } + + [Fact] + public void EmitPrivateMetadata_Events() + { + var source = +@"#nullable disable +public delegate void D(T t); +#nullable enable +public class Program +{ + protected event D ProtectedEvent { add { } remove { } } + internal event D InternalEvent { add { } remove { } } + private event D PrivateEvent { add { } remove { } } +}"; + var expectedPublicOnly = @" +Program + [Nullable({ 1, 2 })] D! ProtectedEvent +"; + var expectedPublicAndInternal = @" +Program + [Nullable({ 1, 2 })] D! ProtectedEvent + [Nullable({ 1, 2 })] D! InternalEvent +"; + var expectedAll = @" +Program + [Nullable({ 1, 2 })] D! ProtectedEvent + [Nullable({ 1, 2 })] D! InternalEvent + [Nullable({ 1, 2 })] D! PrivateEvent +"; + EmitPrivateMetadata(source, expectedPublicOnly, expectedPublicAndInternal, expectedAll); + } + + [Fact] + public void EmitPrivateMetadata_Fields() + { + var source = +@"public class Program +{ + protected object? ProtectedField; + internal object? InternalField; + private object? PrivateField; +}"; + var expectedPublicOnly = @" +Program + [Nullable(2)] System.Object? ProtectedField +"; + var expectedPublicAndInternal = @" +Program + [Nullable(2)] System.Object? ProtectedField + [Nullable(2)] System.Object? InternalField +"; + var expectedAll = @" +Program + [Nullable(2)] System.Object? ProtectedField + [Nullable(2)] System.Object? InternalField + [Nullable(2)] System.Object? PrivateField +"; + EmitPrivateMetadata(source, expectedPublicOnly, expectedPublicAndInternal, expectedAll); + } + + [Fact] + 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; +}"; + var expectedPublicOnly = @" +Program + [Nullable(2)] System.Object? ProtectedMethod(System.Object! arg) + [Nullable(1)] System.Object! arg +"; + 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 +"; + 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 +"; + EmitPrivateMetadata(source, expectedPublicOnly, expectedPublicAndInternal, expectedAll); + } + + [Fact] + public void EmitPrivateMetadata_Properties() + { + var source = +@"public class Program +{ + protected object? ProtectedProperty => null; + internal object? InternalProperty => null; + private object? PrivateProperty => null; +}"; + var expectedPublicOnly = @" +Program + [Nullable(2)] System.Object? ProtectedProperty { get; } +"; + var expectedPublicAndInternal = @" +Program + [Nullable(2)] System.Object? ProtectedProperty { get; } + [Nullable(2)] System.Object? InternalProperty { get; } +"; + var expectedAll = @" +Program + [Nullable(2)] System.Object? ProtectedProperty { get; } + [Nullable(2)] System.Object? InternalProperty { get; } + [Nullable(2)] System.Object? PrivateProperty { get; } +"; + EmitPrivateMetadata(source, expectedPublicOnly, expectedPublicAndInternal, expectedAll); + } + + [Fact] + public void EmitPrivateMetadata_TypeParameters() + { + var source = +@"public class Base { } +public class Program +{ + protected static void ProtectedMethod() + where T : notnull + where U : class + { + } + internal static void InternalMethod() + where T : notnull + where U : class + { + } + private static void PrivateMethod() + where T : notnull + where U : class + { + } +}"; + var expectedPublicOnly = @" +Program + void ProtectedMethod() where T : notnull where U : class! + [Nullable(1)] T + [Nullable(1)] U +"; + var expectedPublicAndInternal = @" +Program + void ProtectedMethod() where T : notnull where U : class! + [Nullable(1)] T + [Nullable(1)] U + void InternalMethod() where T : notnull where U : class! + [Nullable(1)] T + [Nullable(1)] U +"; + var expectedAll = @" +Program + void ProtectedMethod() where T : notnull where U : class! + [Nullable(1)] T + [Nullable(1)] U + void InternalMethod() where T : notnull where U : class! + [Nullable(1)] T + [Nullable(1)] U + void PrivateMethod() where T : notnull where U : class! + [Nullable(1)] T + [Nullable(1)] U +"; + EmitPrivateMetadata(source, expectedPublicOnly, expectedPublicAndInternal, expectedAll); + } + + [Fact] + public void EmitPrivateMetadata_SynthesizedFields() + { + var source = +@"public struct S { } +public class Public +{ + public static void PublicMethod() + { + S s; + System.Action a = () => { s.ToString(); }; + } +}"; + var expectedPublicOnly = @" +S + [Nullable(2)] T +"; + var expectedPublicAndInternal = @" +S + [Nullable(2)] T +"; + var expectedAll = @" +S + [Nullable(2)] T +Public + Public.<>c__DisplayClass0_0 + [Nullable({ 0, 2 })] S s +"; + EmitPrivateMetadata(source, expectedPublicOnly, expectedPublicAndInternal, expectedAll); + } + + [Fact] + public void EmitPrivateMetadata_SynthesizedParameters() + { + var source = +@"public class Public +{ + private static void PrivateMethod(string x) + { + _ = new System.Action((string y) => { }); + } +}"; + var expectedPublicOnly = @""; + var expectedPublicAndInternal = @""; + var expectedAll = @" +Public + void PrivateMethod(System.String! x) + [Nullable(1)] 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 +"; + EmitPrivateMetadata(source, expectedPublicOnly, expectedPublicAndInternal, expectedAll); + } + + private void EmitPrivateMetadata(string source, string expectedPublicOnly, string expectedPublicAndInternal, string expectedAll) + { + var sourceIVTs = +@"using System.Runtime.CompilerServices; +[assembly: InternalsVisibleTo(""Other"")]"; + + var options = WithNonNullTypesTrue().WithMetadataImportOptions(MetadataImportOptions.All); + VerifyNullableAttributes(CreateCompilation(source, options: options), expectedAll); + VerifyNullableAttributes(CreateCompilation(source, options: options.WithEmitPublicNullableMetadataOnly(false)), expectedAll); + VerifyNullableAttributes(CreateCompilation(source, options: options.WithEmitPublicNullableMetadataOnly(true)), expectedPublicOnly); + VerifyNullableAttributes(CreateCompilation(new[] { source, sourceIVTs }, options: options), expectedAll); + VerifyNullableAttributes(CreateCompilation(new[] { source, sourceIVTs }, options: options.WithEmitPublicNullableMetadataOnly(false)), expectedAll); + VerifyNullableAttributes(CreateCompilation(new[] { source, sourceIVTs }, options: options.WithEmitPublicNullableMetadataOnly(true)), expectedPublicAndInternal); + } + + /// + /// Should only require NullableAttribute constructor if nullable annotations are emitted. + /// + [Fact] + public void EmitPrivateMetadata_MissingAttributeConstructor() + { + var sourceAttribute = +@"namespace System.Runtime.CompilerServices +{ + public sealed class NullableAttribute : Attribute { } +}"; + var source = +@"#pragma warning disable 0067 +#pragma warning disable 0169 +#pragma warning disable 8321 +public class A +{ + private object? F; + private static object? M(object arg) => null; + private object? P => null; + private object? this[object x, object? y] => null; + public static void M() + { + object? f(object arg) => arg; + D d = () => new object(); + } +} +internal delegate T D(); +internal interface I { } +internal class B : I +{ + public static object operator!(B b) => b; + public event D E; +}"; + var options = WithNonNullTypesTrue().WithMetadataImportOptions(MetadataImportOptions.All); + + var comp = CreateCompilation(new[] { sourceAttribute, source }, options: options.WithEmitPublicNullableMetadataOnly(false)); + comp.VerifyEmitDiagnostics( + // (6,21): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.NullableAttribute..ctor' + // private object? F; + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "F").WithArguments("System.Runtime.CompilerServices.NullableAttribute", ".ctor").WithLocation(6, 21), + // (7,20): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.NullableAttribute..ctor' + // private static object? M(object arg) => null; + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "object?").WithArguments("System.Runtime.CompilerServices.NullableAttribute", ".ctor").WithLocation(7, 20), + // (7,30): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.NullableAttribute..ctor' + // private static object? M(object arg) => null; + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "object arg").WithArguments("System.Runtime.CompilerServices.NullableAttribute", ".ctor").WithLocation(7, 30), + // (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), + // (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), + // (13,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(13, 26), + // (16,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(16, 19), + // (16,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(16, 23), + // (17,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(17, 22), + // (18,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(18, 16), + // (20,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(20, 19), + // (20,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(20, 36), + // (21,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(21, 29)); + + comp = CreateCompilation(new[] { sourceAttribute, source }, options: options.WithEmitPublicNullableMetadataOnly(true)); + comp.VerifyEmitDiagnostics(); + } + [Fact] public void UseSiteError_LambdaReturnType() { @@ -1843,5 +2321,14 @@ private static void AssertAttributes(MetadataReader reader, CustomAttributeHandl var actualNames = handles.Select(h => reader.Dump(reader.GetCustomAttribute(h).Constructor)).ToArray(); AssertEx.SetEqual(actualNames, expectedNames); } + + private void VerifyNullableAttributes(CSharpCompilation comp, string expected) + { + CompileAndVerify(comp, symbolValidator: module => + { + var actual = NullableAttributesVisitor.GetString(module); + AssertEx.AssertEqualToleratingWhitespaceDifferences(expected, actual); + }); + } } } diff --git a/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_NullableMembers.cs b/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_NullableMembers.cs new file mode 100644 index 0000000000000..a51f4e167080c --- /dev/null +++ b/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_NullableMembers.cs @@ -0,0 +1,10 @@ +// 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.Test.Utilities; + +namespace Microsoft.CodeAnalysis.CSharp.UnitTests +{ + public class AttributeTests_NullableMembers : CSharpTestBase + { + } +} diff --git a/src/Compilers/CSharp/Test/Symbol/Compilation/CSharpCompilationOptionsTests.cs b/src/Compilers/CSharp/Test/Symbol/Compilation/CSharpCompilationOptionsTests.cs index b86873c87ddee..f89dfd75d629e 100644 --- a/src/Compilers/CSharp/Test/Symbol/Compilation/CSharpCompilationOptionsTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Compilation/CSharpCompilationOptionsTests.cs @@ -409,12 +409,14 @@ private static CSharpCompilationOptions CreateCSharpCompilationOptions() var topLevelBinderFlags = BinderFlags.None; var publicSign = false; NullableContextOptions nullableContextOptions = NullableContextOptions.Disable; + bool emitPrivateNullableMetadata = false; return new CSharpCompilationOptions(OutputKind.ConsoleApplication, reportSuppressedDiagnostics, moduleName, mainTypeName, scriptClassName, usings, optimizationLevel, checkOverflow, allowUnsafe, cryptoKeyContainer, cryptoKeyFile, cryptoPublicKey, delaySign, platform, generalDiagnosticOption, warningLevel, specificDiagnosticOptions, concurrentBuild, deterministic, currentLocalTime, debugPlusMode, xmlReferenceResolver, sourceReferenceResolver, metadataReferenceResolver, - assemblyIdentityComparer, strongNameProvider, metadataImportOptions, referencesSupersedeLowerVersions, publicSign, topLevelBinderFlags, nullableContextOptions); + assemblyIdentityComparer, strongNameProvider, metadataImportOptions, referencesSupersedeLowerVersions, publicSign, topLevelBinderFlags, + nullableContextOptions, emitPrivateNullableMetadata); } private sealed class MetadataReferenceResolverWithEquality : MetadataReferenceResolver diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs index a1061f74d95cb..974539aa92ce3 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_NullableMembersAttribute: case WellKnownType.System_Runtime_CompilerServices_IsReadOnlyAttribute: case WellKnownType.System_Runtime_CompilerServices_IsByRefLikeAttribute: case WellKnownType.System_Span_T: @@ -899,6 +900,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_NullableMembersAttribute__ctor: case WellKnownMember.System_Span_T__ctor: case WellKnownMember.System_Span_T__get_Item: case WellKnownMember.System_Span_T__get_Length: diff --git a/src/Compilers/Core/Portable/Symbols/Attributes/AttributeDescription.cs b/src/Compilers/Core/Portable/Symbols/Attributes/AttributeDescription.cs index 02b6e7e269276..1783dfad59e52 100644 --- a/src/Compilers/Core/Portable/Symbols/Attributes/AttributeDescription.cs +++ b/src/Compilers/Core/Portable/Symbols/Attributes/AttributeDescription.cs @@ -411,6 +411,7 @@ static AttributeDescription() }; private static readonly byte[][] s_signaturesOfNullableAttribute = { s_signature_HasThis_Void_Byte, s_signature_HasThis_Void_SzArray_Byte }; + private static readonly byte[][] s_signaturesOfNullableMembersAttribute = { s_signature_HasThis_Void_Byte }; private static readonly byte[][] s_signaturesOfExperimentalAttribute = { s_signature_HasThis_Void }; private static readonly byte[][] s_signaturesOfExcludeFromCodeCoverageAttribute = { s_signature_HasThis_Void }; @@ -538,6 +539,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 NullableMembersAttribute = new AttributeDescription("System.Runtime.CompilerServices", "NullableMembersAttribute", s_signaturesOfNullableMembersAttribute); 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); internal static readonly AttributeDescription EnumeratorCancellationAttribute = new AttributeDescription("System.Runtime.CompilerServices", "EnumeratorCancellationAttribute", s_signaturesOfEnumeratorCancellationAttribute); diff --git a/src/Compilers/Core/Portable/WellKnownMember.cs b/src/Compilers/Core/Portable/WellKnownMember.cs index 5b23c2c7ed7b0..3aeee3ed2384c 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_NullableMembersAttribute__ctor, System_Runtime_CompilerServices_ReferenceAssemblyAttribute__ctor, System_Runtime_CompilerServices_IsReadOnlyAttribute__ctor, System_Runtime_CompilerServices_IsByRefLikeAttribute__ctor, diff --git a/src/Compilers/Core/Portable/WellKnownMembers.cs b/src/Compilers/Core/Portable/WellKnownMembers.cs index f17d81ddfdccc..3d725983f0633 100644 --- a/src/Compilers/Core/Portable/WellKnownMembers.cs +++ b/src/Compilers/Core/Portable/WellKnownMembers.cs @@ -2914,6 +2914,14 @@ static WellKnownMembers() 1, // Method Signature (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Void, (byte)SignatureTypeCode.SZArray, (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Byte, + + // System_Runtime_CompilerServices_NullableMembersAttribute__ctor + (byte)MemberFlags.Constructor, // Flags + (byte)WellKnownType.ExtSentinel, (byte)(WellKnownType.System_Runtime_CompilerServices_NullableMembersAttribute - 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_ReferenceAssemblyAttribute__ctor (byte)MemberFlags.Constructor, // Flags @@ -3783,6 +3791,7 @@ static WellKnownMembers() ".ctor", // System_Runtime_CompilerServices_NullableAttribute__ctorByte ".ctor", // System_Runtime_CompilerServices_NullableAttribute__ctorTransformFlags + ".ctor", // System_Runtime_CompilerServices_NullableMembersAttribute__ctor ".ctor", // System_Runtime_CompilerServices_ReferenceAssemblyAttribute__ctor ".ctor", // System_Runtime_CompilerServices_IsReadOnlyAttribute__ctor ".ctor", // System_Runtime_CompilerServices_IsByRefLikeAttribute__ctor diff --git a/src/Compilers/Core/Portable/WellKnownTypes.cs b/src/Compilers/Core/Portable/WellKnownTypes.cs index aca3a83d6a3d1..64f181e49f5b8 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_NullableMembersAttribute, System_Runtime_CompilerServices_ReferenceAssemblyAttribute, System_Runtime_CompilerServices_IsReadOnlyAttribute, @@ -562,6 +563,7 @@ internal static class WellKnownTypes "Microsoft.CodeAnalysis.Runtime.Instrumentation", "System.Runtime.CompilerServices.NullableAttribute", + "System.Runtime.CompilerServices.NullableMembersAttribute", "System.Runtime.CompilerServices.ReferenceAssemblyAttribute", "System.Runtime.CompilerServices.IsReadOnlyAttribute", diff --git a/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs b/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs index 15da138fa9794..d4da4b0b9a798 100644 --- a/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs +++ b/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs @@ -39,7 +39,7 @@ namespace System.Runtime.CompilerServices AttributeTargets.Parameter | // The type of the parameter is a nullable reference type, or has a nullable reference type as one of its constituents AttributeTargets.ReturnValue | // The return type is a nullable reference type, or has a nullable reference type as one of its constituents AttributeTargets.Property | // The type of the property is a nullable reference type, or has a nullable reference type as one of its constituents - AttributeTargets.Class , // Base type has a nullable reference type as one of its constituents + AttributeTargets.Class, // Base type has a nullable reference type as one of its constituents AllowMultiple = false)] public class NullableAttribute : Attribute { @@ -51,6 +51,26 @@ public NullableAttribute(byte[] transformFlags) } "; + protected const string NullableMembersAttributeDefinition = @" +namespace System.Runtime.CompilerServices +{ + public enum NullableMembers + { + Public = 0, // public and protected only + Internal = 1, // public, protected, internal + All = 2, + } + [System.AttributeUsage(AttributeTargets.Module, AllowMultiple = false)] + public sealed class NullableMembersAttribute : Attribute + { + public readonly NullableMembers Members; + public NullableMembersAttribute(NullableMembers members) + { + Members = members; + } + } +}"; + // Nullable flow analysis attributes are defined at // https://github.com/dotnet/coreclr/blob/4a1275434fff99206f2a28f5f0e87f124069eb7f/src/System.Private.CoreLib/shared/System/Diagnostics/CodeAnalysis/NullableAttributes.cs protected const string AllowNullAttributeDefinition = @" diff --git a/src/Compilers/Test/Utilities/CSharp/NullableAttributesVisitor.cs b/src/Compilers/Test/Utilities/CSharp/NullableAttributesVisitor.cs new file mode 100644 index 0000000000000..e8d1d287505df --- /dev/null +++ b/src/Compilers/Test/Utilities/CSharp/NullableAttributesVisitor.cs @@ -0,0 +1,213 @@ +// 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.Text; +using Microsoft.CodeAnalysis.CSharp.Symbols; + +namespace Microsoft.CodeAnalysis.CSharp.Test.Utilities +{ + /// + /// Returns a string with all symbols containing nullable attributes. + /// + internal sealed class NullableAttributesVisitor : CSharpSymbolVisitor + { + internal static string GetString(Symbol symbol) + { + var builder = new StringBuilder(); + var visitor = new NullableAttributesVisitor(builder); + visitor.Visit(symbol); + return builder.ToString(); + } + + private readonly StringBuilder _builder; + private readonly HashSet _reported; + + private NullableAttributesVisitor(StringBuilder builder) + { + _builder = builder; + _reported = new HashSet(); + } + + public override void DefaultVisit(Symbol symbol) + { + ReportSymbol(symbol); + } + + public override void VisitModule(ModuleSymbol module) + { + Visit(module.GlobalNamespace); + } + + public override void VisitNamespace(NamespaceSymbol @namespace) + { + VisitList(@namespace.GetMembers()); + } + + public override void VisitNamedType(NamedTypeSymbol type) + { + ReportSymbol(type); + VisitList(type.TypeParameters); + VisitList(type.GetMembers()); + } + + public override void VisitMethod(MethodSymbol method) + { + // Skip accessors since those are covered by associated symbol. + if (method.IsAccessor()) return; + + ReportSymbol(method); + VisitList(method.TypeParameters); + VisitList(method.Parameters); + } + + public override void VisitTypeParameter(TypeParameterSymbol typeParameter) + { + ReportSymbol(typeParameter); + } + + private void VisitList(ImmutableArray symbols) where TSymbol : Symbol + { + foreach (var symbol in symbols) + { + Visit(symbol); + } + } + + private static string GetIndentString(Symbol symbol) + { + int level = 0; + while (true) + { + symbol = symbol.ContainingSymbol; + if (symbol.Kind == SymbolKind.Namespace) + { + break; + } + level++; + } + return new string(' ', level * 4); + } + + private static readonly SymbolDisplayFormat _displayFormat = + new SymbolDisplayFormat( + globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.OmittedAsContaining, + typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces, + propertyStyle: SymbolDisplayPropertyStyle.ShowReadWriteDescriptor, + genericsOptions: + SymbolDisplayGenericsOptions.IncludeTypeParameters | + SymbolDisplayGenericsOptions.IncludeVariance | + SymbolDisplayGenericsOptions.IncludeTypeConstraints, + memberOptions: + SymbolDisplayMemberOptions.IncludeParameters | + SymbolDisplayMemberOptions.IncludeType | + SymbolDisplayMemberOptions.IncludeRef | + SymbolDisplayMemberOptions.IncludeExplicitInterface, + parameterOptions: + SymbolDisplayParameterOptions.IncludeOptionalBrackets | + SymbolDisplayParameterOptions.IncludeDefaultValue | + SymbolDisplayParameterOptions.IncludeParamsRefOut | + SymbolDisplayParameterOptions.IncludeExtensionThis | + SymbolDisplayParameterOptions.IncludeType | + SymbolDisplayParameterOptions.IncludeName, + miscellaneousOptions: + SymbolDisplayMiscellaneousOptions.EscapeKeywordIdentifiers | + SymbolDisplayMiscellaneousOptions.UseErrorTypeSymbolName | + SymbolDisplayMiscellaneousOptions.IncludeNullableReferenceTypeModifier, + compilerInternalOptions: + SymbolDisplayCompilerInternalOptions.UseMetadataMethodNames | + SymbolDisplayCompilerInternalOptions.IncludeNonNullableTypeModifier); + + private void ReportContainingSymbols(Symbol symbol) + { + if (symbol.Kind == SymbolKind.Namespace) + { + return; + } + if (_reported.Contains(symbol)) + { + return; + } + ReportContainingSymbols(symbol.ContainingSymbol); + ReportSymbol(symbol, includeAlways: true); + } + + private void ReportSymbol(Symbol symbol, bool includeAlways = false) + { + var attributes = (symbol.Kind == SymbolKind.Method) ? ((MethodSymbol)symbol).GetReturnTypeAttributes() : symbol.GetAttributes(); + var nullableAttribute = GetAttribute(attributes, "System.Runtime.CompilerServices", "NullableAttribute"); + if (!includeAlways && nullableAttribute == null) + { + return; + } + ReportContainingSymbols(symbol.ContainingSymbol); + _builder.Append(GetIndentString(symbol)); + if (nullableAttribute != null) + { + _builder.Append($"{ReportAttribute(nullableAttribute)} "); + } + _builder.AppendLine(symbol.ToDisplayString(_displayFormat)); + _reported.Add(symbol); + } + + private static string ReportAttribute(CSharpAttributeData attribute) + { + var builder = new StringBuilder(); + builder.Append("["); + + var name = attribute.AttributeClass.Name; + if (name.EndsWith("Attribute")) name = name.Substring(0, name.Length - 9); + builder.Append(name); + + var arguments = attribute.ConstructorArguments.ToImmutableArray(); + if (arguments.Length > 0) + { + builder.Append("("); + printValues(builder, arguments); + builder.Append(")"); + } + + builder.Append("]"); + return builder.ToString(); + + static void printValues(StringBuilder builder, ImmutableArray values) + { + for (int i = 0; i < values.Length; i++) + { + if (i > 0) + { + builder.Append(", "); + } + printValue(builder, values[i]); + } + } + + static void printValue(StringBuilder builder, TypedConstant value) + { + if (value.Kind == TypedConstantKind.Array) + { + builder.Append("{ "); + printValues(builder, value.Values); + builder.Append(" }"); + } + else + { + builder.Append(value.Value); + } + } + } + + private static CSharpAttributeData GetAttribute(ImmutableArray attributes, string namespaceName, string name) + { + foreach (var attribute in attributes) + { + var containingType = attribute.AttributeConstructor.ContainingType; + if (containingType.Name == name && containingType.ContainingNamespace.QualifiedName == namespaceName) + { + return attribute; + } + } + return null; + } + } +} diff --git a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/WellKnownTypeValidationTests.vb b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/WellKnownTypeValidationTests.vb index cb83be8cb5497..bea7b364a2543 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_NullableMembersAttribute, WellKnownType.System_Span_T, WellKnownType.System_ReadOnlySpan_T, WellKnownType.System_Index, @@ -641,6 +642,7 @@ End Namespace Case WellKnownMember.System_Array__Empty, WellKnownMember.System_Runtime_CompilerServices_NullableAttribute__ctorByte, WellKnownMember.System_Runtime_CompilerServices_NullableAttribute__ctorTransformFlags, + WellKnownMember.System_Runtime_CompilerServices_NullableMembersAttribute__ctor, WellKnownMember.System_Span_T__ctor, WellKnownMember.System_Span_T__get_Item, WellKnownMember.System_Span_T__get_Length, @@ -778,6 +780,7 @@ End Namespace Case WellKnownMember.System_Array__Empty, WellKnownMember.System_Runtime_CompilerServices_NullableAttribute__ctorByte, WellKnownMember.System_Runtime_CompilerServices_NullableAttribute__ctorTransformFlags, + WellKnownMember.System_Runtime_CompilerServices_NullableMembersAttribute__ctor, WellKnownMember.System_Span_T__ctor, WellKnownMember.System_Span_T__get_Item, WellKnownMember.System_Span_T__get_Length, From eb691f49da189a0390fcf9644cdfb4e995c04807 Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Thu, 13 Jun 2019 09:08:49 -0700 Subject: [PATCH 02/16] Fix tests --- src/Compilers/CSharp/Portable/CSharpCompilationOptions.cs | 6 ++++-- .../Symbol/SymbolsTests/WellKnownTypeValidationTests.vb | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Compilers/CSharp/Portable/CSharpCompilationOptions.cs b/src/Compilers/CSharp/Portable/CSharpCompilationOptions.cs index eeeb73223a9fc..73d96318e1718 100644 --- a/src/Compilers/CSharp/Portable/CSharpCompilationOptions.cs +++ b/src/Compilers/CSharp/Portable/CSharpCompilationOptions.cs @@ -740,7 +740,8 @@ public bool Equals(CSharpCompilationOptions other) return this.AllowUnsafe == other.AllowUnsafe && this.TopLevelBinderFlags == other.TopLevelBinderFlags && (this.Usings == null ? other.Usings == null : this.Usings.SequenceEqual(other.Usings, StringComparer.Ordinal) && - this.NullableContextOptions == other.NullableContextOptions); + this.NullableContextOptions == other.NullableContextOptions && + this.EmitPublicNullableMetadataOnly == other.EmitPublicNullableMetadataOnly); } public override bool Equals(object obj) @@ -753,7 +754,8 @@ public override int GetHashCode() return Hash.Combine(base.GetHashCodeHelper(), Hash.Combine(this.AllowUnsafe, Hash.Combine(Hash.CombineValues(this.Usings, StringComparer.Ordinal), - Hash.Combine(TopLevelBinderFlags.GetHashCode(), this.NullableContextOptions.GetHashCode())))); + Hash.Combine(TopLevelBinderFlags.GetHashCode(), + Hash.Combine(NullableContextOptions.GetHashCode(), EmitPublicNullableMetadataOnly.GetHashCode()))))); } internal override Diagnostic FilterDiagnostic(Diagnostic diagnostic) diff --git a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/WellKnownTypeValidationTests.vb b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/WellKnownTypeValidationTests.vb index bea7b364a2543..8c737c5ca1dba 100644 --- a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/WellKnownTypeValidationTests.vb +++ b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/WellKnownTypeValidationTests.vb @@ -575,6 +575,7 @@ End Namespace Case WellKnownType.System_FormattableString, WellKnownType.System_Runtime_CompilerServices_FormattableStringFactory, WellKnownType.System_Runtime_CompilerServices_NullableAttribute, + WellKnownType.System_Runtime_CompilerServices_NullableMembersAttribute, WellKnownType.System_Span_T, WellKnownType.System_ReadOnlySpan_T, WellKnownType.System_Index, From da97ddc14d12f936ecd249e4547cd70e7eb4ac33 Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Thu, 13 Jun 2019 10:57:04 -0700 Subject: [PATCH 03/16] PR feedback --- docs/features/nullable-metadata.md | 2 +- .../CSharp/Portable/Compilation/CSharpCompilation.cs | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/docs/features/nullable-metadata.md b/docs/features/nullable-metadata.md index acdedd34234f4..86c03e39f8b80 100644 --- a/docs/features/nullable-metadata.md +++ b/docs/features/nullable-metadata.md @@ -170,7 +170,7 @@ The `NullableMembersAttribute` must be emitted if nullable attributes are droppe `private` or `internal` members to allow tools to correctly interpret the nullability of members without explicit `NullableAttribute` attributes. -To reduce the size of metadata, the C#8 compiler will not emit attributes `private` members, +To reduce the size of metadata, the C#8 compiler will not emit attributes for `private` members, and the compiler will only emit attributes for `internal` members if the assembly contains `InternalsVisibleToAttribute` attributes. The compiler will emit a `[module: NullableMembers(...)]` attribute to indicate which members were included. diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs index 63aead06d8e2c..e2ffd792c7145 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs @@ -3274,7 +3274,12 @@ internal bool ShouldEmitNullableAttributes(Symbol symbol) { Debug.Assert(!(symbol is null)); Debug.Assert(symbol.IsDefinition); - Debug.Assert(symbol.ContainingAssembly == SourceAssembly); + + var sourceAssembly = SourceAssembly; + if (symbol.ContainingAssembly != sourceAssembly) + { + return false; + } if (!Options.EmitPublicNullableMetadataOnly) { @@ -3286,7 +3291,7 @@ internal bool ShouldEmitNullableAttributes(Symbol symbol) return false; } - return !isInternal || SourceAssembly.InternalsAreVisible; + return !isInternal || sourceAssembly.InternalsAreVisible; } internal override AnalyzerDriver AnalyzerForLanguage(ImmutableArray analyzers, AnalyzerManager analyzerManager) From 5be3827644381b7f66bb0b2a88330677013c2b3a Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Thu, 13 Jun 2019 12:35:27 -0700 Subject: [PATCH 04/16] Fix tests --- .../Symbols/Source/LocalFunctionSymbol.cs | 2 +- .../Portable/Symbols/Source/ParameterHelpers.cs | 16 ++++++++++++++-- .../Compilation/CSharpCompilationOptionsTests.cs | 3 ++- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs index 606a453fcc9e7..4f9213793dd7a 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs @@ -239,7 +239,7 @@ internal void ComputeReturnType() compilation.EnsureIsReadOnlyAttributeExists(diagnostics, location, modifyCompilation: false); } - if (compilation.ShouldEmitNullableAttributes(this) && + if (compilation?.ShouldEmitNullableAttributes(this) == true && returnType.NeedsNullableAttribute()) { compilation.EnsureNullableAttributeExists(diagnostics, location, modifyCompilation: false); diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs b/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs index b5a600cb53ec3..6512b9805566f 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs @@ -124,7 +124,13 @@ public static ImmutableArray MakeParameters( internal static void EnsureIsReadOnlyAttributeExists(CSharpCompilation compilation, ImmutableArray parameters, DiagnosticBag diagnostics, bool modifyCompilation) { - Debug.Assert(compilation != null); + // These parameters might not come from a compilation (example: lambdas evaluated in EE). + // During rewriting, lowering will take care of flagging the appropriate PEModuleBuilder instead. + if (compilation == null) + { + return; + } + foreach (var parameter in parameters) { if (parameter.RefKind == RefKind.In) @@ -136,7 +142,13 @@ internal static void EnsureIsReadOnlyAttributeExists(CSharpCompilation compilati internal static void EnsureNullableAttributeExists(CSharpCompilation compilation, Symbol container, ImmutableArray parameters, DiagnosticBag diagnostics, bool modifyCompilation) { - Debug.Assert(compilation != null); + // These parameters might not come from a compilation (example: lambdas evaluated in EE). + // During rewriting, lowering will take care of flagging the appropriate PEModuleBuilder instead. + if (compilation == null) + { + return; + } + foreach (var parameter in parameters) { if (compilation.ShouldEmitNullableAttributes(container) && parameter.TypeWithAnnotations.NeedsNullableAttribute()) diff --git a/src/Compilers/CSharp/Test/Symbol/Compilation/CSharpCompilationOptionsTests.cs b/src/Compilers/CSharp/Test/Symbol/Compilation/CSharpCompilationOptionsTests.cs index f89dfd75d629e..494efd39f4536 100644 --- a/src/Compilers/CSharp/Test/Symbol/Compilation/CSharpCompilationOptionsTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Compilation/CSharpCompilationOptionsTests.cs @@ -365,7 +365,8 @@ public void TestFieldsForEqualsAndGetHashCode() "AllowUnsafe", "Usings", "TopLevelBinderFlags", - "NullableContextOptions"); + "NullableContextOptions", + "EmitPublicNullableMetadataOnly"); } [Fact] From fa7713d834f86a7190ec8ec5b7004ab59139648c Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Thu, 13 Jun 2019 13:47:30 -0700 Subject: [PATCH 05/16] PR feedback --- docs/features/nullable-metadata.md | 41 +++++--------- .../Portable/CSharpCompilationOptions.cs | 36 ++---------- .../Portable/Compilation/CSharpCompilation.cs | 15 ++++- ...dEmbeddedNullableMembersAttributeSymbol.cs | 56 ------------------- .../Test/CommandLine/CommandLineTests.cs | 52 ++++++++++++++++- .../Attributes/AttributeTests_Nullable.cs | 50 ++++++++++++++--- ...s => AttributeTests_NullablePublicOnly.cs} | 2 +- .../Symbol/Symbols/MissingSpecialMember.cs | 4 +- .../Attributes/AttributeDescription.cs | 4 +- .../Core/Portable/WellKnownMember.cs | 2 +- .../Core/Portable/WellKnownMembers.cs | 9 ++- src/Compilers/Core/Portable/WellKnownTypes.cs | 4 +- .../Test/Utilities/CSharp/CSharpTestBase.cs | 15 +---- .../WellKnownTypeValidationTests.vb | 8 +-- 14 files changed, 143 insertions(+), 155 deletions(-) delete mode 100644 src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEmbeddedNullableMembersAttributeSymbol.cs rename src/Compilers/CSharp/Test/Emit/Attributes/{AttributeTests_NullableMembers.cs => AttributeTests_NullablePublicOnly.cs} (80%) diff --git a/docs/features/nullable-metadata.md b/docs/features/nullable-metadata.md index 86c03e39f8b80..a4a4cee49ba11 100644 --- a/docs/features/nullable-metadata.md +++ b/docs/features/nullable-metadata.md @@ -76,7 +76,6 @@ namespace System.Runtime.CompilerServices [System.AttributeUsage( AttributeTargets.Module | AttributeTargets.Class | - AttributeTargets.Delegate | AttributeTargets.Interface | AttributeTargets.Method | AttributeTargets.Struct, @@ -138,46 +137,32 @@ class Program } ``` -## NullableMembersAttribute +## Private members -`NullableMembersAttribute` can be used to indicate whether nullable attributes were included for `private` or `internal` members. +To reduce the size of metadata, the C#8 compiler can be configured to not emit attributes +for members that are inaccessible outside the assembly (`private` members, and also `internal` members +if the assembly contains any `InternalsVisibleToAttribute` attributes). + +The compiler behavior is configured from a command-line flag. +For now a feature flag is used: `-feature: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. ```C# namespace System.Runtime.CompilerServices { - public enum NullableMembers - { - Public = 0, // public and protected only - Internal = 1, // public, protected, internal - All = 2, - } [System.AttributeUsage(AttributeTargets.Module, AllowMultiple = false)] - public sealed class NullableMembersAttribute : Attribute + public sealed class NullablePublicOnlyAttribute : Attribute { - public readonly NullableMembers Members; - public NullableMembersAttribute(NullableMembers members) - { - Members = members; - } } } ``` -The `NullableMembersAttribute` type is for compiler use only - it is not permitted in source. +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. -The `NullableMembersAttribute` must be emitted if nullable attributes are dropped for -`private` or `internal` members to allow tools to correctly interpret the nullability of members -without explicit `NullableAttribute` attributes. - -To reduce the size of metadata, the C#8 compiler will not emit attributes for `private` members, -and the compiler will only emit attributes for `internal` members if the assembly contains -`InternalsVisibleToAttribute` attributes. -The compiler will emit a `[module: NullableMembers(...)]` attribute to indicate which members were included. - -_If necessary, a compiler option could be added in a future release to override the default behavior and -explicitly emit or drop nullable attributes for `private` or `internal` members._ - ## Compatibility The nullable metadata does not include an explicit version number. diff --git a/src/Compilers/CSharp/Portable/CSharpCompilationOptions.cs b/src/Compilers/CSharp/Portable/CSharpCompilationOptions.cs index 73d96318e1718..844d64396a19d 100644 --- a/src/Compilers/CSharp/Portable/CSharpCompilationOptions.cs +++ b/src/Compilers/CSharp/Portable/CSharpCompilationOptions.cs @@ -38,13 +38,6 @@ public sealed class CSharpCompilationOptions : CompilationOptions, IEquatable public NullableContextOptions NullableContextOptions { get; private set; } - /// - /// Emit nullable attributes for only those members that are visible outside the assembly - /// (public, protected, and if any [InternalsVisibleTo] attributes, internal members). - /// If false, attributes are emitted for all members regardless of visibility. - /// - internal bool EmitPublicNullableMetadataOnly { get; private set; } - // Defaults correspond to the compiler's defaults or indicate that the user did not specify when that is significant. // That's significant when one option depends on another's setting. SubsystemVersion depends on Platform and Target. public CSharpCompilationOptions( @@ -91,8 +84,7 @@ public CSharpCompilationOptions( referencesSupersedeLowerVersions: false, publicSign: publicSign, topLevelBinderFlags: BinderFlags.None, - nullableContextOptions: nullableContextOptions, - emitPublicNullableMetadataOnly: false) + nullableContextOptions: nullableContextOptions) { } @@ -215,8 +207,7 @@ internal CSharpCompilationOptions( bool referencesSupersedeLowerVersions, bool publicSign, BinderFlags topLevelBinderFlags, - NullableContextOptions nullableContextOptions, - bool emitPublicNullableMetadataOnly) + NullableContextOptions nullableContextOptions) : base(outputKind, reportSuppressedDiagnostics, moduleName, mainTypeName, scriptClassName, cryptoKeyContainer, cryptoKeyFile, cryptoPublicKey, delaySign, publicSign, optimizationLevel, checkOverflow, platform, generalDiagnosticOption, warningLevel, specificDiagnosticOptions.ToImmutableDictionaryOrEmpty(), @@ -228,7 +219,6 @@ internal CSharpCompilationOptions( this.AllowUnsafe = allowUnsafe; this.TopLevelBinderFlags = topLevelBinderFlags; this.NullableContextOptions = nullableContextOptions; - this.EmitPublicNullableMetadataOnly = emitPublicNullableMetadataOnly; } private CSharpCompilationOptions(CSharpCompilationOptions other) : this( @@ -262,8 +252,7 @@ private CSharpCompilationOptions(CSharpCompilationOptions other) : this( reportSuppressedDiagnostics: other.ReportSuppressedDiagnostics, publicSign: other.PublicSign, topLevelBinderFlags: other.TopLevelBinderFlags, - nullableContextOptions: other.NullableContextOptions, - emitPublicNullableMetadataOnly: other.EmitPublicNullableMetadataOnly) + nullableContextOptions: other.NullableContextOptions) { } @@ -411,16 +400,6 @@ public CSharpCompilationOptions WithNullableContextOptions(NullableContextOption return new CSharpCompilationOptions(this) { NullableContextOptions = options }; } - internal CSharpCompilationOptions WithEmitPublicNullableMetadataOnly(bool emitPublicNullableMetadataOnly) - { - if (emitPublicNullableMetadataOnly == this.EmitPublicNullableMetadataOnly) - { - return this; - } - - return new CSharpCompilationOptions(this) { EmitPublicNullableMetadataOnly = emitPublicNullableMetadataOnly }; - } - public CSharpCompilationOptions WithAllowUnsafe(bool enabled) { if (enabled == this.AllowUnsafe) @@ -740,8 +719,7 @@ public bool Equals(CSharpCompilationOptions other) return this.AllowUnsafe == other.AllowUnsafe && this.TopLevelBinderFlags == other.TopLevelBinderFlags && (this.Usings == null ? other.Usings == null : this.Usings.SequenceEqual(other.Usings, StringComparer.Ordinal) && - this.NullableContextOptions == other.NullableContextOptions && - this.EmitPublicNullableMetadataOnly == other.EmitPublicNullableMetadataOnly); + this.NullableContextOptions == other.NullableContextOptions); } public override bool Equals(object obj) @@ -754,8 +732,7 @@ public override int GetHashCode() return Hash.Combine(base.GetHashCodeHelper(), Hash.Combine(this.AllowUnsafe, Hash.Combine(Hash.CombineValues(this.Usings, StringComparer.Ordinal), - Hash.Combine(TopLevelBinderFlags.GetHashCode(), - Hash.Combine(NullableContextOptions.GetHashCode(), EmitPublicNullableMetadataOnly.GetHashCode()))))); + Hash.Combine(TopLevelBinderFlags.GetHashCode(), NullableContextOptions.GetHashCode())))); } internal override Diagnostic FilterDiagnostic(Diagnostic diagnostic) @@ -927,8 +904,7 @@ public CSharpCompilationOptions( referencesSupersedeLowerVersions: false, publicSign: false, topLevelBinderFlags: BinderFlags.None, - nullableContextOptions: NullableContextOptions.Disable, - emitPublicNullableMetadataOnly: false) + nullableContextOptions: NullableContextOptions.Disable) { } } diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs index e2ffd792c7145..f1a4b0a185ed6 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs @@ -108,6 +108,13 @@ internal Conversions Conversions /// private EntryPoint _lazyEntryPoint; + /// + /// Emit nullable attributes for only those members that are visible outside the assembly + /// (public, protected, and if any [InternalsVisibleTo] attributes, internal members). + /// If false, attributes are emitted for all members regardless of visibility. + /// + private ThreeState _lazyEmitNullablePublicOnly; + /// /// The set of trees for which a has been added to the queue. /// @@ -3281,7 +3288,13 @@ internal bool ShouldEmitNullableAttributes(Symbol symbol) return false; } - if (!Options.EmitPublicNullableMetadataOnly) + if (!_lazyEmitNullablePublicOnly.HasValue()) + { + bool value = SyntaxTrees.FirstOrDefault()?.Options?.Features?.ContainsKey("nullablePublicOnly") == true; + _lazyEmitNullablePublicOnly = value.ToThreeState(); + } + + if (_lazyEmitNullablePublicOnly != ThreeState.True) { return true; } diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEmbeddedNullableMembersAttributeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEmbeddedNullableMembersAttributeSymbol.cs deleted file mode 100644 index 9d6b6ebc4d7a1..0000000000000 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEmbeddedNullableMembersAttributeSymbol.cs +++ /dev/null @@ -1,56 +0,0 @@ -// 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 SynthesizedEmbeddedNullableMembersAttributeSymbol : SynthesizedEmbeddedAttributeSymbolBase - { - private readonly ImmutableArray _fields; - private readonly ImmutableArray _constructors; - - public SynthesizedEmbeddedNullableMembersAttributeSymbol( - CSharpCompilation compilation, - DiagnosticBag diagnostics) - : base(AttributeDescription.NullableMembersAttribute, 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.NullableMembersAttribute.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/Test/CommandLine/CommandLineTests.cs b/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs index 78230daa47258..de813a3308f46 100644 --- a/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs +++ b/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs @@ -9031,7 +9031,6 @@ public void ParseAdditionalFile() Assert.Equal(0, args.AdditionalFiles.Length); } - [Fact] public void ParseEditorConfig() { @@ -9074,6 +9073,57 @@ public void ParseEditorConfig() Assert.Equal(0, args.AnalyzerConfigPaths.Length); } + [Fact] + public void NullablePublicOnly() + { + string source = +@"namespace System.Runtime.CompilerServices +{ + public sealed class NullableAttribute : Attribute { } // missing constructor +} +public class Program +{ + private object? P => null; +}"; + string errorMessage = "error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.NullableAttribute..ctor'"; + + string filePath = Temp.CreateFile().WriteAllText(source).Path; + int exitCode; + string output; + + // No /feature + (exitCode, output) = compileAndRun(featureOpt: null); + Assert.Equal(1, exitCode); + Assert.Contains(errorMessage, output, StringComparison.Ordinal); + + // /features:nullablePublicOnly + (exitCode, output) = compileAndRun("/features:nullablePublicOnly"); + Assert.Equal(0, exitCode); + Assert.DoesNotContain(errorMessage, output, StringComparison.Ordinal); + + // /features:nullablePublicOnly=true + (exitCode, output) = compileAndRun("/features:nullablePublicOnly=true"); + Assert.Equal(0, exitCode); + Assert.DoesNotContain(errorMessage, output, StringComparison.Ordinal); + + // /features:nullablePublicOnly=false (the value is ignored) + (exitCode, output) = compileAndRun("/features:nullablePublicOnly=false"); + Assert.Equal(0, exitCode); + Assert.DoesNotContain(errorMessage, output, StringComparison.Ordinal); + + CleanupAllGeneratedFiles(filePath); + + (int, string) compileAndRun(string featureOpt) + { + var args = new[] { "/target:library", "/preferreduilang:en", "/langversion:8", "/nullable+", filePath }; + if (featureOpt != null) args = args.Concat(featureOpt).ToArray(); + var compiler = CreateCSharpCompiler(null, WorkingDirectory, args); + var outWriter = new StringWriter(CultureInfo.InvariantCulture); + int exitCode = compiler.Run(outWriter); + return (exitCode, outWriter.ToString()); + }; + } + private static int OccurrenceCount(string source, string word) { var n = 0; diff --git a/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_Nullable.cs b/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_Nullable.cs index c5c5e91d57023..2da5e26a1b5dc 100644 --- a/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_Nullable.cs +++ b/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_Nullable.cs @@ -1523,12 +1523,11 @@ private void EmitPrivateMetadata(string source, string expectedPublicOnly, strin [assembly: InternalsVisibleTo(""Other"")]"; var options = WithNonNullTypesTrue().WithMetadataImportOptions(MetadataImportOptions.All); - VerifyNullableAttributes(CreateCompilation(source, options: options), expectedAll); - VerifyNullableAttributes(CreateCompilation(source, options: options.WithEmitPublicNullableMetadataOnly(false)), expectedAll); - VerifyNullableAttributes(CreateCompilation(source, options: options.WithEmitPublicNullableMetadataOnly(true)), expectedPublicOnly); - VerifyNullableAttributes(CreateCompilation(new[] { source, sourceIVTs }, options: options), expectedAll); - VerifyNullableAttributes(CreateCompilation(new[] { source, sourceIVTs }, options: options.WithEmitPublicNullableMetadataOnly(false)), expectedAll); - VerifyNullableAttributes(CreateCompilation(new[] { source, sourceIVTs }, options: options.WithEmitPublicNullableMetadataOnly(true)), expectedPublicAndInternal); + 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); } /// @@ -1565,9 +1564,10 @@ internal class B : I public static object operator!(B b) => b; public event D E; }"; - var options = WithNonNullTypesTrue().WithMetadataImportOptions(MetadataImportOptions.All); + var options = WithNonNullTypesTrue(); + var parseOptions = TestOptions.Regular8; - var comp = CreateCompilation(new[] { sourceAttribute, source }, options: options.WithEmitPublicNullableMetadataOnly(false)); + var comp = CreateCompilation(new[] { sourceAttribute, source }, options: options, parseOptions: parseOptions); comp.VerifyEmitDiagnostics( // (6,21): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.NullableAttribute..ctor' // private object? F; @@ -1621,10 +1621,42 @@ internal class B : I // public event D E; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "E").WithArguments("System.Runtime.CompilerServices.NullableAttribute", ".ctor").WithLocation(21, 29)); - comp = CreateCompilation(new[] { sourceAttribute, source }, options: options.WithEmitPublicNullableMetadataOnly(true)); + comp = CreateCompilation(new[] { sourceAttribute, source }, options: options, parseOptions: parseOptions.WithFeature("nullablePublicOnly")); comp.VerifyEmitDiagnostics(); } + [Fact] + public void EmitPrivateMetadata_MissingAttributeConstructor_NullableDisabled() + { + var sourceAttribute = +@"namespace System.Runtime.CompilerServices +{ + public sealed class NullableAttribute : Attribute { } +}"; + var source = +@"public class Program +{ + private object? P => null; +}"; + var options = TestOptions.ReleaseDll; + var parseOptions = TestOptions.Regular8; + + 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' + // 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. + // private object? P => null; + Diagnostic(ErrorCode.WRN_MissingNonNullTypesContextForAnnotation, "?").WithLocation(3, 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. + // private object? P => null; + Diagnostic(ErrorCode.WRN_MissingNonNullTypesContextForAnnotation, "?").WithLocation(3, 19)); + } + [Fact] public void UseSiteError_LambdaReturnType() { diff --git a/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_NullableMembers.cs b/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_NullablePublicOnly.cs similarity index 80% rename from src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_NullableMembers.cs rename to src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_NullablePublicOnly.cs index a51f4e167080c..bf69b558ec64e 100644 --- a/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_NullableMembers.cs +++ b/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_NullablePublicOnly.cs @@ -4,7 +4,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests { - public class AttributeTests_NullableMembers : CSharpTestBase + public class AttributeTests_NullablePublicOnly : CSharpTestBase { } } diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs index 974539aa92ce3..bd253e6013321 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs @@ -574,7 +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_NullableMembersAttribute: + case WellKnownType.System_Runtime_CompilerServices_NullablePublicOnlyAttribute: case WellKnownType.System_Runtime_CompilerServices_IsReadOnlyAttribute: case WellKnownType.System_Runtime_CompilerServices_IsByRefLikeAttribute: case WellKnownType.System_Span_T: @@ -900,7 +900,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_NullableMembersAttribute__ctor: + case WellKnownMember.System_Runtime_CompilerServices_NullablePublicOnlyAttribute__ctor: case WellKnownMember.System_Span_T__ctor: case WellKnownMember.System_Span_T__get_Item: case WellKnownMember.System_Span_T__get_Length: diff --git a/src/Compilers/Core/Portable/Symbols/Attributes/AttributeDescription.cs b/src/Compilers/Core/Portable/Symbols/Attributes/AttributeDescription.cs index 1783dfad59e52..cc08ec834924a 100644 --- a/src/Compilers/Core/Portable/Symbols/Attributes/AttributeDescription.cs +++ b/src/Compilers/Core/Portable/Symbols/Attributes/AttributeDescription.cs @@ -411,7 +411,7 @@ static AttributeDescription() }; private static readonly byte[][] s_signaturesOfNullableAttribute = { s_signature_HasThis_Void_Byte, s_signature_HasThis_Void_SzArray_Byte }; - private static readonly byte[][] s_signaturesOfNullableMembersAttribute = { s_signature_HasThis_Void_Byte }; + private static readonly byte[][] s_signaturesOfNullablePublicOnlyAttribute = { s_signature_HasThis_Void }; private static readonly byte[][] s_signaturesOfExperimentalAttribute = { s_signature_HasThis_Void }; private static readonly byte[][] s_signaturesOfExcludeFromCodeCoverageAttribute = { s_signature_HasThis_Void }; @@ -539,7 +539,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 NullableMembersAttribute = new AttributeDescription("System.Runtime.CompilerServices", "NullableMembersAttribute", s_signaturesOfNullableMembersAttribute); + 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); internal static readonly AttributeDescription EnumeratorCancellationAttribute = new AttributeDescription("System.Runtime.CompilerServices", "EnumeratorCancellationAttribute", s_signaturesOfEnumeratorCancellationAttribute); diff --git a/src/Compilers/Core/Portable/WellKnownMember.cs b/src/Compilers/Core/Portable/WellKnownMember.cs index 3aeee3ed2384c..d0514fd21cf10 100644 --- a/src/Compilers/Core/Portable/WellKnownMember.cs +++ b/src/Compilers/Core/Portable/WellKnownMember.cs @@ -420,7 +420,7 @@ internal enum WellKnownMember System_Runtime_CompilerServices_NullableAttribute__ctorByte, System_Runtime_CompilerServices_NullableAttribute__ctorTransformFlags, - System_Runtime_CompilerServices_NullableMembersAttribute__ctor, + System_Runtime_CompilerServices_NullablePublicOnlyAttribute__ctor, System_Runtime_CompilerServices_ReferenceAssemblyAttribute__ctor, System_Runtime_CompilerServices_IsReadOnlyAttribute__ctor, System_Runtime_CompilerServices_IsByRefLikeAttribute__ctor, diff --git a/src/Compilers/Core/Portable/WellKnownMembers.cs b/src/Compilers/Core/Portable/WellKnownMembers.cs index 3d725983f0633..1168db951e2e2 100644 --- a/src/Compilers/Core/Portable/WellKnownMembers.cs +++ b/src/Compilers/Core/Portable/WellKnownMembers.cs @@ -2915,13 +2915,12 @@ static WellKnownMembers() (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Void, (byte)SignatureTypeCode.SZArray, (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Byte, - // System_Runtime_CompilerServices_NullableMembersAttribute__ctor + // System_Runtime_CompilerServices_NullablePublicOnlyAttribute__ctor (byte)MemberFlags.Constructor, // Flags - (byte)WellKnownType.ExtSentinel, (byte)(WellKnownType.System_Runtime_CompilerServices_NullableMembersAttribute - WellKnownType.ExtSentinel), // DeclaringTypeId + (byte)WellKnownType.ExtSentinel, (byte)(WellKnownType.System_Runtime_CompilerServices_NullablePublicOnlyAttribute - WellKnownType.ExtSentinel), // DeclaringTypeId 0, // Arity - 1, // Method Signature + 0, // Method Signature (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Void, - (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Byte, // System_Runtime_CompilerServices_ReferenceAssemblyAttribute__ctor (byte)MemberFlags.Constructor, // Flags @@ -3791,7 +3790,7 @@ static WellKnownMembers() ".ctor", // System_Runtime_CompilerServices_NullableAttribute__ctorByte ".ctor", // System_Runtime_CompilerServices_NullableAttribute__ctorTransformFlags - ".ctor", // System_Runtime_CompilerServices_NullableMembersAttribute__ctor + ".ctor", // System_Runtime_CompilerServices_NullablePublicOnlyAttribute__ctor ".ctor", // System_Runtime_CompilerServices_ReferenceAssemblyAttribute__ctor ".ctor", // System_Runtime_CompilerServices_IsReadOnlyAttribute__ctor ".ctor", // System_Runtime_CompilerServices_IsByRefLikeAttribute__ctor diff --git a/src/Compilers/Core/Portable/WellKnownTypes.cs b/src/Compilers/Core/Portable/WellKnownTypes.cs index 64f181e49f5b8..9b8c212fb3236 100644 --- a/src/Compilers/Core/Portable/WellKnownTypes.cs +++ b/src/Compilers/Core/Portable/WellKnownTypes.cs @@ -264,7 +264,7 @@ internal enum WellKnownType Microsoft_CodeAnalysis_Runtime_Instrumentation, System_Runtime_CompilerServices_NullableAttribute, - System_Runtime_CompilerServices_NullableMembersAttribute, + System_Runtime_CompilerServices_NullablePublicOnlyAttribute, System_Runtime_CompilerServices_ReferenceAssemblyAttribute, System_Runtime_CompilerServices_IsReadOnlyAttribute, @@ -563,7 +563,7 @@ internal static class WellKnownTypes "Microsoft.CodeAnalysis.Runtime.Instrumentation", "System.Runtime.CompilerServices.NullableAttribute", - "System.Runtime.CompilerServices.NullableMembersAttribute", + "System.Runtime.CompilerServices.NullablePublicOnlyAttribute", "System.Runtime.CompilerServices.ReferenceAssemblyAttribute", "System.Runtime.CompilerServices.IsReadOnlyAttribute", diff --git a/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs b/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs index d4da4b0b9a798..60b01f43c95ec 100644 --- a/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs +++ b/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs @@ -51,23 +51,12 @@ public NullableAttribute(byte[] transformFlags) } "; - protected const string NullableMembersAttributeDefinition = @" + protected const string NullablePublicOnlyAttributeDefinition = @" namespace System.Runtime.CompilerServices { - public enum NullableMembers - { - Public = 0, // public and protected only - Internal = 1, // public, protected, internal - All = 2, - } [System.AttributeUsage(AttributeTargets.Module, AllowMultiple = false)] - public sealed class NullableMembersAttribute : Attribute + public sealed class NullablePublicOnlyAttribute : Attribute { - public readonly NullableMembers Members; - public NullableMembersAttribute(NullableMembers members) - { - Members = members; - } } }"; diff --git a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/WellKnownTypeValidationTests.vb b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/WellKnownTypeValidationTests.vb index 8c737c5ca1dba..b52215bed63da 100644 --- a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/WellKnownTypeValidationTests.vb +++ b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/WellKnownTypeValidationTests.vb @@ -513,7 +513,7 @@ End Namespace Case WellKnownType.System_FormattableString, WellKnownType.System_Runtime_CompilerServices_FormattableStringFactory, WellKnownType.System_Runtime_CompilerServices_NullableAttribute, - WellKnownType.System_Runtime_CompilerServices_NullableMembersAttribute, + WellKnownType.System_Runtime_CompilerServices_NullablePublicOnlyAttribute, WellKnownType.System_Span_T, WellKnownType.System_ReadOnlySpan_T, WellKnownType.System_Index, @@ -575,7 +575,7 @@ End Namespace Case WellKnownType.System_FormattableString, WellKnownType.System_Runtime_CompilerServices_FormattableStringFactory, WellKnownType.System_Runtime_CompilerServices_NullableAttribute, - WellKnownType.System_Runtime_CompilerServices_NullableMembersAttribute, + WellKnownType.System_Runtime_CompilerServices_NullablePublicOnlyAttribute, WellKnownType.System_Span_T, WellKnownType.System_ReadOnlySpan_T, WellKnownType.System_Index, @@ -643,7 +643,7 @@ End Namespace Case WellKnownMember.System_Array__Empty, WellKnownMember.System_Runtime_CompilerServices_NullableAttribute__ctorByte, WellKnownMember.System_Runtime_CompilerServices_NullableAttribute__ctorTransformFlags, - WellKnownMember.System_Runtime_CompilerServices_NullableMembersAttribute__ctor, + WellKnownMember.System_Runtime_CompilerServices_NullablePublicOnlyAttribute__ctor, WellKnownMember.System_Span_T__ctor, WellKnownMember.System_Span_T__get_Item, WellKnownMember.System_Span_T__get_Length, @@ -781,7 +781,7 @@ End Namespace Case WellKnownMember.System_Array__Empty, WellKnownMember.System_Runtime_CompilerServices_NullableAttribute__ctorByte, WellKnownMember.System_Runtime_CompilerServices_NullableAttribute__ctorTransformFlags, - WellKnownMember.System_Runtime_CompilerServices_NullableMembersAttribute__ctor, + WellKnownMember.System_Runtime_CompilerServices_NullablePublicOnlyAttribute__ctor, WellKnownMember.System_Span_T__ctor, WellKnownMember.System_Span_T__get_Item, WellKnownMember.System_Span_T__get_Length, From 557ab0b66dc5be25ce5fcb3e5bf63b3b0d17ee23 Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Thu, 13 Jun 2019 21:22:52 -0700 Subject: [PATCH 06/16] Fix build --- .../CSharp/Portable/Binder/Semantics/AccessCheck.cs | 4 ++++ .../Symbol/Compilation/CSharpCompilationOptionsTests.cs | 7 ++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/AccessCheck.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/AccessCheck.cs index 4c042ebe0409c..c9d92c8ec95ff 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/AccessCheck.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/AccessCheck.cs @@ -56,6 +56,10 @@ public static bool IsSymbolAccessible( return IsSymbolAccessibleCore(symbol, within, throughTypeOpt, out failedThroughTypeCheck, within.DeclaringCompilation, ref useSiteDiagnostics, basesBeingResolved); } + /// + /// Returns true if the symbol is effectively public or internal based on + /// the declared accessibility of the symbol and any containing symbols. + /// internal static bool IsPublicOrInternal(Symbol symbol, out bool isInternal) { Debug.Assert(!(symbol is null)); diff --git a/src/Compilers/CSharp/Test/Symbol/Compilation/CSharpCompilationOptionsTests.cs b/src/Compilers/CSharp/Test/Symbol/Compilation/CSharpCompilationOptionsTests.cs index 494efd39f4536..b86873c87ddee 100644 --- a/src/Compilers/CSharp/Test/Symbol/Compilation/CSharpCompilationOptionsTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Compilation/CSharpCompilationOptionsTests.cs @@ -365,8 +365,7 @@ public void TestFieldsForEqualsAndGetHashCode() "AllowUnsafe", "Usings", "TopLevelBinderFlags", - "NullableContextOptions", - "EmitPublicNullableMetadataOnly"); + "NullableContextOptions"); } [Fact] @@ -410,14 +409,12 @@ private static CSharpCompilationOptions CreateCSharpCompilationOptions() var topLevelBinderFlags = BinderFlags.None; var publicSign = false; NullableContextOptions nullableContextOptions = NullableContextOptions.Disable; - bool emitPrivateNullableMetadata = false; return new CSharpCompilationOptions(OutputKind.ConsoleApplication, reportSuppressedDiagnostics, moduleName, mainTypeName, scriptClassName, usings, optimizationLevel, checkOverflow, allowUnsafe, cryptoKeyContainer, cryptoKeyFile, cryptoPublicKey, delaySign, platform, generalDiagnosticOption, warningLevel, specificDiagnosticOptions, concurrentBuild, deterministic, currentLocalTime, debugPlusMode, xmlReferenceResolver, sourceReferenceResolver, metadataReferenceResolver, - assemblyIdentityComparer, strongNameProvider, metadataImportOptions, referencesSupersedeLowerVersions, publicSign, topLevelBinderFlags, - nullableContextOptions, emitPrivateNullableMetadata); + assemblyIdentityComparer, strongNameProvider, metadataImportOptions, referencesSupersedeLowerVersions, publicSign, topLevelBinderFlags, nullableContextOptions); } private sealed class MetadataReferenceResolverWithEquality : MetadataReferenceResolver From 1b5bb9595258f9c59b9cafbd55ee793b71bd47cc Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Thu, 13 Jun 2019 21:38:46 -0700 Subject: [PATCH 07/16] Emit NullablePublicOnlyAttribute --- .../Portable/Compilation/CSharpCompilation.cs | 21 +- .../Emitter/Model/PEAssemblyBuilder.cs | 63 +++--- .../Portable/Emitter/Model/PEModuleBuilder.cs | 93 +++----- .../Symbols/Compilation_WellKnownMembers.cs | 174 ++++++--------- .../Portable/Symbols/EmbeddedAttributes.cs | 16 ++ .../Symbols/Source/SourceModuleSymbol.cs | 35 +++ .../Attributes/AttributeTests_Nullable.cs | 2 +- .../AttributeTests_NullablePublicOnly.cs | 203 ++++++++++++++++++ .../Attributes/AttributeTests_RefReadOnly.cs | 29 +-- 9 files changed, 422 insertions(+), 214 deletions(-) create mode 100644 src/Compilers/CSharp/Portable/Symbols/EmbeddedAttributes.cs diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs index f1a4b0a185ed6..cc2ef83e7318c 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs @@ -3277,6 +3277,19 @@ internal bool CanEmitSpecialType(SpecialType type) return (diagnostic == null) || (diagnostic.Severity != DiagnosticSeverity.Error); } + internal bool EmitNullablePublicOnly + { + get + { + if (!_lazyEmitNullablePublicOnly.HasValue()) + { + bool value = SyntaxTrees.FirstOrDefault()?.Options?.Features?.ContainsKey("nullablePublicOnly") == true; + _lazyEmitNullablePublicOnly = value.ToThreeState(); + } + return _lazyEmitNullablePublicOnly.Value(); + } + } + internal bool ShouldEmitNullableAttributes(Symbol symbol) { Debug.Assert(!(symbol is null)); @@ -3288,13 +3301,7 @@ internal bool ShouldEmitNullableAttributes(Symbol symbol) return false; } - if (!_lazyEmitNullablePublicOnly.HasValue()) - { - bool value = SyntaxTrees.FirstOrDefault()?.Options?.Features?.ContainsKey("nullablePublicOnly") == true; - _lazyEmitNullablePublicOnly = value.ToThreeState(); - } - - if (_lazyEmitNullablePublicOnly != ThreeState.True) + if (!EmitNullablePublicOnly) { return true; } diff --git a/src/Compilers/CSharp/Portable/Emitter/Model/PEAssemblyBuilder.cs b/src/Compilers/CSharp/Portable/Emitter/Model/PEAssemblyBuilder.cs index 91534978100a6..e9d9427a2de87 100644 --- a/src/Compilers/CSharp/Portable/Emitter/Model/PEAssemblyBuilder.cs +++ b/src/Compilers/CSharp/Portable/Emitter/Model/PEAssemblyBuilder.cs @@ -26,6 +26,7 @@ internal abstract class PEAssemblyBuilderBase : PEModuleBuilder, Cci.IAssemblyRe private SynthesizedEmbeddedAttributeSymbol _lazyIsByRefLikeAttribute; private SynthesizedEmbeddedAttributeSymbol _lazyIsUnmanagedAttribute; private SynthesizedEmbeddedNullableAttributeSymbol _lazyNullableAttribute; + private SynthesizedEmbeddedAttributeSymbol _lazyNullablePublicOnlyAttribute; /// /// The behavior of the C# command-line compiler is as follows: @@ -76,30 +77,13 @@ internal override ImmutableArray GetEmbeddedTypes(DiagnosticBag var builder = ArrayBuilder.GetInstance(); CreateEmbeddedAttributesIfNeeded(diagnostics); - if ((object)_lazyEmbeddedAttribute != null) - { - builder.Add(_lazyEmbeddedAttribute); - } - if ((object)_lazyIsReadOnlyAttribute != null) - { - builder.Add(_lazyIsReadOnlyAttribute); - } - - if ((object)_lazyIsUnmanagedAttribute != null) - { - builder.Add(_lazyIsUnmanagedAttribute); - } - - if ((object)_lazyIsByRefLikeAttribute != null) - { - builder.Add(_lazyIsByRefLikeAttribute); - } - - if ((object)_lazyNullableAttribute != null) - { - builder.Add(_lazyNullableAttribute); - } + builder.AddIfNotNull(_lazyEmbeddedAttribute); + builder.AddIfNotNull(_lazyIsReadOnlyAttribute); + builder.AddIfNotNull(_lazyIsUnmanagedAttribute); + builder.AddIfNotNull(_lazyIsByRefLikeAttribute); + builder.AddIfNotNull(_lazyNullableAttribute); + builder.AddIfNotNull(_lazyNullablePublicOnlyAttribute); return builder.ToImmutableAndFree(); } @@ -211,6 +195,19 @@ internal override SynthesizedAttributeData SynthesizeNullableAttribute(WellKnown return base.SynthesizeNullableAttribute(member, arguments); } + internal override SynthesizedAttributeData SynthesizeNullablePublicOnlyAttribute() + { + if ((object)_lazyNullablePublicOnlyAttribute != null) + { + return new SynthesizedAttributeData( + _lazyNullablePublicOnlyAttribute.Constructors[0], + ImmutableArray.Empty, + ImmutableArray>.Empty); + } + + return base.SynthesizeNullablePublicOnlyAttribute(); + } + protected override SynthesizedAttributeData TrySynthesizeIsReadOnlyAttribute() { if ((object)_lazyIsReadOnlyAttribute != null) @@ -252,7 +249,9 @@ protected override SynthesizedAttributeData TrySynthesizeIsByRefLikeAttribute() private void CreateEmbeddedAttributesIfNeeded(DiagnosticBag diagnostics) { - if (this.NeedsGeneratedIsReadOnlyAttribute) + EmbeddedAttributes needsAttributes = GetNeedsGeneratedAttributes(); + + if ((needsAttributes & EmbeddedAttributes.IsReadOnlyAttribute) != 0) { CreateEmbeddedAttributeItselfIfNeeded(diagnostics); @@ -262,7 +261,7 @@ private void CreateEmbeddedAttributesIfNeeded(DiagnosticBag diagnostics) AttributeDescription.IsReadOnlyAttribute); } - if (this.NeedsGeneratedIsByRefLikeAttribute) + if ((needsAttributes & EmbeddedAttributes.IsByRefLikeAttribute) != 0) { CreateEmbeddedAttributeItselfIfNeeded(diagnostics); @@ -272,7 +271,7 @@ private void CreateEmbeddedAttributesIfNeeded(DiagnosticBag diagnostics) AttributeDescription.IsByRefLikeAttribute); } - if (this.NeedsGeneratedIsUnmanagedAttribute) + if ((needsAttributes & EmbeddedAttributes.IsUnmanagedAttribute) != 0) { CreateEmbeddedAttributeItselfIfNeeded(diagnostics); @@ -282,7 +281,7 @@ private void CreateEmbeddedAttributesIfNeeded(DiagnosticBag diagnostics) AttributeDescription.IsUnmanagedAttribute); } - if (this.NeedsGeneratedNullableAttribute) + if ((needsAttributes & EmbeddedAttributes.NullableAttribute) != 0) { CreateEmbeddedAttributeItselfIfNeeded(diagnostics); @@ -290,6 +289,16 @@ private void CreateEmbeddedAttributesIfNeeded(DiagnosticBag diagnostics) ref _lazyNullableAttribute, diagnostics); } + + if ((needsAttributes & EmbeddedAttributes.NullablePublicOnlyAttribute) != 0) + { + CreateEmbeddedAttributeItselfIfNeeded(diagnostics); + + CreateEmbeddedAttributeIfNeeded( + ref _lazyNullablePublicOnlyAttribute, + diagnostics, + AttributeDescription.NullablePublicOnlyAttribute); + } } private void CreateEmbeddedAttributeItselfIfNeeded(DiagnosticBag diagnostics) diff --git a/src/Compilers/CSharp/Portable/Emitter/Model/PEModuleBuilder.cs b/src/Compilers/CSharp/Portable/Emitter/Model/PEModuleBuilder.cs index f63813d3bbf03..e05264d338ef8 100644 --- a/src/Compilers/CSharp/Portable/Emitter/Model/PEModuleBuilder.cs +++ b/src/Compilers/CSharp/Portable/Emitter/Model/PEModuleBuilder.cs @@ -39,52 +39,29 @@ public override NoPia.EmbeddedTypesManager EmbeddedTypesManagerOpt /// private Dictionary _fixedImplementationTypes; - private bool _needsGeneratedIsReadOnlyAttribute_Value; - private bool _needsGeneratedIsUnmanagedAttribute_Value; + private int _needsGeneratedAttributes; private bool _needsGeneratedAttributes_IsFrozen; - private bool _needsGeneratedNullableAttribute_Value; /// - /// Returns a value indicating whether this builder has a symbol that needs IsReadOnlyAttribute to be generated during emit phase. - /// The value is set during lowering the symbols that need that attribute, and is frozen on first trial to get it. + /// Returns a value indicating which embedded attributes should be generated during emit phase. + /// The value is set during binding the symbols that need those attributes, and is frozen on first trial to get it. /// Freezing is needed to make sure that nothing tries to modify the value after the value is read. /// - protected bool NeedsGeneratedIsReadOnlyAttribute + internal EmbeddedAttributes GetNeedsGeneratedAttributes() { - get - { - _needsGeneratedAttributes_IsFrozen = true; - return Compilation.NeedsGeneratedIsReadOnlyAttribute || _needsGeneratedIsReadOnlyAttribute_Value; - } + _needsGeneratedAttributes_IsFrozen = true; + return GetNeedsGeneratedAttributesInternal(); } - /// - /// Returns a value indicating whether this builder has a symbol that needs IsByRefLikeAttribute to be generated during emit phase. - /// - protected bool NeedsGeneratedIsByRefLikeAttribute + private EmbeddedAttributes GetNeedsGeneratedAttributesInternal() { - get - { - return Compilation.NeedsGeneratedIsByRefLikeAttribute; - } - } - - internal bool NeedsGeneratedIsUnmanagedAttribute - { - get - { - _needsGeneratedAttributes_IsFrozen = true; - return Compilation.NeedsGeneratedIsUnmanagedAttribute || _needsGeneratedIsUnmanagedAttribute_Value; - } + return (EmbeddedAttributes)_needsGeneratedAttributes | Compilation.GetNeedsGeneratedAttributes(); } - protected bool NeedsGeneratedNullableAttribute + internal void SetNeedsGeneratedAttributes(EmbeddedAttributes attributes) { - get - { - _needsGeneratedAttributes_IsFrozen = true; - return Compilation.NeedsGeneratedNullableAttribute || _needsGeneratedNullableAttribute_Value; - } + Debug.Assert(!_needsGeneratedAttributes_IsFrozen); + ThreadSafeFlagOperations.Set(ref _needsGeneratedAttributes, (int)attributes); } internal PEModuleBuilder( @@ -1535,6 +1512,12 @@ internal virtual SynthesizedAttributeData SynthesizeNullableAttribute(WellKnownM return Compilation.TrySynthesizeAttribute(member, arguments, isOptionalUse: true); } + internal virtual SynthesizedAttributeData SynthesizeNullablePublicOnlyAttribute() + { + // For modules, this attribute should be present. Only assemblies generate and embed this type. + return Compilation.TrySynthesizeAttribute(WellKnownMember.System_Runtime_CompilerServices_NullablePublicOnlyAttribute__ctor); + } + protected virtual SynthesizedAttributeData TrySynthesizeIsReadOnlyAttribute() { // For modules, this attribute should be present. Only assemblies generate and embed this type. @@ -1553,52 +1536,40 @@ protected virtual SynthesizedAttributeData TrySynthesizeIsByRefLikeAttribute() return Compilation.TrySynthesizeAttribute(WellKnownMember.System_Runtime_CompilerServices_IsByRefLikeAttribute__ctor); } - internal void EnsureIsReadOnlyAttributeExists() + private void EnsureEmbeddedAttributeExists(EmbeddedAttributes attribute) { Debug.Assert(!_needsGeneratedAttributes_IsFrozen); - if (_needsGeneratedIsReadOnlyAttribute_Value || Compilation.NeedsGeneratedIsReadOnlyAttribute) + if ((GetNeedsGeneratedAttributesInternal() & attribute) != 0) { return; } // Don't report any errors. They should be reported during binding. - if (Compilation.CheckIfIsReadOnlyAttributeShouldBeEmbedded(diagnosticsOpt: null, locationOpt: null)) + if (Compilation.CheckIfAttributeShouldBeEmbedded(attribute, diagnosticsOpt: null, locationOpt: null)) { - _needsGeneratedIsReadOnlyAttribute_Value = true; + SetNeedsGeneratedAttributes(attribute); } } - internal void EnsureIsUnmanagedAttributeExists() + internal void EnsureIsReadOnlyAttributeExists() { - Debug.Assert(!_needsGeneratedAttributes_IsFrozen); - - if (_needsGeneratedIsUnmanagedAttribute_Value || Compilation.NeedsGeneratedIsUnmanagedAttribute) - { - return; - } + EnsureEmbeddedAttributeExists(EmbeddedAttributes.IsReadOnlyAttribute); + } - // Don't report any errors. They should be reported during binding. - if (Compilation.CheckIfIsUnmanagedAttributeShouldBeEmbedded(diagnosticsOpt: null, locationOpt: null)) - { - _needsGeneratedIsUnmanagedAttribute_Value = true; - } + internal void EnsureIsUnmanagedAttributeExists() + { + EnsureEmbeddedAttributeExists(EmbeddedAttributes.IsUnmanagedAttribute); } internal void EnsureNullableAttributeExists() { - Debug.Assert(!_needsGeneratedAttributes_IsFrozen); - - if (_needsGeneratedNullableAttribute_Value || Compilation.NeedsGeneratedNullableAttribute) - { - return; - } + EnsureEmbeddedAttributeExists(EmbeddedAttributes.NullableAttribute); + } - // Don't report any errors. They should be reported during binding. - if (Compilation.CheckIfNullableAttributeShouldBeEmbedded(diagnosticsOpt: null, locationOpt: null)) - { - _needsGeneratedNullableAttribute_Value = true; - } + internal void EnsureNullablePublicOnlyAttributeExists() + { + EnsureEmbeddedAttributeExists(EmbeddedAttributes.NullablePublicOnlyAttribute); } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Compilation_WellKnownMembers.cs b/src/Compilers/CSharp/Portable/Symbols/Compilation_WellKnownMembers.cs index 036a43dd7ec63..ca7c01bb78160 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Compilation_WellKnownMembers.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Compilation_WellKnownMembers.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; -using System.Linq; using System.Threading; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.PooledObjects; @@ -13,9 +12,6 @@ namespace Microsoft.CodeAnalysis.CSharp { - // TODO: (tomat) translated 1:1 from VB, might need adjustments - // TODO: (tomat) can we share more with VB? - public partial class CSharpCompilation { internal readonly WellKnownMembersSignatureComparer WellKnownMemberSignatureComparer; @@ -32,62 +28,24 @@ public partial class CSharpCompilation /// private Symbol[] _lazyWellKnownTypeMembers; - private bool _needsGeneratedIsReadOnlyAttribute_Value; - private bool _needsGeneratedIsByRefLikeAttribute_Value; - private bool _needsGeneratedIsUnmanagedAttribute_Value; - private bool _needsGeneratedNullableAttribute_Value; - + private int _needsGeneratedAttributes; private bool _needsGeneratedAttributes_IsFrozen; /// - /// Returns a value indicating whether this compilation has a member that needs IsReadOnlyAttribute to be generated during emit phase. - /// The value is set during binding the symbols that need that attribute, and is frozen on first trial to get it. - /// Freezing is needed to make sure that nothing tries to modify the value after the value is read. - /// - internal bool NeedsGeneratedIsReadOnlyAttribute - { - get - { - _needsGeneratedAttributes_IsFrozen = true; - return _needsGeneratedIsReadOnlyAttribute_Value; - } - } - - /// - /// Returns a value indicating whether this compilation has a member that needs IsIsByRefLikeAttribute to be generated during emit phase. - /// The value is set during binding the symbols that need that attribute, and is frozen on first trial to get it. + /// Returns a value indicating which embedded attributes should be generated during emit phase. + /// The value is set during binding the symbols that need those attributes, and is frozen on first trial to get it. /// Freezing is needed to make sure that nothing tries to modify the value after the value is read. /// - internal bool NeedsGeneratedIsByRefLikeAttribute - { - get - { - _needsGeneratedAttributes_IsFrozen = true; - return _needsGeneratedIsByRefLikeAttribute_Value; - } - } - - internal bool NeedsGeneratedNullableAttribute + internal EmbeddedAttributes GetNeedsGeneratedAttributes() { - get - { - _needsGeneratedAttributes_IsFrozen = true; - return _needsGeneratedNullableAttribute_Value; - } + _needsGeneratedAttributes_IsFrozen = true; + return (EmbeddedAttributes)_needsGeneratedAttributes; } - /// - /// Returns a value indicating whether this compilation has a member that needs IsUnmanagedAttribute to be generated during emit phase. - /// The value is set during binding the symbols that need that attribute, and is frozen on first trial to get it. - /// Freezing is needed to make sure that nothing tries to modify the value after the value is read. - /// - internal bool NeedsGeneratedIsUnmanagedAttribute + internal void SetNeedsGeneratedAttributes(EmbeddedAttributes attributes) { - get - { - _needsGeneratedAttributes_IsFrozen = true; - return _needsGeneratedIsUnmanagedAttribute_Value; - } + Debug.Assert(!_needsGeneratedAttributes_IsFrozen); + ThreadSafeFlagOperations.Set(ref _needsGeneratedAttributes, (int)attributes); } /// @@ -490,90 +448,94 @@ internal SynthesizedAttributeData SynthesizeDebuggerStepThroughAttribute() return TrySynthesizeAttribute(WellKnownMember.System_Diagnostics_DebuggerStepThroughAttribute__ctor); } - internal void EnsureIsReadOnlyAttributeExists(DiagnosticBag diagnostics, Location location, bool modifyCompilation) + private void EnsureEmbeddedAttributeExists(EmbeddedAttributes attribute, DiagnosticBag diagnostics, Location location, bool modifyCompilation) { Debug.Assert(!modifyCompilation || !_needsGeneratedAttributes_IsFrozen); - var isNeeded = CheckIfIsReadOnlyAttributeShouldBeEmbedded(diagnostics, location); - - if (isNeeded && modifyCompilation) + if (CheckIfAttributeShouldBeEmbedded(attribute, diagnostics, location) && modifyCompilation) { - _needsGeneratedIsReadOnlyAttribute_Value = true; + SetNeedsGeneratedAttributes(attribute); } } - internal void EnsureIsByRefLikeAttributeExists(DiagnosticBag diagnostics, Location location, bool modifyCompilation) + internal void EnsureIsReadOnlyAttributeExists(DiagnosticBag diagnostics, Location location, bool modifyCompilation) { - Debug.Assert(!modifyCompilation || !_needsGeneratedAttributes_IsFrozen); - - var isNeeded = CheckIfIsByRefLikeAttributeShouldBeEmbedded(diagnostics, location); - - if (isNeeded && modifyCompilation) - { - _needsGeneratedIsByRefLikeAttribute_Value = true; - } + EnsureEmbeddedAttributeExists(EmbeddedAttributes.IsReadOnlyAttribute, diagnostics, location, modifyCompilation); } - internal void EnsureIsUnmanagedAttributeExists(DiagnosticBag diagnostics, Location location, bool modifyCompilationForIsUnmanaged) + internal void EnsureIsByRefLikeAttributeExists(DiagnosticBag diagnostics, Location location, bool modifyCompilation) { - Debug.Assert(!modifyCompilationForIsUnmanaged || !_needsGeneratedAttributes_IsFrozen); - - var isNeeded = CheckIfIsUnmanagedAttributeShouldBeEmbedded(diagnostics, location); - - if (isNeeded && modifyCompilationForIsUnmanaged) - { - _needsGeneratedIsUnmanagedAttribute_Value = true; - } + EnsureEmbeddedAttributeExists(EmbeddedAttributes.IsByRefLikeAttribute, diagnostics, location, modifyCompilation); } - internal void EnsureNullableAttributeExists(DiagnosticBag diagnostics, Location location, bool modifyCompilation) + internal void EnsureIsUnmanagedAttributeExists(DiagnosticBag diagnostics, Location location, bool modifyCompilation) { - Debug.Assert(!modifyCompilation || !_needsGeneratedAttributes_IsFrozen); - - var isNeeded = CheckIfNullableAttributeShouldBeEmbedded(diagnostics, location); - - if (isNeeded && modifyCompilation) - { - _needsGeneratedNullableAttribute_Value = true; - } + EnsureEmbeddedAttributeExists(EmbeddedAttributes.IsUnmanagedAttribute, diagnostics, location, modifyCompilation); } - internal bool CheckIfIsReadOnlyAttributeShouldBeEmbedded(DiagnosticBag diagnosticsOpt, Location locationOpt) + internal void EnsureNullableAttributeExists(DiagnosticBag diagnostics, Location location, bool modifyCompilation) { - return CheckIfAttributeShouldBeEmbedded( - diagnosticsOpt, - locationOpt, - WellKnownType.System_Runtime_CompilerServices_IsReadOnlyAttribute, - WellKnownMember.System_Runtime_CompilerServices_IsReadOnlyAttribute__ctor); + EnsureEmbeddedAttributeExists(EmbeddedAttributes.NullableAttribute, diagnostics, location, modifyCompilation); } - internal bool CheckIfIsByRefLikeAttributeShouldBeEmbedded(DiagnosticBag diagnosticsOpt, Location locationOpt) + internal void EnsureNullablePublicOnlyAttributeExists(DiagnosticBag diagnostics, Location location, bool modifyCompilation) { - return CheckIfAttributeShouldBeEmbedded( - diagnosticsOpt, - locationOpt, - WellKnownType.System_Runtime_CompilerServices_IsByRefLikeAttribute, - WellKnownMember.System_Runtime_CompilerServices_IsByRefLikeAttribute__ctor); + EnsureEmbeddedAttributeExists(EmbeddedAttributes.NullablePublicOnlyAttribute, diagnostics, location, modifyCompilation); } - internal bool CheckIfIsUnmanagedAttributeShouldBeEmbedded(DiagnosticBag diagnosticsOpt, Location locationOpt) + internal bool CheckIfAttributeShouldBeEmbedded(EmbeddedAttributes attribute, DiagnosticBag diagnosticsOpt, Location locationOpt) { - return CheckIfAttributeShouldBeEmbedded( - diagnosticsOpt, - locationOpt, - WellKnownType.System_Runtime_CompilerServices_IsUnmanagedAttribute, - WellKnownMember.System_Runtime_CompilerServices_IsUnmanagedAttribute__ctor); + switch (attribute) + { + case EmbeddedAttributes.IsReadOnlyAttribute: + return CheckIfAttributeShouldBeEmbedded( + diagnosticsOpt, + locationOpt, + WellKnownType.System_Runtime_CompilerServices_IsReadOnlyAttribute, + WellKnownMember.System_Runtime_CompilerServices_IsReadOnlyAttribute__ctor); + + case EmbeddedAttributes.IsByRefLikeAttribute: + return CheckIfAttributeShouldBeEmbedded( + diagnosticsOpt, + locationOpt, + WellKnownType.System_Runtime_CompilerServices_IsByRefLikeAttribute, + WellKnownMember.System_Runtime_CompilerServices_IsByRefLikeAttribute__ctor); + + case EmbeddedAttributes.IsUnmanagedAttribute: + return CheckIfAttributeShouldBeEmbedded( + diagnosticsOpt, + locationOpt, + WellKnownType.System_Runtime_CompilerServices_IsUnmanagedAttribute, + WellKnownMember.System_Runtime_CompilerServices_IsUnmanagedAttribute__ctor); + + case EmbeddedAttributes.NullableAttribute: + // Note: if the type exists, we'll check both constructors, regardless of which one(s) we'll eventually need + return CheckIfAttributeShouldBeEmbedded( + diagnosticsOpt, + locationOpt, + WellKnownType.System_Runtime_CompilerServices_NullableAttribute, + WellKnownMember.System_Runtime_CompilerServices_NullableAttribute__ctorByte, + WellKnownMember.System_Runtime_CompilerServices_NullableAttribute__ctorTransformFlags); + + case EmbeddedAttributes.NullablePublicOnlyAttribute: + return CheckIfAttributeShouldBeEmbedded( + diagnosticsOpt, + locationOpt, + WellKnownType.System_Runtime_CompilerServices_NullablePublicOnlyAttribute, + WellKnownMember.System_Runtime_CompilerServices_NullablePublicOnlyAttribute__ctor); + + default: + throw ExceptionUtilities.UnexpectedValue(attribute); + } } - internal bool CheckIfNullableAttributeShouldBeEmbedded(DiagnosticBag diagnosticsOpt, Location locationOpt) + internal bool CheckIfNullablePublicOnlyAttributeShouldBeEmbedded(DiagnosticBag diagnosticsOpt, Location locationOpt) { - // Note: if the type exists, we'll check both constructors, regardless of which one(s) we'll eventually need return CheckIfAttributeShouldBeEmbedded( diagnosticsOpt, locationOpt, - WellKnownType.System_Runtime_CompilerServices_NullableAttribute, - WellKnownMember.System_Runtime_CompilerServices_NullableAttribute__ctorByte, - WellKnownMember.System_Runtime_CompilerServices_NullableAttribute__ctorTransformFlags); + WellKnownType.System_Runtime_CompilerServices_NullablePublicOnlyAttribute, + WellKnownMember.System_Runtime_CompilerServices_NullablePublicOnlyAttribute__ctor); } private bool CheckIfAttributeShouldBeEmbedded(DiagnosticBag diagnosticsOpt, Location locationOpt, WellKnownType attributeType, WellKnownMember attributeCtor, WellKnownMember? secondAttributeCtor = null) diff --git a/src/Compilers/CSharp/Portable/Symbols/EmbeddedAttributes.cs b/src/Compilers/CSharp/Portable/Symbols/EmbeddedAttributes.cs new file mode 100644 index 0000000000000..be80be66e19db --- /dev/null +++ b/src/Compilers/CSharp/Portable/Symbols/EmbeddedAttributes.cs @@ -0,0 +1,16 @@ +// 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; + +namespace Microsoft.CodeAnalysis.CSharp +{ + [Flags] + internal enum EmbeddedAttributes + { + IsReadOnlyAttribute = 0x01, + IsByRefLikeAttribute = 0x02, + IsUnmanagedAttribute = 0x04, + NullableAttribute = 0x08, + NullablePublicOnlyAttribute = 0x10, + } +} diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceModuleSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceModuleSymbol.cs index 95423ac71bca4..57022785ff726 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceModuleSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceModuleSymbol.cs @@ -255,6 +255,18 @@ internal override void ForceComplete(SourceLocation locationOpt, CancellationTok _state.SpinWaitComplete(CompletionPart.FinishValidatingReferencedAssemblies, cancellationToken); break; + case CompletionPart.StartMemberChecks: + case CompletionPart.FinishMemberChecks: + if (_state.NotePartComplete(CompletionPart.StartMemberChecks)) + { + var diagnostics = DiagnosticBag.GetInstance(); + AfterMembersChecks(diagnostics); + AddDeclarationDiagnostics(diagnostics); + diagnostics.Free(); + _state.NotePartComplete(CompletionPart.FinishMemberChecks); + } + break; + case CompletionPart.MembersCompleted: this.GlobalNamespace.ForceComplete(locationOpt, cancellationToken); @@ -513,6 +525,24 @@ internal override void DecodeWellKnownAttribute(ref DecodeWellKnownAttributeArgu } } + private bool EmitNullablePublicOnly + { + get + { + var compilation = DeclaringCompilation; + return compilation.EmitNullablePublicOnly && + compilation.IsFeatureEnabled(MessageID.IDS_FeatureNullableReferenceTypes); + } + } + + private void AfterMembersChecks(DiagnosticBag diagnostics) + { + if (EmitNullablePublicOnly) + { + DeclaringCompilation.EnsureNullablePublicOnlyAttributeExists(diagnostics, location: NoLocation.Singleton, modifyCompilation: true); + } + } + internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, ref ArrayBuilder attributes) { base.AddSynthesizedAttributes(moduleBuilder, ref attributes); @@ -527,6 +557,11 @@ internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, r WellKnownMember.System_Security_UnverifiableCodeAttribute__ctor)); } } + + if (EmitNullablePublicOnly) + { + AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeNullablePublicOnlyAttribute()); + } } internal override bool HasAssemblyCompilationRelaxationsAttribute diff --git a/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_Nullable.cs b/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_Nullable.cs index 2da5e26a1b5dc..a7fe728643ee6 100644 --- a/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_Nullable.cs +++ b/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_Nullable.cs @@ -71,7 +71,7 @@ static void F(object? x, object?[] y) { } } [Fact] - public void ExplicitAttribute_MissingParameterlessConstructor() + public void ExplicitAttribute_MissingSingleByteConstructor() { var source = @"namespace System.Runtime.CompilerServices diff --git a/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_NullablePublicOnly.cs b/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_NullablePublicOnly.cs index bf69b558ec64e..d7ad813411d90 100644 --- a/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_NullablePublicOnly.cs +++ b/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_NullablePublicOnly.cs @@ -1,10 +1,213 @@ // 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.Immutable; +using System.Linq; +using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; +using Microsoft.CodeAnalysis.Test.Utilities; +using Roslyn.Test.Utilities; +using Xunit; namespace Microsoft.CodeAnalysis.CSharp.UnitTests { public class AttributeTests_NullablePublicOnly : CSharpTestBase { + [Fact] + public void ExplicitAttribute_FromSource() + { + var source1 = +@"namespace System.Runtime.CompilerServices +{ + public sealed class NullablePublicOnlyAttribute : Attribute + { + } +}"; + var source2 = +@"public class A +{ +} +public class B : A +{ +}"; + var options = WithNonNullTypesTrue().WithMetadataImportOptions(MetadataImportOptions.All); + var parseOptions = TestOptions.Regular8; + + var comp = CreateCompilation(new[] { source1, source2 }, options: options, parseOptions: parseOptions); + CompileAndVerify(comp, symbolValidator: AssertNoNullablePublicOnlyAttribute); + + comp = CreateCompilation(new[] { source1, source2 }, options: options, parseOptions: parseOptions.WithFeature("nullablePublicOnly")); + CompileAndVerify(comp, symbolValidator: AssertNullablePublicOnlyAttribute); + } + + [Fact] + public void ExplicitAttribute_FromMetadata() + { + var source1 = +@"namespace System.Runtime.CompilerServices +{ + public sealed class NullablePublicOnlyAttribute : Attribute + { + } +}"; + var source2 = +@"public class A +{ +} +public class B : A +{ +}"; + var comp = CreateCompilation(source1, parseOptions: TestOptions.Regular7); + comp.VerifyDiagnostics(); + var ref1 = comp.EmitToImageReference(); + + var options = WithNonNullTypesTrue().WithMetadataImportOptions(MetadataImportOptions.All); + var parseOptions = TestOptions.Regular8; + + comp = CreateCompilation(source2, references: new[] { ref1 }, options: options, parseOptions: parseOptions); + CompileAndVerify(comp, symbolValidator: AssertNoNullablePublicOnlyAttribute); + + comp = CreateCompilation(source2, references: new[] { ref1 }, options: options, parseOptions: parseOptions.WithFeature("nullablePublicOnly")); + CompileAndVerify(comp, symbolValidator: AssertNullablePublicOnlyAttribute); + } + + [Fact] + public void ExplicitAttribute_MissingParameterlessConstructor() + { + var source1 = +@"namespace System.Runtime.CompilerServices +{ + public sealed class NullablePublicOnlyAttribute : Attribute + { + public NullablePublicOnlyAttribute(bool b) { } + } +}"; + var source2 = +@"public class A +{ +} +public class B : A +{ +}"; + var options = WithNonNullTypesTrue().WithMetadataImportOptions(MetadataImportOptions.All); + var parseOptions = TestOptions.Regular8; + + var comp = CreateCompilation(new[] { source1, source2 }, options: options, parseOptions: parseOptions); + comp.VerifyEmitDiagnostics(); + + comp = CreateCompilation(new[] { source1, source2 }, options: options, parseOptions: parseOptions.WithFeature("nullablePublicOnly")); + comp.VerifyEmitDiagnostics( + // error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.NullablePublicOnlyAttribute..ctor' + Diagnostic(ErrorCode.ERR_MissingPredefinedMember).WithArguments("System.Runtime.CompilerServices.NullablePublicOnlyAttribute", ".ctor").WithLocation(1, 1)); + } + + [Fact] + public void EmptyProject() + { + var source = @""; + var options = WithNonNullTypesTrue().WithMetadataImportOptions(MetadataImportOptions.All); + var parseOptions = TestOptions.Regular8; + + var comp = CreateCompilation(source, options: options, parseOptions: parseOptions); + CompileAndVerify(comp, symbolValidator: AssertNoNullablePublicOnlyAttribute); + + comp = CreateCompilation(source, options: options, parseOptions: parseOptions.WithFeature("nullablePublicOnly")); + CompileAndVerify(comp, symbolValidator: AssertNullablePublicOnlyAttribute); + } + + [Fact] + public void CSharp7_NoAttribute() + { + var source = +@"public class A +{ +} +public class B : A +{ +}"; + var options = TestOptions.ReleaseDll.WithMetadataImportOptions(MetadataImportOptions.All); + var parseOptions = TestOptions.Regular7; + + var comp = CreateCompilation(source, options: options, parseOptions: parseOptions); + CompileAndVerify(comp, symbolValidator: AssertNoNullablePublicOnlyAttribute); + + comp = CreateCompilation(source, options: options, parseOptions: parseOptions.WithFeature("nullablePublicOnly")); + CompileAndVerify(comp, symbolValidator: AssertNoNullablePublicOnlyAttribute); + } + + [Fact] + public void NullableEnabled() + { + var source = +@"public class A +{ +} +public class B : A +{ +}"; + var options = WithNonNullTypesTrue().WithMetadataImportOptions(MetadataImportOptions.All); + var parseOptions = TestOptions.Regular8; + + var comp = CreateCompilation(source, options: options, parseOptions: parseOptions); + CompileAndVerify(comp, symbolValidator: AssertNoNullablePublicOnlyAttribute); + + comp = CreateCompilation(source, options: options, parseOptions: parseOptions.WithFeature("nullablePublicOnly")); + CompileAndVerify(comp, symbolValidator: AssertNullablePublicOnlyAttribute); + } + + [Fact] + public void NullableEnabled_NoPublicMembers() + { + var source = +@"class A +{ +} +class B : A +{ +}"; + var options = WithNonNullTypesTrue().WithMetadataImportOptions(MetadataImportOptions.All); + var parseOptions = TestOptions.Regular8; + + var comp = CreateCompilation(source, options: options, parseOptions: parseOptions); + CompileAndVerify(comp, symbolValidator: AssertNoNullablePublicOnlyAttribute); + + comp = CreateCompilation(source, options: options, parseOptions: parseOptions.WithFeature("nullablePublicOnly")); + CompileAndVerify(comp, symbolValidator: AssertNullablePublicOnlyAttribute); + } + + [Fact] + public void NullableDisabled() + { + var source = +@"public class A +{ +} +public class B : A +{ +}"; + var options = WithNonNullTypesFalse().WithMetadataImportOptions(MetadataImportOptions.All); + var parseOptions = TestOptions.Regular8; + + var comp = CreateCompilation(source, options: options, parseOptions: parseOptions); + CompileAndVerify(comp, symbolValidator: AssertNoNullablePublicOnlyAttribute); + + comp = CreateCompilation(source, options: options, parseOptions: parseOptions.WithFeature("nullablePublicOnly")); + CompileAndVerify(comp, symbolValidator: AssertNullablePublicOnlyAttribute); + } + + private static void AssertNoNullablePublicOnlyAttribute(ModuleSymbol module) + { + AssertAttributes(module.GetAttributes()); + } + + private static void AssertNullablePublicOnlyAttribute(ModuleSymbol module) + { + AssertAttributes(module.GetAttributes(), "System.Runtime.CompilerServices.NullablePublicOnlyAttribute"); + } + + private static void AssertAttributes(ImmutableArray attributes, params string[] expectedNames) + { + var actualNames = attributes.Select(a => a.AttributeClass.ToTestDisplayString()).ToArray(); + AssertEx.SetEqual(actualNames, expectedNames); + } } } diff --git a/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_RefReadOnly.cs b/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_RefReadOnly.cs index 373b0d88801f3..ecc7b3fedaf9d 100644 --- a/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_RefReadOnly.cs +++ b/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_RefReadOnly.cs @@ -1359,7 +1359,7 @@ public void TryingToBindFromSemanticModelDoesNotPolluteCompilation_Lambdas_Param public delegate int D (in int x); ").VerifyEmitDiagnostics(); - Assert.True(reference.NeedsGeneratedIsReadOnlyAttribute); + Assert.True(NeedsGeneratedIsReadOnlyAttribute(reference)); var compilation = CreateCompilation(@" public class Test @@ -1372,7 +1372,7 @@ void User() }", references: new[] { reference.ToMetadataReference() }); compilation.VerifyEmitDiagnostics(); - Assert.False(compilation.NeedsGeneratedIsReadOnlyAttribute); + Assert.False(NeedsGeneratedIsReadOnlyAttribute(compilation)); var tree = compilation.SyntaxTrees.Single(); var model = compilation.GetSemanticModel(tree, ignoreAccessibility: false); @@ -1386,7 +1386,7 @@ void User() Assert.Equal(CandidateReason.None, result.CandidateReason); Assert.Empty(result.CandidateSymbols); - Assert.False(compilation.NeedsGeneratedIsReadOnlyAttribute); + Assert.False(NeedsGeneratedIsReadOnlyAttribute(compilation)); } [Fact] @@ -1396,7 +1396,7 @@ public void TryingToBindFromSemanticModelDoesNotPolluteCompilation_Lambdas_Retur public delegate ref readonly int D (); ").VerifyEmitDiagnostics(); - Assert.True(reference.NeedsGeneratedIsReadOnlyAttribute); + Assert.True(NeedsGeneratedIsReadOnlyAttribute(reference)); var compilation = CreateCompilation(@" public class Test @@ -1414,7 +1414,7 @@ void User() }", references: new[] { reference.ToMetadataReference() }); compilation.VerifyEmitDiagnostics(); - Assert.False(compilation.NeedsGeneratedIsReadOnlyAttribute); + Assert.False(NeedsGeneratedIsReadOnlyAttribute(compilation)); var tree = compilation.SyntaxTrees.Single(); var model = compilation.GetSemanticModel(tree, ignoreAccessibility: false); @@ -1428,7 +1428,7 @@ void User() Assert.Equal(CandidateReason.None, result.CandidateReason); Assert.Empty(result.CandidateSymbols); - Assert.False(compilation.NeedsGeneratedIsReadOnlyAttribute); + Assert.False(NeedsGeneratedIsReadOnlyAttribute(compilation)); } [Fact] @@ -1443,7 +1443,7 @@ void User() }"); compilation.VerifyEmitDiagnostics(); - Assert.False(compilation.NeedsGeneratedIsReadOnlyAttribute); + Assert.False(NeedsGeneratedIsReadOnlyAttribute(compilation)); var tree = compilation.SyntaxTrees.Single(); var model = compilation.GetSemanticModel(tree, ignoreAccessibility: false); @@ -1455,7 +1455,7 @@ void User() Assert.True(model.TryGetSpeculativeSemanticModel(position, localfunction, out var newModel)); var localFunctionSymbol = newModel.GetDeclaredSymbol(localfunction); Assert.NotNull(localFunctionSymbol); - Assert.False(compilation.NeedsGeneratedIsReadOnlyAttribute); + Assert.False(NeedsGeneratedIsReadOnlyAttribute(compilation)); } [Fact] @@ -1470,7 +1470,7 @@ void User() }"); compilation.VerifyEmitDiagnostics(); - Assert.False(compilation.NeedsGeneratedIsReadOnlyAttribute); + Assert.False(NeedsGeneratedIsReadOnlyAttribute(compilation)); var tree = compilation.SyntaxTrees.Single(); var model = compilation.GetSemanticModel(tree, ignoreAccessibility: false); @@ -1482,7 +1482,7 @@ void User() Assert.True(model.TryGetSpeculativeSemanticModel(position, localfunction, out var newModel)); var localFunctionSymbol = newModel.GetDeclaredSymbol(localfunction); Assert.NotNull(localFunctionSymbol); - Assert.False(compilation.NeedsGeneratedIsReadOnlyAttribute); + Assert.False(NeedsGeneratedIsReadOnlyAttribute(compilation)); } [Fact] @@ -1493,7 +1493,7 @@ public void TryingPossibleBindingsForRefReadOnlyDoesNotPolluteCompilationForInva public delegate ref int D2 (); ").VerifyEmitDiagnostics(); - Assert.True(reference.NeedsGeneratedIsReadOnlyAttribute); + Assert.True(NeedsGeneratedIsReadOnlyAttribute(reference)); var compilation = CreateCompilation(@" public class Test @@ -1509,7 +1509,7 @@ void User() }", references: new[] { reference.ToMetadataReference() }); compilation.VerifyEmitDiagnostics(); - Assert.False(compilation.NeedsGeneratedIsReadOnlyAttribute); + Assert.False(NeedsGeneratedIsReadOnlyAttribute(compilation)); } [Fact] @@ -2344,5 +2344,10 @@ private void AssertGeneratedEmbeddedAttribute(AssemblySymbol assembly, string ex Assert.Equal(WellKnownTypes.GetMetadataName(WellKnownType.System_Runtime_CompilerServices_CompilerGeneratedAttribute), attributes[0].AttributeClass.ToDisplayString()); Assert.Equal(AttributeDescription.CodeAnalysisEmbeddedAttribute.FullName, attributes[1].AttributeClass.ToDisplayString()); } + + private static bool NeedsGeneratedIsReadOnlyAttribute(CSharpCompilation compilation) + { + return (compilation.GetNeedsGeneratedAttributes() & EmbeddedAttributes.IsReadOnlyAttribute) != 0; + } } } From 7a9dbcb302501237ac4632a44095fd40845687cf Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Fri, 14 Jun 2019 10:15:35 -0700 Subject: [PATCH 08/16] Remove unused method --- .../Portable/Symbols/Compilation_WellKnownMembers.cs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Symbols/Compilation_WellKnownMembers.cs b/src/Compilers/CSharp/Portable/Symbols/Compilation_WellKnownMembers.cs index ca7c01bb78160..0a7674abaad82 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Compilation_WellKnownMembers.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Compilation_WellKnownMembers.cs @@ -529,15 +529,6 @@ internal bool CheckIfAttributeShouldBeEmbedded(EmbeddedAttributes attribute, Dia } } - internal bool CheckIfNullablePublicOnlyAttributeShouldBeEmbedded(DiagnosticBag diagnosticsOpt, Location locationOpt) - { - return CheckIfAttributeShouldBeEmbedded( - diagnosticsOpt, - locationOpt, - WellKnownType.System_Runtime_CompilerServices_NullablePublicOnlyAttribute, - WellKnownMember.System_Runtime_CompilerServices_NullablePublicOnlyAttribute__ctor); - } - private bool CheckIfAttributeShouldBeEmbedded(DiagnosticBag diagnosticsOpt, Location locationOpt, WellKnownType attributeType, WellKnownMember attributeCtor, WellKnownMember? secondAttributeCtor = null) { var userDefinedAttribute = GetWellKnownType(attributeType); From 942e0f544e2c3b835a677c509de24e46f44e1a94 Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Fri, 14 Jun 2019 10:23:20 -0700 Subject: [PATCH 09/16] PR feedback --- docs/features/nullable-metadata.md | 3 +- .../Synthesized/SynthesizedFieldSymbolBase.cs | 13 +++---- .../Attributes/AttributeTests_Nullable.cs | 12 +++---- .../AttributeTests_NullablePublicOnly.cs | 28 ++++----------- .../CSharp/NullableAttributesVisitor.cs | 36 ++++--------------- 5 files changed, 29 insertions(+), 63 deletions(-) diff --git a/docs/features/nullable-metadata.md b/docs/features/nullable-metadata.md index a4a4cee49ba11..140bce4a0a35f 100644 --- a/docs/features/nullable-metadata.md +++ b/docs/features/nullable-metadata.md @@ -76,6 +76,7 @@ namespace System.Runtime.CompilerServices [System.AttributeUsage( AttributeTargets.Module | AttributeTargets.Class | + AttributeTargets.Delegate | AttributeTargets.Interface | AttributeTargets.Method | AttributeTargets.Struct, @@ -141,7 +142,7 @@ class Program To reduce the size of metadata, the C#8 compiler can be configured to not emit attributes for members that are inaccessible outside the assembly (`private` members, and also `internal` members -if the assembly contains any `InternalsVisibleToAttribute` attributes). +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`. diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedFieldSymbolBase.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedFieldSymbolBase.cs index 6d3ce10a654ab..011d44c61bfaf 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedFieldSymbolBase.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedFieldSymbolBase.cs @@ -44,7 +44,8 @@ internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, r base.AddSynthesizedAttributes(moduleBuilder, ref attributes); CSharpCompilation compilation = this.DeclaringCompilation; - var type = this.TypeWithAnnotations; + var typeWithAnnotations = this.TypeWithAnnotations; + var type = typeWithAnnotations.Type; // do not emit CompilerGenerated attributes for fields inside compiler generated types: if (!_containingType.IsImplicitlyDeclared) @@ -53,14 +54,14 @@ internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, r } if (!this.SuppressDynamicAttribute && - this.Type.ContainsDynamic() && + type.ContainsDynamic() && compilation.HasDynamicEmitAttributes() && compilation.CanEmitBoolean()) { - AddSynthesizedAttribute(ref attributes, compilation.SynthesizeDynamicAttribute(this.Type, type.CustomModifiers.Length)); + AddSynthesizedAttribute(ref attributes, compilation.SynthesizeDynamicAttribute(type, typeWithAnnotations.CustomModifiers.Length)); } - if (this.Type.ContainsTupleNames() && + if (type.ContainsTupleNames() && compilation.HasTupleNamesAttributes && compilation.CanEmitSpecialType(SpecialType.System_String)) { @@ -68,9 +69,9 @@ internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, r compilation.SynthesizeTupleNamesAttribute(Type)); } - if (compilation.ShouldEmitNullableAttributes(this) && type.NeedsNullableAttribute()) + if (compilation.ShouldEmitNullableAttributes(this) && typeWithAnnotations.NeedsNullableAttribute()) { - AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeNullableAttribute(this, type)); + AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeNullableAttribute(this, typeWithAnnotations)); } } diff --git a/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_Nullable.cs b/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_Nullable.cs index a7fe728643ee6..2b55b3673daad 100644 --- a/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_Nullable.cs +++ b/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_Nullable.cs @@ -1301,18 +1301,18 @@ private event D PrivateEvent { add { } remove { } } }"; var expectedPublicOnly = @" Program - [Nullable({ 1, 2 })] D! ProtectedEvent + [Nullable({ 1, 2 })] event D! ProtectedEvent "; var expectedPublicAndInternal = @" Program - [Nullable({ 1, 2 })] D! ProtectedEvent - [Nullable({ 1, 2 })] D! InternalEvent + [Nullable({ 1, 2 })] event D! ProtectedEvent + [Nullable({ 1, 2 })] event D! InternalEvent "; var expectedAll = @" Program - [Nullable({ 1, 2 })] D! ProtectedEvent - [Nullable({ 1, 2 })] D! InternalEvent - [Nullable({ 1, 2 })] D! PrivateEvent + [Nullable({ 1, 2 })] event D! ProtectedEvent + [Nullable({ 1, 2 })] event D! InternalEvent + [Nullable({ 1, 2 })] event D! PrivateEvent "; EmitPrivateMetadata(source, expectedPublicOnly, expectedPublicAndInternal, expectedAll); } diff --git a/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_NullablePublicOnly.cs b/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_NullablePublicOnly.cs index d7ad813411d90..ea56845373dae 100644 --- a/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_NullablePublicOnly.cs +++ b/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_NullablePublicOnly.cs @@ -15,14 +15,7 @@ public class AttributeTests_NullablePublicOnly : CSharpTestBase [Fact] public void ExplicitAttribute_FromSource() { - var source1 = -@"namespace System.Runtime.CompilerServices -{ - public sealed class NullablePublicOnlyAttribute : Attribute - { - } -}"; - var source2 = + var source = @"public class A { } @@ -32,41 +25,34 @@ public class B : A var options = WithNonNullTypesTrue().WithMetadataImportOptions(MetadataImportOptions.All); var parseOptions = TestOptions.Regular8; - var comp = CreateCompilation(new[] { source1, source2 }, options: options, parseOptions: parseOptions); + var comp = CreateCompilation(new[] { NullablePublicOnlyAttributeDefinition, source }, options: options, parseOptions: parseOptions); CompileAndVerify(comp, symbolValidator: AssertNoNullablePublicOnlyAttribute); - comp = CreateCompilation(new[] { source1, source2 }, options: options, parseOptions: parseOptions.WithFeature("nullablePublicOnly")); + comp = CreateCompilation(new[] { NullablePublicOnlyAttributeDefinition, source }, options: options, parseOptions: parseOptions.WithFeature("nullablePublicOnly")); CompileAndVerify(comp, symbolValidator: AssertNullablePublicOnlyAttribute); } [Fact] public void ExplicitAttribute_FromMetadata() { - var source1 = -@"namespace System.Runtime.CompilerServices -{ - public sealed class NullablePublicOnlyAttribute : Attribute - { - } -}"; - var source2 = + var source = @"public class A { } public class B : A { }"; - var comp = CreateCompilation(source1, parseOptions: TestOptions.Regular7); + var comp = CreateCompilation(NullablePublicOnlyAttributeDefinition, parseOptions: TestOptions.Regular7); comp.VerifyDiagnostics(); var ref1 = comp.EmitToImageReference(); var options = WithNonNullTypesTrue().WithMetadataImportOptions(MetadataImportOptions.All); var parseOptions = TestOptions.Regular8; - comp = CreateCompilation(source2, references: new[] { ref1 }, options: options, parseOptions: parseOptions); + comp = CreateCompilation(source, references: new[] { ref1 }, options: options, parseOptions: parseOptions); CompileAndVerify(comp, symbolValidator: AssertNoNullablePublicOnlyAttribute); - comp = CreateCompilation(source2, references: new[] { ref1 }, options: options, parseOptions: parseOptions.WithFeature("nullablePublicOnly")); + comp = CreateCompilation(source, references: new[] { ref1 }, options: options, parseOptions: parseOptions.WithFeature("nullablePublicOnly")); CompileAndVerify(comp, symbolValidator: AssertNullablePublicOnlyAttribute); } diff --git a/src/Compilers/Test/Utilities/CSharp/NullableAttributesVisitor.cs b/src/Compilers/Test/Utilities/CSharp/NullableAttributesVisitor.cs index e8d1d287505df..75fc7f4b2cae1 100644 --- a/src/Compilers/Test/Utilities/CSharp/NullableAttributesVisitor.cs +++ b/src/Compilers/Test/Utilities/CSharp/NullableAttributesVisitor.cs @@ -89,34 +89,12 @@ private static string GetIndentString(Symbol symbol) return new string(' ', level * 4); } - private static readonly SymbolDisplayFormat _displayFormat = - new SymbolDisplayFormat( - globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.OmittedAsContaining, - typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces, - propertyStyle: SymbolDisplayPropertyStyle.ShowReadWriteDescriptor, - genericsOptions: - SymbolDisplayGenericsOptions.IncludeTypeParameters | - SymbolDisplayGenericsOptions.IncludeVariance | - SymbolDisplayGenericsOptions.IncludeTypeConstraints, - memberOptions: - SymbolDisplayMemberOptions.IncludeParameters | - SymbolDisplayMemberOptions.IncludeType | - SymbolDisplayMemberOptions.IncludeRef | - SymbolDisplayMemberOptions.IncludeExplicitInterface, - parameterOptions: - SymbolDisplayParameterOptions.IncludeOptionalBrackets | - SymbolDisplayParameterOptions.IncludeDefaultValue | - SymbolDisplayParameterOptions.IncludeParamsRefOut | - SymbolDisplayParameterOptions.IncludeExtensionThis | - SymbolDisplayParameterOptions.IncludeType | - SymbolDisplayParameterOptions.IncludeName, - miscellaneousOptions: - SymbolDisplayMiscellaneousOptions.EscapeKeywordIdentifiers | - SymbolDisplayMiscellaneousOptions.UseErrorTypeSymbolName | - SymbolDisplayMiscellaneousOptions.IncludeNullableReferenceTypeModifier, - compilerInternalOptions: - SymbolDisplayCompilerInternalOptions.UseMetadataMethodNames | - SymbolDisplayCompilerInternalOptions.IncludeNonNullableTypeModifier); + private static readonly SymbolDisplayFormat _displayFormat = SymbolDisplayFormat.TestFormatWithConstraints. + WithMemberOptions( + SymbolDisplayMemberOptions.IncludeParameters | + SymbolDisplayMemberOptions.IncludeType | + SymbolDisplayMemberOptions.IncludeRef | + SymbolDisplayMemberOptions.IncludeExplicitInterface); private void ReportContainingSymbols(Symbol symbol) { @@ -134,7 +112,7 @@ private void ReportContainingSymbols(Symbol symbol) private void ReportSymbol(Symbol symbol, bool includeAlways = false) { - var attributes = (symbol.Kind == SymbolKind.Method) ? ((MethodSymbol)symbol).GetReturnTypeAttributes() : symbol.GetAttributes(); + var attributes = (symbol is MethodSymbol method) ? method.GetReturnTypeAttributes() : symbol.GetAttributes(); var nullableAttribute = GetAttribute(attributes, "System.Runtime.CompilerServices", "NullableAttribute"); if (!includeAlways && nullableAttribute == null) { From aa025b8322121338f2a169ae724ee55ed37e2812 Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Fri, 14 Jun 2019 11:11:50 -0700 Subject: [PATCH 10/16] PR feedback --- .../Portable/Binder/Semantics/AccessCheck.cs | 2 +- .../Portable/Compilation/CSharpCompilation.cs | 7 +- .../Attributes/AttributeTests_Nullable.cs | 89 +++++++++++++++---- 3 files changed, 77 insertions(+), 21 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/AccessCheck.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/AccessCheck.cs index c9d92c8ec95ff..3a93907588a91 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/AccessCheck.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/AccessCheck.cs @@ -60,7 +60,7 @@ public static bool IsSymbolAccessible( /// Returns true if the symbol is effectively public or internal based on /// the declared accessibility of the symbol and any containing symbols. /// - internal static bool IsPublicOrInternal(Symbol symbol, out bool isInternal) + internal static bool IsEffectivelyPublicOrInternal(Symbol symbol, out bool isInternal) { Debug.Assert(!(symbol is null)); diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs index cc2ef83e7318c..909f1b9590ddd 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs @@ -3295,8 +3295,7 @@ internal bool ShouldEmitNullableAttributes(Symbol symbol) Debug.Assert(!(symbol is null)); Debug.Assert(symbol.IsDefinition); - var sourceAssembly = SourceAssembly; - if (symbol.ContainingAssembly != sourceAssembly) + if (symbol.ContainingModule != SourceModule) { return false; } @@ -3306,12 +3305,12 @@ internal bool ShouldEmitNullableAttributes(Symbol symbol) return true; } - if (!AccessCheck.IsPublicOrInternal(symbol, out bool isInternal)) + if (!AccessCheck.IsEffectivelyPublicOrInternal(symbol, out bool isInternal)) { return false; } - return !isInternal || sourceAssembly.InternalsAreVisible; + return !isInternal || SourceAssembly.InternalsAreVisible; } internal override AnalyzerDriver AnalyzerForLanguage(ImmutableArray analyzers, AnalyzerManager analyzerManager) diff --git a/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_Nullable.cs b/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_Nullable.cs index 2b55b3673daad..f2a4a7a4c9532 100644 --- a/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_Nullable.cs +++ b/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_Nullable.cs @@ -1516,6 +1516,52 @@ void PrivateMethod(System.String! x) EmitPrivateMetadata(source, expectedPublicOnly, expectedPublicAndInternal, expectedAll); } + [Fact] + public void EmitPrivateMetadata_AnonymousType() + { + var source = +@"public class Program +{ + public static void Main() + { + _ = new { A = new object(), B = (string?)null }; + } +}"; + var expectedPublicOnly = @""; + var expectedPublicAndInternal = @""; + var expectedAll = @""; + EmitPrivateMetadata(source, expectedPublicOnly, expectedPublicAndInternal, expectedAll); + } + + [Fact] + public void EmitPrivateMetadata_Iterator() + { + var source = +@"using System.Collections.Generic; +public class Program +{ + public static IEnumerable F() + { + yield break; + } +}"; + var expectedPublicOnly = @" +Program + [Nullable({ 1, 2 })] System.Collections.Generic.IEnumerable! F() +"; + var expectedPublicAndInternal = @" +Program + [Nullable({ 1, 2 })] System.Collections.Generic.IEnumerable! F() +"; + var expectedAll = @" +Program + [Nullable({ 1, 2 })] System.Collections.Generic.IEnumerable! F() + Program.d__0 + [Nullable(2)] System.Object? <>2__current +"; + EmitPrivateMetadata(source, expectedPublicOnly, expectedPublicAndInternal, expectedAll); + } + private void EmitPrivateMetadata(string source, string expectedPublicOnly, string expectedPublicAndInternal, string expectedAll) { var sourceIVTs = @@ -1554,6 +1600,7 @@ public class A public static void M() { object? f(object arg) => arg; + object? l(object arg) { return arg; } D d = () => new object(); } } @@ -1563,6 +1610,7 @@ internal class B : I { public static object operator!(B b) => b; public event D E; + private (object, object?) F; }"; var options = WithNonNullTypesTrue(); var parseOptions = TestOptions.Regular8; @@ -1596,30 +1644,39 @@ internal class B : I // (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), - // (13,26): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.NullableAttribute..ctor' + // (13,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(13, 9), + // (13,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(13, 19), + // (14,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(13, 26), - // (16,19): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.NullableAttribute..ctor' + 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' // internal delegate T D(); - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "T").WithArguments("System.Runtime.CompilerServices.NullableAttribute", ".ctor").WithLocation(16, 19), - // (16,23): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.NullableAttribute..ctor' + 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' // internal delegate T D(); - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "T").WithArguments("System.Runtime.CompilerServices.NullableAttribute", ".ctor").WithLocation(16, 23), - // (17,22): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.NullableAttribute..ctor' + 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' // internal interface I { } - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "T").WithArguments("System.Runtime.CompilerServices.NullableAttribute", ".ctor").WithLocation(17, 22), - // (18,16): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.NullableAttribute..ctor' + 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' // internal class B : I - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "B").WithArguments("System.Runtime.CompilerServices.NullableAttribute", ".ctor").WithLocation(18, 16), - // (20,19): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.NullableAttribute..ctor' + 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' // public static object operator!(B b) => b; - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "object").WithArguments("System.Runtime.CompilerServices.NullableAttribute", ".ctor").WithLocation(20, 19), - // (20,36): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.NullableAttribute..ctor' + 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' // public static object operator!(B b) => b; - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "B b").WithArguments("System.Runtime.CompilerServices.NullableAttribute", ".ctor").WithLocation(20, 36), - // (21,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(21, 36), + // (22,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(21, 29)); + 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' + // private (object, object?) F; + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "F").WithArguments("System.Runtime.CompilerServices.NullableAttribute", ".ctor").WithLocation(23, 31)); comp = CreateCompilation(new[] { sourceAttribute, source }, options: options, parseOptions: parseOptions.WithFeature("nullablePublicOnly")); comp.VerifyEmitDiagnostics(); From 6cd04bb1e93d1e11278aff4ce083da67ba3baa50 Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Fri, 14 Jun 2019 11:42:35 -0700 Subject: [PATCH 11/16] PR feedback --- .../Portable/CSharpCompilationOptions.cs | 2 +- .../Symbols/Source/ParameterHelpers.cs | 9 +++-- ...thesizedEmbeddedNullableAttributeSymbol.cs | 40 +++++++++---------- 3 files changed, 27 insertions(+), 24 deletions(-) diff --git a/src/Compilers/CSharp/Portable/CSharpCompilationOptions.cs b/src/Compilers/CSharp/Portable/CSharpCompilationOptions.cs index 844d64396a19d..3cbb4c019fec4 100644 --- a/src/Compilers/CSharp/Portable/CSharpCompilationOptions.cs +++ b/src/Compilers/CSharp/Portable/CSharpCompilationOptions.cs @@ -732,7 +732,7 @@ public override int GetHashCode() return Hash.Combine(base.GetHashCodeHelper(), Hash.Combine(this.AllowUnsafe, Hash.Combine(Hash.CombineValues(this.Usings, StringComparer.Ordinal), - Hash.Combine(TopLevelBinderFlags.GetHashCode(), NullableContextOptions.GetHashCode())))); + Hash.Combine(TopLevelBinderFlags.GetHashCode(), this.NullableContextOptions.GetHashCode())))); } internal override Diagnostic FilterDiagnostic(Diagnostic diagnostic) diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs b/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs index 6512b9805566f..76ec3e1144685 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs @@ -149,11 +149,14 @@ internal static void EnsureNullableAttributeExists(CSharpCompilation compilation return; } - foreach (var parameter in parameters) + if (parameters.Length > 0 && compilation.ShouldEmitNullableAttributes(container)) { - if (compilation.ShouldEmitNullableAttributes(container) && parameter.TypeWithAnnotations.NeedsNullableAttribute()) + foreach (var parameter in parameters) { - compilation.EnsureNullableAttributeExists(diagnostics, parameter.GetNonNullSyntaxNode().Location, modifyCompilation); + if (parameter.TypeWithAnnotations.NeedsNullableAttribute()) + { + compilation.EnsureNullableAttributeExists(diagnostics, parameter.GetNonNullSyntaxNode().Location, modifyCompilation); + } } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEmbeddedNullableAttributeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEmbeddedNullableAttributeSymbol.cs index e4db998f9412b..7fdce9a74358d 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 ) ); } - } - internal sealed class SynthesizedEmbeddedAttributeConstructorWithBodySymbol : SynthesizedInstanceConstructor - { - private readonly ImmutableArray _parameters; + private sealed class SynthesizedEmbeddedAttributeConstructorWithBodySymbol : SynthesizedInstanceConstructor + { + private readonly ImmutableArray _parameters; - private readonly Action, ImmutableArray> _getConstructorBody; + private readonly Action, ImmutableArray> _getConstructorBody; - internal SynthesizedEmbeddedAttributeConstructorWithBodySymbol( - NamedTypeSymbol containingType, - Func> getParameters, - Action, ImmutableArray> getConstructorBody) : - base(containingType) - { - _parameters = getParameters(this); - _getConstructorBody = getConstructorBody; - } + internal SynthesizedEmbeddedAttributeConstructorWithBodySymbol( + NamedTypeSymbol containingType, + Func> getParameters, + Action, ImmutableArray> getConstructorBody) : + base(containingType) + { + _parameters = getParameters(this); + _getConstructorBody = getConstructorBody; + } - public override ImmutableArray Parameters => _parameters; + public override ImmutableArray Parameters => _parameters; - internal override void GenerateMethodBody(TypeCompilationState compilationState, DiagnosticBag diagnostics) - { - GenerateMethodBodyCore(compilationState, diagnostics); - } + 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); + protected override void GenerateMethodBodyStatements(SyntheticBoundNodeFactory factory, ArrayBuilder statements, DiagnosticBag diagnostics) => _getConstructorBody(factory, statements, _parameters); + } } } From 8b06fb7b747ba8af5b1c1787a0c571dbbec7a3e1 Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Fri, 14 Jun 2019 11:50:51 -0700 Subject: [PATCH 12/16] PR feedback --- docs/features/nullable-metadata.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/features/nullable-metadata.md b/docs/features/nullable-metadata.md index 140bce4a0a35f..6306a7c02385d 100644 --- a/docs/features/nullable-metadata.md +++ b/docs/features/nullable-metadata.md @@ -43,7 +43,7 @@ The `byte[]` is constructed as follows: - Nullable value type: the representation of the type argument only - Value type: the representation of the type arguments in order including containing types - Array: the nullability (0, 1, or 2), followed by the representation of the element type -- Tuple: the representation of the tuple elements in order +- Tuple: the representation of the underlying constructed type - Type parameter reference: the nullability (0, 1, or 2, with 0 for unconstrained type parameter) Note that certain type references are represented by an empty `byte[]`: From d3e14c242299b0e3d66dec0ef231f0acbf2fda0f Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Fri, 14 Jun 2019 12:52:41 -0700 Subject: [PATCH 13/16] PR feedback --- .../Emitter/Model/PEAssemblyBuilder.cs | 38 ++++++++----------- .../Portable/Emitter/Model/PEModuleBuilder.cs | 18 ++++----- .../Symbols/Compilation_WellKnownMembers.cs | 30 +++++++-------- ...dAttributes.cs => EmbeddableAttributes.cs} | 2 +- .../Source/SourceTypeParameterSymbol.cs | 1 + .../Attributes/AttributeTests_RefReadOnly.cs | 2 +- 6 files changed, 42 insertions(+), 49 deletions(-) rename src/Compilers/CSharp/Portable/Symbols/{EmbeddedAttributes.cs => EmbeddableAttributes.cs} (91%) diff --git a/src/Compilers/CSharp/Portable/Emitter/Model/PEAssemblyBuilder.cs b/src/Compilers/CSharp/Portable/Emitter/Model/PEAssemblyBuilder.cs index e9d9427a2de87..95743684c5001 100644 --- a/src/Compilers/CSharp/Portable/Emitter/Model/PEAssemblyBuilder.cs +++ b/src/Compilers/CSharp/Portable/Emitter/Model/PEAssemblyBuilder.cs @@ -249,51 +249,51 @@ protected override SynthesizedAttributeData TrySynthesizeIsByRefLikeAttribute() private void CreateEmbeddedAttributesIfNeeded(DiagnosticBag diagnostics) { - EmbeddedAttributes needsAttributes = GetNeedsGeneratedAttributes(); + EmbeddableAttributes needsAttributes = GetNeedsGeneratedAttributes(); - if ((needsAttributes & EmbeddedAttributes.IsReadOnlyAttribute) != 0) + if (needsAttributes == 0) { - CreateEmbeddedAttributeItselfIfNeeded(diagnostics); + return; + } + CreateEmbeddedAttributeIfNeeded( + ref _lazyEmbeddedAttribute, + diagnostics, + AttributeDescription.CodeAnalysisEmbeddedAttribute); + + if ((needsAttributes & EmbeddableAttributes.IsReadOnlyAttribute) != 0) + { CreateEmbeddedAttributeIfNeeded( ref _lazyIsReadOnlyAttribute, diagnostics, AttributeDescription.IsReadOnlyAttribute); } - if ((needsAttributes & EmbeddedAttributes.IsByRefLikeAttribute) != 0) + if ((needsAttributes & EmbeddableAttributes.IsByRefLikeAttribute) != 0) { - CreateEmbeddedAttributeItselfIfNeeded(diagnostics); - CreateEmbeddedAttributeIfNeeded( ref _lazyIsByRefLikeAttribute, diagnostics, AttributeDescription.IsByRefLikeAttribute); } - if ((needsAttributes & EmbeddedAttributes.IsUnmanagedAttribute) != 0) + if ((needsAttributes & EmbeddableAttributes.IsUnmanagedAttribute) != 0) { - CreateEmbeddedAttributeItselfIfNeeded(diagnostics); - CreateEmbeddedAttributeIfNeeded( ref _lazyIsUnmanagedAttribute, diagnostics, AttributeDescription.IsUnmanagedAttribute); } - if ((needsAttributes & EmbeddedAttributes.NullableAttribute) != 0) + if ((needsAttributes & EmbeddableAttributes.NullableAttribute) != 0) { - CreateEmbeddedAttributeItselfIfNeeded(diagnostics); - CreateEmbeddedNullableAttributeIfNeeded( ref _lazyNullableAttribute, diagnostics); } - if ((needsAttributes & EmbeddedAttributes.NullablePublicOnlyAttribute) != 0) + if ((needsAttributes & EmbeddableAttributes.NullablePublicOnlyAttribute) != 0) { - CreateEmbeddedAttributeItselfIfNeeded(diagnostics); - CreateEmbeddedAttributeIfNeeded( ref _lazyNullablePublicOnlyAttribute, diagnostics, @@ -301,14 +301,6 @@ private void CreateEmbeddedAttributesIfNeeded(DiagnosticBag diagnostics) } } - private void CreateEmbeddedAttributeItselfIfNeeded(DiagnosticBag diagnostics) - { - CreateEmbeddedAttributeIfNeeded( - ref _lazyEmbeddedAttribute, - diagnostics, - AttributeDescription.CodeAnalysisEmbeddedAttribute); - } - private void CreateEmbeddedAttributeIfNeeded( ref SynthesizedEmbeddedAttributeSymbol symbol, DiagnosticBag diagnostics, diff --git a/src/Compilers/CSharp/Portable/Emitter/Model/PEModuleBuilder.cs b/src/Compilers/CSharp/Portable/Emitter/Model/PEModuleBuilder.cs index e05264d338ef8..0dcdd0151c126 100644 --- a/src/Compilers/CSharp/Portable/Emitter/Model/PEModuleBuilder.cs +++ b/src/Compilers/CSharp/Portable/Emitter/Model/PEModuleBuilder.cs @@ -47,18 +47,18 @@ public override NoPia.EmbeddedTypesManager EmbeddedTypesManagerOpt /// The value is set during binding the symbols that need those attributes, and is frozen on first trial to get it. /// Freezing is needed to make sure that nothing tries to modify the value after the value is read. /// - internal EmbeddedAttributes GetNeedsGeneratedAttributes() + internal EmbeddableAttributes GetNeedsGeneratedAttributes() { _needsGeneratedAttributes_IsFrozen = true; return GetNeedsGeneratedAttributesInternal(); } - private EmbeddedAttributes GetNeedsGeneratedAttributesInternal() + private EmbeddableAttributes GetNeedsGeneratedAttributesInternal() { - return (EmbeddedAttributes)_needsGeneratedAttributes | Compilation.GetNeedsGeneratedAttributes(); + return (EmbeddableAttributes)_needsGeneratedAttributes | Compilation.GetNeedsGeneratedAttributes(); } - internal void SetNeedsGeneratedAttributes(EmbeddedAttributes attributes) + internal void SetNeedsGeneratedAttributes(EmbeddableAttributes attributes) { Debug.Assert(!_needsGeneratedAttributes_IsFrozen); ThreadSafeFlagOperations.Set(ref _needsGeneratedAttributes, (int)attributes); @@ -1536,7 +1536,7 @@ protected virtual SynthesizedAttributeData TrySynthesizeIsByRefLikeAttribute() return Compilation.TrySynthesizeAttribute(WellKnownMember.System_Runtime_CompilerServices_IsByRefLikeAttribute__ctor); } - private void EnsureEmbeddedAttributeExists(EmbeddedAttributes attribute) + private void EnsureEmbeddableAttributeExists(EmbeddableAttributes attribute) { Debug.Assert(!_needsGeneratedAttributes_IsFrozen); @@ -1554,22 +1554,22 @@ private void EnsureEmbeddedAttributeExists(EmbeddedAttributes attribute) internal void EnsureIsReadOnlyAttributeExists() { - EnsureEmbeddedAttributeExists(EmbeddedAttributes.IsReadOnlyAttribute); + EnsureEmbeddableAttributeExists(EmbeddableAttributes.IsReadOnlyAttribute); } internal void EnsureIsUnmanagedAttributeExists() { - EnsureEmbeddedAttributeExists(EmbeddedAttributes.IsUnmanagedAttribute); + EnsureEmbeddableAttributeExists(EmbeddableAttributes.IsUnmanagedAttribute); } internal void EnsureNullableAttributeExists() { - EnsureEmbeddedAttributeExists(EmbeddedAttributes.NullableAttribute); + EnsureEmbeddableAttributeExists(EmbeddableAttributes.NullableAttribute); } internal void EnsureNullablePublicOnlyAttributeExists() { - EnsureEmbeddedAttributeExists(EmbeddedAttributes.NullablePublicOnlyAttribute); + EnsureEmbeddableAttributeExists(EmbeddableAttributes.NullablePublicOnlyAttribute); } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Compilation_WellKnownMembers.cs b/src/Compilers/CSharp/Portable/Symbols/Compilation_WellKnownMembers.cs index 0a7674abaad82..9ba580e3269f8 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Compilation_WellKnownMembers.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Compilation_WellKnownMembers.cs @@ -36,13 +36,13 @@ public partial class CSharpCompilation /// The value is set during binding the symbols that need those attributes, and is frozen on first trial to get it. /// Freezing is needed to make sure that nothing tries to modify the value after the value is read. /// - internal EmbeddedAttributes GetNeedsGeneratedAttributes() + internal EmbeddableAttributes GetNeedsGeneratedAttributes() { _needsGeneratedAttributes_IsFrozen = true; - return (EmbeddedAttributes)_needsGeneratedAttributes; + return (EmbeddableAttributes)_needsGeneratedAttributes; } - internal void SetNeedsGeneratedAttributes(EmbeddedAttributes attributes) + internal void SetNeedsGeneratedAttributes(EmbeddableAttributes attributes) { Debug.Assert(!_needsGeneratedAttributes_IsFrozen); ThreadSafeFlagOperations.Set(ref _needsGeneratedAttributes, (int)attributes); @@ -448,7 +448,7 @@ internal SynthesizedAttributeData SynthesizeDebuggerStepThroughAttribute() return TrySynthesizeAttribute(WellKnownMember.System_Diagnostics_DebuggerStepThroughAttribute__ctor); } - private void EnsureEmbeddedAttributeExists(EmbeddedAttributes attribute, DiagnosticBag diagnostics, Location location, bool modifyCompilation) + private void EnsureEmbeddableAttributeExists(EmbeddableAttributes attribute, DiagnosticBag diagnostics, Location location, bool modifyCompilation) { Debug.Assert(!modifyCompilation || !_needsGeneratedAttributes_IsFrozen); @@ -460,55 +460,55 @@ private void EnsureEmbeddedAttributeExists(EmbeddedAttributes attribute, Diagnos internal void EnsureIsReadOnlyAttributeExists(DiagnosticBag diagnostics, Location location, bool modifyCompilation) { - EnsureEmbeddedAttributeExists(EmbeddedAttributes.IsReadOnlyAttribute, diagnostics, location, modifyCompilation); + EnsureEmbeddableAttributeExists(EmbeddableAttributes.IsReadOnlyAttribute, diagnostics, location, modifyCompilation); } internal void EnsureIsByRefLikeAttributeExists(DiagnosticBag diagnostics, Location location, bool modifyCompilation) { - EnsureEmbeddedAttributeExists(EmbeddedAttributes.IsByRefLikeAttribute, diagnostics, location, modifyCompilation); + EnsureEmbeddableAttributeExists(EmbeddableAttributes.IsByRefLikeAttribute, diagnostics, location, modifyCompilation); } internal void EnsureIsUnmanagedAttributeExists(DiagnosticBag diagnostics, Location location, bool modifyCompilation) { - EnsureEmbeddedAttributeExists(EmbeddedAttributes.IsUnmanagedAttribute, diagnostics, location, modifyCompilation); + EnsureEmbeddableAttributeExists(EmbeddableAttributes.IsUnmanagedAttribute, diagnostics, location, modifyCompilation); } internal void EnsureNullableAttributeExists(DiagnosticBag diagnostics, Location location, bool modifyCompilation) { - EnsureEmbeddedAttributeExists(EmbeddedAttributes.NullableAttribute, diagnostics, location, modifyCompilation); + EnsureEmbeddableAttributeExists(EmbeddableAttributes.NullableAttribute, diagnostics, location, modifyCompilation); } internal void EnsureNullablePublicOnlyAttributeExists(DiagnosticBag diagnostics, Location location, bool modifyCompilation) { - EnsureEmbeddedAttributeExists(EmbeddedAttributes.NullablePublicOnlyAttribute, diagnostics, location, modifyCompilation); + EnsureEmbeddableAttributeExists(EmbeddableAttributes.NullablePublicOnlyAttribute, diagnostics, location, modifyCompilation); } - internal bool CheckIfAttributeShouldBeEmbedded(EmbeddedAttributes attribute, DiagnosticBag diagnosticsOpt, Location locationOpt) + internal bool CheckIfAttributeShouldBeEmbedded(EmbeddableAttributes attribute, DiagnosticBag diagnosticsOpt, Location locationOpt) { switch (attribute) { - case EmbeddedAttributes.IsReadOnlyAttribute: + case EmbeddableAttributes.IsReadOnlyAttribute: return CheckIfAttributeShouldBeEmbedded( diagnosticsOpt, locationOpt, WellKnownType.System_Runtime_CompilerServices_IsReadOnlyAttribute, WellKnownMember.System_Runtime_CompilerServices_IsReadOnlyAttribute__ctor); - case EmbeddedAttributes.IsByRefLikeAttribute: + case EmbeddableAttributes.IsByRefLikeAttribute: return CheckIfAttributeShouldBeEmbedded( diagnosticsOpt, locationOpt, WellKnownType.System_Runtime_CompilerServices_IsByRefLikeAttribute, WellKnownMember.System_Runtime_CompilerServices_IsByRefLikeAttribute__ctor); - case EmbeddedAttributes.IsUnmanagedAttribute: + case EmbeddableAttributes.IsUnmanagedAttribute: return CheckIfAttributeShouldBeEmbedded( diagnosticsOpt, locationOpt, WellKnownType.System_Runtime_CompilerServices_IsUnmanagedAttribute, WellKnownMember.System_Runtime_CompilerServices_IsUnmanagedAttribute__ctor); - case EmbeddedAttributes.NullableAttribute: + case EmbeddableAttributes.NullableAttribute: // Note: if the type exists, we'll check both constructors, regardless of which one(s) we'll eventually need return CheckIfAttributeShouldBeEmbedded( diagnosticsOpt, @@ -517,7 +517,7 @@ internal bool CheckIfAttributeShouldBeEmbedded(EmbeddedAttributes attribute, Dia WellKnownMember.System_Runtime_CompilerServices_NullableAttribute__ctorByte, WellKnownMember.System_Runtime_CompilerServices_NullableAttribute__ctorTransformFlags); - case EmbeddedAttributes.NullablePublicOnlyAttribute: + case EmbeddableAttributes.NullablePublicOnlyAttribute: return CheckIfAttributeShouldBeEmbedded( diagnosticsOpt, locationOpt, diff --git a/src/Compilers/CSharp/Portable/Symbols/EmbeddedAttributes.cs b/src/Compilers/CSharp/Portable/Symbols/EmbeddableAttributes.cs similarity index 91% rename from src/Compilers/CSharp/Portable/Symbols/EmbeddedAttributes.cs rename to src/Compilers/CSharp/Portable/Symbols/EmbeddableAttributes.cs index be80be66e19db..cdb7082f6bed6 100644 --- a/src/Compilers/CSharp/Portable/Symbols/EmbeddedAttributes.cs +++ b/src/Compilers/CSharp/Portable/Symbols/EmbeddableAttributes.cs @@ -5,7 +5,7 @@ namespace Microsoft.CodeAnalysis.CSharp { [Flags] - internal enum EmbeddedAttributes + internal enum EmbeddableAttributes { IsReadOnlyAttribute = 0x01, IsByRefLikeAttribute = 0x02, diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceTypeParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceTypeParameterSymbol.cs index b71d215717f55..82450177fa878 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceTypeParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceTypeParameterSymbol.cs @@ -306,6 +306,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)) diff --git a/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_RefReadOnly.cs b/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_RefReadOnly.cs index ecc7b3fedaf9d..44a78a9196b6b 100644 --- a/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_RefReadOnly.cs +++ b/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_RefReadOnly.cs @@ -2347,7 +2347,7 @@ private void AssertGeneratedEmbeddedAttribute(AssemblySymbol assembly, string ex private static bool NeedsGeneratedIsReadOnlyAttribute(CSharpCompilation compilation) { - return (compilation.GetNeedsGeneratedAttributes() & EmbeddedAttributes.IsReadOnlyAttribute) != 0; + return (compilation.GetNeedsGeneratedAttributes() & EmbeddableAttributes.IsReadOnlyAttribute) != 0; } } } From 7edf5560d08447c3c366dff494534bea7396c4f0 Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Fri, 14 Jun 2019 13:25:01 -0700 Subject: [PATCH 14/16] PR feedback --- .../Symbols/Source/LocalFunctionSymbol.cs | 43 +++++++++++-------- .../Test/ExpressionCompiler/LocalsTests.cs | 43 +++++++++++++++++++ 2 files changed, 68 insertions(+), 18 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs index 4f9213793dd7a..54426e4c3cd2c 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs @@ -217,33 +217,40 @@ internal void ComputeReturnType() } var diagnostics = DiagnosticBag.GetInstance(); - var compilation = DeclaringCompilation; + TypeSyntax returnTypeSyntax = _syntax.ReturnType; TypeWithAnnotations returnType = _binder.BindType(returnTypeSyntax.SkipRef(), diagnostics); - if (this.IsAsync) + var compilation = DeclaringCompilation; + + // Skip some diagnostics when the local function is not associated with a compilation + // (specifically, local functions nested in expressions in the EE). + if (!(compilation is null)) { - if (this.RefKind != RefKind.None) + if (this.IsAsync) { - ReportBadRefToken(returnTypeSyntax, diagnostics); + if (this.RefKind != RefKind.None) + { + ReportBadRefToken(returnTypeSyntax, diagnostics); + } + else if (returnType.Type.IsBadAsyncReturn(compilation)) + { + diagnostics.Add(ErrorCode.ERR_BadAsyncReturn, this.Locations[0]); + } } - else if (returnType.Type.IsBadAsyncReturn(compilation)) + + var location = _syntax.ReturnType.Location; + if (_refKind == RefKind.RefReadOnly) { - diagnostics.Add(ErrorCode.ERR_BadAsyncReturn, this.Locations[0]); + compilation.EnsureIsReadOnlyAttributeExists(diagnostics, location, modifyCompilation: false); } - } - - var location = _syntax.ReturnType.Location; - if (_refKind == RefKind.RefReadOnly) - { - compilation.EnsureIsReadOnlyAttributeExists(diagnostics, location, modifyCompilation: false); - } - if (compilation?.ShouldEmitNullableAttributes(this) == true && - returnType.NeedsNullableAttribute()) - { - compilation.EnsureNullableAttributeExists(diagnostics, location, modifyCompilation: false); - // Note: we don't need to warn on annotations used in #nullable disable context for local functions, as this is handled in binding already + if (compilation.ShouldEmitNullableAttributes(this) == true && + returnType.NeedsNullableAttribute()) + { + compilation.EnsureNullableAttributeExists(diagnostics, location, modifyCompilation: false); + // Note: we don't need to warn on annotations used in #nullable disable context for local functions, as this is handled in binding already + } } // span-like types are returnable in general diff --git a/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/LocalsTests.cs b/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/LocalsTests.cs index 38e0daa0500e7..254a57478fb04 100644 --- a/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/LocalsTests.cs +++ b/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/LocalsTests.cs @@ -1448,6 +1448,49 @@ .maxstack 2 }); } + [Fact] + public void RefReadOnlyLocalFunction() + { + var source = @" +using System; +class C +{ + void M() { } +}"; + var compilation0 = CreateCompilation(source, options: TestOptions.DebugDll); + WithRuntimeInstance(compilation0, runtime => + { + var context = CreateMethodContext(runtime, "C.M"); + var testData = new CompilationTestData(); + context.CompileExpression( +@"new Action(() => +{ + int y = 0; + ref readonly int F(ref int x) => ref x; + F(ref y); +}).Invoke()", + out var error, + testData); + Assert.Null(error); + testData.GetMethodData("<>x.<>m0").VerifyIL( +@"{ + // Code size 37 (0x25) + .maxstack 2 + IL_0000: ldsfld ""System.Action <>x.<>c.<>9__0_0"" + IL_0005: dup + IL_0006: brtrue.s IL_001f + IL_0008: pop + IL_0009: ldsfld ""<>x.<>c <>x.<>c.<>9"" + IL_000e: ldftn ""void <>x.<>c.<<>m0>b__0_0()"" + IL_0014: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0019: dup + IL_001a: stsfld ""System.Action <>x.<>c.<>9__0_0"" + IL_001f: callvirt ""void System.Action.Invoke()"" + IL_0024: ret +}"); + }); + } + [Fact] public void NestedLambdas() { From 331ad5566cd2b6fdd807a5fb2d8756bfb75685ac Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Fri, 14 Jun 2019 14:06:09 -0700 Subject: [PATCH 15/16] Rename property --- .../CSharp/Portable/Symbols/Source/SourceModuleSymbol.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceModuleSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceModuleSymbol.cs index 57022785ff726..aa143e6c90bf8 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceModuleSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceModuleSymbol.cs @@ -525,7 +525,7 @@ internal override void DecodeWellKnownAttribute(ref DecodeWellKnownAttributeArgu } } - private bool EmitNullablePublicOnly + private bool EmitNullablePublicOnlyAttribute { get { @@ -537,7 +537,7 @@ private bool EmitNullablePublicOnly private void AfterMembersChecks(DiagnosticBag diagnostics) { - if (EmitNullablePublicOnly) + if (EmitNullablePublicOnlyAttribute) { DeclaringCompilation.EnsureNullablePublicOnlyAttributeExists(diagnostics, location: NoLocation.Singleton, modifyCompilation: true); } @@ -558,7 +558,7 @@ internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, r } } - if (EmitNullablePublicOnly) + if (EmitNullablePublicOnlyAttribute) { AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeNullablePublicOnlyAttribute()); } From 85a9cb2f3458353cd90a00be1c3a9f6c777cc362 Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Fri, 14 Jun 2019 14:12:55 -0700 Subject: [PATCH 16/16] Misc. --- .../CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs index 54426e4c3cd2c..7768299e750fc 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs @@ -217,7 +217,6 @@ internal void ComputeReturnType() } var diagnostics = DiagnosticBag.GetInstance(); - TypeSyntax returnTypeSyntax = _syntax.ReturnType; TypeWithAnnotations returnType = _binder.BindType(returnTypeSyntax.SkipRef(), diagnostics); @@ -245,7 +244,7 @@ internal void ComputeReturnType() compilation.EnsureIsReadOnlyAttributeExists(diagnostics, location, modifyCompilation: false); } - if (compilation.ShouldEmitNullableAttributes(this) == true && + if (compilation.ShouldEmitNullableAttributes(this) && returnType.NeedsNullableAttribute()) { compilation.EnsureNullableAttributeExists(diagnostics, location, modifyCompilation: false);