File: BaseCompilerFeatureRequiredTests.cs
Web Access
Project: src\src\Compilers\Test\Core\Microsoft.CodeAnalysis.Test.Utilities.csproj (Microsoft.CodeAnalysis.Test.Utilities)
// 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.Linq;
using System.Reflection.Metadata;
using Microsoft.CodeAnalysis.Test.Utilities;
using Xunit;
 
namespace Microsoft.CodeAnalysis.UnitTests;
 
public abstract class BaseCompilerFeatureRequiredTests<TCompilation, TSource> : CommonTestBase where TCompilation : Compilation
{
    private static string CompilerFeatureRequiredApplication(
        bool? isOptional)
    {
        var builder = new BlobBuilder();
        builder.WriteSerializedString("test");
        var featureLengthAndName = string.Join(" ", builder.ToImmutableArray().Select(b => $"{b:x2}"));
 
        var isOptionalText = isOptional switch
        {
            true => "01 00 54 02 0a 49 73 4f 70 74 69 6f 6e 61 6c 01", // One optional parameter, "IsOptional", true
            false => "01 00 54 02 0a 49 73 4f 70 74 69 6f 6e 61 6c 00", // One optional parameter, "IsOptional", false
            null => "00 00" // No optional parameters
        };
 
        return $"""
                 .custom instance void System.Runtime.CompilerServices.CompilerFeatureRequiredAttribute::.ctor(string) = (
                     01 00 {featureLengthAndName} {isOptionalText}
                 )
                 """;
    }
 
    private const string CompilerFeatureRequiredIl =
        """
        .class public auto ansi sealed beforefieldinit System.Runtime.CompilerServices.CompilerFeatureRequiredAttribute
             extends [mscorlib]System.Attribute
         {
             .custom instance void [mscorlib]System.AttributeUsageAttribute::.ctor(valuetype [mscorlib]System.AttributeTargets) = (
                 01 00 ff 7f 00 00 02 00 54 02 0d 41 6c 6c 6f 77
                 4d 75 6c 74 69 70 6c 65 01 54 02 09 49 6e 68 65
                 72 69 74 65 64 00
             )
             // Fields
             .field private initonly string '<FeatureName>k__BackingField'
             .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (
                 01 00 00 00
             )
             .field private initonly bool '<IsOptional>k__BackingField'
             .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (
                 01 00 00 00
             )
 
             .field public static literal string RefStructs = "RefStructs"
             .field public static literal string RequiredMembers = "RequiredMembers"
         
             // Methods
             .method public hidebysig specialname rtspecialname 
                 instance void .ctor (
                     string featureName
                 ) cil managed 
             {
                 ldarg.0
                 call instance void [mscorlib]System.Attribute::.ctor()
                 ldarg.0
                 ldarg.1
                 stfld string System.Runtime.CompilerServices.CompilerFeatureRequiredAttribute::'<FeatureName>k__BackingField'
                 ret
             } // end of method CompilerFeatureRequiredAttribute::.ctor
         
             .method public hidebysig specialname 
                 instance string get_FeatureName () cil managed 
             {
                 ldarg.0
                 ldfld string System.Runtime.CompilerServices.CompilerFeatureRequiredAttribute::'<FeatureName>k__BackingField'
                 ret
             } // end of method CompilerFeatureRequiredAttribute::get_FeatureName
         
             .method public hidebysig specialname 
                 instance bool get_IsOptional () cil managed 
             {
                 ldarg.0
                 ldfld bool System.Runtime.CompilerServices.CompilerFeatureRequiredAttribute::'<IsOptional>k__BackingField'
                 ret
             } // end of method CompilerFeatureRequiredAttribute::get_IsOptional
         
             .method public hidebysig specialname 
                 instance void modreq([mscorlib]System.Runtime.CompilerServices.IsExternalInit) set_IsOptional (
                     bool 'value'
                 ) cil managed 
             {
                 ldarg.0
                 ldarg.1
                 stfld bool System.Runtime.CompilerServices.CompilerFeatureRequiredAttribute::'<IsOptional>k__BackingField'
                 ret
             } // end of method CompilerFeatureRequiredAttribute::set_IsOptional
         
             // Properties
             .property instance string FeatureName()
             {
                 .get instance string System.Runtime.CompilerServices.CompilerFeatureRequiredAttribute::get_FeatureName()
             }
             .property instance bool IsOptional()
             {
                 .get instance bool System.Runtime.CompilerServices.CompilerFeatureRequiredAttribute::get_IsOptional()
                 .set instance void modreq([mscorlib]System.Runtime.CompilerServices.IsExternalInit) System.Runtime.CompilerServices.CompilerFeatureRequiredAttribute::set_IsOptional(bool)
             }
         
         } // end of class System.Runtime.CompilerServices.CompilerFeatureRequiredAttribute
        """;
 
    // This IL is equivalent to the following definition, which format holes for the required attribute under test:
    // [CompilerFeatureRequired("test")]
    // public class OnType
    // {
    // }
    // 
    // public class OnMethod
    // {
    //     [CompilerFeatureRequired("test")]
    //     public static void M() {}
    // }
    // 
    // public class OnMethodReturn
    // {
    //     [return: CompilerFeatureRequired("test")]
    //     public static void M() {}
    // }
    // 
    // public class OnParameter
    // {
    //     public static void M([CompilerFeatureRequired("test")] int param) {}
    // }
    // 
    // public class OnField
    // {
    //     [CompilerFeatureRequired("test")]
    //     public static int Field;
    // }
    // 
    // public class OnProperty
    // {
    //     [CompilerFeatureRequired("test")]
    //     public static int Property { get => 0; set {} }
    // }
    // 
    // public class OnPropertySetter
    // {
    //     public static int Property { get => 0; [CompilerFeatureRequired("test")] set {} }
    // }
    // 
    // public class OnPropertyGetter
    // {
    //     public static int Property { [CompilerFeatureRequired("test")] get => 0; set {} }
    // }
    // 
    // public class OnEvent
    // {
    //     [CompilerFeatureRequired("test")]
    //     public static event Action Event { add {} remove {} }
    // }
    // 
    // public class OnEventAdder
    // {
    //     public static event Action Event { [CompilerFeatureRequired("test")] add {} remove {} }
    // }
    // 
    // public class OnEventRemover
    // {
    //     public static event Action Event { [CompilerFeatureRequired("test")] add {} remove {} }
    // }
    // 
    // [CompilerFeatureRequired("test")]
    // public enum OnEnum
    // {
    //     A
    // }
    // 
    // public enum OnEnumMember
    // {
    //     [CompilerFeatureRequired("test")] A
    // }
    // 
    // public class OnClassTypeParameter<[CompilerFeatureRequired("test")] T>
    // {
    // }
    // 
    // public class OnMethodTypeParameter
    // {
    //     public static void M<[CompilerFeatureRequired("test")] T>() {}
    // }
    // 
    // [CompilerFeatureRequired("test")]
    // public delegate void OnDelegateType();
    //
    // VB:
    // Public Class OnIndexedPropertyParameter
    //    Public Property [Property](<CompilerFeatureRequired("test")> param As Integer) As Integer
    //        Get
    //            Return 1
    //        End Get
    //        Set
    //        End Set
    //    End Property
    // End Class
    private string GetTestIl(string attributeApplication)
    {
        return $$"""
            .class public auto ansi beforefieldinit OnType
                extends [mscorlib]System.Object
            {
                {{attributeApplication}}
                // Methods
                .method public hidebysig static
                    void M () cil managed 
                {
                    ret
                } // end of method OnMethod::M
            } // end of class OnType
            
            .class public auto ansi beforefieldinit OnMethod
                extends [mscorlib]System.Object
            {
                // Methods
                .method public hidebysig static
                    void M () cil managed 
                {
                    {{attributeApplication}}
                    ret
                } // end of method OnMethod::M
            } // end of class OnMethod
            
            .class public auto ansi beforefieldinit OnMethodReturn
                extends [mscorlib]System.Object
            {
                // Methods
                .method public hidebysig static
                    void M () cil managed 
                {
                    .param [0]
                        {{attributeApplication}}
                    ret
                } // end of method OnMethodReturn::M
            } // end of class OnMethodReturn
            
            .class public auto ansi beforefieldinit OnParameter
                extends [mscorlib]System.Object
            {
                // Methods
                .method public hidebysig static
                    void M (
                        int32 param
                    ) cil managed 
                {
                    .param [1]
                        {{attributeApplication}}
                    ret
                } // end of method OnParameter::M
            } // end of class OnParameter
            
            .class public auto ansi beforefieldinit OnField
                extends [mscorlib]System.Object
            {
                // Fields
                .field public static int32 Field
                {{attributeApplication}}
            } // end of class OnField
            
            .class public auto ansi beforefieldinit OnProperty
                extends [mscorlib]System.Object
            {
                // Methods
                .method public hidebysig specialname static
                    int32 get_Property () cil managed 
                {
                    ldc.i4.0
                    ret
                } // end of method OnProperty::get_Property
            
                .method public hidebysig specialname static
                    void set_Property (
                        int32 'value'
                    ) cil managed 
                {
                    ret
                } // end of method OnProperty::set_Property
            
                // Properties
                .property int32 Property()
                {
                    {{attributeApplication}}
                    .get int32 OnProperty::get_Property()
                    .set void OnProperty::set_Property(int32)
                }
            
            } // end of class OnProperty
            
            .class public auto ansi beforefieldinit OnPropertySetter
                extends [mscorlib]System.Object
            {
                // Methods
                .method public hidebysig specialname static
                    int32 get_Property () cil managed 
                {
                    ldc.i4.0
                    ret
                } // end of method OnPropertySetter::get_Property
 
                .method public hidebysig specialname static
                    void set_Property (
                        int32 'value'
                    ) cil managed 
                {
                    {{attributeApplication}}
                    ret
                } // end of method OnPropertySetter::set_Property
 
                // Properties
                .property int32 Property()
                {
                    .get int32 OnPropertySetter::get_Property()
                    .set void OnPropertySetter::set_Property(int32)
                }
            } // end of class OnPropertySetter
 
            .class public auto ansi beforefieldinit OnPropertyGetter
                extends [mscorlib]System.Object
            {
                // Methods
                .method public hidebysig specialname static
                    int32 get_Property () cil managed 
                {
                    {{attributeApplication}}
                    ldc.i4.0
                    ret
                } // end of method OnPropertyGetter::get_Property
            
                .method public hidebysig specialname static
                    void set_Property (
                        int32 'value'
                    ) cil managed 
                {
                    ret
                } // end of method OnPropertyGetter::set_Property
 
                // Properties
                .property int32 Property()
                {
                    .get int32 OnPropertyGetter::get_Property()
                    .set void OnPropertyGetter::set_Property(int32)
                }
            
            } // end of class OnPropertyGetter
            
            .class public auto ansi beforefieldinit OnEvent
                extends [mscorlib]System.Object
            {
                // Methods
                .method public hidebysig specialname static
                    void add_Event (
                        class [mscorlib]System.Action 'value'
                    ) cil managed 
                {
                    ret
                } // end of method OnEvent::add_Event
            
                .method public hidebysig specialname static
                    void remove_Event (
                        class [mscorlib]System.Action 'value'
                    ) cil managed 
                {
                    ret
                } // end of method OnEvent::remove_Event
 
                // Events
                .event [mscorlib]System.Action Event
                {
                    {{attributeApplication}}
                    .addon void OnEvent::add_Event(class [mscorlib]System.Action)
                    .removeon void OnEvent::remove_Event(class [mscorlib]System.Action)
                }
            
            
            } // end of class OnEvent
            
            .class public auto ansi beforefieldinit OnEventAdder
                extends [mscorlib]System.Object
            {
                // Methods
                .method public hidebysig specialname static
                    void add_Event (
                        class [mscorlib]System.Action 'value'
                    ) cil managed 
                {
                    {{attributeApplication}}
                    ret
                } // end of method OnEventAdder::add_Event
 
                .method public hidebysig specialname static
                    void remove_Event (
                        class [mscorlib]System.Action 'value'
                    ) cil managed 
                {
                    ret
                } // end of method OnEventAdder::remove_Event
 
                // Events
                .event [mscorlib]System.Action Event
                {
                    .addon void OnEventAdder::add_Event(class [mscorlib]System.Action)
                    .removeon void OnEventAdder::remove_Event(class [mscorlib]System.Action)
                }
            
            
            } // end of class OnEventAdder
            
            .class public auto ansi beforefieldinit OnEventRemover
                extends [mscorlib]System.Object
            {
                // Methods
                .method public hidebysig specialname static
                    void add_Event (
                        class [mscorlib]System.Action 'value'
                    ) cil managed 
                {
                    ret
                } // end of method OnEventRemover::add_Event
            
                .method public hidebysig specialname static
                    void remove_Event (
                        class [mscorlib]System.Action 'value'
                    ) cil managed 
                {
                    {{attributeApplication}}
                    ret
                } // end of method OnEventRemover::remove_Event
 
                // Events
                .event [mscorlib]System.Action Event
                {
                    .addon void OnEventRemover::add_Event(class [mscorlib]System.Action)
                    .removeon void OnEventRemover::remove_Event(class [mscorlib]System.Action)
                }
            
            
            } // end of class OnEventRemover
            
            .class public auto ansi sealed OnEnum
                extends [mscorlib]System.Enum
            {
                {{attributeApplication}}
                // Fields
                .field public specialname rtspecialname int32 value__
                .field public static literal valuetype OnEnum A = int32(0)
            
            } // end of class OnEnum
            
            .class public auto ansi sealed OnEnumMember
                extends [mscorlib]System.Enum
            {
                // Fields
                .field public specialname rtspecialname int32 value__
                .field public static literal valuetype OnEnumMember A = int32(0)
                {{attributeApplication}}
            
            } // end of class OnEnumMember
            
            .class public auto ansi beforefieldinit OnClassTypeParameter`1<T>
                extends [mscorlib]System.Object
            {
                .param type T
                    {{attributeApplication}}
            } // end of class OnClassTypeParameter`1
            
            .class public auto ansi beforefieldinit OnMethodTypeParameter
                extends [mscorlib]System.Object
            {
                // Methods
                .method public hidebysig static
                    void M<T> () cil managed 
                {
                    .param type T
                        {{attributeApplication}}
                    ret
                } // end of method OnMethodTypeParameter::M
            } // end of class OnMethodTypeParameter
            
            .class public auto ansi sealed OnDelegateType
                extends [mscorlib]System.MulticastDelegate
            {
                {{attributeApplication}}
                // Methods
                .method public hidebysig specialname rtspecialname 
                    instance void .ctor (
                        object 'object',
                        native int 'method'
                    ) runtime managed 
                {
                } // end of method OnDelegateType::.ctor
            
                .method public hidebysig newslot virtual 
                    instance void Invoke () runtime managed 
                {
                } // end of method OnDelegateType::Invoke
            
                .method public hidebysig newslot virtual 
                    instance class [mscorlib]System.IAsyncResult BeginInvoke (
                        class [mscorlib]System.AsyncCallback callback,
                        object 'object'
                    ) runtime managed 
                {
                } // end of method OnDelegateType::BeginInvoke
            
                .method public hidebysig newslot virtual 
                    instance void EndInvoke (
                        class [mscorlib]System.IAsyncResult result
                    ) runtime managed 
                {
                } // end of method OnDelegateType::EndInvoke
            
            } // end of class OnDelegateType
 
            .class public auto ansi OnIndexedPropertyParameter
                extends [mscorlib]System.Object
            {
                // Methods
                .method public specialname static
                    int32 get_Property (
                        int32 param
                    ) cil managed 
                {
                    .param [1]
                        {{attributeApplication}}
 
                    ldc.i4.1
                    stloc.0
                    ldloc.0
                    ret
                } // end of method OnIndexedPropertyParameter::get_Property
            
                .method public specialname static
                    void set_Property (
                        int32 param,
                        int32 Value
                    ) cil managed 
                {
                    .param [1]
                        {{attributeApplication}}
            
                    ret
                } // end of method OnIndexedPropertyParameter::set_Property
            
                // Properties
                .property int32 Property(
                    int32 param
                )
                {
                    .get int32 OnIndexedPropertyParameter::get_Property(int32)
                    .set void OnIndexedPropertyParameter::set_Property(int32, int32)
                }
            
            } // end of class OnIndexedPropertyParameter
 
            .class public auto ansi beforefieldinit OnThisIndexerParameter
                extends [mscorlib]System.Object
            {
                .custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string) = (
                    01 00 04 49 74 65 6d 00 00
                )
                // Methods
                .method public hidebysig specialname 
                    instance int32 get_Item (
                        int32 i
                    ) cil managed 
                {
                    .param [1]
                        {{attributeApplication}}
                    ldc.i4.0
                    ret
                } // end of method OnThisIndexerParameter::get_Item
 
                .method public hidebysig specialname 
                    instance void set_Item (
                        int32 i,
                        int32 'value'
                    ) cil managed 
                {
                    .param [1]
                        {{attributeApplication}}
                    ret
                } // end of method OnThisIndexerParameter::set_Item
 
                .method public hidebysig specialname rtspecialname 
                    instance void .ctor () cil managed 
                {
                    ldarg.0
                    call instance void [mscorlib]System.Object::.ctor()
                    ret
                } // end of method OnThisIndexerParameter::.ctor
 
                // Properties
                .property instance int32 Item(
                    int32 i
                )
                {
                    .get instance int32 OnThisIndexerParameter::get_Item(int32)
                    .set instance void OnThisIndexerParameter::set_Item(int32, int32)
                }
            } // end of class OnThisIndexerParameter
            """;
    }
 
    protected abstract TSource GetUsage();
    protected abstract TCompilation CreateCompilationWithIL(TSource source, string ilSource);
    protected abstract TCompilation CreateCompilation(TSource source, MetadataReference[] references);
    protected abstract CompilationVerifier CompileAndVerify(TCompilation compilation);
 
    [Theory]
    [InlineData(true)]
    [InlineData(false)]
    [InlineData(null)]
    public void OnNormalSymbols(bool? isOptional)
    {
        var testIl = $"""
        {CompilerFeatureRequiredIl}
        {GetTestIl(CompilerFeatureRequiredApplication(isOptional: isOptional))}
        """;
        var comp = CreateCompilationWithIL(source: GetUsage(), ilSource: testIl);
 
        if (isOptional == true)
        {
            CompileAndVerify(comp).VerifyDiagnostics();
        }
        else
        {
            AssertNormalErrors(comp);
        }
    }
 
    protected abstract void AssertNormalErrors(TCompilation compilation);
 
    [Theory]
    [InlineData(true)]
    [InlineData(false)]
    [InlineData(null)]
    public void OnAssembly(bool? isOptional)
    {
        var il = $$"""
        .assembly 'AssemblyTest'
        {
            {{CompilerFeatureRequiredApplication(isOptional: isOptional)}}
        }
 
        .assembly extern mscorlib 
        {
            .publickeytoken = (B7 7A 5C 56 19 34 E0 89)
            .ver 4:0:0:0
        } 
 
        {{CompilerFeatureRequiredIl}}
        {{GetTestIl(attributeApplication: "")}}
        """;
 
        var compiledIl = CompileIL(il, prependDefaultHeader: false);
 
        var comp = CreateCompilation(source: GetUsage(), references: new[] { compiledIl });
 
        if (isOptional == true)
        {
            CompileAndVerify(comp).VerifyDiagnostics();
        }
        else
        {
            AssertAssemblyErrors(comp, compiledIl);
        }
    }
 
    protected abstract void AssertAssemblyErrors(TCompilation compilation, MetadataReference ilRef);
 
    [Theory]
    [InlineData(true)]
    [InlineData(false)]
    [InlineData(null)]
    public void OnModule(bool? isOptional)
    {
        var il = $$"""
        .module OnModule
        {{CompilerFeatureRequiredApplication(isOptional: isOptional)}}
 
        {{CompilerFeatureRequiredIl}}
        {{GetTestIl(attributeApplication: "")}}
        """;
 
        var compiledIl = CompileIL(il);
        var comp = CreateCompilation(source: GetUsage(), references: new[] { compiledIl });
 
        if (isOptional == true)
        {
            CompileAndVerify(comp).VerifyDiagnostics();
        }
        else
        {
            AssertModuleErrors(comp, compiledIl);
        }
    }
 
    protected abstract void AssertModuleErrors(TCompilation compilation, MetadataReference ilRef);
}