From 6fe401010db6c37b55f82d1b50c74d12518f9cd9 Mon Sep 17 00:00:00 2001 From: Joseph Musser Date: Tue, 28 Apr 2020 00:03:27 -0400 Subject: [PATCH 1/8] Codegen for module initializers feature (#43281) --- .../CSharp/Portable/CSharpResources.resx | 3 + .../Portable/Compilation/CSharpCompilation.cs | 36 ++++ .../CSharp/Portable/Errors/MessageID.cs | 2 + .../SourceMethodSymbolWithAttributes.cs | 6 + .../Portable/xlf/CSharpResources.cs.xlf | 5 + .../Portable/xlf/CSharpResources.de.xlf | 5 + .../Portable/xlf/CSharpResources.es.xlf | 5 + .../Portable/xlf/CSharpResources.fr.xlf | 5 + .../Portable/xlf/CSharpResources.it.xlf | 5 + .../Portable/xlf/CSharpResources.ja.xlf | 5 + .../Portable/xlf/CSharpResources.ko.xlf | 5 + .../Portable/xlf/CSharpResources.pl.xlf | 5 + .../Portable/xlf/CSharpResources.pt-BR.xlf | 5 + .../Portable/xlf/CSharpResources.ru.xlf | 5 + .../Portable/xlf/CSharpResources.tr.xlf | 5 + .../Portable/xlf/CSharpResources.zh-Hans.xlf | 5 + .../Portable/xlf/CSharpResources.zh-Hant.xlf | 5 + .../Symbol/Symbols/ModuleInitializersTests.cs | 141 +++++++++++++++ .../Portable/Emit/CommonPEModuleBuilder.cs | 9 +- .../PEWriter/RootModuleStaticConstructor.cs | 162 ++++++++++++++++++ .../Core/Portable/PEWriter/RootModuleType.cs | 25 ++- .../Attributes/AttributeDescription.cs | 3 + .../Portable/Traits/CompilerFeature.cs | 1 + 23 files changed, 441 insertions(+), 12 deletions(-) create mode 100644 src/Compilers/CSharp/Test/Symbol/Symbols/ModuleInitializersTests.cs create mode 100644 src/Compilers/Core/Portable/PEWriter/RootModuleStaticConstructor.cs diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 73669cf03a73b..c7eee212e5687 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -6070,4 +6070,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Generator failed to initialize. + + module initializers + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs index 997bb6b31a945..a4af9691431ff 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs @@ -16,6 +16,7 @@ using System.Reflection.Metadata; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeGen; using Microsoft.CodeAnalysis.CSharp.Emit; using Microsoft.CodeAnalysis.CSharp.Symbols; @@ -2015,6 +2016,14 @@ private protected override bool IsSymbolAccessibleWithinCore( throw new NotImplementedException(); } + private ConcurrentSet? _moduleInitializerMethods; + + internal void AddModuleInitializerMethod(MethodSymbol method) + { + Debug.Assert(!_declarationDiagnosticsFrozen); + LazyInitializer.EnsureInitialized(ref _moduleInitializerMethods).Add(method); + } + #endregion #region Binding @@ -2802,6 +2811,8 @@ internal override bool CompileMethods( filterOpt: filterOpt, cancellationToken: cancellationToken); + GenerateModuleInitializer(moduleBeingBuilt, methodBodyDiagnosticBag); + bool hasMethodBodyError = !FilterAndAppendAndFreeDiagnostics(diagnostics, ref methodBodyDiagnosticBag); if (hasDeclarationErrors || hasMethodBodyError) @@ -2813,6 +2824,31 @@ internal override bool CompileMethods( return true; } + private void GenerateModuleInitializer(PEModuleBuilder moduleBeingBuilt, DiagnosticBag methodBodyDiagnosticBag) + { + Debug.Assert(_declarationDiagnosticsFrozen); + + if (_moduleInitializerMethods is object) + { + var ilBuilder = new ILBuilder(moduleBeingBuilt, new LocalSlotManager(slotAllocator: null), OptimizationLevel.Release, areLocalsZeroed: false); + + // PROTOTYPE(module-initializers): require deterministic order + foreach (var method in _moduleInitializerMethods) + { + ilBuilder.EmitOpCode(ILOpCode.Call, stackAdjustment: 0); + + ilBuilder.EmitToken( + moduleBeingBuilt.Translate(method, methodBodyDiagnosticBag, needDeclaration: true), + CSharpSyntaxTree.Dummy.GetRoot(), + methodBodyDiagnosticBag); + } + + ilBuilder.EmitRet(isVoid: true); + ilBuilder.Realize(); + moduleBeingBuilt.RootModuleType.SetStaticConstructorBody(ilBuilder.RealizedIL); + } + } + internal override bool GenerateResourcesAndDocumentationComments( CommonPEModuleBuilder moduleBuilder, Stream? xmlDocStream, diff --git a/src/Compilers/CSharp/Portable/Errors/MessageID.cs b/src/Compilers/CSharp/Portable/Errors/MessageID.cs index ee1d0255674a3..825184ddf6673 100644 --- a/src/Compilers/CSharp/Portable/Errors/MessageID.cs +++ b/src/Compilers/CSharp/Portable/Errors/MessageID.cs @@ -192,6 +192,7 @@ internal enum MessageID IDS_FeatureMemberNotNull = MessageBase + 12768, IDS_FeatureNativeInt = MessageBase + 12769, IDS_FeatureTargetTypedObjectCreation = MessageBase + 12770, + IDS_FeatureModuleInitializers = MessageBase + 12771, } // Message IDs may refer to strings that need to be localized. @@ -305,6 +306,7 @@ internal static LanguageVersion RequiredVersion(this MessageID feature) case MessageID.IDS_FeatureTargetTypedObjectCreation: // syntax check case MessageID.IDS_FeatureMemberNotNull: case MessageID.IDS_FeatureNativeInt: + case MessageID.IDS_FeatureModuleInitializers: // semantic check on method attribute return LanguageVersion.Preview; // C# 8.0 features. diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs index c697063b029ec..f0f9672e4feb3 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs @@ -482,6 +482,12 @@ private void DecodeWellKnownAttributeAppliedToMethod(ref DecodeWellKnownAttribut MessageID.IDS_FeatureMemberNotNull.CheckFeatureAvailability(arguments.Diagnostics, arguments.AttributeSyntaxOpt); CSharpAttributeData.DecodeMemberNotNullWhenAttribute(ContainingType, ref arguments); } + else if (attribute.IsTargetAttribute(this, AttributeDescription.ModuleInitializerAttribute)) + { + MessageID.IDS_FeatureModuleInitializers.CheckFeatureAvailability(arguments.Diagnostics, arguments.AttributeSyntaxOpt); + // PROTOTYPE(module-initializers): diagnostics + DeclaringCompilation.AddModuleInitializerMethod(this); + } else { var compilation = this.DeclaringCompilation; diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index 3652cf943e922..d99d9f87d50bc 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -944,6 +944,11 @@ MemberNotNull attribute + + module initializers + module initializers + + name shadowing in nested functions skrývání názvů ve vnořených funkcích diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index 89f99f58ae35c..fbf7625e8ad87 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -944,6 +944,11 @@ MemberNotNull attribute + + module initializers + module initializers + + name shadowing in nested functions Namensshadowing in geschachtelten Funktionen diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index 4cd1fafa42e47..c71d7b59e9882 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -944,6 +944,11 @@ MemberNotNull attribute + + module initializers + module initializers + + name shadowing in nested functions sombreado de nombres en funciones anidadas diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index 62e8a589e10f8..c381951541e58 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -944,6 +944,11 @@ MemberNotNull attribute + + module initializers + module initializers + + name shadowing in nested functions ombrage des noms dans les fonctions imbriquées diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index 3658daa426382..3c74d409c59e5 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -944,6 +944,11 @@ MemberNotNull attribute + + module initializers + module initializers + + name shadowing in nested functions shadowing dei nomi nelle funzioni annidate diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index 209c8a0cfc10d..1c448e377867c 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -944,6 +944,11 @@ MemberNotNull attribute + + module initializers + module initializers + + name shadowing in nested functions 入れ子になった関数での名前シャドウイング diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index 3f40cdc84e2b8..2a55ffc77840f 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -944,6 +944,11 @@ MemberNotNull attribute + + module initializers + module initializers + + name shadowing in nested functions 중첩된 함수의 이름 섀도잉 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index 9646a0784ee7c..d64fcc15e22a7 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -944,6 +944,11 @@ MemberNotNull attribute + + module initializers + module initializers + + name shadowing in nested functions zasłanianie nazw w funkcjach zagnieżdżonych diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index bc6654efe8ed8..e4944db4ed774 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -942,6 +942,11 @@ MemberNotNull attribute + + module initializers + module initializers + + name shadowing in nested functions sombreamento de nome em funções aninhadas diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index bef3946e7ecba..a58a44f35c529 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -944,6 +944,11 @@ MemberNotNull attribute + + module initializers + module initializers + + name shadowing in nested functions скрытие имен во вложенных функциях diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index 3fbf2a504f69d..61ffc836a9555 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -944,6 +944,11 @@ MemberNotNull attribute + + module initializers + module initializers + + name shadowing in nested functions iç içe işlevlerde ad gölgeleme diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index 0a9d95c7c3bd3..1311c9a8a9891 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -944,6 +944,11 @@ MemberNotNull attribute + + module initializers + module initializers + + name shadowing in nested functions 在嵌套函数中的名称映射 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index d9b702bbc5e6a..743ae017dc580 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -944,6 +944,11 @@ MemberNotNull attribute + + module initializers + module initializers + + name shadowing in nested functions 巢狀函式中的名稱鏡像處理 diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/ModuleInitializersTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/ModuleInitializersTests.cs new file mode 100644 index 0000000000000..558aab4d66028 --- /dev/null +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/ModuleInitializersTests.cs @@ -0,0 +1,141 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Reflection; +using Microsoft.CodeAnalysis.CSharp.Symbols; +using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE; +using Microsoft.CodeAnalysis.CSharp.Test.Utilities; +using Microsoft.CodeAnalysis.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Symbols +{ + [CompilerTrait(CompilerFeature.ModuleInitializers)] + public sealed class ModuleInitializersTests : CSharpTestBase + { + private static readonly CSharpParseOptions s_parseOptions = TestOptions.RegularPreview; + + [Fact] + public static void LastLanguageVersionNotSupportingModuleInitializersIs8() + { + var source = +@"using System.Runtime.CompilerServices; + +class C +{ + [ModuleInitializer] + internal static void M() { } +} + +namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : System.Attribute { } } +"; + var compilation = CreateCompilation(source, parseOptions: TestOptions.Regular8); + + compilation.VerifyDiagnostics( + // (5,6): error CS8652: The feature 'module initializers' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // [ModuleInitializer] + Diagnostic(ErrorCode.ERR_FeatureInPreview, "ModuleInitializer").WithArguments("module initializers").WithLocation(5, 6) + ); + } + + [Fact] + public static void FirstLanguageVersionSupportingModuleInitializersIs9() + { + var source = +@"using System.Runtime.CompilerServices; + +class C +{ + [ModuleInitializer] + internal static void M() { } +} + +namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : System.Attribute { } } +"; + var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularPreview); + + compilation.VerifyDiagnostics(); + } + + [Fact] + public void ModuleTypeStaticConstructorIsNotEmittedWhenNoMethodIsMarkedWithModuleInitializerAttribute() + { + string source = @" +using System; +using System.Runtime.CompilerServices; + +class C +{ + internal static void M() => Console.WriteLine(""C.M""); +} + +class Program +{ + static void Main() => Console.WriteLine(""Program.Main""); +} + +namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : System.Attribute { } } +"; + + CompileAndVerify( + source, + parseOptions: s_parseOptions, + options: new CSharpCompilationOptions(OutputKind.ConsoleApplication, metadataImportOptions: MetadataImportOptions.All), + symbolValidator: module => + { + var rootModuleType = (TypeSymbol)module.GlobalNamespace.GetMember(""); + Assert.Null(rootModuleType.GetMember(".cctor")); + }, + expectedOutput: @" +Program.Main"); + } + + [Fact] + public void ModuleTypeStaticConstructorCallsMethodMarkedWithModuleInitializerAttribute() + { + string source = @" +using System; +using System.Runtime.CompilerServices; + +class C +{ + [ModuleInitializer] + internal static void M() => Console.WriteLine(""C.M""); +} + +class Program +{ + static void Main() => Console.WriteLine(""Program.Main""); +} + +namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : System.Attribute { } } +"; + + CompileAndVerify( + source, + parseOptions: s_parseOptions, + options: TestOptions.DebugExe.WithMetadataImportOptions(MetadataImportOptions.All), + symbolValidator: module => + { + var rootModuleType = (TypeSymbol)module.GlobalNamespace.GetMember(""); + var staticConstructor = (PEMethodSymbol)rootModuleType.GetMember(".cctor"); + + Assert.NotNull(staticConstructor); + Assert.Equal(MethodKind.StaticConstructor, staticConstructor.MethodKind); + + var expectedFlags = + MethodAttributes.Private + | MethodAttributes.Static + | MethodAttributes.SpecialName + | MethodAttributes.RTSpecialName + | MethodAttributes.HideBySig; + + Assert.Equal(expectedFlags, staticConstructor.Flags); + }, + expectedOutput: @" +C.M +Program.Main"); + } + } +} diff --git a/src/Compilers/Core/Portable/Emit/CommonPEModuleBuilder.cs b/src/Compilers/Core/Portable/Emit/CommonPEModuleBuilder.cs index b21826e1bb012..e845b4632380d 100644 --- a/src/Compilers/Core/Portable/Emit/CommonPEModuleBuilder.cs +++ b/src/Compilers/Core/Portable/Emit/CommonPEModuleBuilder.cs @@ -459,8 +459,6 @@ internal abstract class PEModuleBuilder { - private readonly Cci.RootModuleType _rootModuleType = new Cci.RootModuleType(); - internal readonly TSourceModuleSymbol SourceModule; internal readonly TCompilation Compilation; @@ -469,6 +467,7 @@ internal abstract class PEModuleBuilder _namesOfTopLevelTypes; internal readonly TModuleCompilationState CompilationState; + public Cci.RootModuleType RootModuleType { get; } = new Cci.RootModuleType(); public abstract TEmbeddedTypesManager EmbeddedTypesManagerOpt { get; } @@ -552,9 +551,9 @@ protected bool ContainsTopLevelType(string fullEmittedName) Dispatch(typeReferenceIndexer); } - AddTopLevelType(names, _rootModuleType); - VisitTopLevelType(typeReferenceIndexer, _rootModuleType); - yield return _rootModuleType; + AddTopLevelType(names, RootModuleType); + VisitTopLevelType(typeReferenceIndexer, RootModuleType); + yield return RootModuleType; foreach (var typeDef in GetAnonymousTypeDefinitions(context)) { diff --git a/src/Compilers/Core/Portable/PEWriter/RootModuleStaticConstructor.cs b/src/Compilers/Core/Portable/PEWriter/RootModuleStaticConstructor.cs new file mode 100644 index 0000000000000..2c20dc559a616 --- /dev/null +++ b/src/Compilers/Core/Portable/PEWriter/RootModuleStaticConstructor.cs @@ -0,0 +1,162 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Reflection; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeGen; +using Microsoft.CodeAnalysis.Debugging; +using Microsoft.CodeAnalysis.Emit; +using Roslyn.Utilities; + +namespace Microsoft.Cci +{ + internal sealed partial class RootModuleStaticConstructor : IMethodDefinition, IMethodBody + { + public RootModuleStaticConstructor(ITypeDefinition containingTypeDefinition, ImmutableArray il) + { + ContainingTypeDefinition = containingTypeDefinition; + IL = il; + } + + // IMethodDefinition implementation + + public ITypeDefinition ContainingTypeDefinition { get; } + + public string Name => WellKnownMemberNames.StaticConstructorName; + + public IMethodBody GetBody(EmitContext context) => this; + + public IEnumerable GenericParameters => SpecializedCollections.EmptyEnumerable(); + + public bool IsImplicitlyDeclared => true; + + public bool HasDeclarativeSecurity => false; + + public bool IsAbstract => false; + + public bool IsAccessCheckedOnOverride => false; + + public bool IsConstructor => false; + + public bool IsExternal => false; + + public bool IsHiddenBySignature => true; + + public bool IsNewSlot => false; + + public bool IsPlatformInvoke => false; + + public bool IsRuntimeSpecial => true; + + public bool IsSealed => false; + + public bool IsSpecialName => true; + + public bool IsStatic => true; + + public bool IsVirtual => false; + + public ImmutableArray Parameters => ImmutableArray.Empty; + + public IPlatformInvokeInformation PlatformInvokeData => null; + + public bool RequiresSecurityObject => false; + + public bool ReturnValueIsMarshalledExplicitly => false; + + public IMarshallingInformation ReturnValueMarshallingInformation => null; + + public ImmutableArray ReturnValueMarshallingDescriptor => default; + + public IEnumerable SecurityAttributes => null; + + public INamespace ContainingNamespace => null; + + public TypeMemberVisibility Visibility => TypeMemberVisibility.Private; + + public bool AcceptsExtraArguments => false; + + public ushort GenericParameterCount => 0; + + public bool IsGeneric => false; + + public ImmutableArray ExtraParameters => ImmutableArray.Empty; + + public IGenericMethodInstanceReference AsGenericMethodInstanceReference => null; + + public ISpecializedMethodReference AsSpecializedMethodReference => null; + + public CallingConvention CallingConvention => CallingConvention.Default; + + public ushort ParameterCount => 0; + + public ImmutableArray ReturnValueCustomModifiers => ImmutableArray.Empty; + + public ImmutableArray RefCustomModifiers => ImmutableArray.Empty; + + public bool ReturnValueIsByRef => false; + + public IDefinition AsDefinition(EmitContext context) => this; + + public void Dispatch(MetadataVisitor visitor) => visitor.Visit((IMethodDefinition)this); + + public IEnumerable GetAttributes(EmitContext context) => SpecializedCollections.EmptyEnumerable(); + + public ITypeReference GetContainingType(EmitContext context) => ContainingTypeDefinition; + + public MethodImplAttributes GetImplementationAttributes(EmitContext context) => default; + + public ImmutableArray GetParameters(EmitContext context) => ImmutableArray.Empty; + + public IMethodDefinition GetResolvedMethod(EmitContext context) => this; + + public IEnumerable GetReturnValueAttributes(EmitContext context) => SpecializedCollections.EmptyEnumerable(); + + public ITypeReference GetType(EmitContext context) => context.Module.GetPlatformType(PlatformType.SystemVoid, context); + + // IMethodBody implementation + + public ushort MaxStack => 0; + + public ImmutableArray IL { get; } + + public IMethodDefinition MethodDefinition => this; + + public ImmutableArray ExceptionRegions => ImmutableArray.Empty; + + public bool AreLocalsZeroed => false; + + public bool HasStackalloc => false; + + public ImmutableArray LocalVariables => ImmutableArray.Empty; + + public StateMachineMoveNextBodyDebugInfo MoveNextBodyInfo => null; + + public ImmutableArray SequencePoints => ImmutableArray.Empty; + + public bool HasDynamicLocalVariables => false; + + public ImmutableArray LocalScopes => ImmutableArray.Empty; + + public IImportScope ImportScope => null; + + public DebugId MethodId => default; + + public ImmutableArray StateMachineHoistedLocalScopes => ImmutableArray.Empty; + + public string StateMachineTypeName => null; + + public ImmutableArray StateMachineHoistedLocalSlots => ImmutableArray.Empty; + + public ImmutableArray StateMachineAwaiterSlots => ImmutableArray.Empty; + + public ImmutableArray ClosureDebugInfo => ImmutableArray.Empty; + + public ImmutableArray LambdaDebugInfo => ImmutableArray.Empty; + + public DynamicAnalysisMethodBodyData DynamicAnalysisData => null; + } +} diff --git a/src/Compilers/Core/Portable/PEWriter/RootModuleType.cs b/src/Compilers/Core/Portable/PEWriter/RootModuleType.cs index 64ce7bc27441f..220de7aa6f0d5 100644 --- a/src/Compilers/Core/Portable/PEWriter/RootModuleType.cs +++ b/src/Compilers/Core/Portable/PEWriter/RootModuleType.cs @@ -4,10 +4,11 @@ #nullable enable -using System; using System.Collections.Generic; -using System.Runtime.InteropServices; +using System.Collections.Immutable; +using System.Diagnostics; using System.Reflection.Metadata; +using System.Runtime.InteropServices; using Roslyn.Utilities; using EmitContext = Microsoft.CodeAnalysis.Emit.EmitContext; @@ -18,6 +19,21 @@ namespace Microsoft.Cci /// internal class RootModuleType : INamespaceTypeDefinition { + private IReadOnlyList? _methods; + + public void SetStaticConstructorBody(ImmutableArray il) + { + Debug.Assert(_methods is null); + + _methods = SpecializedCollections.SingletonReadOnlyList( + new RootModuleStaticConstructor(containingTypeDefinition: this, il)); + } + + public IEnumerable GetMethods(EmitContext context) + { + return _methods ??= SpecializedCollections.EmptyReadOnlyList(); + } + public TypeDefinitionHandle TypeDef { get { return default(TypeDefinitionHandle); } @@ -138,11 +154,6 @@ public LayoutKind Layout get { return LayoutKind.Auto; } } - public IEnumerable GetMethods(EmitContext context) - { - return SpecializedCollections.EmptyEnumerable(); - } - public IEnumerable GetNestedTypes(EmitContext context) { return SpecializedCollections.EmptyEnumerable(); diff --git a/src/Compilers/Core/Portable/Symbols/Attributes/AttributeDescription.cs b/src/Compilers/Core/Portable/Symbols/Attributes/AttributeDescription.cs index 7ac415b963564..f9cbe8c2d133f 100644 --- a/src/Compilers/Core/Portable/Symbols/Attributes/AttributeDescription.cs +++ b/src/Compilers/Core/Portable/Symbols/Attributes/AttributeDescription.cs @@ -428,6 +428,8 @@ static AttributeDescription() private static readonly byte[][] s_signaturesOfEnumeratorCancellationAttribute = { s_signature_HasThis_Void }; private static readonly byte[][] s_signaturesOfNativeIntegerAttribute = { s_signature_HasThis_Void, s_signature_HasThis_Void_SzArray_Boolean }; + private static readonly byte[][] s_signaturesOfModuleInitializerAttribute = { s_signature_HasThis_Void }; + // early decoded attributes: internal static readonly AttributeDescription OptionalAttribute = new AttributeDescription("System.Runtime.InteropServices", "OptionalAttribute", s_signaturesOfOptionalAttribute); internal static readonly AttributeDescription ComImportAttribute = new AttributeDescription("System.Runtime.InteropServices", "ComImportAttribute", s_signaturesOfComImportAttribute); @@ -559,5 +561,6 @@ static AttributeDescription() internal static readonly AttributeDescription EnumeratorCancellationAttribute = new AttributeDescription("System.Runtime.CompilerServices", "EnumeratorCancellationAttribute", s_signaturesOfEnumeratorCancellationAttribute); internal static readonly AttributeDescription SkipLocalsInitAttribute = new AttributeDescription("System.Runtime.CompilerServices", "SkipLocalsInitAttribute", s_signaturesOfSkipLocalsInitAttribute); internal static readonly AttributeDescription NativeIntegerAttribute = new AttributeDescription("System.Runtime.CompilerServices", "NativeIntegerAttribute", s_signaturesOfNativeIntegerAttribute); + internal static readonly AttributeDescription ModuleInitializerAttribute = new AttributeDescription("System.Runtime.CompilerServices", "ModuleInitializerAttribute", s_signaturesOfModuleInitializerAttribute); } } diff --git a/src/Test/Utilities/Portable/Traits/CompilerFeature.cs b/src/Test/Utilities/Portable/Traits/CompilerFeature.cs index 618e811949382..efcd371c5fd75 100644 --- a/src/Test/Utilities/Portable/Traits/CompilerFeature.cs +++ b/src/Test/Utilities/Portable/Traits/CompilerFeature.cs @@ -35,5 +35,6 @@ public enum CompilerFeature DefaultInterfaceImplementation, LambdaDiscardParameters, StatementAttributes, + ModuleInitializers, } } From 8aa968692a05850f456244090f2023a565381f0a Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Mon, 4 May 2020 16:12:22 -0700 Subject: [PATCH 2/8] Fix merge conflicts --- src/Compilers/CSharp/Portable/CSharpResources.resx | 10 +++------- src/Compilers/CSharp/Portable/Errors/MessageID.cs | 5 +---- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 720517872729d..532cbfc2fca9a 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -6097,12 +6097,6 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Generator failed to initialize. -<<<<<<< HEAD - - module initializers - - -======= A variable may not be declared within a 'not' or 'or' pattern. @@ -6112,5 +6106,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Relational patterns may not be used for a floating-point NaN. + + module initializers + ->>>>>>> upstream/master diff --git a/src/Compilers/CSharp/Portable/Errors/MessageID.cs b/src/Compilers/CSharp/Portable/Errors/MessageID.cs index ed856723a97fc..a9f82674b792c 100644 --- a/src/Compilers/CSharp/Portable/Errors/MessageID.cs +++ b/src/Compilers/CSharp/Portable/Errors/MessageID.cs @@ -192,16 +192,13 @@ internal enum MessageID IDS_FeatureMemberNotNull = MessageBase + 12768, IDS_FeatureNativeInt = MessageBase + 12769, IDS_FeatureTargetTypedObjectCreation = MessageBase + 12770, -<<<<<<< HEAD - IDS_FeatureModuleInitializers = MessageBase + 12771, -======= IDS_FeatureTypePattern = MessageBase + 12771, IDS_FeatureParenthesizedPattern = MessageBase + 12772, IDS_FeatureOrPattern = MessageBase + 12773, IDS_FeatureAndPattern = MessageBase + 12774, IDS_FeatureNotPattern = MessageBase + 12775, IDS_FeatureRelationalPattern = MessageBase + 12776, ->>>>>>> upstream/master + IDS_FeatureModuleInitializers = MessageBase + 12777, } // Message IDs may refer to strings that need to be localized. From 00f92ac78e2eda91e8cdadd4d38e14ff8234c9ca Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Wed, 6 May 2020 17:46:04 -0700 Subject: [PATCH 3/8] Module initializers deterministic ordering (#43964) --- .../Portable/Compilation/CSharpCompilation.cs | 8 +- .../Compilation/LexicalOrderSymbolComparer.cs | 10 +- .../Symbol/Symbols/ModuleInitializersTests.cs | 384 ++++++++++++++++++ .../InternalUtilities/ConcurrentSet.cs | 9 +- 4 files changed, 402 insertions(+), 9 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs index a4af9691431ff..a5fbf6b653c21 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs @@ -2811,7 +2811,10 @@ internal override bool CompileMethods( filterOpt: filterOpt, cancellationToken: cancellationToken); - GenerateModuleInitializer(moduleBeingBuilt, methodBodyDiagnosticBag); + if (!hasDeclarationErrors && !CommonCompiler.HasUnsuppressableErrors(methodBodyDiagnosticBag)) + { + GenerateModuleInitializer(moduleBeingBuilt, methodBodyDiagnosticBag); + } bool hasMethodBodyError = !FilterAndAppendAndFreeDiagnostics(diagnostics, ref methodBodyDiagnosticBag); @@ -2832,8 +2835,7 @@ private void GenerateModuleInitializer(PEModuleBuilder moduleBeingBuilt, Diagnos { var ilBuilder = new ILBuilder(moduleBeingBuilt, new LocalSlotManager(slotAllocator: null), OptimizationLevel.Release, areLocalsZeroed: false); - // PROTOTYPE(module-initializers): require deterministic order - foreach (var method in _moduleInitializerMethods) + foreach (MethodSymbol method in _moduleInitializerMethods.OrderBy(LexicalOrderSymbolComparer.Instance)) { ilBuilder.EmitOpCode(ILOpCode.Call, stackAdjustment: 0); diff --git a/src/Compilers/CSharp/Portable/Compilation/LexicalOrderSymbolComparer.cs b/src/Compilers/CSharp/Portable/Compilation/LexicalOrderSymbolComparer.cs index 72e22335d964d..4039b9ad4c519 100644 --- a/src/Compilers/CSharp/Portable/Compilation/LexicalOrderSymbolComparer.cs +++ b/src/Compilers/CSharp/Portable/Compilation/LexicalOrderSymbolComparer.cs @@ -8,10 +8,11 @@ namespace Microsoft.CodeAnalysis.CSharp { - /// This is an implementation of a special symbol comparer, which is supposed to be used for - /// sorting original definition symbols (explicitly or explicitly declared in source within the same - /// container) in lexical order of their declarations. It will not work on anything that uses non-source locations. - /// + /// + /// This is an implementation of a special symbol comparer, which is supposed to be used for sorting + /// original definition symbols (explicitly or implicitly declared in source within the same compilation) + /// in lexical order of their declarations. It will not work on anything that uses non-source locations. + /// internal class LexicalOrderSymbolComparer : IComparer { public static readonly LexicalOrderSymbolComparer Instance = new LexicalOrderSymbolComparer(); @@ -30,6 +31,7 @@ public int Compare(Symbol x, Symbol y) var xSortKey = x.GetLexicalSortKey(); var ySortKey = y.GetLexicalSortKey(); + Debug.Assert((object)x.DeclaringCompilation == y.DeclaringCompilation); comparison = LexicalSortKey.Compare(xSortKey, ySortKey); if (comparison != 0) diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/ModuleInitializersTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/ModuleInitializersTests.cs index 558aab4d66028..07354d26d4399 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/ModuleInitializersTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/ModuleInitializersTests.cs @@ -7,6 +7,7 @@ using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Test.Utilities; +using Roslyn.Test.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Symbols @@ -137,5 +138,388 @@ namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : S C.M Program.Main"); } + + [Fact] + public void MultipleInitializers_SingleFile() + { + string source = @" +using System; +using System.Runtime.CompilerServices; + +class C1 +{ + [ModuleInitializer] + internal static void M1() => Console.Write(1); + + internal class C2 + { + [ModuleInitializer] + internal static void M2() => Console.Write(2); + } + + [ModuleInitializer] + internal static void M3() => Console.Write(3); +} + +class Program +{ + static void Main() => Console.Write(4); +} + +namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : System.Attribute { } } +"; + + CompileAndVerify( + source, + parseOptions: s_parseOptions, + expectedOutput: "1234"); + } + + [Fact] + public void MultipleInitializers_DifferentContainingTypeKinds() + { + string source = @" +using System; +using System.Runtime.CompilerServices; + +class C1 +{ + [ModuleInitializer] + internal static void M1() => Console.Write(1); +} + +struct S1 +{ + [ModuleInitializer] + internal static void M2() => Console.Write(2); +} + +interface I1 +{ + [ModuleInitializer] + internal static void M3() => Console.Write(3); +} + +class Program +{ + static void Main() => Console.Write(4); +} + +namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : System.Attribute { } } +"; + + CompileAndVerify( + source, + parseOptions: s_parseOptions, + targetFramework: TargetFramework.NetStandardLatest, + expectedOutput: !ExecutionConditionUtil.IsMonoOrCoreClr ? null : "1234", + verify: !ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Skipped : Verification.Passes); + } + + [Fact] + public void MultipleInitializers_MultipleFiles() + { + string source1 = @" +using System; +using System.Runtime.CompilerServices; + +class C1 +{ + [ModuleInitializer] + internal static void M1() => Console.Write(1); + [ModuleInitializer] + internal static void M2() => Console.Write(2); +} + +namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : System.Attribute { } } +"; + string source2 = @" +using System; +using System.Runtime.CompilerServices; + +class C2 +{ + internal class C3 + { + [ModuleInitializer] + internal static void M3() => Console.Write(3); + } + + [ModuleInitializer] + internal static void M4() => Console.Write(4); +} + +class Program +{ + static void Main() => Console.Write(6); +} +"; + + string source3 = @" +using System; +using System.Runtime.CompilerServices; + +class C4 +{ + // shouldn't be called + internal static void M() => Console.Write(0); + + [ModuleInitializer] + internal static void M5() => Console.Write(5); +} +"; + + CompileAndVerify( + new[] { source1, source2, source3 }, + parseOptions: s_parseOptions, + expectedOutput: "123456"); + } + + [Fact] + public void StaticConstructor_Ordering() + { + const string text = @" +using System; +using System.Runtime.CompilerServices; + +class C1 +{ + [ModuleInitializer] + internal static void Init() => Console.Write(1); +} + +class C2 +{ + static C2() => Console.Write(2); + + static void Main() + { + Console.Write(3); + } +} + +namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : System.Attribute { } } +"; + var verifier = CompileAndVerify(text, parseOptions: s_parseOptions, expectedOutput: "123"); + verifier.VerifyDiagnostics(); + } + + [Fact] + public void StaticConstructor_Ordering_SameType() + { + const string text = @" +using System; +using System.Runtime.CompilerServices; + +class C +{ + static C() => Console.Write(1); + + [ModuleInitializer] + internal static void Init() => Console.Write(2); + + static void Main() + { + Console.Write(3); + } +} + +namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : System.Attribute { } } +"; + var verifier = CompileAndVerify(text, parseOptions: s_parseOptions, expectedOutput: "123"); + verifier.VerifyDiagnostics(); + } + + [Fact] + public void StaticConstructor_DefaultInitializer_SameType() + { + const string text = @" +using System; +using System.Runtime.CompilerServices; + +class C +{ + internal static string s1 = null; + + [ModuleInitializer] + internal static void Init() + { + s1 = ""hello""; + } + + static void Main() + { + Console.Write(s1); + } +} + +namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : System.Attribute { } } +"; + var verifier = CompileAndVerify( + text, + parseOptions: s_parseOptions, + options: TestOptions.DebugExe.WithMetadataImportOptions(MetadataImportOptions.All), + expectedOutput: "hello", + symbolValidator: validator); + verifier.VerifyDiagnostics(); + + void validator(ModuleSymbol module) + { + var cType = module.ContainingAssembly.GetTypeByMetadataName("C"); + // static constructor should be optimized out + Assert.Null(cType.GetMember(".cctor")); + + var moduleType = module.ContainingAssembly.GetTypeByMetadataName(""); + Assert.NotNull(moduleType.GetMember(".cctor")); + } + } + + [Fact] + public void StaticConstructor_EffectingInitializer_SameType() + { + const string text = @" +using System; +using System.Runtime.CompilerServices; + +class C +{ + internal static int i = InitField(); + + internal static int InitField() + { + Console.Write(1); + return -1; + } + + [ModuleInitializer] + internal static void Init() + { + i = 2; + } + + static void Main() + { + Console.Write(i); + } +} + +namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : System.Attribute { } } +"; + var verifier = CompileAndVerify( + text, + parseOptions: s_parseOptions, + options: TestOptions.DebugExe.WithMetadataImportOptions(MetadataImportOptions.All), + expectedOutput: "12", + symbolValidator: validator); + verifier.VerifyDiagnostics(); + + void validator(ModuleSymbol module) + { + var cType = module.ContainingAssembly.GetTypeByMetadataName("C"); + Assert.NotNull(cType.GetMember(".cctor")); + + var moduleType = module.ContainingAssembly.GetTypeByMetadataName(""); + Assert.NotNull(moduleType.GetMember(".cctor")); + } + } + + [Fact] + public void StaticConstructor_DefaultInitializer_OtherType() + { + const string text = @" +using System; +using System.Runtime.CompilerServices; + +class C1 +{ + [ModuleInitializer] + internal static void Init() + { + C2.s1 = ""hello""; + } +} + +class C2 +{ + internal static string s1 = null; + + static void Main() + { + Console.Write(s1); + } +} + +namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : System.Attribute { } } +"; + var verifier = CompileAndVerify( + text, + parseOptions: s_parseOptions, + options: TestOptions.DebugExe.WithMetadataImportOptions(MetadataImportOptions.All), + expectedOutput: "hello", + symbolValidator: validator); + verifier.VerifyDiagnostics(); + + void validator(ModuleSymbol module) + { + var c2Type = module.ContainingAssembly.GetTypeByMetadataName("C2"); + // static constructor should be optimized out + Assert.Null(c2Type.GetMember(".cctor")); + + var moduleType = module.ContainingAssembly.GetTypeByMetadataName(""); + Assert.NotNull(moduleType.GetMember(".cctor")); + } + } + + [Fact] + public void StaticConstructor_EffectingInitializer_OtherType() + { + const string text = @" +using System; +using System.Runtime.CompilerServices; + +class C1 +{ + [ModuleInitializer] + internal static void Init() + { + C2.i = 2; + } +} + +class C2 +{ + internal static int i = InitField(); + + static int InitField() + { + Console.Write(1); + return -1; + } + + static void Main() + { + Console.Write(i); + } +} + +namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : System.Attribute { } } +"; + var verifier = CompileAndVerify( + text, + parseOptions: s_parseOptions, + options: TestOptions.DebugExe.WithMetadataImportOptions(MetadataImportOptions.All), + expectedOutput: "12", + symbolValidator: validator); + verifier.VerifyDiagnostics(); + + void validator(ModuleSymbol module) + { + var c2Type = module.ContainingAssembly.GetTypeByMetadataName("C2"); + Assert.NotNull(c2Type.GetMember(".cctor")); + + var moduleType = module.ContainingAssembly.GetTypeByMetadataName(""); + Assert.NotNull(moduleType.GetMember(".cctor")); + } + } } } diff --git a/src/Compilers/Core/Portable/InternalUtilities/ConcurrentSet.cs b/src/Compilers/Core/Portable/InternalUtilities/ConcurrentSet.cs index bf23405bf7252..b8a194ba97a43 100644 --- a/src/Compilers/Core/Portable/InternalUtilities/ConcurrentSet.cs +++ b/src/Compilers/Core/Portable/InternalUtilities/ConcurrentSet.cs @@ -4,7 +4,6 @@ #nullable enable -using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; @@ -178,7 +177,13 @@ void ICollection.Add(T item) public void CopyTo(T[] array, int arrayIndex) { - throw new NotImplementedException(); + // PERF: Do not use dictionary.Keys here because that creates a snapshot + // of the collection resulting in a List allocation. + // Instead, enumerate the set and copy over the elements. + foreach (var element in this) + { + array[arrayIndex++] = element; + } } } } From 8145427c79a12ee4c523633a77d894dacd5959f4 Mon Sep 17 00:00:00 2001 From: Joseph Musser Date: Tue, 12 May 2020 11:18:07 -0400 Subject: [PATCH 4/8] Diagnostics for module initializers feature (#43301) Diagnostics for module initializers feature --- .../CSharp/Portable/CSharpResources.resx | 12 + .../CSharp/Portable/Errors/ErrorCode.cs | 5 + .../SourceMethodSymbolWithAttributes.cs | 44 +- .../Portable/xlf/CSharpResources.cs.xlf | 20 + .../Portable/xlf/CSharpResources.de.xlf | 20 + .../Portable/xlf/CSharpResources.es.xlf | 20 + .../Portable/xlf/CSharpResources.fr.xlf | 20 + .../Portable/xlf/CSharpResources.it.xlf | 20 + .../Portable/xlf/CSharpResources.ja.xlf | 20 + .../Portable/xlf/CSharpResources.ko.xlf | 20 + .../Portable/xlf/CSharpResources.pl.xlf | 20 + .../Portable/xlf/CSharpResources.pt-BR.xlf | 20 + .../Portable/xlf/CSharpResources.ru.xlf | 20 + .../Portable/xlf/CSharpResources.tr.xlf | 20 + .../Portable/xlf/CSharpResources.zh-Hans.xlf | 20 + .../Portable/xlf/CSharpResources.zh-Hant.xlf | 20 + .../ModuleInitializers/AccessibilityTests.cs | 214 ++++++++ .../ModuleInitializers/GenericsTests.cs | 85 +++ .../ModuleInitializers/IgnoredTests.cs | 295 ++++++++++ .../ModuleInitializersTests.cs | 261 ++++++++- .../ModuleInitializers/SignatureTests.cs | 176 ++++++ .../ModuleInitializers/TargetsTests.cs | 513 ++++++++++++++++++ 22 files changed, 1861 insertions(+), 4 deletions(-) create mode 100644 src/Compilers/CSharp/Test/Symbol/Symbols/ModuleInitializers/AccessibilityTests.cs create mode 100644 src/Compilers/CSharp/Test/Symbol/Symbols/ModuleInitializers/GenericsTests.cs create mode 100644 src/Compilers/CSharp/Test/Symbol/Symbols/ModuleInitializers/IgnoredTests.cs rename src/Compilers/CSharp/Test/Symbol/Symbols/{ => ModuleInitializers}/ModuleInitializersTests.cs (66%) create mode 100644 src/Compilers/CSharp/Test/Symbol/Symbols/ModuleInitializers/SignatureTests.cs create mode 100644 src/Compilers/CSharp/Test/Symbol/Symbols/ModuleInitializers/TargetsTests.cs diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 532cbfc2fca9a..d7c61ab882cc6 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -6109,4 +6109,16 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ module initializers + + Module initializer method '{0}' must be accessible at the module level + + + Module initializer method '{0}' must be static, must have no parameters, and must return 'void' + + + Module initializer method '{0}' must not be generic and must not be contained in a generic type + + + A module initializer must be an ordinary member method + diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index 85fad8f748ffd..310b4b5df747a 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -1786,6 +1786,11 @@ internal enum ErrorCode WRN_GivenExpressionAlwaysMatchesPattern = 8793, WRN_IsPatternAlways = 8794, + ERR_ModuleInitializerMethodMustBeOrdinary = 8795, + ERR_ModuleInitializerMethodMustBeAccessibleOutsideTopLevelType = 8796, + ERR_ModuleInitializerMethodMustBeStaticParameterlessVoid = 8797, + ERR_ModuleInitializerMethodAndContainingTypesMustNotBeGeneric = 8798, + #endregion diagnostics introduced for C# 9.0 // Note: you will need to re-generate compiler code after adding warnings (eng\generate-compiler-code.cmd) diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs index f0f9672e4feb3..1eb7ff5106e1f 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs @@ -9,6 +9,7 @@ using System.Linq; using System.Runtime.InteropServices; using System.Threading; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; using Roslyn.Utilities; @@ -485,8 +486,7 @@ private void DecodeWellKnownAttributeAppliedToMethod(ref DecodeWellKnownAttribut else if (attribute.IsTargetAttribute(this, AttributeDescription.ModuleInitializerAttribute)) { MessageID.IDS_FeatureModuleInitializers.CheckFeatureAvailability(arguments.Diagnostics, arguments.AttributeSyntaxOpt); - // PROTOTYPE(module-initializers): diagnostics - DeclaringCompilation.AddModuleInitializerMethod(this); + DecodeModuleInitializerAttribute(arguments); } else { @@ -763,6 +763,46 @@ private void DecodeDllImportAttribute(ref DecodeWellKnownAttributeArguments arguments) + { + Debug.Assert(arguments.AttributeSyntaxOpt is object); + + if (MethodKind != MethodKind.Ordinary) + { + arguments.Diagnostics.Add(ErrorCode.ERR_ModuleInitializerMethodMustBeOrdinary, arguments.AttributeSyntaxOpt.Location); + return; + } + + Debug.Assert(ContainingType is object); + var hasError = false; + + HashSet? useSiteDiagnostics = null; + if (!AccessCheck.IsSymbolAccessible(this, ContainingAssembly, ref useSiteDiagnostics)) + { + arguments.Diagnostics.Add(ErrorCode.ERR_ModuleInitializerMethodMustBeAccessibleOutsideTopLevelType, arguments.AttributeSyntaxOpt.Location, Name); + hasError = true; + } + + arguments.Diagnostics.Add(arguments.AttributeSyntaxOpt, useSiteDiagnostics); + + if (!IsStatic || ParameterCount > 0 || !ReturnsVoid) + { + arguments.Diagnostics.Add(ErrorCode.ERR_ModuleInitializerMethodMustBeStaticParameterlessVoid, arguments.AttributeSyntaxOpt.Location, Name); + hasError = true; + } + + if (IsGenericMethod || ContainingType.IsGenericType) + { + arguments.Diagnostics.Add(ErrorCode.ERR_ModuleInitializerMethodAndContainingTypesMustNotBeGeneric, arguments.AttributeSyntaxOpt.Location, Name); + hasError = true; + } + + if (!hasError && !CallsAreOmitted(arguments.AttributeSyntaxOpt.SyntaxTree)) + { + DeclaringCompilation.AddModuleInitializerMethod(this); + } + } #nullable restore internal sealed override void PostDecodeWellKnownAttributes(ImmutableArray boundAttributes, ImmutableArray allAttributeSyntaxNodes, DiagnosticBag diagnostics, AttributeLocation symbolPart, WellKnownAttributeData decodedData) diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index 090299eae1dc3..0ec02ac0570cf 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -332,6 +332,26 @@ Chybějící vzor + + Module initializer method '{0}' must not be generic and must not be contained in a generic type + Module initializer method '{0}' must not be generic and must not be contained in a generic type + + + + Module initializer method '{0}' must be accessible at the module level + Module initializer method '{0}' must be accessible at the module level + + + + A module initializer must be an ordinary member method + A module initializer must be an ordinary member method + + + + Module initializer method '{0}' must be static, must have no parameters, and must return 'void' + Module initializer method '{0}' must be static, must have no parameters, and must return 'void' + + Multiple analyzer config files cannot be in the same directory ('{0}'). Ve stejném adresáři nemůže být více konfiguračních souborů analyzátoru ({0}). diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index 2df11724d7dfd..36d6d9e6765ed 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -332,6 +332,26 @@ Muster fehlt. + + Module initializer method '{0}' must not be generic and must not be contained in a generic type + Module initializer method '{0}' must not be generic and must not be contained in a generic type + + + + Module initializer method '{0}' must be accessible at the module level + Module initializer method '{0}' must be accessible at the module level + + + + A module initializer must be an ordinary member method + A module initializer must be an ordinary member method + + + + Module initializer method '{0}' must be static, must have no parameters, and must return 'void' + Module initializer method '{0}' must be static, must have no parameters, and must return 'void' + + Multiple analyzer config files cannot be in the same directory ('{0}'). Dasselbe Verzeichnis ({0}) darf nicht mehrere Konfigurationsdateien des Analysetools enthalten. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index 0fa212f629364..615595a2771cb 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -332,6 +332,26 @@ Falta un patrón. + + Module initializer method '{0}' must not be generic and must not be contained in a generic type + Module initializer method '{0}' must not be generic and must not be contained in a generic type + + + + Module initializer method '{0}' must be accessible at the module level + Module initializer method '{0}' must be accessible at the module level + + + + A module initializer must be an ordinary member method + A module initializer must be an ordinary member method + + + + Module initializer method '{0}' must be static, must have no parameters, and must return 'void' + Module initializer method '{0}' must be static, must have no parameters, and must return 'void' + + Multiple analyzer config files cannot be in the same directory ('{0}'). No es posible que un mismo directorio ("{0}") contenga varios archivos de configuración del analizador. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index 13d8b76a8b9b7..9ce2a3fae747a 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -332,6 +332,26 @@ Modèle manquant + + Module initializer method '{0}' must not be generic and must not be contained in a generic type + Module initializer method '{0}' must not be generic and must not be contained in a generic type + + + + Module initializer method '{0}' must be accessible at the module level + Module initializer method '{0}' must be accessible at the module level + + + + A module initializer must be an ordinary member method + A module initializer must be an ordinary member method + + + + Module initializer method '{0}' must be static, must have no parameters, and must return 'void' + Module initializer method '{0}' must be static, must have no parameters, and must return 'void' + + Multiple analyzer config files cannot be in the same directory ('{0}'). Plusieurs fichiers config d'analyseur ne peuvent pas figurer dans le même répertoire ('{0}'). diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index 787e663a08f07..8b0421c394d32 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -332,6 +332,26 @@ Criterio mancante + + Module initializer method '{0}' must not be generic and must not be contained in a generic type + Module initializer method '{0}' must not be generic and must not be contained in a generic type + + + + Module initializer method '{0}' must be accessible at the module level + Module initializer method '{0}' must be accessible at the module level + + + + A module initializer must be an ordinary member method + A module initializer must be an ordinary member method + + + + Module initializer method '{0}' must be static, must have no parameters, and must return 'void' + Module initializer method '{0}' must be static, must have no parameters, and must return 'void' + + Multiple analyzer config files cannot be in the same directory ('{0}'). La stessa directory ('{0}') non può contenere più file di configurazione dell'analizzatore. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index da1927353dd22..ba0ebc76b4f1f 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -332,6 +332,26 @@ パターンがありません + + Module initializer method '{0}' must not be generic and must not be contained in a generic type + Module initializer method '{0}' must not be generic and must not be contained in a generic type + + + + Module initializer method '{0}' must be accessible at the module level + Module initializer method '{0}' must be accessible at the module level + + + + A module initializer must be an ordinary member method + A module initializer must be an ordinary member method + + + + Module initializer method '{0}' must be static, must have no parameters, and must return 'void' + Module initializer method '{0}' must be static, must have no parameters, and must return 'void' + + Multiple analyzer config files cannot be in the same directory ('{0}'). 複数のアナライザー構成ファイルを同じディレクトリに入れることはできません ('{0}')。 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index edbafc48f39e8..2c83d33ad0967 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -332,6 +332,26 @@ 패턴이 없습니다. + + Module initializer method '{0}' must not be generic and must not be contained in a generic type + Module initializer method '{0}' must not be generic and must not be contained in a generic type + + + + Module initializer method '{0}' must be accessible at the module level + Module initializer method '{0}' must be accessible at the module level + + + + A module initializer must be an ordinary member method + A module initializer must be an ordinary member method + + + + Module initializer method '{0}' must be static, must have no parameters, and must return 'void' + Module initializer method '{0}' must be static, must have no parameters, and must return 'void' + + Multiple analyzer config files cannot be in the same directory ('{0}'). 분석기 구성 파일 여러 개가 동일한 디렉터리('{0}')에 있을 수 없습니다. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index fb63fea6048f3..c05242676c0b0 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -332,6 +332,26 @@ Brak wzorca + + Module initializer method '{0}' must not be generic and must not be contained in a generic type + Module initializer method '{0}' must not be generic and must not be contained in a generic type + + + + Module initializer method '{0}' must be accessible at the module level + Module initializer method '{0}' must be accessible at the module level + + + + A module initializer must be an ordinary member method + A module initializer must be an ordinary member method + + + + Module initializer method '{0}' must be static, must have no parameters, and must return 'void' + Module initializer method '{0}' must be static, must have no parameters, and must return 'void' + + Multiple analyzer config files cannot be in the same directory ('{0}'). Wiele plików konfiguracji analizatora nie może znajdować się w tym samym katalogu („{0}”). diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index dda54405a92e7..62d13d071bab7 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -332,6 +332,26 @@ Padrão ausente + + Module initializer method '{0}' must not be generic and must not be contained in a generic type + Module initializer method '{0}' must not be generic and must not be contained in a generic type + + + + Module initializer method '{0}' must be accessible at the module level + Module initializer method '{0}' must be accessible at the module level + + + + A module initializer must be an ordinary member method + A module initializer must be an ordinary member method + + + + Module initializer method '{0}' must be static, must have no parameters, and must return 'void' + Module initializer method '{0}' must be static, must have no parameters, and must return 'void' + + Multiple analyzer config files cannot be in the same directory ('{0}'). Não é possível que haja vários arquivos de configuração do analisador no mesmo diretório ('{0}'). diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index 629b748803284..0d95c67bfc8b3 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -332,6 +332,26 @@ Отсутствует шаблон + + Module initializer method '{0}' must not be generic and must not be contained in a generic type + Module initializer method '{0}' must not be generic and must not be contained in a generic type + + + + Module initializer method '{0}' must be accessible at the module level + Module initializer method '{0}' must be accessible at the module level + + + + A module initializer must be an ordinary member method + A module initializer must be an ordinary member method + + + + Module initializer method '{0}' must be static, must have no parameters, and must return 'void' + Module initializer method '{0}' must be static, must have no parameters, and must return 'void' + + Multiple analyzer config files cannot be in the same directory ('{0}'). В одном каталоге ("{0}") не может находиться несколько файлов конфигурации анализатора. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index b85a6464896e0..379fdcc864b72 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -332,6 +332,26 @@ Desen eksik + + Module initializer method '{0}' must not be generic and must not be contained in a generic type + Module initializer method '{0}' must not be generic and must not be contained in a generic type + + + + Module initializer method '{0}' must be accessible at the module level + Module initializer method '{0}' must be accessible at the module level + + + + A module initializer must be an ordinary member method + A module initializer must be an ordinary member method + + + + Module initializer method '{0}' must be static, must have no parameters, and must return 'void' + Module initializer method '{0}' must be static, must have no parameters, and must return 'void' + + Multiple analyzer config files cannot be in the same directory ('{0}'). Birden çok çözümleyici yapılandırma dosyası aynı dizinde ('{0}') olamaz. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index 074663735c4ac..c2d43e86da3e2 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -332,6 +332,26 @@ 模式缺失 + + Module initializer method '{0}' must not be generic and must not be contained in a generic type + Module initializer method '{0}' must not be generic and must not be contained in a generic type + + + + Module initializer method '{0}' must be accessible at the module level + Module initializer method '{0}' must be accessible at the module level + + + + A module initializer must be an ordinary member method + A module initializer must be an ordinary member method + + + + Module initializer method '{0}' must be static, must have no parameters, and must return 'void' + Module initializer method '{0}' must be static, must have no parameters, and must return 'void' + + Multiple analyzer config files cannot be in the same directory ('{0}'). 多个分析器配置文件不能位于同一目录({0})中。 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index 68d35da33da98..ba05f881d5a1d 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -332,6 +332,26 @@ 缺少模式 + + Module initializer method '{0}' must not be generic and must not be contained in a generic type + Module initializer method '{0}' must not be generic and must not be contained in a generic type + + + + Module initializer method '{0}' must be accessible at the module level + Module initializer method '{0}' must be accessible at the module level + + + + A module initializer must be an ordinary member method + A module initializer must be an ordinary member method + + + + Module initializer method '{0}' must be static, must have no parameters, and must return 'void' + Module initializer method '{0}' must be static, must have no parameters, and must return 'void' + + Multiple analyzer config files cannot be in the same directory ('{0}'). 多個分析器組態檔無法處於相同目錄 ('{0}') 中。 diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/ModuleInitializers/AccessibilityTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/ModuleInitializers/AccessibilityTests.cs new file mode 100644 index 0000000000000..10e75f994b21d --- /dev/null +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/ModuleInitializers/AccessibilityTests.cs @@ -0,0 +1,214 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis.CSharp.Test.Utilities; +using Microsoft.CodeAnalysis.Test.Utilities; +using Roslyn.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Symbols.ModuleInitializers +{ + [CompilerTrait(CompilerFeature.ModuleInitializers)] + public sealed class AccessibilityTests : CSharpTestBase + { + private static readonly CSharpParseOptions s_parseOptions = TestOptions.RegularPreview; + + [Theory] + [InlineData("private")] + [InlineData("protected")] + [InlineData("private protected")] + public void DisallowedMethodAccessibility(string keywords) + { + string source = @" +using System.Runtime.CompilerServices; + +class C +{ + [ModuleInitializer] + " + keywords + @" static void M() { } +} + +namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : System.Attribute { } } +"; + var compilation = CreateCompilation(source, parseOptions: s_parseOptions); + compilation.VerifyEmitDiagnostics( + // (6,6): error CS8796: Module initializer method 'M' must be accessible at the module level + // [ModuleInitializer] + Diagnostic(ErrorCode.ERR_ModuleInitializerMethodMustBeAccessibleOutsideTopLevelType, "ModuleInitializer").WithArguments("M").WithLocation(6, 6) + ); + } + + [Theory] + [InlineData("public")] + [InlineData("internal")] + [InlineData("protected internal")] + public void AllowedMethodAccessibility(string keywords) + { + string source = @" +using System; +using System.Runtime.CompilerServices; + +class C +{ + [ModuleInitializer] + " + keywords + @" static void M() => Console.WriteLine(""C.M""); +} + +class Program +{ + static void Main() => Console.WriteLine(""Program.Main""); +} + +namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : System.Attribute { } } +"; + CompileAndVerify(source, parseOptions: s_parseOptions, expectedOutput: @" +C.M +Program.Main"); + } + + [Theory] + [InlineData("public")] + [InlineData("internal")] + public void AllowedTopLevelTypeAccessibility(string keywords) + { + string source = @" +using System; +using System.Runtime.CompilerServices; + +" + keywords + @" class C +{ + [ModuleInitializer] + public static void M() => Console.WriteLine(""C.M""); +} + +class Program +{ + static void Main() => Console.WriteLine(""Program.Main""); +} + +namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : System.Attribute { } } +"; + CompileAndVerify(source, parseOptions: s_parseOptions, expectedOutput: @" +C.M +Program.Main"); + } + + [Theory] + [InlineData("private")] + [InlineData("protected")] + [InlineData("private protected")] + public void DisallowedNestedTypeAccessibility(string keywords) + { + string source = @" +using System.Runtime.CompilerServices; + +public class C +{ + " + keywords + @" class Nested + { + [ModuleInitializer] + public static void M() { } + } +} + +namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : System.Attribute { } } +"; + var compilation = CreateCompilation(source, parseOptions: s_parseOptions); + compilation.VerifyEmitDiagnostics( + // (8,10): error CS8796: Module initializer method 'M' must be accessible at the module level + // [ModuleInitializer] + Diagnostic(ErrorCode.ERR_ModuleInitializerMethodMustBeAccessibleOutsideTopLevelType, "ModuleInitializer").WithArguments("M").WithLocation(8, 10) + ); + } + + [Theory] + [InlineData("public")] + [InlineData("internal")] + [InlineData("protected internal")] + public void AllowedNestedTypeAccessibility(string keywords) + { + string source = @" +using System; +using System.Runtime.CompilerServices; + +public class C +{ + " + keywords + @" class Nested + { + [ModuleInitializer] + public static void M() => Console.WriteLine(""C.M""); + } +} + +class Program +{ + static void Main() => Console.WriteLine(""Program.Main""); +} + +namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : System.Attribute { } } +"; + CompileAndVerify(source, parseOptions: s_parseOptions, expectedOutput: @" +C.M +Program.Main"); + } + + [Fact] + public void ImplicitPublicInterfaceMethodAccessibility() + { + string source = @" +using System; +using System.Runtime.CompilerServices; + +interface I +{ + [ModuleInitializer] + static void M() => Console.WriteLine(""I.M""); +} + +class Program +{ + static void Main() => Console.WriteLine(""Program.Main""); +} + +namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : System.Attribute { } } +"; + CompileAndVerify( + source, + parseOptions: s_parseOptions, + targetFramework: TargetFramework.NetStandardLatest, + expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @" +I.M +Program.Main" : null, + verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped); + } + + [Fact] + public void ImplicitPublicInterfaceNestedTypeAccessibility() + { + string source = @" +using System; +using System.Runtime.CompilerServices; + +interface I +{ + class Nested + { + [ModuleInitializer] + internal static void M() => Console.WriteLine(""C.M""); + } +} + +class Program +{ + static void Main() => Console.WriteLine(""Program.Main""); +} + +namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : System.Attribute { } } +"; + CompileAndVerify(source, parseOptions: s_parseOptions, expectedOutput: @" +C.M +Program.Main"); + } + } +} diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/ModuleInitializers/GenericsTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/ModuleInitializers/GenericsTests.cs new file mode 100644 index 0000000000000..0e607580838cc --- /dev/null +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/ModuleInitializers/GenericsTests.cs @@ -0,0 +1,85 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis.CSharp.Test.Utilities; +using Microsoft.CodeAnalysis.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Symbols.ModuleInitializers +{ + [CompilerTrait(CompilerFeature.ModuleInitializers)] + public sealed class GenericsTests : CSharpTestBase + { + private static readonly CSharpParseOptions s_parseOptions = TestOptions.RegularPreview; + + [Fact] + public void MustNotBeGenericMethod() + { + string source = @" +using System.Runtime.CompilerServices; + +class C +{ + [ModuleInitializer] + internal static void M() { } +} + +namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : System.Attribute { } } +"; + var compilation = CreateCompilation(source, parseOptions: s_parseOptions); + compilation.VerifyEmitDiagnostics( + // (6,6): error CS8798: Module initializer method 'M' must not be generic and must not be contained in a generic type + // [ModuleInitializer] + Diagnostic(ErrorCode.ERR_ModuleInitializerMethodAndContainingTypesMustNotBeGeneric, "ModuleInitializer").WithArguments("M").WithLocation(6, 6) + ); + } + + [Fact] + public void MustNotBeContainedInGenericType() + { + string source = @" +using System.Runtime.CompilerServices; + +class C +{ + [ModuleInitializer] + internal static void M() { } +} + +namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : System.Attribute { } } +"; + var compilation = CreateCompilation(source, parseOptions: s_parseOptions); + compilation.VerifyEmitDiagnostics( + // (6,6): error CS8798: Module initializer method 'M' must not be generic and must not be contained in a generic type + // [ModuleInitializer] + Diagnostic(ErrorCode.ERR_ModuleInitializerMethodAndContainingTypesMustNotBeGeneric, "ModuleInitializer").WithArguments("M").WithLocation(6, 6) + ); + } + + [Fact] + public void MustNotBeContainedInGenericTypeWithParametersDeclaredByContainingGenericType() + { + string source = @" +using System.Runtime.CompilerServices; + +class C +{ + internal class Nested + { + [ModuleInitializer] + internal static void M() { } + } +} + +namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : System.Attribute { } } +"; + var compilation = CreateCompilation(source, parseOptions: s_parseOptions); + compilation.VerifyEmitDiagnostics( + // (8,10): error CS8798: Module initializer method 'M' must not be generic and must not be contained in a generic type + // [ModuleInitializer] + Diagnostic(ErrorCode.ERR_ModuleInitializerMethodAndContainingTypesMustNotBeGeneric, "ModuleInitializer").WithArguments("M").WithLocation(8, 10) + ); + } + } +} diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/ModuleInitializers/IgnoredTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/ModuleInitializers/IgnoredTests.cs new file mode 100644 index 0000000000000..a2b6c65e5438e --- /dev/null +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/ModuleInitializers/IgnoredTests.cs @@ -0,0 +1,295 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis.CSharp.Symbols; +using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE; +using Microsoft.CodeAnalysis.CSharp.Test.Utilities; +using Microsoft.CodeAnalysis.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Symbols.ModuleInitializers +{ + [CompilerTrait(CompilerFeature.ModuleInitializers)] + public sealed class IgnoredTests : CSharpTestBase + { + [Fact] + public void IgnoredOnReturnValue() + { + string source = @" +using System.Runtime.CompilerServices; + +class C +{ + [return: ModuleInitializer] + internal static void M() + { + } +} + +namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : System.Attribute { } } +"; + CompileAndVerify( + source, + options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All), + symbolValidator: module => + { + Assert.Equal(MetadataImportOptions.All, ((PEModuleSymbol)module).ImportOptions); + var rootModuleType = (TypeSymbol)module.GlobalNamespace.GetMember(""); + Assert.Null(rootModuleType.GetMember(".cctor")); + }); + } + + [Fact] + public void IgnoredOnMethodParameter() + { + string source = @" +using System.Runtime.CompilerServices; + +class C +{ + internal static void M([ModuleInitializer] int p) + { + } +} + +namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : System.Attribute { } } +"; + CompileAndVerify( + source, + options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All), + symbolValidator: module => + { + Assert.Equal(MetadataImportOptions.All, ((PEModuleSymbol)module).ImportOptions); + var rootModuleType = (TypeSymbol)module.GlobalNamespace.GetMember(""); + Assert.Null(rootModuleType.GetMember(".cctor")); + }); + } + + [Fact] + public void IgnoredOnGenericParameter() + { + string source = @" +using System.Runtime.CompilerServices; + +class C +{ + internal static void M<[ModuleInitializer] T>() + { + } +} + +namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : System.Attribute { } } +"; + CompileAndVerify( + source, + options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All), + symbolValidator: module => + { + Assert.Equal(MetadataImportOptions.All, ((PEModuleSymbol)module).ImportOptions); + var rootModuleType = (TypeSymbol)module.GlobalNamespace.GetMember(""); + Assert.Null(rootModuleType.GetMember(".cctor")); + }); + } + + [Fact] + public void IgnoredOnClass() + { + string source = @" +using System.Runtime.CompilerServices; + +[ModuleInitializer] +class C +{ + internal static void M() { } +} + +namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : System.Attribute { } } +"; + CompileAndVerify( + source, + options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All), + symbolValidator: module => + { + Assert.Equal(MetadataImportOptions.All, ((PEModuleSymbol)module).ImportOptions); + var rootModuleType = (TypeSymbol)module.GlobalNamespace.GetMember(""); + Assert.Null(rootModuleType.GetMember(".cctor")); + }); + } + + [Fact] + public void IgnoredOnEvent() + { + string source = @" +using System.Runtime.CompilerServices; + +class C +{ + [ModuleInitializer] + public event System.Action E; +} + +namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : System.Attribute { } } +"; + CompileAndVerify( + source, + options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All), + symbolValidator: module => + { + Assert.Equal(MetadataImportOptions.All, ((PEModuleSymbol)module).ImportOptions); + var rootModuleType = (TypeSymbol)module.GlobalNamespace.GetMember(""); + Assert.Null(rootModuleType.GetMember(".cctor")); + }); + } + + [Fact] + public void IgnoredOnProperty() + { + string source = @" +using System.Runtime.CompilerServices; + +class C +{ + [ModuleInitializer] + public int P { get; set; } +} + +namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : System.Attribute { } } +"; + CompileAndVerify( + source, + options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All), + symbolValidator: module => + { + Assert.Equal(MetadataImportOptions.All, ((PEModuleSymbol)module).ImportOptions); + var rootModuleType = (TypeSymbol)module.GlobalNamespace.GetMember(""); + Assert.Null(rootModuleType.GetMember(".cctor")); + }); + } + + [Fact] + public void IgnoredOnIndexer() + { + string source = @" +using System.Runtime.CompilerServices; + +class C +{ + [ModuleInitializer] + public int this[int p] => p; +} + +namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : System.Attribute { } } +"; + CompileAndVerify( + source, + options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All), + symbolValidator: module => + { + Assert.Equal(MetadataImportOptions.All, ((PEModuleSymbol)module).ImportOptions); + var rootModuleType = (TypeSymbol)module.GlobalNamespace.GetMember(""); + Assert.Null(rootModuleType.GetMember(".cctor")); + }); + } + + [Fact] + public void IgnoredOnField() + { + string source = @" +using System.Runtime.CompilerServices; + +class C +{ + [ModuleInitializer] + public int F; +} + +namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : System.Attribute { } } +"; + CompileAndVerify( + source, + options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All), + symbolValidator: module => + { + Assert.Equal(MetadataImportOptions.All, ((PEModuleSymbol)module).ImportOptions); + var rootModuleType = (TypeSymbol)module.GlobalNamespace.GetMember(""); + Assert.Null(rootModuleType.GetMember(".cctor")); + }); + } + + [Fact] + public void IgnoredOnModule() + { + string source = @" +using System.Runtime.CompilerServices; + +[module: ModuleInitializer] + +namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : System.Attribute { } } +"; + CompileAndVerify( + source, + options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All), + symbolValidator: module => + { + Assert.Equal(MetadataImportOptions.All, ((PEModuleSymbol)module).ImportOptions); + var rootModuleType = (TypeSymbol)module.GlobalNamespace.GetMember(""); + Assert.Null(rootModuleType.GetMember(".cctor")); + }); + } + + [Fact] + public void IgnoredOnAssembly() + { + string source = @" +using System.Runtime.CompilerServices; + +[assembly: ModuleInitializer] + +namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : System.Attribute { } } +"; + CompileAndVerify( + source, + options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All), + symbolValidator: module => + { + Assert.Equal(MetadataImportOptions.All, ((PEModuleSymbol)module).ImportOptions); + var rootModuleType = (TypeSymbol)module.GlobalNamespace.GetMember(""); + Assert.Null(rootModuleType.GetMember(".cctor")); + }); + } + + [Fact] + public void IgnoredWhenConstructorArgumentIsSpecified() + { + string source = @" +using System.Runtime.CompilerServices; + +class C +{ + [ModuleInitializer(42)] + internal static void M() + { + } +} + +namespace System.Runtime.CompilerServices +{ + class ModuleInitializerAttribute : System.Attribute + { + public ModuleInitializerAttribute(int p) { } + } +} +"; + CompileAndVerify( + source, + options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All), + symbolValidator: module => + { + Assert.Equal(MetadataImportOptions.All, ((PEModuleSymbol)module).ImportOptions); + var rootModuleType = (TypeSymbol)module.GlobalNamespace.GetMember(""); + Assert.Null(rootModuleType.GetMember(".cctor")); + }); + } + } +} diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/ModuleInitializersTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/ModuleInitializers/ModuleInitializersTests.cs similarity index 66% rename from src/Compilers/CSharp/Test/Symbol/Symbols/ModuleInitializersTests.cs rename to src/Compilers/CSharp/Test/Symbol/Symbols/ModuleInitializers/ModuleInitializersTests.cs index 07354d26d4399..f4080a50ed14a 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/ModuleInitializersTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/ModuleInitializers/ModuleInitializersTests.cs @@ -10,7 +10,7 @@ using Roslyn.Test.Utilities; using Xunit; -namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Symbols +namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Symbols.ModuleInitializers { [CompilerTrait(CompilerFeature.ModuleInitializers)] public sealed class ModuleInitializersTests : CSharpTestBase @@ -82,9 +82,10 @@ namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : S CompileAndVerify( source, parseOptions: s_parseOptions, - options: new CSharpCompilationOptions(OutputKind.ConsoleApplication, metadataImportOptions: MetadataImportOptions.All), + options: TestOptions.DebugExe.WithMetadataImportOptions(MetadataImportOptions.All), symbolValidator: module => { + Assert.Equal(MetadataImportOptions.All, ((PEModuleSymbol)module).ImportOptions); var rootModuleType = (TypeSymbol)module.GlobalNamespace.GetMember(""); Assert.Null(rootModuleType.GetMember(".cctor")); }, @@ -119,6 +120,7 @@ namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : S options: TestOptions.DebugExe.WithMetadataImportOptions(MetadataImportOptions.All), symbolValidator: module => { + Assert.Equal(MetadataImportOptions.All, ((PEModuleSymbol)module).ImportOptions); var rootModuleType = (TypeSymbol)module.GlobalNamespace.GetMember(""); var staticConstructor = (PEMethodSymbol)rootModuleType.GetMember(".cctor"); @@ -139,6 +141,140 @@ namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : S Program.Main"); } + [Fact] + public void SingleCallIsGeneratedWhenMethodIsMarkedTwice() + { + string source = @" +using System; +using System.Runtime.CompilerServices; + +class C +{ + [ModuleInitializer, ModuleInitializer] + internal static void M() => Console.WriteLine(""C.M""); +} + +class Program +{ + static void Main() { } +} + +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + class ModuleInitializerAttribute : System.Attribute { } +} +"; + CompileAndVerify(source, parseOptions: s_parseOptions, expectedOutput: "C.M"); + } + + [Fact] + public void AttributeCanBeAppliedWithinItsOwnDefinition() + { + string source = @" +using System; + +class Program +{ + static void Main() => Console.WriteLine(""Program.Main""); +} + +namespace System.Runtime.CompilerServices +{ + class ModuleInitializerAttribute : System.Attribute + { + [ModuleInitializer] + internal static void M() => Console.WriteLine(""ModuleInitializerAttribute.M""); + } +} +"; + CompileAndVerify(source, parseOptions: s_parseOptions, expectedOutput: @" +ModuleInitializerAttribute.M +Program.Main"); + } + + [Fact] + public void ExternMethodCanBeModuleInitializer() + { + string source = @" +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +class C +{ + [ModuleInitializer, DllImport(""dllName"")] + internal static extern void M(); +} + +namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : System.Attribute { } } +"; + CompileAndVerify( + source, + parseOptions: s_parseOptions, + options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All), + symbolValidator: module => + { + Assert.Equal(MetadataImportOptions.All, ((PEModuleSymbol)module).ImportOptions); + var rootModuleType = module.ContainingAssembly.GetTypeByMetadataName(""); + Assert.NotNull(rootModuleType.GetMember(".cctor")); + }); + } + + [Fact] + public void MayBeDeclaredByStruct() + { + string source = @" +using System; +using System.Runtime.CompilerServices; + +struct S +{ + [ModuleInitializer] + internal static void M() => Console.WriteLine(""S.M""); +} + +class Program +{ + static void Main() => Console.WriteLine(""Program.Main""); +} + +namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : System.Attribute { } } +"; + CompileAndVerify(source, parseOptions: s_parseOptions, expectedOutput: @" +S.M +Program.Main"); + } + + [Fact] + public void MayBeDeclaredByInterface() + { + string source = @" +using System; +using System.Runtime.CompilerServices; + +interface I +{ + [ModuleInitializer] + internal static void M() => Console.WriteLine(""I.M""); +} + +class Program +{ + static void Main() => Console.WriteLine(""Program.Main""); +} + +namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : System.Attribute { } } +"; + CompileAndVerify( + source, + parseOptions: s_parseOptions, + targetFramework: TargetFramework.NetStandardLatest, + expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @" +I.M +Program.Main" : null, + verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped); + } + [Fact] public void MultipleInitializers_SingleFile() { @@ -521,5 +657,126 @@ void validator(ModuleSymbol module) Assert.NotNull(moduleType.GetMember(".cctor")); } } + + [Fact] + public void ModuleInitializerAttributeIncludedByConditionalAttribute() + { + string source = @" +#define INCLUDE + +using System; +using System.Runtime.CompilerServices; + +class C +{ + [ModuleInitializer] + internal static void M() => Console.WriteLine(""C.M""); +} + +class Program +{ + static void Main() => Console.WriteLine(""Program.Main""); +} + +namespace System.Runtime.CompilerServices +{ + [System.Diagnostics.Conditional(""INCLUDE"")] + class ModuleInitializerAttribute : System.Attribute { } +} +"; + CompileAndVerify(source, parseOptions: s_parseOptions, expectedOutput: @" +C.M +Program.Main"); + } + + [Fact] + public void ModuleInitializerAttributeExcludedByConditionalAttribute() + { + string source = @" +using System; +using System.Runtime.CompilerServices; + +class C +{ + [ModuleInitializer] + internal static void M() => Console.WriteLine(""C.M""); +} + +class Program +{ + static void Main() => Console.WriteLine(""Program.Main""); +} + +namespace System.Runtime.CompilerServices +{ + [System.Diagnostics.Conditional(""EXCLUDE"")] + class ModuleInitializerAttribute : System.Attribute { } +} +"; + CompileAndVerify(source, parseOptions: s_parseOptions, expectedOutput: @" +C.M +Program.Main"); + } + + [Fact] + public void ModuleInitializerMethodIncludedByConditionalAttribute() + { + string source = @" +#define INCLUDE + +using System; +using System.Runtime.CompilerServices; + +class C +{ + [System.Diagnostics.Conditional(""INCLUDE""), ModuleInitializer] + internal static void Preceding() => Console.WriteLine(""C.Preceding""); + + [ModuleInitializer, System.Diagnostics.Conditional(""INCLUDE"")] + internal static void Following() => Console.WriteLine(""C.Following""); +} + +class Program +{ + static void Main() => Console.WriteLine(""Program.Main""); +} + +namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : System.Attribute { } } +"; + CompileAndVerify(source, parseOptions: s_parseOptions, expectedOutput: @" +C.Preceding +C.Following +Program.Main"); + } + + [Fact] + public void ModuleInitializerMethodExcludedByConditionalAttribute() + { + string source = @" +using System; +using System.Runtime.CompilerServices; + +class C +{ + [System.Diagnostics.Conditional(""EXCLUDE""), ModuleInitializer] + internal static void Preceding() { } + + [ModuleInitializer, System.Diagnostics.Conditional(""EXCLUDE"")] + internal static void Following() { } +} + +namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : System.Attribute { } } +"; + CompileAndVerify( + source, + parseOptions: s_parseOptions, + options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All), + symbolValidator: module => + { + Assert.Equal(MetadataImportOptions.All, ((PEModuleSymbol)module).ImportOptions); + var rootModuleType = module.ContainingAssembly.GetTypeByMetadataName(""); + Assert.Null(rootModuleType.GetMember(".cctor")); + }); + } } } diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/ModuleInitializers/SignatureTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/ModuleInitializers/SignatureTests.cs new file mode 100644 index 0000000000000..55e9e253f4a10 --- /dev/null +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/ModuleInitializers/SignatureTests.cs @@ -0,0 +1,176 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis.CSharp.Test.Utilities; +using Microsoft.CodeAnalysis.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Symbols.ModuleInitializers +{ + [CompilerTrait(CompilerFeature.ModuleInitializers)] + public sealed class SignatureTests : CSharpTestBase + { + private static readonly CSharpParseOptions s_parseOptions = TestOptions.RegularPreview; + + [Fact] + public void MustNotBeInstanceMethod() + { + string source = @" +using System.Runtime.CompilerServices; + +class C +{ + [ModuleInitializer] + internal void M() { } +} + +namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : System.Attribute { } } +"; + var compilation = CreateCompilation(source, parseOptions: s_parseOptions); + compilation.VerifyEmitDiagnostics( + // (6,6): error CS8797: must be static, must have no parameters, and must return 'void' + // [ModuleInitializer] + Diagnostic(ErrorCode.ERR_ModuleInitializerMethodMustBeStaticParameterlessVoid, "ModuleInitializer").WithArguments("M").WithLocation(6, 6) + ); + } + + [Fact] + public void MustNotHaveParameters() + { + string source = @" +using System.Runtime.CompilerServices; + +static class C +{ + [ModuleInitializer] + internal static void M(object p) { } +} + +namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : System.Attribute { } } +"; + var compilation = CreateCompilation(source, parseOptions: s_parseOptions); + compilation.VerifyEmitDiagnostics( + // (6,6): error CS8797: must be static, must have no parameters, and must return 'void' + // [ModuleInitializer] + Diagnostic(ErrorCode.ERR_ModuleInitializerMethodMustBeStaticParameterlessVoid, "ModuleInitializer").WithArguments("M").WithLocation(6, 6) + ); + } + + [Fact] + public void MustNotHaveOptionalParameters() + { + string source = @" +using System.Runtime.CompilerServices; + +static class C +{ + [ModuleInitializer] + internal static void M(object p = null) { } +} + +namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : System.Attribute { } } +"; + var compilation = CreateCompilation(source, parseOptions: s_parseOptions); + compilation.VerifyEmitDiagnostics( + // (6,6): error CS8797: must be static, must have no parameters, and must return 'void' + // [ModuleInitializer] + Diagnostic(ErrorCode.ERR_ModuleInitializerMethodMustBeStaticParameterlessVoid, "ModuleInitializer").WithArguments("M").WithLocation(6, 6) + ); + } + + [Fact] + public void MustNotHaveParamsArrayParameters() + { + string source = @" +using System.Runtime.CompilerServices; + +static class C +{ + [ModuleInitializer] + internal static void M(params object[] p) { } +} + +namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : System.Attribute { } } +"; + var compilation = CreateCompilation(source, parseOptions: s_parseOptions); + compilation.VerifyEmitDiagnostics( + // (6,6): error CS8797: must be static, must have no parameters, and must return 'void' + // [ModuleInitializer] + Diagnostic(ErrorCode.ERR_ModuleInitializerMethodMustBeStaticParameterlessVoid, "ModuleInitializer").WithArguments("M").WithLocation(6, 6) + ); + } + + [Fact] + public void MustNotReturnAValue() + { + string source = @" +using System.Runtime.CompilerServices; + +static class C +{ + [ModuleInitializer] + internal static object M() => null; +} + +namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : System.Attribute { } } +"; + var compilation = CreateCompilation(source, parseOptions: s_parseOptions); + compilation.VerifyEmitDiagnostics( + // (6,6): error CS8797: must be static, must have no parameters, and must return 'void' + // [ModuleInitializer] + Diagnostic(ErrorCode.ERR_ModuleInitializerMethodMustBeStaticParameterlessVoid, "ModuleInitializer").WithArguments("M").WithLocation(6, 6) + ); + } + + [Fact] + public void MayBeAsyncVoid() + { + string source = @" +using System; +using System.Runtime.CompilerServices; + +static class C +{ + [ModuleInitializer] + internal static async void M() => Console.WriteLine(""C.M""); +} + +class Program +{ + static void Main() => Console.WriteLine(""Program.Main""); +} + +namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : System.Attribute { } } +"; + CompileAndVerify(source, parseOptions: s_parseOptions, expectedOutput: @" +C.M +Program.Main"); + } + + [Fact] + public void MayNotReturnAwaitableWithVoidResult() + { + string source = @" +using System.Runtime.CompilerServices; +using System.Threading.Tasks; + +static class C +{ + [ModuleInitializer] + internal static async Task M() { } +} + +namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : System.Attribute { } } +"; + var compilation = CreateCompilation(source, parseOptions: s_parseOptions); + compilation.VerifyEmitDiagnostics( + // (7,6): error CS8797: must be static, must have no parameters, and must return 'void' + // [ModuleInitializer] + Diagnostic(ErrorCode.ERR_ModuleInitializerMethodMustBeStaticParameterlessVoid, "ModuleInitializer").WithArguments("M").WithLocation(7, 6), + // (8,32): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. + // internal static async Task M() { } + Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M").WithLocation(8, 32)); + } + } +} diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/ModuleInitializers/TargetsTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/ModuleInitializers/TargetsTests.cs new file mode 100644 index 0000000000000..41ac4acc4880e --- /dev/null +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/ModuleInitializers/TargetsTests.cs @@ -0,0 +1,513 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis.CSharp.Test.Utilities; +using Microsoft.CodeAnalysis.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Symbols.ModuleInitializers +{ + [CompilerTrait(CompilerFeature.ModuleInitializers)] + public sealed class TargetsTests : CSharpTestBase + { + private static readonly CSharpParseOptions s_parseOptions = TestOptions.RegularPreview; + + [Fact] + public void TargetMustNotBeLocalFunction() + { + string source = @" +using System.Runtime.CompilerServices; + +class C +{ + internal static void M() + { + LocalFunction(); + [ModuleInitializer] + static void LocalFunction() { } + } +} + +namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : System.Attribute { } } +"; + var compilation = CreateCompilation(source, parseOptions: s_parseOptions); + compilation.VerifyEmitDiagnostics( + // (9,10): error CS8795: A module initializer must be an ordinary member method + // [ModuleInitializer] + Diagnostic(ErrorCode.ERR_ModuleInitializerMethodMustBeOrdinary, "ModuleInitializer").WithLocation(9, 10) + ); + } + + [Fact] + public void IgnoredOnLocalFunctionWithBadAttributeTargets() + { + string source = @" +using System; +using System.Runtime.CompilerServices; + +class C +{ + internal static void M() + { + LocalFunction(); + [ModuleInitializer] + static void LocalFunction() { } + } +} + +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.Class)] + class ModuleInitializerAttribute : System.Attribute { } +} +"; + var compilation = CreateCompilation(source, parseOptions: s_parseOptions); + compilation.VerifyEmitDiagnostics( + // (10,10): error CS0592: Attribute 'ModuleInitializer' is not valid on this declaration type. It is only valid on 'class' declarations. + // [ModuleInitializer] + Diagnostic(ErrorCode.ERR_AttributeOnBadSymbolType, "ModuleInitializer").WithArguments("ModuleInitializer", "class").WithLocation(10, 10) + ); + } + + [Fact] + public void TargetMustNotBeDestructor() + { + string source = @" +using System.Runtime.CompilerServices; + +class C +{ + [ModuleInitializer] + ~C() { } +} + +namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : System.Attribute { } } +"; + var compilation = CreateCompilation(source, parseOptions: s_parseOptions); + compilation.VerifyEmitDiagnostics( + // (6,6): error CS8795: A module initializer must be an ordinary member method + // [ModuleInitializer] + Diagnostic(ErrorCode.ERR_ModuleInitializerMethodMustBeOrdinary, "ModuleInitializer").WithLocation(6, 6) + ); + } + + [Fact] + public void IgnoredOnDestructorWithBadAttributeTargets() + { + string source = @" +using System; +using System.Runtime.CompilerServices; + +class C +{ + [ModuleInitializer] + ~C() { } +} + +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.Class)] + class ModuleInitializerAttribute : System.Attribute { } +} +"; + var compilation = CreateCompilation(source, parseOptions: s_parseOptions); + compilation.VerifyEmitDiagnostics( + // (7,6): error CS0592: Attribute 'ModuleInitializer' is not valid on this declaration type. It is only valid on 'class' declarations. + // [ModuleInitializer] + Diagnostic(ErrorCode.ERR_AttributeOnBadSymbolType, "ModuleInitializer").WithArguments("ModuleInitializer", "class").WithLocation(7, 6) + ); + } + + [Fact] + public void TargetMustNotBeOperator() + { + string source = @" +using System.Runtime.CompilerServices; + +class C +{ + [ModuleInitializer] + public static C operator -(C p) => p; +} + +namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : System.Attribute { } } +"; + var compilation = CreateCompilation(source, parseOptions: s_parseOptions); + compilation.VerifyEmitDiagnostics( + // (6,6): error CS8795: A module initializer must be an ordinary member method + // [ModuleInitializer] + Diagnostic(ErrorCode.ERR_ModuleInitializerMethodMustBeOrdinary, "ModuleInitializer").WithLocation(6, 6) + ); + } + + [Fact] + public void IgnoredOnOperatorWithBadAttributeTargets() + { + string source = @" +using System; +using System.Runtime.CompilerServices; + +class C +{ + [ModuleInitializer] + public static C operator -(C p) => p; +} + +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.Class)] + class ModuleInitializerAttribute : System.Attribute { } +} +"; + var compilation = CreateCompilation(source, parseOptions: s_parseOptions); + compilation.VerifyEmitDiagnostics( + // (7,6): error CS0592: Attribute 'ModuleInitializer' is not valid on this declaration type. It is only valid on 'class' declarations. + // [ModuleInitializer] + Diagnostic(ErrorCode.ERR_AttributeOnBadSymbolType, "ModuleInitializer").WithArguments("ModuleInitializer", "class").WithLocation(7, 6) + ); + } + + [Fact] + public void TargetMustNotBeConversionOperator() + { + string source = @" +using System.Runtime.CompilerServices; + +class C +{ + [ModuleInitializer] + public static explicit operator int(C p) => default; +} + +namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : System.Attribute { } } +"; + var compilation = CreateCompilation(source, parseOptions: s_parseOptions); + compilation.VerifyEmitDiagnostics( + // (6,6): error CS8795: A module initializer must be an ordinary member method + // [ModuleInitializer] + Diagnostic(ErrorCode.ERR_ModuleInitializerMethodMustBeOrdinary, "ModuleInitializer").WithLocation(6, 6) + ); + } + + [Fact] + public void IgnoredOnConversionOperatorWithBadAttributeTargets() + { + string source = @" +using System; +using System.Runtime.CompilerServices; + +class C +{ + [ModuleInitializer] + public static explicit operator int(C p) => default; +} + +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.Class)] + class ModuleInitializerAttribute : System.Attribute { } +} +"; + var compilation = CreateCompilation(source, parseOptions: s_parseOptions); + compilation.VerifyEmitDiagnostics( + // (7,6): error CS0592: Attribute 'ModuleInitializer' is not valid on this declaration type. It is only valid on 'class' declarations. + // [ModuleInitializer] + Diagnostic(ErrorCode.ERR_AttributeOnBadSymbolType, "ModuleInitializer").WithArguments("ModuleInitializer", "class").WithLocation(7, 6) + ); + } + + [Fact] + public void TargetMustNotBeEventAccessor() + { + string source = @" +using System.Runtime.CompilerServices; + +class C +{ + public event System.Action E + { + [ModuleInitializer] + add { } + [ModuleInitializer] + remove { } + } +} + +namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : System.Attribute { } } +"; + var compilation = CreateCompilation(source, parseOptions: s_parseOptions); + compilation.VerifyEmitDiagnostics( + // (8,10): error CS8795: A module initializer must be an ordinary member method + // [ModuleInitializer] + Diagnostic(ErrorCode.ERR_ModuleInitializerMethodMustBeOrdinary, "ModuleInitializer").WithLocation(8, 10), + // (10,10): error CS8795: A module initializer must be an ordinary member method + // [ModuleInitializer] + Diagnostic(ErrorCode.ERR_ModuleInitializerMethodMustBeOrdinary, "ModuleInitializer").WithLocation(10, 10) + ); + } + + [Fact] + public void IgnoredOnEventAccessorsWithBadAttributeTargets() + { + string source = @" +using System; +using System.Runtime.CompilerServices; + +class C +{ + public event System.Action E + { + [ModuleInitializer] + add { } + [ModuleInitializer] + remove { } + } +} + +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.Class)] + class ModuleInitializerAttribute : System.Attribute { } +} +"; + var compilation = CreateCompilation(source, parseOptions: s_parseOptions); + compilation.VerifyEmitDiagnostics( + // (9,10): error CS0592: Attribute 'ModuleInitializer' is not valid on this declaration type. It is only valid on 'class' declarations. + // [ModuleInitializer] + Diagnostic(ErrorCode.ERR_AttributeOnBadSymbolType, "ModuleInitializer").WithArguments("ModuleInitializer", "class").WithLocation(9, 10), + // (11,10): error CS0592: Attribute 'ModuleInitializer' is not valid on this declaration type. It is only valid on 'class' declarations. + // [ModuleInitializer] + Diagnostic(ErrorCode.ERR_AttributeOnBadSymbolType, "ModuleInitializer").WithArguments("ModuleInitializer", "class").WithLocation(11, 10) + ); + } + + [Fact] + public void TargetMustNotBePropertyAccessor() + { + string source = @" +using System.Runtime.CompilerServices; + +class C +{ + public int P + { + [ModuleInitializer] + get; + [ModuleInitializer] + set; + } +} + +namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : System.Attribute { } } +"; + var compilation = CreateCompilation(source, parseOptions: s_parseOptions); + compilation.VerifyEmitDiagnostics( + // (8,10): error CS8795: A module initializer must be an ordinary member method + // [ModuleInitializer] + Diagnostic(ErrorCode.ERR_ModuleInitializerMethodMustBeOrdinary, "ModuleInitializer").WithLocation(8, 10), + // (10,10): error CS8795: A module initializer must be an ordinary member method + // [ModuleInitializer] + Diagnostic(ErrorCode.ERR_ModuleInitializerMethodMustBeOrdinary, "ModuleInitializer").WithLocation(10, 10) + ); + } + + [Fact] + public void IgnoredOnPropertyAccessorsWithBadAttributeTargets() + { + string source = @" +using System; +using System.Runtime.CompilerServices; + +class C +{ + public int P + { + [ModuleInitializer] + get; + [ModuleInitializer] + set; + } +} + +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.Class)] + class ModuleInitializerAttribute : System.Attribute { } +} +"; + var compilation = CreateCompilation(source, parseOptions: s_parseOptions); + compilation.VerifyEmitDiagnostics( + // (9,10): error CS0592: Attribute 'ModuleInitializer' is not valid on this declaration type. It is only valid on 'class' declarations. + // [ModuleInitializer] + Diagnostic(ErrorCode.ERR_AttributeOnBadSymbolType, "ModuleInitializer").WithArguments("ModuleInitializer", "class").WithLocation(9, 10), + // (11,10): error CS0592: Attribute 'ModuleInitializer' is not valid on this declaration type. It is only valid on 'class' declarations. + // [ModuleInitializer] + Diagnostic(ErrorCode.ERR_AttributeOnBadSymbolType, "ModuleInitializer").WithArguments("ModuleInitializer", "class").WithLocation(11, 10) + ); + } + + [Fact] + public void TargetMustNotBeIndexerAccessor() + { + string source = @" +using System.Runtime.CompilerServices; + +class C +{ + public int this[int p] + { + [ModuleInitializer] + get => p; + [ModuleInitializer] + set { } + } +} + +namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : System.Attribute { } } +"; + var compilation = CreateCompilation(source, parseOptions: s_parseOptions); + compilation.VerifyEmitDiagnostics( + // (8,10): error CS8795: A module initializer must be an ordinary member method + // [ModuleInitializer] + Diagnostic(ErrorCode.ERR_ModuleInitializerMethodMustBeOrdinary, "ModuleInitializer").WithLocation(8, 10), + // (10,10): error CS8795: A module initializer must be an ordinary member method + // [ModuleInitializer] + Diagnostic(ErrorCode.ERR_ModuleInitializerMethodMustBeOrdinary, "ModuleInitializer").WithLocation(10, 10) + ); + } + + [Fact] + public void IgnoredOnIndexerAccessorsWithBadAttributeTargets() + { + string source = @" +using System; +using System.Runtime.CompilerServices; + +class C +{ + public int this[int p] + { + [ModuleInitializer] + get => p; + [ModuleInitializer] + set { } + } +} + +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.Class)] + class ModuleInitializerAttribute : System.Attribute { } +} +"; + var compilation = CreateCompilation(source, parseOptions: s_parseOptions); + compilation.VerifyEmitDiagnostics( + // (9,10): error CS0592: Attribute 'ModuleInitializer' is not valid on this declaration type. It is only valid on 'class' declarations. + // [ModuleInitializer] + Diagnostic(ErrorCode.ERR_AttributeOnBadSymbolType, "ModuleInitializer").WithArguments("ModuleInitializer", "class").WithLocation(9, 10), + // (11,10): error CS0592: Attribute 'ModuleInitializer' is not valid on this declaration type. It is only valid on 'class' declarations. + // [ModuleInitializer] + Diagnostic(ErrorCode.ERR_AttributeOnBadSymbolType, "ModuleInitializer").WithArguments("ModuleInitializer", "class").WithLocation(11, 10) + ); + } + + [Fact] + public void TargetMustNotBeStaticConstructor() + { + string source = @" +using System.Runtime.CompilerServices; + +class C +{ + [ModuleInitializer] + static C() { } +} + +namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : System.Attribute { } } +"; + var compilation = CreateCompilation(source, parseOptions: s_parseOptions); + compilation.VerifyEmitDiagnostics( + // (6,6): error CS8795: A module initializer must be an ordinary member method + // [ModuleInitializer] + Diagnostic(ErrorCode.ERR_ModuleInitializerMethodMustBeOrdinary, "ModuleInitializer").WithLocation(6, 6) + ); + } + + [Fact] + public void IgnoredOnStaticConstructorWithBadAttributeTargets() + { + string source = @" +using System; +using System.Runtime.CompilerServices; + +class C +{ + [ModuleInitializer] + static C() { } +} + +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.Method)] + class ModuleInitializerAttribute : System.Attribute { } +} +"; + var compilation = CreateCompilation(source, parseOptions: s_parseOptions); + compilation.VerifyEmitDiagnostics( + // (7,6): error CS0592: Attribute 'ModuleInitializer' is not valid on this declaration type. It is only valid on 'method' declarations. + // [ModuleInitializer] + Diagnostic(ErrorCode.ERR_AttributeOnBadSymbolType, "ModuleInitializer").WithArguments("ModuleInitializer", "method").WithLocation(7, 6) + ); + } + + [Fact] + public void TargetMustNotBeInstanceConstructor() + { + string source = @" +using System.Runtime.CompilerServices; + +class C +{ + [ModuleInitializer] + public C() { } +} + +namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : System.Attribute { } } +"; + var compilation = CreateCompilation(source, parseOptions: s_parseOptions); + compilation.VerifyEmitDiagnostics( + // (6,6): error CS8795: A module initializer must be an ordinary member method + // [ModuleInitializer] + Diagnostic(ErrorCode.ERR_ModuleInitializerMethodMustBeOrdinary, "ModuleInitializer").WithLocation(6, 6) + ); + } + + [Fact] + public void IgnoredOnInstanceConstructorWithBadAttributeTargets() + { + string source = @" +using System; +using System.Runtime.CompilerServices; + +class C +{ + [ModuleInitializer] + public C() { } +} + +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.Method)] + class ModuleInitializerAttribute : System.Attribute { } +} +"; + var compilation = CreateCompilation(source, parseOptions: s_parseOptions); + compilation.VerifyEmitDiagnostics( + // (7,6): error CS0592: Attribute 'ModuleInitializer' is not valid on this declaration type. It is only valid on 'method' declarations. + // [ModuleInitializer] + Diagnostic(ErrorCode.ERR_AttributeOnBadSymbolType, "ModuleInitializer").WithArguments("ModuleInitializer", "method").WithLocation(7, 6) + ); + } + } +} From 3790b6b96db39b8a0529f628d656898734c54189 Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Fri, 15 May 2020 09:48:21 -0700 Subject: [PATCH 5/8] Disallow module initializers in VB (#44227) --- .../Symbols/Source/SourceMethodSymbol.vb | 2 + .../AttributeTests_WellKnownAttributes.vb | 407 ++++++++++++++++++ 2 files changed, 409 insertions(+) diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceMethodSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceMethodSymbol.vb index f72d9d7a581f6..d8d928756aa88 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceMethodSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceMethodSymbol.vb @@ -1721,6 +1721,8 @@ lReportErrorOnTwoTokens: End If ElseIf VerifyObsoleteAttributeAppliedToMethod(arguments, AttributeDescription.ObsoleteAttribute) Then ElseIf VerifyObsoleteAttributeAppliedToMethod(arguments, AttributeDescription.DeprecatedAttribute) Then + ElseIf arguments.Attribute.IsTargetAttribute(Me, AttributeDescription.ModuleInitializerAttribute) Then + arguments.Diagnostics.Add(ERRID.WRN_AttributeNotSupportedInVB, arguments.AttributeSyntaxOpt.Location, AttributeDescription.ModuleInitializerAttribute.FullName) Else Dim methodImpl As MethodSymbol = If(Me.IsPartial, PartialImplementationPart, Me) diff --git a/src/Compilers/VisualBasic/Test/Emit/Attributes/AttributeTests_WellKnownAttributes.vb b/src/Compilers/VisualBasic/Test/Emit/Attributes/AttributeTests_WellKnownAttributes.vb index 5a0d76947435e..2879dbadd8fd6 100644 --- a/src/Compilers/VisualBasic/Test/Emit/Attributes/AttributeTests_WellKnownAttributes.vb +++ b/src/Compilers/VisualBasic/Test/Emit/Attributes/AttributeTests_WellKnownAttributes.vb @@ -5812,6 +5812,413 @@ BC30657: 'sc1_method' has a return type that is not supported or parameter types ]]>) End Sub +#End Region + +#Region "ModuleInitializerAttribute" + + Public Sub ModuleInitializerAttributeOnMethod() + Dim source = + + + Sub S() + End Sub + + + Function F() As Integer + Return 1 + End Function +End Class +]]> + + + + Dim compilation = CreateCompilationWithMscorlib40(source) + CompilationUtils.AssertTheseDiagnostics(compilation, + + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +BC42381: 'System.Runtime.CompilerServices.ModuleInitializerAttribute' is not supported in VB. + + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +]]>) + End Sub + + + Public Sub ModuleInitializerAttributeOnClass() + Dim source = + + +Class C +End Class +]]> + + + + + Dim comp = CreateCompilationWithMscorlib40(source) + + CompilationUtils.AssertNoDiagnostics(comp) + End Sub + + + Public Sub ModuleInitializerAttributeOnProperty() + Dim source = + + + Property P As Integer + Get + Return 1 + End Get + + Set + End Set + End Property +End Class +]]> + + + + + Dim comp = CreateCompilationWithMscorlib40(source) + + CompilationUtils.AssertNoDiagnostics(comp) + End Sub + + + Public Sub ModuleInitializerAttributeOnAccessors() + Dim source = + + + Get + Return 1 + End Get + + + Set + End Set + End Property +End Class +]]> + + + + + Dim comp = CreateCompilationWithMscorlib40(source) + + CompilationUtils.AssertTheseDiagnostics(comp, + + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +BC42381: 'System.Runtime.CompilerServices.ModuleInitializerAttribute' is not supported in VB. + + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +]]>) + End Sub + + + Public Sub ModuleInitializerAttributeOnModule() + Dim source = + + + +Namespace System.Runtime.CompilerServices + Class ModuleInitializerAttribute + Inherits System.Attribute + End Class +End Namespace +]]> + + + + + Dim comp = CreateCompilationWithMscorlib40(source) + + CompilationUtils.AssertNoDiagnostics(comp) + End Sub + + + Public Sub ModuleInitializerAttributeOnAssembly() + Dim source = + + + +Namespace System.Runtime.CompilerServices + Class ModuleInitializerAttribute + Inherits System.Attribute + End Class +End Namespace +]]> + + + + + Dim comp = CreateCompilationWithMscorlib40(source) + + CompilationUtils.AssertNoDiagnostics(comp) + End Sub + + + Public Sub ModuleInitializerAttributeOnEnum() + Dim source = + + +Enum E + Member +End Enum +]]> + + + + + Dim comp = CreateCompilationWithMscorlib40(source) + + CompilationUtils.AssertNoDiagnostics(comp) + End Sub + + + Public Sub ModuleInitializerAttributeOnEnumMember() + Dim source = + + + Member1 + + Member2 +End Enum +]]> + + + + + Dim comp = CreateCompilationWithMscorlib40(source) + + CompilationUtils.AssertNoDiagnostics(comp) + End Sub + + + Public Sub ModuleInitializerAttributeOnEvent() + Dim source = + + + Event E(ByVal i As Integer) +End Class +]]> + + + + + Dim comp = CreateCompilationWithMscorlib40(source) + + CompilationUtils.AssertNoDiagnostics(comp) + End Sub + + + Public Sub ModuleInitializerAttributeOnDelegate() + Dim source = + + + Delegate Sub D() +End Class +]]> + + + + + Dim comp = CreateCompilationWithMscorlib40(source) + + CompilationUtils.AssertNoDiagnostics(comp) + End Sub + + + Public Sub ModuleInitializerAttributeOnInterface() + Dim source = + + +Interface I +End Interface +]]> + + + + + Dim comp = CreateCompilationWithMscorlib40(source) + + CompilationUtils.AssertNoDiagnostics(comp) + End Sub + + + Public Sub ModuleInitializerAttributeOnStructure() + Dim source = + + +Structure S +End Structure +]]> + + + + + Dim comp = CreateCompilationWithMscorlib40(source) + + CompilationUtils.AssertNoDiagnostics(comp) + End Sub + + + Public Sub ModuleInitializerAttributeOnReturnValue() + Dim source = + + Integer + Return 1 + End Function +End Class +]]> + + + + + Dim comp = CreateCompilationWithMscorlib40(source) + + CompilationUtils.AssertNoDiagnostics(comp) + End Sub + + + Public Sub ModuleInitializerAttributeOnParameter() + Dim source = + + ByVal i As Integer) + End Sub +End Class +]]> + + + + + Dim comp = CreateCompilationWithMscorlib40(source) + + CompilationUtils.AssertNoDiagnostics(comp) + End Sub + + + Public Sub ModuleInitializerAttributeOnField() + Dim source = + + + Dim i As Integer +End Class +]]> + + + + + Dim comp = CreateCompilationWithMscorlib40(source) + + CompilationUtils.AssertNoDiagnostics(comp) + End Sub + #End Region From d5faa63b9d99390dff6856247844bce9721528ff Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Sat, 16 May 2020 14:56:21 -0700 Subject: [PATCH 6/8] Test netmodules containing module initializers (#44228) --- .../ModuleInitializersTests.cs | 166 ++++++++++++++++++ 1 file changed, 166 insertions(+) diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/ModuleInitializers/ModuleInitializersTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/ModuleInitializers/ModuleInitializersTests.cs index f4080a50ed14a..4ed6d92920037 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/ModuleInitializers/ModuleInitializersTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/ModuleInitializers/ModuleInitializersTests.cs @@ -778,5 +778,171 @@ namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : S Assert.Null(rootModuleType.GetMember(".cctor")); }); } + + [ConditionalFact(typeof(WindowsDesktopOnly), Reason = ConditionalSkipReason.NetModulesNeedDesktop)] + public void MultipleNetmodules() + { + var moduleOptions = TestOptions.ReleaseModule.WithMetadataImportOptions(MetadataImportOptions.All); + var s1 = @" +using System; +using System.Runtime.CompilerServices; + +public class A +{ + [ModuleInitializer] + public static void M1() + { + Console.Write(1); + } +} + +namespace System.Runtime.CompilerServices { public class ModuleInitializerAttribute : System.Attribute { } }"; + var comp1 = CreateCompilation(s1, options: moduleOptions.WithModuleName("A"), parseOptions: s_parseOptions); + comp1.VerifyDiagnostics(); + var ref1 = comp1.EmitToImageReference(); + CompileAndVerify(comp1, symbolValidator: validateModuleInitializer, verify: Verification.Skipped); + + var s2 = @" +using System; +using System.Runtime.CompilerServices; + +public class B +{ + [ModuleInitializer] + public static void M2() + { + Console.Write(2); + } +}"; + var comp2 = CreateCompilation(s2, options: moduleOptions.WithModuleName("B"), parseOptions: s_parseOptions, references: new[] { ref1 }); + comp2.VerifyDiagnostics(); + var ref2 = comp2.EmitToImageReference(); + CompileAndVerify(comp2, symbolValidator: validateModuleInitializer, verify: Verification.Skipped); + + var exeOptions = TestOptions.ReleaseExe + .WithMetadataImportOptions(MetadataImportOptions.All) + .WithModuleName("C"); + var s3 = @" +using System; + +public class Program +{ + public static void Main(string[] args) + { + Console.Write(3); + } +}"; + var comp3 = CreateCompilation(s3, options: exeOptions, parseOptions: s_parseOptions, references: new[] { ref1, ref2 }); + comp3.VerifyDiagnostics(); + CompileAndVerify(comp3, symbolValidator: validateNoModuleInitializer, expectedOutput: "3"); + + var s4 = @" +using System; + +public class Program +{ + public static void Main(string[] args) + { + new A(); + new B(); + Console.Write(3); + } +}"; + var comp4 = CreateCompilation(s4, options: exeOptions, parseOptions: s_parseOptions, references: new[] { ref1, ref2 }); + comp4.VerifyDiagnostics(); + CompileAndVerify(comp4, symbolValidator: validateNoModuleInitializer, expectedOutput: "123"); + + var s5 = @" +using System; + +public class Program +{ + public static void Main(string[] args) + { + new B(); + Console.Write(3); + new A(); + } +}"; + var comp5 = CreateCompilation(s5, options: exeOptions, parseOptions: s_parseOptions, references: new[] { ref1, ref2 }); + comp5.VerifyDiagnostics(); + // This order seems surprising, but is likely related to the order in which types are loaded when a method is called. + CompileAndVerify(comp5, symbolValidator: validateNoModuleInitializer, expectedOutput: "213"); + + var s6 = @" +using System; + +public class Program +{ + public static void Main(string[] args) + { + new A(); + Console.Write(3); + } +}"; + var comp6 = CreateCompilation(s6, options: exeOptions, parseOptions: s_parseOptions, references: new[] { ref1, ref2 }); + comp6.VerifyDiagnostics(); + CompileAndVerify(comp6, symbolValidator: validateNoModuleInitializer, expectedOutput: "13"); + + var s7 = @" +using System; +using System.Runtime.CompilerServices; + +public class Program +{ + [ModuleInitializer] + public static void Init() + { + Console.Write(0); + } + + public static void Main(string[] args) + { + new B(); + Console.Write(3); + } +}"; + var comp7 = CreateCompilation(s7, options: exeOptions, parseOptions: s_parseOptions, references: new[] { ref1, ref2 }); + comp7.VerifyDiagnostics(); + CompileAndVerify(comp7, symbolValidator: validateModuleInitializer, expectedOutput: "023"); + + var s8 = @" +using System; +using System.Runtime.CompilerServices; + +public class Program +{ + [ModuleInitializer] + public static void Init() + { + Console.Write(0); + new A(); + } + + public static void Main(string[] args) + { + new A(); + new B(); + Console.Write(3); + } +}"; + var comp8 = CreateCompilation(s8, options: exeOptions, parseOptions: s_parseOptions, references: new[] { ref1, ref2 }); + comp8.VerifyDiagnostics(); + CompileAndVerify(comp8, symbolValidator: validateModuleInitializer, expectedOutput: "1023"); + + void validateModuleInitializer(ModuleSymbol module) + { + Assert.Equal(MetadataImportOptions.All, ((PEModuleSymbol)module).ImportOptions); + var moduleType = module.ContainingAssembly.GetTypeByMetadataName(""); + Assert.NotNull(moduleType.GetMember(".cctor")); + } + + void validateNoModuleInitializer(ModuleSymbol module) + { + Assert.Equal(MetadataImportOptions.All, ((PEModuleSymbol)module).ImportOptions); + var moduleType = module.ContainingAssembly.GetTypeByMetadataName(""); + Assert.Null(moduleType.GetMember(".cctor")); + } + } } } From 9564bd70a460c904a551cf77b720af228515c8c5 Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Wed, 15 Jul 2020 15:40:03 -0700 Subject: [PATCH 7/8] Add tests (#45969) --- .../ModuleInitializers/AccessibilityTests.cs | 108 +++++++++++ .../ModuleInitializers/GenericsTests.cs | 22 +++ .../ModuleInitializersTests.cs | 178 ++++++++++++++++++ .../ModuleInitializers/SignatureTests.cs | 41 +++- 4 files changed, 343 insertions(+), 6 deletions(-) diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/ModuleInitializers/AccessibilityTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/ModuleInitializers/AccessibilityTests.cs index 10e75f994b21d..faf9ecdf98cd3 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/ModuleInitializers/AccessibilityTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/ModuleInitializers/AccessibilityTests.cs @@ -122,6 +122,114 @@ namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : S ); } + [Fact] + public void ModuleInitializerOnPrivatePartialMethod() + { + string source = @" +using System; +using System.Runtime.CompilerServices; + +partial class C +{ + [ModuleInitializer] // 1 + static partial void M1(); + + [ModuleInitializer] // 2 + static partial void M2(); + static partial void M2() { } + + static partial void M3(); + [ModuleInitializer] // 3 + static partial void M3() { } + + [ModuleInitializer] // 4 + static partial void M4(); + [ModuleInitializer] // 5 + static partial void M4() { } +} + +class Program +{ +} + +namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : System.Attribute { } } +"; + var compilation = CreateCompilation(source, parseOptions: s_parseOptions); + compilation.VerifyEmitDiagnostics( + // (7,6): error CS8814: Module initializer method 'M1' must be accessible at the module level + // [ModuleInitializer] // 1 + Diagnostic(ErrorCode.ERR_ModuleInitializerMethodMustBeAccessibleOutsideTopLevelType, "ModuleInitializer").WithArguments("M1").WithLocation(7, 6), + // (10,6): error CS8814: Module initializer method 'M2' must be accessible at the module level + // [ModuleInitializer] // 2 + Diagnostic(ErrorCode.ERR_ModuleInitializerMethodMustBeAccessibleOutsideTopLevelType, "ModuleInitializer").WithArguments("M2").WithLocation(10, 6), + // (15,6): error CS8814: Module initializer method 'M3' must be accessible at the module level + // [ModuleInitializer] // 3 + Diagnostic(ErrorCode.ERR_ModuleInitializerMethodMustBeAccessibleOutsideTopLevelType, "ModuleInitializer").WithArguments("M3").WithLocation(15, 6), + // (18,6): error CS8814: Module initializer method 'M4' must be accessible at the module level + // [ModuleInitializer] // 4 + Diagnostic(ErrorCode.ERR_ModuleInitializerMethodMustBeAccessibleOutsideTopLevelType, "ModuleInitializer").WithArguments("M4").WithLocation(18, 6), + // (20,6): error CS0579: Duplicate 'ModuleInitializer' attribute + // [ModuleInitializer] // 5 + Diagnostic(ErrorCode.ERR_DuplicateAttribute, "ModuleInitializer").WithArguments("ModuleInitializer").WithLocation(20, 6) + ); + } + + [Fact] + public void ModuleInitializerOnPublicPartialMethod() + { + string source = @" +using System; +using System.Runtime.CompilerServices; + +partial class C +{ + [ModuleInitializer] + public static partial void M1(); + public static partial void M1() { Console.Write(1); } + + public static partial void M2(); + [ModuleInitializer] + public static partial void M2() { Console.Write(2); } +} + +class Program +{ + public static void Main() + { + Console.Write(3); + } +} + +namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : System.Attribute { } } +"; + CompileAndVerify(source, parseOptions: s_parseOptions, expectedOutput: @"123"); + } + + [Fact] + public void DuplicateModuleInitializerOnPublicPartialMethod() + { + string source = @" +using System; +using System.Runtime.CompilerServices; + +partial class C +{ + [ModuleInitializer] + public static partial void M1(); + [ModuleInitializer] // 1 + public static partial void M1() { } +} + +namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : System.Attribute { } } +"; + var compilation = CreateCompilation(source, parseOptions: s_parseOptions); + compilation.VerifyEmitDiagnostics( + // (9,6): error CS0579: Duplicate 'ModuleInitializer' attribute + // [ModuleInitializer] // 1 + Diagnostic(ErrorCode.ERR_DuplicateAttribute, "ModuleInitializer").WithArguments("ModuleInitializer").WithLocation(9, 6) + ); + } + [Theory] [InlineData("public")] [InlineData("internal")] diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/ModuleInitializers/GenericsTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/ModuleInitializers/GenericsTests.cs index 0e607580838cc..8d813148ebd02 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/ModuleInitializers/GenericsTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/ModuleInitializers/GenericsTests.cs @@ -57,6 +57,28 @@ namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : S ); } + [Fact] + public void MustNotBeGenericAndContainedInGenericType() + { + string source = @" +using System.Runtime.CompilerServices; + +class C +{ + [ModuleInitializer] + internal static void M() { } +} + +namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : System.Attribute { } } +"; + var compilation = CreateCompilation(source, parseOptions: s_parseOptions); + compilation.VerifyEmitDiagnostics( + // (6,6): error CS8816: Module initializer method 'M' must not be generic and must not be contained in a generic type + // [ModuleInitializer] + Diagnostic(ErrorCode.ERR_ModuleInitializerMethodAndContainingTypesMustNotBeGeneric, "ModuleInitializer").WithArguments("M").WithLocation(6, 6) + ); + } + [Fact] public void MustNotBeContainedInGenericTypeWithParametersDeclaredByContainingGenericType() { diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/ModuleInitializers/ModuleInitializersTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/ModuleInitializers/ModuleInitializersTests.cs index 4ed6d92920037..218ff132e9137 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/ModuleInitializers/ModuleInitializersTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/ModuleInitializers/ModuleInitializersTests.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Collections.Immutable; using System.Reflection; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE; @@ -779,6 +780,31 @@ namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : S }); } + [Fact] + public void ModuleInitializerMethodIsObsolete() + { + string source = @" +using System; +using System.Runtime.CompilerServices; + +class C +{ + [Obsolete, ModuleInitializer] + internal static void Init() => Console.WriteLine(""C.Init""); +} + +class Program +{ + static void Main() => Console.WriteLine(""Program.Main""); +} + +namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : System.Attribute { } } +"; + CompileAndVerify(source, parseOptions: s_parseOptions, expectedOutput: @" +C.Init +Program.Main"); + } + [ConditionalFact(typeof(WindowsDesktopOnly), Reason = ConditionalSkipReason.NetModulesNeedDesktop)] public void MultipleNetmodules() { @@ -944,5 +970,157 @@ void validateNoModuleInitializer(ModuleSymbol module) Assert.Null(moduleType.GetMember(".cctor")); } } + + [ConditionalFact(typeof(WindowsDesktopOnly), Reason = ConditionalSkipReason.NetModulesNeedDesktop)] + public void NetmoduleFromIL_InitializerNotCalled() + { + var il = @" +.class public auto ansi beforefieldinit A + extends [mscorlib]System.Object +{ + .method public hidebysig static void M1() cil managed + { + .custom instance void System.Runtime.CompilerServices.ModuleInitializerAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 9 (0x9) + .maxstack 8 + IL_0000: nop + IL_0001: ldc.i4.0 + IL_0002: call void [mscorlib]System.Console::Write(int32) + IL_0007: nop + IL_0008: ret + } // end of method A::M1 + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method A::.ctor + +} // end of class A + +.class public auto ansi beforefieldinit System.Runtime.CompilerServices.ModuleInitializerAttribute + extends [mscorlib]System.Attribute +{ + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Attribute::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method ModuleInitializerAttribute::.ctor + +} // end of class System.Runtime.CompilerServices.ModuleInitializerAttribute"; + + var source1 = @" +using System; +using System.Runtime.CompilerServices; + +public class A +{ + [ModuleInitializer] + public static void M1() + { + Console.Write(1); + } + + public static void Main() + { + Console.Write(2); + } +} + +namespace System.Runtime.CompilerServices { public class ModuleInitializerAttribute : System.Attribute { } } +"; + + var source2 = @" +using System; +using System.Runtime.CompilerServices; + +public class A +{ + public static void M1() + { + Console.Write(0); + } + + public static void Main() + { + Console.Write(1); + } +} + +namespace System.Runtime.CompilerServices { public class ModuleInitializerAttribute : System.Attribute { } } +"; + var exeOptions = TestOptions.ReleaseExe + .WithMetadataImportOptions(MetadataImportOptions.All) + .WithModuleName("C"); + + var comp = CreateCompilationWithIL(source1, il, parseOptions: s_parseOptions, options: exeOptions); + CompileAndVerify(comp, symbolValidator: validateModuleInitializer, verify: Verification.Skipped, expectedOutput: "12"); + + comp = CreateCompilationWithIL(source2, il, parseOptions: s_parseOptions, options: exeOptions); + CompileAndVerify(comp, symbolValidator: validateNoModuleInitializer, verify: Verification.Skipped, expectedOutput: "1"); + + void validateModuleInitializer(ModuleSymbol module) + { + Assert.Equal(MetadataImportOptions.All, ((PEModuleSymbol)module).ImportOptions); + var moduleType = module.ContainingAssembly.GetTypeByMetadataName(""); + Assert.NotNull(moduleType.GetMember(".cctor")); + } + + void validateNoModuleInitializer(ModuleSymbol module) + { + Assert.Equal(MetadataImportOptions.All, ((PEModuleSymbol)module).ImportOptions); + var moduleType = module.ContainingAssembly.GetTypeByMetadataName(""); + Assert.Null(moduleType.GetMember(".cctor")); + } + } + + [Fact] + public void MultipleAttributesViaExternAlias() + { + var source1 = @" +namespace System.Runtime.CompilerServices { public class ModuleInitializerAttribute : System.Attribute { } } +"; + + var ref1 = CreateCompilation(source1).ToMetadataReference(aliases: ImmutableArray.Create("Alias1")); + var ref2 = CreateCompilation(source1).ToMetadataReference(aliases: ImmutableArray.Create("Alias2")); + + var source = @" +extern alias Alias1; +extern alias Alias2; + +using System; + +class Program +{ + [Alias1::System.Runtime.CompilerServices.ModuleInitializer] + internal static void Init1() + { + Console.Write(1); + } + + [Alias2::System.Runtime.CompilerServices.ModuleInitializer] + internal static void Init2() + { + Console.Write(2); + } + + static void Main() + { + Console.Write(3); + } +} +"; + CompileAndVerify(source, parseOptions: s_parseOptions, references: new[] { ref1, ref2 }, expectedOutput: "123"); + } } } diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/ModuleInitializers/SignatureTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/ModuleInitializers/SignatureTests.cs index 55e9e253f4a10..6c422471791b2 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/ModuleInitializers/SignatureTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/ModuleInitializers/SignatureTests.cs @@ -4,6 +4,7 @@ using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Test.Utilities; +using Roslyn.Test.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Symbols.ModuleInitializers @@ -29,12 +30,40 @@ namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : S "; var compilation = CreateCompilation(source, parseOptions: s_parseOptions); compilation.VerifyEmitDiagnostics( - // (6,6): error CS8797: must be static, must have no parameters, and must return 'void' + // (6,6): error CS8815: Module initializer method 'M' must be static, must have no parameters, and must return 'void' // [ModuleInitializer] Diagnostic(ErrorCode.ERR_ModuleInitializerMethodMustBeStaticParameterlessVoid, "ModuleInitializer").WithArguments("M").WithLocation(6, 6) ); } + [Fact] + public void MustNotBeInstanceMethodInInterface() + { + string source = @" +using System.Runtime.CompilerServices; + +interface i +{ + [ModuleInitializer] + internal void M1(); + + [ModuleInitializer] + internal void M2() { } +} + +namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : System.Attribute { } } +"; + var compilation = CreateCompilation(source, parseOptions: s_parseOptions, targetFramework: TargetFramework.NetStandardLatest); + compilation.VerifyEmitDiagnostics( + // (6,6): error CS8815: Module initializer method 'M1' must be static, must have no parameters, and must return 'void' + // [ModuleInitializer] + Diagnostic(ErrorCode.ERR_ModuleInitializerMethodMustBeStaticParameterlessVoid, "ModuleInitializer").WithArguments("M1").WithLocation(6, 6), + // (9,6): error CS8815: Module initializer method 'M2' must be static, must have no parameters, and must return 'void' + // [ModuleInitializer] + Diagnostic(ErrorCode.ERR_ModuleInitializerMethodMustBeStaticParameterlessVoid, "ModuleInitializer").WithArguments("M2").WithLocation(9, 6) + ); + } + [Fact] public void MustNotHaveParameters() { @@ -51,7 +80,7 @@ namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : S "; var compilation = CreateCompilation(source, parseOptions: s_parseOptions); compilation.VerifyEmitDiagnostics( - // (6,6): error CS8797: must be static, must have no parameters, and must return 'void' + // (6,6): error CS8815: Module initializer method 'M' must be static, must have no parameters, and must return 'void' // [ModuleInitializer] Diagnostic(ErrorCode.ERR_ModuleInitializerMethodMustBeStaticParameterlessVoid, "ModuleInitializer").WithArguments("M").WithLocation(6, 6) ); @@ -73,7 +102,7 @@ namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : S "; var compilation = CreateCompilation(source, parseOptions: s_parseOptions); compilation.VerifyEmitDiagnostics( - // (6,6): error CS8797: must be static, must have no parameters, and must return 'void' + // (6,6): error CS8815: Module initializer method 'M' must be static, must have no parameters, and must return 'void' // [ModuleInitializer] Diagnostic(ErrorCode.ERR_ModuleInitializerMethodMustBeStaticParameterlessVoid, "ModuleInitializer").WithArguments("M").WithLocation(6, 6) ); @@ -95,7 +124,7 @@ namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : S "; var compilation = CreateCompilation(source, parseOptions: s_parseOptions); compilation.VerifyEmitDiagnostics( - // (6,6): error CS8797: must be static, must have no parameters, and must return 'void' + // (6,6): error CS8815: Module initializer method 'M' must be static, must have no parameters, and must return 'void' // [ModuleInitializer] Diagnostic(ErrorCode.ERR_ModuleInitializerMethodMustBeStaticParameterlessVoid, "ModuleInitializer").WithArguments("M").WithLocation(6, 6) ); @@ -117,7 +146,7 @@ namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : S "; var compilation = CreateCompilation(source, parseOptions: s_parseOptions); compilation.VerifyEmitDiagnostics( - // (6,6): error CS8797: must be static, must have no parameters, and must return 'void' + // (6,6): error CS8815: Module initializer method 'M' must be static, must have no parameters, and must return 'void' // [ModuleInitializer] Diagnostic(ErrorCode.ERR_ModuleInitializerMethodMustBeStaticParameterlessVoid, "ModuleInitializer").WithArguments("M").WithLocation(6, 6) ); @@ -165,7 +194,7 @@ namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : S "; var compilation = CreateCompilation(source, parseOptions: s_parseOptions); compilation.VerifyEmitDiagnostics( - // (7,6): error CS8797: must be static, must have no parameters, and must return 'void' + // (6,6): error CS8815: Module initializer method 'M' must be static, must have no parameters, and must return 'void' // [ModuleInitializer] Diagnostic(ErrorCode.ERR_ModuleInitializerMethodMustBeStaticParameterlessVoid, "ModuleInitializer").WithArguments("M").WithLocation(7, 6), // (8,32): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. From ab38a9d4f3caaa2fe6c77fcf7934b9a86f43a24f Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Thu, 16 Jul 2020 17:10:34 -0700 Subject: [PATCH 8/8] Add additional module initializers tests from review (#46020) --- .../ModuleInitializers/AccessibilityTests.cs | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/ModuleInitializers/AccessibilityTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/ModuleInitializers/AccessibilityTests.cs index faf9ecdf98cd3..7611a93656664 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/ModuleInitializers/AccessibilityTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/ModuleInitializers/AccessibilityTests.cs @@ -174,6 +174,62 @@ namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : S ); } + [Fact] + public void ModuleInitializerOnPrivatePartialMethod_AllowMultiple() + { + string source = @" +using System; +using System.Runtime.CompilerServices; + +partial class C +{ + [ModuleInitializer] // 1 + static partial void M1(); + + [ModuleInitializer] // 2 + static partial void M2(); + static partial void M2() { } + + static partial void M3(); + [ModuleInitializer] // 3 + static partial void M3() { } + + [ModuleInitializer] // 4 + static partial void M4(); + [ModuleInitializer] // 5 + static partial void M4() { } +} + +class Program +{ +} + +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + class ModuleInitializerAttribute : System.Attribute { } +} +"; + var compilation = CreateCompilation(source, parseOptions: s_parseOptions); + compilation.VerifyEmitDiagnostics( + // (7,6): error CS8814: Module initializer method 'M1' must be accessible at the module level + // [ModuleInitializer] // 1 + Diagnostic(ErrorCode.ERR_ModuleInitializerMethodMustBeAccessibleOutsideTopLevelType, "ModuleInitializer").WithArguments("M1").WithLocation(7, 6), + // (10,6): error CS8814: Module initializer method 'M2' must be accessible at the module level + // [ModuleInitializer] // 2 + Diagnostic(ErrorCode.ERR_ModuleInitializerMethodMustBeAccessibleOutsideTopLevelType, "ModuleInitializer").WithArguments("M2").WithLocation(10, 6), + // (15,6): error CS8814: Module initializer method 'M3' must be accessible at the module level + // [ModuleInitializer] // 3 + Diagnostic(ErrorCode.ERR_ModuleInitializerMethodMustBeAccessibleOutsideTopLevelType, "ModuleInitializer").WithArguments("M3").WithLocation(15, 6), + // (18,6): error CS8814: Module initializer method 'M4' must be accessible at the module level + // [ModuleInitializer] // 4 + Diagnostic(ErrorCode.ERR_ModuleInitializerMethodMustBeAccessibleOutsideTopLevelType, "ModuleInitializer").WithArguments("M4").WithLocation(18, 6), + // (20,6): error CS8814: Module initializer method 'M4' must be accessible at the module level + // [ModuleInitializer] // 5 + Diagnostic(ErrorCode.ERR_ModuleInitializerMethodMustBeAccessibleOutsideTopLevelType, "ModuleInitializer").WithArguments("M4").WithLocation(20, 6) + ); + } + [Fact] public void ModuleInitializerOnPublicPartialMethod() { @@ -230,6 +286,38 @@ namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : S ); } + [Fact] + public void DuplicateModuleInitializerOnPublicPartialMethod_AllowMultiple() + { + string source = @" +using System; +using System.Runtime.CompilerServices; + +partial class C +{ + [ModuleInitializer] + public static partial void M1(); + [ModuleInitializer] + public static partial void M1() { Console.Write(1); } +} + +class Program +{ + static void Main() + { + Console.Write(2); + } +} + +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + class ModuleInitializerAttribute : System.Attribute { } +} +"; + CompileAndVerify(source, expectedOutput: "12", parseOptions: s_parseOptions); + } + [Theory] [InlineData("public")] [InlineData("internal")]