File: Attributes\AttributeTests_NullablePublicOnly.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 System;
using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Symbols;
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
{
    public class AttributeTests_NullablePublicOnly : CSharpTestBase
    {
        [Fact]
        public void ExplicitAttribute_FromSource()
        {
            var source =
@"public class A<T>
{
}
public class B : A<object?>
{
}";
            var options = WithNullableEnable().WithMetadataImportOptions(MetadataImportOptions.All);
            var parseOptions = TestOptions.Regular8;
            CSharpTestSource sources = new[] { NullablePublicOnlyAttributeDefinition, source };
 
            var comp = CreateCompilation(sources, options: options, parseOptions: parseOptions);
            CompileAndVerify(comp, symbolValidator: m => AssertNullablePublicOnlyAttribute(m, includesAttributeDefinition: true, includesAttributeUse: false, publicDefinition: true));
 
            comp = CreateCompilation(sources, options: options, parseOptions: parseOptions.WithNullablePublicOnly());
            CompileAndVerify(comp, symbolValidator: m => AssertNullablePublicOnlyAttribute(m, includesAttributeDefinition: true, includesAttributeUse: true, publicDefinition: true));
        }
 
        [Fact]
        public void ExplicitAttribute_FromMetadata()
        {
            var source =
@"public class A<T>
{
}
public class B : A<object?>
{
}";
            var comp = CreateCompilation(NullablePublicOnlyAttributeDefinition, parseOptions: TestOptions.Regular7);
            comp.VerifyDiagnostics();
            var ref1 = comp.EmitToImageReference();
 
            var options = WithNullableEnable().WithMetadataImportOptions(MetadataImportOptions.All);
            var parseOptions = TestOptions.Regular8;
 
            comp = CreateCompilation(source, references: new[] { ref1 }, options: options, parseOptions: parseOptions);
            CompileAndVerify(comp, symbolValidator: m => AssertNullablePublicOnlyAttribute(m, includesAttributeDefinition: false, includesAttributeUse: false, publicDefinition: true));
 
            comp = CreateCompilation(source, references: new[] { ref1 }, options: options, parseOptions: parseOptions.WithNullablePublicOnly());
            CompileAndVerify(comp, symbolValidator: m => AssertNullablePublicOnlyAttribute(m, includesAttributeDefinition: false, includesAttributeUse: true, publicDefinition: true));
        }
 
        [Fact]
        public void ExplicitAttribute_MissingSingleValueConstructor()
        {
            var source1 =
@"namespace System.Runtime.CompilerServices
{
    public sealed class NullablePublicOnlyAttribute : Attribute
    {
    }
}";
            var source2 =
@"public class A<T>
{
}
public class B : A<object?>
{
}";
            var options = WithNullableEnable().WithMetadataImportOptions(MetadataImportOptions.All);
            var parseOptions = TestOptions.Regular8;
 
            var comp = CreateCompilation(new[] { source1, source2 }, options: options, parseOptions: parseOptions);
            comp.VerifyEmitDiagnostics();
 
            comp = CreateCompilation(new[] { source1, source2 }, options: options, parseOptions: parseOptions.WithNullablePublicOnly());
            comp.VerifyEmitDiagnostics(
                // error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.NullablePublicOnlyAttribute..ctor'
                Diagnostic(ErrorCode.ERR_MissingPredefinedMember).WithArguments("System.Runtime.CompilerServices.NullablePublicOnlyAttribute", ".ctor").WithLocation(1, 1));
        }
 
        [Fact]
        public void EmptyProject()
        {
            var source = @"";
            var options = WithNullableEnable().WithMetadataImportOptions(MetadataImportOptions.All);
            var parseOptions = TestOptions.Regular8;
 
            var comp = CreateCompilation(source, options: options, parseOptions: parseOptions);
            CompileAndVerify(comp, symbolValidator: AssertNoNullablePublicOnlyAttribute);
 
            comp = CreateCompilation(source, options: options, parseOptions: parseOptions.WithNullablePublicOnly());
            CompileAndVerify(comp, symbolValidator: AssertNoNullablePublicOnlyAttribute);
        }
 
        [Fact]
        public void CSharp7_NoAttribute()
        {
            var source =
@"public class A<T>
{
}
public class B : A<object>
{
}";
            var options = TestOptions.ReleaseDll.WithMetadataImportOptions(MetadataImportOptions.All);
            var parseOptions = TestOptions.Regular7;
 
            var comp = CreateCompilation(source, options: options, parseOptions: parseOptions);
            CompileAndVerify(comp, symbolValidator: AssertNoNullablePublicOnlyAttribute);
 
            comp = CreateCompilation(source, options: options, parseOptions: parseOptions.WithNullablePublicOnly());
            CompileAndVerify(comp, symbolValidator: AssertNoNullablePublicOnlyAttribute);
        }
 
        [Fact]
        public void NullableEnabled()
        {
            var source =
@"public class A<T>
{
}
public class B : A<object?>
{
}";
            var options = WithNullableEnable().WithMetadataImportOptions(MetadataImportOptions.All);
            var parseOptions = TestOptions.Regular8;
 
            var comp = CreateCompilation(source, options: options, parseOptions: parseOptions);
            CompileAndVerify(comp, symbolValidator: AssertNoNullablePublicOnlyAttribute);
 
            comp = CreateCompilation(source, options: options, parseOptions: parseOptions.WithNullablePublicOnly());
            CompileAndVerify(comp, symbolValidator: AssertNullablePublicOnlyAttribute);
        }
 
        [Fact]
        public void NullableEnabled_NoPublicMembers()
        {
            var source =
@"class A<T>
{
}
class B : A<object?>
{
}";
            var options = WithNullableEnable().WithMetadataImportOptions(MetadataImportOptions.All);
            var parseOptions = TestOptions.Regular8;
 
            var comp = CreateCompilation(source, options: options, parseOptions: parseOptions);
            CompileAndVerify(comp, symbolValidator: AssertNoNullablePublicOnlyAttribute);
 
            comp = CreateCompilation(source, options: options, parseOptions: parseOptions.WithNullablePublicOnly());
            CompileAndVerify(comp, symbolValidator: AssertNoNullablePublicOnlyAttribute);
        }
 
        [Fact]
        public void NullableDisabled()
        {
            var source =
@"public class A<T>
{
}
public class B : A<object>
{
}";
            var options = WithNullableDisable().WithMetadataImportOptions(MetadataImportOptions.All);
            var parseOptions = TestOptions.Regular8;
 
            var comp = CreateCompilation(source, options: options, parseOptions: parseOptions);
            CompileAndVerify(comp, symbolValidator: AssertNoNullablePublicOnlyAttribute);
 
            comp = CreateCompilation(source, options: options, parseOptions: parseOptions.WithNullablePublicOnly());
            CompileAndVerify(comp, symbolValidator: AssertNoNullablePublicOnlyAttribute);
        }
 
        [Fact]
        public void NullableDisabled_Annotation()
        {
            var source =
@"public class A<T>
{
}
public class B : A<object?>
{
}";
            var options = WithNullableDisable().WithMetadataImportOptions(MetadataImportOptions.All);
            var parseOptions = TestOptions.Regular8;
 
            var comp = CreateCompilation(source, options: options, parseOptions: parseOptions);
            CompileAndVerify(comp, symbolValidator: AssertNoNullablePublicOnlyAttribute);
 
            comp = CreateCompilation(source, options: options, parseOptions: parseOptions.WithNullablePublicOnly());
            CompileAndVerify(comp, symbolValidator: AssertNullablePublicOnlyAttribute);
        }
 
        [Fact]
        public void NullableDisabled_OtherNullableAttributeDefinitions()
        {
            var source =
@"public class Program
{
}";
            var options = WithNullableDisable().WithMetadataImportOptions(MetadataImportOptions.All);
            var parseOptions = TestOptions.Regular8;
            CSharpTestSource sources = new[] { NullableAttributeDefinition, NullableContextAttributeDefinition, source };
 
            var comp = CreateCompilation(sources, options: options, parseOptions: parseOptions);
            CompileAndVerify(comp, symbolValidator: AssertNoNullablePublicOnlyAttribute);
 
            comp = CreateCompilation(sources, options: options, parseOptions: parseOptions.WithNullablePublicOnly());
            CompileAndVerify(comp, symbolValidator: AssertNoNullablePublicOnlyAttribute);
        }
 
        [Fact]
        public void NullableEnabled_MethodBodyOnly()
        {
            var source0 =
@"#nullable enable
public class A
{
    public static object? F;
    public static void M(object o) { }
}";
            var comp = CreateCompilation(source0);
            comp.VerifyDiagnostics();
            var ref0 = comp.EmitToImageReference();
 
            var source1 =
@"public class B
{
    static void Main()
    {
#nullable enable
        A.M(A.F);
    }
}";
            comp = CreateCompilation(
                source1,
                references: new[] { ref0 },
                options: TestOptions.ReleaseDll.WithMetadataImportOptions(MetadataImportOptions.All),
                parseOptions: TestOptions.Regular8.WithNullablePublicOnly());
            comp.VerifyDiagnostics(
                // (6,13): warning CS8604: Possible null reference argument for parameter 'o' in 'void A.M(object o)'.
                //         A.M(A.F);
                Diagnostic(ErrorCode.WRN_NullReferenceArgument, "A.F").WithArguments("o", "void A.M(object o)").WithLocation(6, 13));
            CompileAndVerify(comp, symbolValidator: AssertNoNullablePublicOnlyAttribute);
        }
 
        [Fact]
        public void NullableEnabled_AllNullableAttributeDefinitions_01()
        {
            var source =
@"#nullable enable
public class Program
{
}";
            var options = WithNullableDisable().WithMetadataImportOptions(MetadataImportOptions.All);
            var parseOptions = TestOptions.Regular8;
            CSharpTestSource sources = new[] { NullableAttributeDefinition, NullableContextAttributeDefinition, NullablePublicOnlyAttributeDefinition, source };
 
            var comp = CreateCompilation(sources, options: options, parseOptions: parseOptions);
            CompileAndVerify(comp, symbolValidator: m => AssertNullablePublicOnlyAttribute(m, includesAttributeDefinition: true, includesAttributeUse: false, publicDefinition: true));
 
            comp = CreateCompilation(sources, options: options, parseOptions: parseOptions.WithNullablePublicOnly());
            CompileAndVerify(comp, symbolValidator: m => AssertNullablePublicOnlyAttribute(m, includesAttributeDefinition: true, includesAttributeUse: false, publicDefinition: true));
        }
 
        [Fact]
        public void NullableEnabled_AllNullableAttributeDefinitions_02()
        {
            var source =
@"#nullable enable
public class Program
{
    public object F() => null!;
}";
            var options = WithNullableDisable().WithMetadataImportOptions(MetadataImportOptions.All);
            var parseOptions = TestOptions.Regular8;
            CSharpTestSource sources = new[] { NullableAttributeDefinition, NullableContextAttributeDefinition, NullablePublicOnlyAttributeDefinition, source };
 
            var comp = CreateCompilation(sources, options: options, parseOptions: parseOptions);
            CompileAndVerify(comp, symbolValidator: m => AssertNullablePublicOnlyAttribute(m, includesAttributeDefinition: true, includesAttributeUse: false, publicDefinition: true));
 
            comp = CreateCompilation(sources, options: options, parseOptions: parseOptions.WithNullablePublicOnly());
            CompileAndVerify(comp, symbolValidator: m => AssertNullablePublicOnlyAttribute(m, includesAttributeDefinition: true, includesAttributeUse: true, publicDefinition: true));
        }
 
        [Fact]
        public void NullableEnabled_OtherNullableAttributeDefinitions_01()
        {
            var source =
@"#nullable enable
public class Program
{
}";
            var options = WithNullableDisable().WithMetadataImportOptions(MetadataImportOptions.All);
            var parseOptions = TestOptions.Regular8;
            CSharpTestSource sources = new[] { NullableAttributeDefinition, NullableContextAttributeDefinition, source };
 
            var comp = CreateCompilation(sources, options: options, parseOptions: parseOptions);
            CompileAndVerify(comp, symbolValidator: AssertNoNullablePublicOnlyAttribute);
 
            comp = CreateCompilation(sources, options: options, parseOptions: parseOptions.WithNullablePublicOnly());
            CompileAndVerify(comp, symbolValidator: AssertNoNullablePublicOnlyAttribute);
        }
 
        [Fact]
        public void NullableEnabled_OtherNullableAttributeDefinitions_02()
        {
            var source =
@"#nullable enable
public class Program
{
    public object F() => null!;
}";
            var options = WithNullableDisable().WithMetadataImportOptions(MetadataImportOptions.All);
            var parseOptions = TestOptions.Regular8;
            CSharpTestSource sources = new[] { NullableAttributeDefinition, NullableContextAttributeDefinition, source };
 
            var comp = CreateCompilation(sources, options: options, parseOptions: parseOptions);
            CompileAndVerify(comp, symbolValidator: AssertNoNullablePublicOnlyAttribute);
 
            comp = CreateCompilation(sources, options: options, parseOptions: parseOptions.WithNullablePublicOnly());
            CompileAndVerify(comp, symbolValidator: AssertNullablePublicOnlyAttribute);
        }
 
        [Fact]
        public void LocalFunctionReturnType_SynthesizedAttributeDefinitions()
        {
            var source =
@"public class Program
{
    static void Main()
    {
#nullable enable
        object? L() => null;
        L();
    }
}";
            var options = TestOptions.ReleaseExe.WithMetadataImportOptions(MetadataImportOptions.All);
            var parseOptions = TestOptions.Regular8;
 
            var comp = CreateCompilation(source, options: options, parseOptions: parseOptions);
            CompileAndVerify(comp, symbolValidator: AssertNoNullablePublicOnlyAttribute);
 
            comp = CreateCompilation(source, options: options, parseOptions: parseOptions.WithNullablePublicOnly());
            CompileAndVerify(comp, symbolValidator: AssertNoNullablePublicOnlyAttribute);
        }
 
        [Fact]
        public void LocalFunctionReturnType_ExplicitAttributeDefinitions()
        {
            var source =
@"public class Program
{
    static void Main()
    {
#nullable enable
        object? L() => null;
        L();
    }
}";
            var options = TestOptions.ReleaseExe.WithMetadataImportOptions(MetadataImportOptions.All);
            var parseOptions = TestOptions.Regular8;
            CSharpTestSource sources = new[] { NullableAttributeDefinition, NullableContextAttributeDefinition, NullablePublicOnlyAttributeDefinition, source };
 
            var comp = CreateCompilation(sources, options: options, parseOptions: parseOptions);
            CompileAndVerify(comp, symbolValidator: m => AssertNullablePublicOnlyAttribute(m, includesAttributeDefinition: true, includesAttributeUse: false, publicDefinition: true));
 
            comp = CreateCompilation(sources, options: options, parseOptions: parseOptions.WithNullablePublicOnly());
            CompileAndVerify(comp, symbolValidator: m => AssertNullablePublicOnlyAttribute(m, includesAttributeDefinition: true, includesAttributeUse: false, publicDefinition: true));
        }
 
        [Fact]
        [WorkItem(36457, "https://github.com/dotnet/roslyn/issues/36457")]
        public void ExplicitAttribute_ReferencedInSource_Assembly()
        {
            var sourceAttribute =
@"namespace System.Runtime.CompilerServices
{
    internal class NullablePublicOnlyAttribute : System.Attribute
    {
        internal NullablePublicOnlyAttribute(bool b) { }
    }
}";
            var source =
@"using System.Runtime.CompilerServices;
[assembly: NullablePublicOnly(false)]
[module: NullablePublicOnly(false)]
";
 
            // C#7
            var comp = CreateCompilation(new[] { sourceAttribute, source }, parseOptions: TestOptions.Regular7);
            verifyDiagnostics(comp);
 
            // C#8
            comp = CreateCompilation(new[] { sourceAttribute, source });
            verifyDiagnostics(comp);
 
            static void verifyDiagnostics(CSharpCompilation comp)
            {
                comp.VerifyDiagnostics(
                // (3,10): error CS8335: Do not use 'System.Runtime.CompilerServices.NullablePublicOnlyAttribute'. This is reserved for compiler usage.
                // [module: NullablePublicOnly(false)]
                Diagnostic(ErrorCode.ERR_ExplicitReservedAttr, "NullablePublicOnly(false)").WithArguments("System.Runtime.CompilerServices.NullablePublicOnlyAttribute").WithLocation(3, 10));
            }
        }
 
        [Fact]
        [WorkItem(36457, "https://github.com/dotnet/roslyn/issues/36457")]
        public void ExplicitAttribute_ReferencedInSource_Other()
        {
            var sourceAttribute =
@"namespace System.Runtime.CompilerServices
{
    internal class NullablePublicOnlyAttribute : System.Attribute
    {
        internal NullablePublicOnlyAttribute(bool b) { }
    }
}";
            var source =
@"using System.Runtime.CompilerServices;
[NullablePublicOnly(false)]
class Program
{
}";
            var comp = CreateCompilation(new[] { sourceAttribute, source });
            comp.VerifyDiagnostics();
        }
 
        [Fact]
        public void ExplicitAttribute_WithNullableAttribute()
        {
            var sourceAttribute =
@"#nullable enable
namespace System.Runtime.CompilerServices
{
    public class NullablePublicOnlyAttribute : System.Attribute
    {
        public NullablePublicOnlyAttribute(bool b) { }
        public NullablePublicOnlyAttribute(
            object x,
            object? y,
#nullable disable
            object z)
        {
        }
    }
}";
            var comp = CreateCompilation(sourceAttribute);
            var ref0 = comp.EmitToImageReference();
            var expected =
@"System.Runtime.CompilerServices.NullablePublicOnlyAttribute
    NullablePublicOnlyAttribute(System.Object! x, System.Object? y, System.Object z)
        [Nullable(1)] System.Object! x
        [Nullable(2)] System.Object? y
";
            AssertNullableAttributes(comp, expected);
 
            var source =
@"#nullable enable
public class Program
{
    public object _f1;
    public object? _f2;
#nullable disable
    public object _f3;
}";
            comp = CreateCompilation(source, references: new[] { ref0 });
            expected =
@"Program
    [Nullable(1)] System.Object! _f1
    [Nullable(2)] System.Object? _f2
";
            AssertNullableAttributes(comp, expected);
        }
 
        [Fact]
        [WorkItem(36934, "https://github.com/dotnet/roslyn/issues/36934")]
        public void AttributeUsage()
        {
            var source =
@"#nullable enable
public class Program
{
    public object? F;
}";
            var comp = CreateCompilation(source, parseOptions: TestOptions.Regular.WithNullablePublicOnly(), options: TestOptions.ReleaseDll.WithMetadataImportOptions(MetadataImportOptions.All));
            CompileAndVerify(comp, symbolValidator: module =>
            {
                var attributeType = module.GlobalNamespace.GetMember<NamedTypeSymbol>("System.Runtime.CompilerServices.NullablePublicOnlyAttribute");
                AttributeUsageInfo attributeUsage = attributeType.GetAttributeUsageInfo();
                Assert.False(attributeUsage.Inherited);
                Assert.False(attributeUsage.AllowMultiple);
                Assert.True(attributeUsage.HasValidAttributeTargets);
                Assert.Equal(AttributeTargets.Module, attributeUsage.ValidTargets);
            });
        }
 
        [Fact]
        public void MissingAttributeUsageAttribute()
        {
            var source =
@"#nullable enable
public class Program
{
    public object? F;
}";
            var comp = CreateCompilation(source, parseOptions: TestOptions.Regular.WithNullablePublicOnly().WithNoRefSafetyRulesAttribute());
            comp.MakeTypeMissing(WellKnownType.System_AttributeUsageAttribute);
            comp.VerifyEmitDiagnostics(
                // 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),
                // 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 AttributeField()
        {
            var source =
@"#nullable enable
using System;
using System.Linq;
using System.Reflection;
public class Program
{
    public static void Main(string[] args)
    {
        var value = GetAttributeValue(typeof(Program).Assembly.Modules.First());
        Console.WriteLine(value == null ? ""<null>"" : value.ToString());
    }
    static bool? GetAttributeValue(Module module)
    {
        var attribute = module.GetCustomAttributes(false).SingleOrDefault(a => a.GetType().Name == ""NullablePublicOnlyAttribute"");
        if (attribute == null) return null;
        var field = attribute.GetType().GetField(""IncludesInternals"");
        return (bool)field.GetValue(attribute);
    }
}";
            var sourceIVTs =
@"using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo(""Other"")]";
 
            var parseOptions = TestOptions.Regular8;
            CompileAndVerify(source, parseOptions: parseOptions, expectedOutput: "<null>");
            CompileAndVerify(source, parseOptions: parseOptions.WithNullablePublicOnly(), expectedOutput: "False");
            CompileAndVerify(new[] { source, sourceIVTs }, parseOptions: parseOptions, expectedOutput: "<null>");
            CompileAndVerify(new[] { source, sourceIVTs }, parseOptions: parseOptions.WithNullablePublicOnly(), expectedOutput: "True");
        }
 
        private static void AssertNoNullablePublicOnlyAttribute(ModuleSymbol module)
        {
            AssertNullablePublicOnlyAttribute(module, includesAttributeDefinition: false, includesAttributeUse: false, publicDefinition: false);
        }
 
        private static void AssertNullablePublicOnlyAttribute(ModuleSymbol module)
        {
            AssertNullablePublicOnlyAttribute(module, includesAttributeDefinition: true, includesAttributeUse: true, publicDefinition: false);
        }
 
        private static void AssertNullablePublicOnlyAttribute(ModuleSymbol module, bool includesAttributeDefinition, bool includesAttributeUse, bool publicDefinition)
        {
            const string attributeName = "System.Runtime.CompilerServices.NullablePublicOnlyAttribute";
            var type = (NamedTypeSymbol)module.GlobalNamespace.GetMember(attributeName);
            var attribute = module.GetAttributes().SingleOrDefault();
            if (includesAttributeDefinition)
            {
                Assert.NotNull(type);
            }
            else
            {
                Assert.Null(type);
                if (includesAttributeUse)
                {
                    type = attribute.AttributeClass;
                }
            }
            if (type is object)
            {
                Assert.Equal(publicDefinition ? Accessibility.Public : Accessibility.Internal, type.DeclaredAccessibility);
            }
            if (includesAttributeUse)
            {
                Assert.Equal(type, attribute.AttributeClass);
            }
            else
            {
                Assert.Null(attribute);
            }
        }
 
        private void AssertNullableAttributes(CSharpCompilation comp, string expected)
        {
            CompileAndVerify(comp, symbolValidator: module => AssertNullableAttributes(module, expected));
        }
 
        private static void AssertNullableAttributes(ModuleSymbol module, string expected)
        {
            var actual = NullableAttributesVisitor.GetString((PEModuleSymbol)module);
            AssertEx.AssertEqualToleratingWhitespaceDifferences(expected, actual);
        }
    }
}