File: Attributes\AttributeTests_Embedded.cs
Web Access
Project: src\src\Compilers\CSharp\Test\Emit3\Microsoft.CodeAnalysis.CSharp.Emit3.UnitTests.csproj (Microsoft.CodeAnalysis.CSharp.Emit3.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 Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.Test.Utilities;
using System.Linq;
using Xunit;
 
namespace Microsoft.CodeAnalysis.CSharp.UnitTests
{
    public class AttributeTests_Embedded : CSharpTestBase
    {
        [Fact]
        public void ReferencingEmbeddedAttributesFromTheSameAssemblySucceeds()
        {
            var code = @"
namespace Microsoft.CodeAnalysis
{
    internal class EmbeddedAttribute : System.Attribute { }
}
namespace TestReference
{
    [Microsoft.CodeAnalysis.Embedded]
    internal class TestType1 { }
 
    [Microsoft.CodeAnalysis.EmbeddedAttribute]
    internal class TestType2 { }
 
    internal class TestType3 { }
}
class Program
{
    public static void Main()
    {
        var obj1 = new TestReference.TestType1();
        var obj2 = new TestReference.TestType2();
        var obj3 = new TestReference.TestType3();
    }
}";
 
            CreateCompilation(code, parseOptions: TestOptions.Regular.WithNoRefSafetyRulesAttribute()).VerifyEmitDiagnostics();
        }
 
        [Fact]
        public void ReferencingEmbeddedAttributesFromADifferentAssemblyFails_Internal()
        {
            var reference = CreateCompilation(@"
[assembly:System.Runtime.CompilerServices.InternalsVisibleToAttribute(""Source"")]
namespace Microsoft.CodeAnalysis
{
    internal class EmbeddedAttribute : System.Attribute { }
}
namespace TestReference
{
    [Microsoft.CodeAnalysis.Embedded]
    internal class TestType1 { }
 
    [Microsoft.CodeAnalysis.EmbeddedAttribute]
    internal class TestType2 { }
 
    internal class TestType3 { }
}");
 
            var code = @"
class Program
{
    public static void Main()
    {
        var obj1 = new TestReference.TestType1();
        var obj2 = new TestReference.TestType2();
        var obj3 = new TestReference.TestType3(); // This should be fine
    }
}";
 
            CreateCompilation(code, references: new[] { reference.ToMetadataReference() }, assemblyName: "Source").VerifyDiagnostics(
                // (6,38): error CS0234: The type or namespace name 'TestType1' does not exist in the namespace 'TestReference' (are you missing an assembly reference?)
                //         var obj1 = new TestReference.TestType1();
                Diagnostic(ErrorCode.ERR_DottedTypeNameNotFoundInNS, "TestType1").WithArguments("TestType1", "TestReference").WithLocation(6, 38),
                // (7,38): error CS0234: The type or namespace name 'TestType2' does not exist in the namespace 'TestReference' (are you missing an assembly reference?)
                //         var obj2 = new TestReference.TestType2();
                Diagnostic(ErrorCode.ERR_DottedTypeNameNotFoundInNS, "TestType2").WithArguments("TestType2", "TestReference").WithLocation(7, 38));
        }
 
        [Fact]
        public void ReferencingEmbeddedAttributesFromADifferentAssemblyFails_Module()
        {
            var module = CreateCompilation(@"
namespace Microsoft.CodeAnalysis
{
    internal class EmbeddedAttribute : System.Attribute { }
}
namespace TestReference
{
    [Microsoft.CodeAnalysis.Embedded]
    internal class TestType1 { }
 
    [Microsoft.CodeAnalysis.EmbeddedAttribute]
    internal class TestType2 { }
 
    internal class TestType3 { }
}", options: TestOptions.ReleaseModule);
 
            var reference = ModuleMetadata.CreateFromImage(module.EmitToArray()).GetReference();
 
            var code = @"
class Program
{
    public static void Main()
    {
        var obj1 = new TestReference.TestType1();
        var obj2 = new TestReference.TestType2();
        var obj3 = new TestReference.TestType3(); // This should be fine
    }
}";
 
            CreateCompilation(code, references: new[] { reference }, assemblyName: "Source").VerifyDiagnostics(
                // (6,38): error CS0234: The type or namespace name 'TestType1' does not exist in the namespace 'TestReference' (are you missing an assembly reference?)
                //         var obj1 = new TestReference.TestType1();
                Diagnostic(ErrorCode.ERR_DottedTypeNameNotFoundInNS, "TestType1").WithArguments("TestType1", "TestReference").WithLocation(6, 38),
                // (7,38): error CS0234: The type or namespace name 'TestType2' does not exist in the namespace 'TestReference' (are you missing an assembly reference?)
                //         var obj2 = new TestReference.TestType2();
                Diagnostic(ErrorCode.ERR_DottedTypeNameNotFoundInNS, "TestType2").WithArguments("TestType2", "TestReference").WithLocation(7, 38));
        }
 
        [Fact]
        public void ReferencingEmbeddedAttributesFromADifferentAssemblyFails_Public()
        {
            var reference = CreateCompilation(@"
namespace Microsoft.CodeAnalysis
{
    internal class EmbeddedAttribute : System.Attribute { }
}
namespace TestReference
{
    [Microsoft.CodeAnalysis.Embedded]
    public class TestType1 { }
 
    [Microsoft.CodeAnalysis.EmbeddedAttribute]
    public class TestType2 { }
 
    public class TestType3 { }
}");
 
            var code = @"
class Program
{
    public static void Main()
    {
        var obj1 = new TestReference.TestType1();
        var obj2 = new TestReference.TestType2();
        var obj3 = new TestReference.TestType3(); // This should be fine
    }
}";
 
            CreateCompilation(code, references: new[] { reference.ToMetadataReference() }).VerifyDiagnostics(
                // (6,38): error CS0234: The type or namespace name 'TestType1' does not exist in the namespace 'TestReference' (are you missing an assembly reference?)
                //         var obj1 = new TestReference.TestType1();
                Diagnostic(ErrorCode.ERR_DottedTypeNameNotFoundInNS, "TestType1").WithArguments("TestType1", "TestReference").WithLocation(6, 38),
                // (7,38): error CS0234: The type or namespace name 'TestType2' does not exist in the namespace 'TestReference' (are you missing an assembly reference?)
                //         var obj2 = new TestReference.TestType2();
                Diagnostic(ErrorCode.ERR_DottedTypeNameNotFoundInNS, "TestType2").WithArguments("TestType2", "TestReference").WithLocation(7, 38));
        }
 
        [Fact]
        public void EmbeddedAttributeInSourceIsAllowedIfCompilerDoesNotNeedToGenerateOne()
        {
            var code = @"
namespace Microsoft.CodeAnalysis
{
    public class EmbeddedAttribute : System.Attribute { }
}
namespace OtherNamespace
{
    [Microsoft.CodeAnalysis.EmbeddedAttribute]
    public class TestReference
    {
        public static int GetValue() => 3;
    }
}
class Test
{
    public static void Main()
    {
        // This should be fine, as the compiler doesn't need to use an embedded attribute for this compilation
        System.Console.Write(OtherNamespace.TestReference.GetValue());
    }
}";
 
            CompileAndVerify(code, parseOptions: TestOptions.Regular.WithNoRefSafetyRulesAttribute(), verify: Verification.Passes, expectedOutput: "3");
        }
 
        [Fact]
        public void EmbeddedAttributeInSourceShouldTriggerAnErrorIfCompilerNeedsToGenerateOne()
        {
            var code = @"
namespace Microsoft.CodeAnalysis
{
    public class EmbeddedAttribute : System.Attribute { }
}
class Test
{
    public void M(in int p)
    {
        // This should trigger generating another EmbeddedAttribute
    }
}";
 
            CreateCompilation(code, assemblyName: "testModule").VerifyEmitDiagnostics(
                // (4,18): error CS8336: The type name 'Microsoft.CodeAnalysis.EmbeddedAttribute' is reserved to be used by the compiler.
                //     public class EmbeddedAttribute : System.Attribute { }
                Diagnostic(ErrorCode.ERR_TypeReserved, "EmbeddedAttribute").WithArguments("Microsoft.CodeAnalysis.EmbeddedAttribute").WithLocation(4, 18));
        }
 
        [Fact]
        public void EmbeddedAttributeInReferencedModuleShouldTriggerAnErrorIfCompilerNeedsToGenerateOne()
        {
            var module = CreateCompilation(options: TestOptions.ReleaseModule, assemblyName: "testModule", source: @"
namespace Microsoft.CodeAnalysis
{
    public class EmbeddedAttribute : System.Attribute { }
}");
 
            var moduleRef = ModuleMetadata.CreateFromImage(module.EmitToArray()).GetReference();
 
            var code = @"
class Test
{
    public void M(in int p)
    {
        // This should trigger generating another EmbeddedAttribute
    }
}";
 
            CreateCompilation(code, references: new[] { moduleRef }).VerifyEmitDiagnostics(
                // error CS8004: Type 'EmbeddedAttribute' exported from module 'testModule.netmodule' conflicts with type declared in primary module of this assembly.
                Diagnostic(ErrorCode.ERR_ExportedTypeConflictsWithDeclaration).WithArguments("Microsoft.CodeAnalysis.EmbeddedAttribute", "testModule.netmodule").WithLocation(1, 1));
        }
 
        [Fact]
        public void EmbeddedAttributeForwardedToAnotherAssemblyShouldTriggerAnError()
        {
            var reference = CreateCompilation(@"
namespace Microsoft.CodeAnalysis
{
    public class EmbeddedAttribute : System.Attribute { }
}", assemblyName: "reference").ToMetadataReference();
 
            var code = @"
[assembly: System.Runtime.CompilerServices.TypeForwardedToAttribute(typeof(Microsoft.CodeAnalysis.EmbeddedAttribute))]
class Test
{
    public void M(in int p)
    {
        // This should trigger generating another EmbeddedAttribute
    }
}";
 
            CreateCompilation(code, references: new[] { reference }).VerifyEmitDiagnostics(
                // error CS8006: Forwarded type 'EmbeddedAttribute' conflicts with type declared in primary module of this assembly.
                Diagnostic(ErrorCode.ERR_ForwardedTypeConflictsWithDeclaration).WithArguments("Microsoft.CodeAnalysis.EmbeddedAttribute").WithLocation(1, 1));
        }
 
        [Fact]
        public void CompilerShouldIgnorePublicEmbeddedAttributesInReferencedAssemblies()
        {
            var reference = CreateCompilation(assemblyName: "testRef", parseOptions: TestOptions.Regular.WithNoRefSafetyRulesAttribute(), source: @"
namespace Microsoft.CodeAnalysis
{
    public class EmbeddedAttribute : System.Attribute { }
}
namespace OtherNamespace
{
    public class TestReference { }
}").ToMetadataReference();
 
            var code = @"
class Test
{
    // This should trigger generating another EmbeddedAttribute
    public void M(in int p)
    {
        var obj = new OtherNamespace.TestReference(); // This should be fine
    }
}";
 
            CompileAndVerify(code, verify: Verification.Passes, references: new[] { reference }, symbolValidator: module =>
            {
                var attributeName = AttributeDescription.CodeAnalysisEmbeddedAttribute.FullName;
 
                var referenceAttribute = module.GetReferencedAssemblySymbols().Single(assembly => assembly.Name == "testRef").GetTypeByMetadataName(attributeName);
                Assert.NotNull(referenceAttribute);
 
                var generatedAttribute = module.ContainingAssembly.GetTypeByMetadataName(attributeName);
                Assert.NotNull(generatedAttribute);
 
                Assert.False(referenceAttribute.Equals(generatedAttribute));
            });
        }
 
        [Fact]
        public void SynthesizingAttributeRequiresSystemAttribute_NonExisting()
        {
            var code = @"
namespace System
{
    public class Object {}
    public class Void {}
    public class ValueType {}
    public struct Int32 { }
}
public class Test
{
    public void M(in object x) { } // should trigger synthesizing IsReadOnly
}";
 
            CreateEmptyCompilation(code).VerifyEmitDiagnostics(CodeAnalysis.Emit.EmitOptions.Default.WithRuntimeMetadataVersion("v4.0.30319"),
                // error CS0518: Predefined type 'System.Attribute' is not defined or imported
                Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound).WithArguments("System.Attribute").WithLocation(1, 1),
                // error CS0518: Predefined type 'System.Attribute' is not defined or imported
                Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound).WithArguments("System.Attribute").WithLocation(1, 1),
                // error CS0518: Predefined type 'System.Attribute' is not defined or imported
                Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound).WithArguments("System.Attribute").WithLocation(1, 1),
                // error CS0656: Missing compiler required member 'System.AttributeUsageAttribute..ctor'
                Diagnostic(ErrorCode.ERR_MissingPredefinedMember).WithArguments("System.AttributeUsageAttribute", ".ctor").WithLocation(1, 1),
                // error CS0656: Missing compiler required member 'System.AttributeUsageAttribute.AllowMultiple'
                Diagnostic(ErrorCode.ERR_MissingPredefinedMember).WithArguments("System.AttributeUsageAttribute", "AllowMultiple").WithLocation(1, 1),
                // error CS0656: Missing compiler required member 'System.AttributeUsageAttribute.Inherited'
                Diagnostic(ErrorCode.ERR_MissingPredefinedMember).WithArguments("System.AttributeUsageAttribute", "Inherited").WithLocation(1, 1));
        }
 
        [Fact]
        public void SynthesizingAttributeRequiresSystemAttribute_NoSystemObject()
        {
            var code = @"
namespace System
{
    public class Attribute {}
    public class AttributeUsageAttribute : Attribute
    {
        public AttributeUsageAttribute(AttributeTargets t) { }
        public bool AllowMultiple { get; set; }
        public bool Inherited { get; set; }
    }
    public class ValueType { }
    public struct Enum { }
    public enum AttributeTargets { }
    public class Void {}
    public struct Int32 { }
    public struct Boolean { }
}
public class Test
{
    public object M(in object x) { return x; } // should trigger synthesizing IsReadOnly
}";
 
            CreateEmptyCompilation(code).VerifyEmitDiagnostics(CodeAnalysis.Emit.EmitOptions.Default.WithRuntimeMetadataVersion("v4.0.30319"),
                // (4,18): error CS0518: Predefined type 'System.Object' is not defined or imported
                //     public class Attribute {}
                Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "Attribute").WithArguments("System.Object").WithLocation(4, 18),
                // (11,18): error CS0518: Predefined type 'System.Object' is not defined or imported
                //     public class ValueType { }
                Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "ValueType").WithArguments("System.Object").WithLocation(11, 18),
                // (14,18): error CS0518: Predefined type 'System.Object' is not defined or imported
                //     public class Void {}
                Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "Void").WithArguments("System.Object").WithLocation(14, 18),
                // (18,14): error CS0518: Predefined type 'System.Object' is not defined or imported
                // public class Test
                Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "Test").WithArguments("System.Object").WithLocation(18, 14),
                // (20,24): error CS0518: Predefined type 'System.Object' is not defined or imported
                //     public object M(in object x) { return x; } // should trigger synthesizing IsReadOnly
                Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "object").WithArguments("System.Object").WithLocation(20, 24),
                // (20,12): error CS0518: Predefined type 'System.Object' is not defined or imported
                //     public object M(in object x) { return x; } // should trigger synthesizing IsReadOnly
                Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "object").WithArguments("System.Object").WithLocation(20, 12),
                // (7,40): error CS0518: Predefined type 'System.Object' is not defined or imported
                //         public AttributeUsageAttribute(AttributeTargets t) { }
                Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "AttributeTargets").WithArguments("System.Object").WithLocation(7, 40),
                // (14,18): error CS1729: 'object' does not contain a constructor that takes 0 arguments
                //     public class Void {}
                Diagnostic(ErrorCode.ERR_BadCtorArgCount, "Void").WithArguments("object", "0").WithLocation(14, 18),
                // (4,18): error CS1729: 'object' does not contain a constructor that takes 0 arguments
                //     public class Attribute {}
                Diagnostic(ErrorCode.ERR_BadCtorArgCount, "Attribute").WithArguments("object", "0").WithLocation(4, 18),
                // (11,18): error CS1729: 'object' does not contain a constructor that takes 0 arguments
                //     public class ValueType { }
                Diagnostic(ErrorCode.ERR_BadCtorArgCount, "ValueType").WithArguments("object", "0").WithLocation(11, 18),
                // (18,14): error CS1729: 'object' does not contain a constructor that takes 0 arguments
                // public class Test
                Diagnostic(ErrorCode.ERR_BadCtorArgCount, "Test").WithArguments("object", "0").WithLocation(18, 14));
        }
 
        [Fact]
        public void SynthesizingAttributeRequiresSystemAttribute_NoSystemVoid()
        {
            var code = @"
namespace System
{
    public class Attribute {}
    public class AttributeUsageAttribute : Attribute
    {
        public AttributeUsageAttribute(AttributeTargets t) { }
        public bool AllowMultiple { get; set; }
        public bool Inherited { get; set; }
    }
    public class ValueType { }
    public struct Int32 { }
    public struct Boolean { }
    public struct Enum { }
    public enum AttributeTargets { }
    public class Object {}
}
public class Test
{
    public object M(in object x) { return x; } // should trigger synthesizing IsReadOnly
}";
 
            CreateEmptyCompilation(code).VerifyEmitDiagnostics(CodeAnalysis.Emit.EmitOptions.Default.WithRuntimeMetadataVersion("v4.0.30319"),
                // (8,42): error CS0518: Predefined type 'System.Void' is not defined or imported
                //         public bool AllowMultiple { get; set; }
                Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "set;").WithArguments("System.Void").WithLocation(8, 42),
                // (9,38): error CS0518: Predefined type 'System.Void' is not defined or imported
                //         public bool Inherited { get; set; }
                Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "set;").WithArguments("System.Void").WithLocation(9, 38),
                // (7,9): error CS0518: Predefined type 'System.Void' is not defined or imported
                //         public AttributeUsageAttribute(AttributeTargets t) { }
                Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "public AttributeUsageAttribute(AttributeTargets t) { }").WithArguments("System.Void").WithLocation(7, 9),
                // (4,18): error CS0518: Predefined type 'System.Void' is not defined or imported
                //     public class Attribute {}
                Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "Attribute").WithArguments("System.Void").WithLocation(4, 18),
                // (11,18): error CS0518: Predefined type 'System.Void' is not defined or imported
                //     public class ValueType { }
                Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "ValueType").WithArguments("System.Void").WithLocation(11, 18),
                // (18,14): error CS0518: Predefined type 'System.Void' is not defined or imported
                // public class Test
                Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "Test").WithArguments("System.Void").WithLocation(18, 14),
                // (7,16): error CS0518: Predefined type 'System.Void' is not defined or imported
                //         public AttributeUsageAttribute(AttributeTargets t) { }
                Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "AttributeUsageAttribute").WithArguments("System.Void").WithLocation(7, 16),
                // error CS0518: Predefined type 'System.Void' is not defined or imported
                Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound).WithArguments("System.Void").WithLocation(1, 1),
                // error CS0518: Predefined type 'System.Void' is not defined or imported
                Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound).WithArguments("System.Void").WithLocation(1, 1),
                // error CS0518: Predefined type 'System.Void' is not defined or imported
                Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound).WithArguments("System.Void").WithLocation(1, 1));
        }
 
        [Fact]
        public void SynthesizingAttributeRequiresSystemAttribute_NoDefaultConstructor()
        {
            var code = @"
namespace System
{
    public class Object {}
    public class Void {}
    public class ValueType { }
    public struct Int32 { }
    public struct Boolean { }
    public class Attribute
    {
        public Attribute(object p) { }
    }
    public class AttributeUsageAttribute : Attribute
    {
        public AttributeUsageAttribute(AttributeTargets t) : base(null) { }
        public bool AllowMultiple { get; set; }
        public bool Inherited { get; set; }
    }
    public struct Enum { }
    public enum AttributeTargets { }
}
public class Test
{
    public void M(in object x) { } // should trigger synthesizing IsReadOnly
}";
 
            CreateEmptyCompilation(code).VerifyEmitDiagnostics(CodeAnalysis.Emit.EmitOptions.Default.WithRuntimeMetadataVersion("v4.0.30319"),
                // error CS1729: 'Attribute' does not contain a constructor that takes 0 arguments
                Diagnostic(ErrorCode.ERR_BadCtorArgCount).WithArguments("System.Attribute", "0").WithLocation(1, 1),
                // error CS1729: 'Attribute' does not contain a constructor that takes 0 arguments
                Diagnostic(ErrorCode.ERR_BadCtorArgCount).WithArguments("System.Attribute", "0").WithLocation(1, 1),
                // error CS1729: 'Attribute' does not contain a constructor that takes 0 arguments
                Diagnostic(ErrorCode.ERR_BadCtorArgCount).WithArguments("System.Attribute", "0").WithLocation(1, 1));
        }
 
        [Fact]
        public void EmbeddedTypesInAnAssemblyAreNotExposedExternally()
        {
            var compilation1 = CreateCompilation(parseOptions: TestOptions.Regular.WithNoRefSafetyRulesAttribute(), source: @"
namespace Microsoft.CodeAnalysis
{
    public class EmbeddedAttribute : System.Attribute { }
}
[Microsoft.CodeAnalysis.Embedded]
public class TestReference1 { }
public class TestReference2 { }
");
 
            Assert.NotNull(compilation1.GetTypeByMetadataName("TestReference1"));
            Assert.NotNull(compilation1.GetTypeByMetadataName("TestReference2"));
 
            var compilation2 = CreateCompilation("", references: new[] { compilation1.EmitToImageReference() });
 
            Assert.Null(compilation2.GetTypeByMetadataName("TestReference1"));
            Assert.NotNull(compilation2.GetTypeByMetadataName("TestReference2"));
        }
    }
}