File: Symbols\ModuleInitializers\ModuleInitializersTests.cs
Web Access
Project: src\src\Compilers\CSharp\Test\Symbol\Microsoft.CodeAnalysis.CSharp.Symbol.UnitTests.csproj (Microsoft.CodeAnalysis.CSharp.Symbol.UnitTests)
// 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.
 
#nullable disable
 
using System.Collections.Immutable;
using System.Reflection;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.Emit;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Roslyn.Utilities;
using Xunit;
 
namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Symbols.ModuleInitializers
{
    [CompilerTrait(CompilerFeature.ModuleInitializers)]
    public sealed class ModuleInitializersTests : CSharpTestBase
    {
        private static readonly CSharpParseOptions s_parseOptions = TestOptions.Regular9;
 
        [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 CS8400: Feature 'module initializers' is not available in C# 8.0. Please use language version 9.0 or greater.
                //     [ModuleInitializer]
                Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion8, "ModuleInitializer").WithArguments("module initializers", "9.0").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.Regular9);
 
            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: TestOptions.DebugExe.WithMetadataImportOptions(MetadataImportOptions.All),
                symbolValidator: module =>
                {
                    Assert.Equal(MetadataImportOptions.All, ((PEModuleSymbol)module).ImportOptions);
                    var rootModuleType = (TypeSymbol)module.GlobalNamespace.GetMember("<Module>");
                    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 =>
                {
                    Assert.Equal(MetadataImportOptions.All, ((PEModuleSymbol)module).ImportOptions);
                    var rootModuleType = (TypeSymbol)module.GlobalNamespace.GetMember("<Module>");
                    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");
        }
 
        [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("<Module>");
                    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.NetCoreApp,
                expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @"
I.M
Program.Main" : null,
                verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped);
        }
 
        [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.NetCoreApp,
                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<MethodSymbol>(".cctor"));
 
                var moduleType = module.ContainingAssembly.GetTypeByMetadataName("<Module>");
                Assert.NotNull(moduleType.GetMember<MethodSymbol>(".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<MethodSymbol>(".cctor"));
 
                var moduleType = module.ContainingAssembly.GetTypeByMetadataName("<Module>");
                Assert.NotNull(moduleType.GetMember<MethodSymbol>(".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<MethodSymbol>(".cctor"));
 
                var moduleType = module.ContainingAssembly.GetTypeByMetadataName("<Module>");
                Assert.NotNull(moduleType.GetMember<MethodSymbol>(".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<MethodSymbol>(".cctor"));
 
                var moduleType = module.ContainingAssembly.GetTypeByMetadataName("<Module>");
                Assert.NotNull(moduleType.GetMember<MethodSymbol>(".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("<Module>");
                    Assert.Null(rootModuleType.GetMember(".cctor"));
                });
        }
 
        [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()
        {
            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("<Module>");
                Assert.NotNull(moduleType.GetMember<MethodSymbol>(".cctor"));
            }
 
            void validateNoModuleInitializer(ModuleSymbol module)
            {
                Assert.Equal(MetadataImportOptions.All, ((PEModuleSymbol)module).ImportOptions);
                var moduleType = module.ContainingAssembly.GetTypeByMetadataName("<Module>");
                Assert.Null(moduleType.GetMember<MethodSymbol>(".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("<Module>");
                Assert.NotNull(moduleType.GetMember<MethodSymbol>(".cctor"));
            }
 
            void validateNoModuleInitializer(ModuleSymbol module)
            {
                Assert.Equal(MetadataImportOptions.All, ((PEModuleSymbol)module).ImportOptions);
                var moduleType = module.ContainingAssembly.GetTypeByMetadataName("<Module>");
                Assert.Null(moduleType.GetMember<MethodSymbol>(".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");
        }
 
        [Fact]
        [WorkItem(56412, "https://github.com/dotnet/roslyn/issues/56412")]
        public void Issue56412()
        {
            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,
                options: TestOptions.ReleaseExe,
                emitOptions: EmitOptions.Default.WithDebugInformationFormat(PathUtilities.IsUnixLikePlatform ? DebugInformationFormat.PortablePdb : DebugInformationFormat.Pdb),
                expectedOutput: @"
C.M
Program.Main");
        }
    }
}