/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/CSharp/Test/CSharp15/UnsafeEvolutionTests.cs
15 419 строк
766 KB
Jan Jones
Unsafe evolution: allow await in unsafe context (#84616)
19 минут назад
Не верифицирован
19 минут назад
461c2ef
Код
Авторство
О чём код?
// 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; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection.Metadata; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; using Roslyn.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Semantics; [CompilerTrait(CompilerFeature.Unsafe)] public sealed class UnsafeEvolutionTests : CompilingTestBase { private enum AttributeDefinition { None, Synthesized, FromSource, } /// <param name="expectedUnsafeSymbols">See <see cref="VerifyRequiresUnsafeAttribute"/>.</param> /// <param name="expectedSafeSymbols">See <see cref="VerifyRequiresUnsafeAttribute"/>.</param> private void CompileAndVerifyUnsafe( string lib, string caller, object[] expectedUnsafeSymbols, object[] expectedSafeSymbols, DiagnosticDescription[] expectedDiagnostics, ReadOnlySpan<string> additionalSources = default, Verification verify = default, CallerUnsafeMode expectedUnsafeMode = CallerUnsafeMode.Explicit, object[]? skipSymbolsInSource = null, CSharpParseOptions? parseOptions = null, CSharpCompilationOptions? optionsDll = null, TargetFramework targetFramework = TargetFramework.Standard, DiagnosticDescription[]? expectedDiagnosticsWhenReferencingLegacyLib = null, DiagnosticDescription[]? expectedDiagnosticsForLegacyCaller = null, DiagnosticDescription[]? expectedDiagnosticsWithOldLangVersion = null, DiagnosticDescription[]? expectedLibDiagnostics = null, DiagnosticDescription[]? expectedLegacyLibDiagnostics = null, AttributeDefinition expectedRulesAttribute = AttributeDefinition.Synthesized, AttributeDefinition expectedRequiresUnsafeAttribute = AttributeDefinition.Synthesized) { optionsDll ??= TestOptions.UnsafeReleaseDll; var optionsExe = optionsDll.WithOutputKind(OutputKind.ConsoleApplication); Assert.False(optionsDll.UseUpdatedMemorySafetyRules); var expectedCombinedDiagnostics = expectedLibDiagnostics is { } ? [.. expectedDiagnostics, .. expectedLibDiagnostics] : expectedDiagnostics; CreateCompilation([lib, caller, .. additionalSources], targetFramework: targetFramework, parseOptions: parseOptions, options: optionsExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(expectedCombinedDiagnostics); CreateCompilation([lib, caller, .. additionalSources], targetFramework: targetFramework, parseOptions: TestOptions.RegularNext, options: optionsExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(expectedCombinedDiagnostics); if (expectedDiagnosticsWithOldLangVersion is { }) { CreateCompilation([lib, caller, .. additionalSources], targetFramework: targetFramework, parseOptions: TestOptions.Regular14, options: optionsExe) .VerifyDiagnostics(expectedDiagnosticsWithOldLangVersion); } var libUpdatedHasErrors = expectedLibDiagnostics?.Any(d => !ErrorFacts.IsWarning((ErrorCode)d.Code)) == true; MetadataReference[] libUpdatedRefs; if (libUpdatedHasErrors) { var libUpdated = CreateCompilation([lib, .. additionalSources], targetFramework: targetFramework, parseOptions: parseOptions, options: optionsDll.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(expectedLibDiagnostics ?? []); libUpdatedRefs = [libUpdated.ToMetadataReference()]; } else { var libUpdated = CompileAndVerify([lib, .. additionalSources], targetFramework: targetFramework, parseOptions: parseOptions, options: optionsDll.WithUpdatedMemorySafetyRules(), verify: verify, symbolValidator: symbolValidator) .VerifyDiagnostics(expectedLibDiagnostics ?? []); libUpdatedRefs = [libUpdated.GetImageReference(), libUpdated.Compilation.ToMetadataReference()]; } foreach (var libUpdatedRef in libUpdatedRefs) { var libAssemblySymbol = CreateCompilation(caller, [libUpdatedRef], targetFramework: targetFramework, parseOptions: parseOptions, options: optionsExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(expectedDiagnostics) .GetReferencedAssemblySymbol(libUpdatedRef); symbolValidator(libAssemblySymbol.Modules.Single()); // Updated-rules library referenced by legacy-rules caller. CreateCompilation(caller, [libUpdatedRef], targetFramework: targetFramework, parseOptions: parseOptions, options: optionsExe) .VerifyDiagnostics(expectedDiagnosticsForLegacyCaller ?? []); } if (expectedLegacyLibDiagnostics is { }) { CreateCompilation([lib, .. additionalSources], targetFramework: targetFramework, parseOptions: parseOptions, options: optionsDll) .VerifyDiagnostics(expectedLegacyLibDiagnostics); } else { var libLegacy = CompileAndVerify([lib, .. additionalSources], targetFramework: targetFramework, parseOptions: parseOptions, options: optionsDll, verify: verify, symbolValidator: module => { VerifyMemorySafetyRulesAttribute(module, expectedDefinition: AttributeDefinition.None, includesAttributeUse: false); // Under legacy rules, `unsafe` keyword does not make members requires-unsafe. VerifyRequiresUnsafeAttribute( module, expectedUnsafeSymbols: [], expectedSafeSymbols: [ .. skipSymbolsInSource is [..] ? expectedUnsafeSymbols.Except(skipSymbolsInSource).ToArray() : expectedUnsafeSymbols, .. skipSymbolsInSource is [..] ? expectedSafeSymbols.Except(skipSymbolsInSource).ToArray() : expectedSafeSymbols, ], expectedDefinition: expectedSourceAttributeDefinition(expectedRequiresUnsafeAttribute)); }) .VerifyDiagnostics() .GetImageReference(); CreateCompilation(caller, [libLegacy], targetFramework: targetFramework, parseOptions: parseOptions, options: optionsExe.WithUpdatedMemorySafetyRules()) .VerifyEmitDiagnostics(expectedDiagnosticsWhenReferencingLegacyLib ?? []); // Legacy-rules library referenced by legacy-rules caller. CreateCompilation(caller, [libLegacy], targetFramework: targetFramework, parseOptions: parseOptions, options: optionsExe) .VerifyEmitDiagnostics(expectedDiagnosticsForLegacyCaller ?? []); } void symbolValidator(ModuleSymbol module) { var isSource = module is SourceModuleSymbol; VerifyMemorySafetyRulesAttribute( module, expectedDefinition: isSource ? expectedSourceAttributeDefinition(expectedRulesAttribute) : expectedRulesAttribute, includesAttributeUse: !isSource); VerifyRequiresUnsafeAttribute( module, expectedUnsafeSymbols: exceptSymbolsSkippedInSource(expectedUnsafeSymbols), expectedSafeSymbols: exceptSymbolsSkippedInSource(expectedSafeSymbols), expectedUnsafeMode: expectedUnsafeMode, expectedDefinition: isSource ? expectedSourceAttributeDefinition(expectedRequiresUnsafeAttribute) : expectedRequiresUnsafeAttribute); [return: NotNullIfNotNull(nameof(original))] object[]? exceptSymbolsSkippedInSource(object[]? original) { return isSource && skipSymbolsInSource is [..] && original is [..] ? original.Except(skipSymbolsInSource).ToArray() : original; } } static AttributeDefinition expectedSourceAttributeDefinition(AttributeDefinition expectedDefinition) => expectedDefinition == AttributeDefinition.Synthesized ? AttributeDefinition.None : expectedDefinition; } private static Func<ModuleSymbol, Symbol> ExtensionMember(string containerName, string memberName) { return module => module.GlobalNamespace .GetMember<NamedTypeSymbol>(containerName) .GetMembers("") .Cast<NamedTypeSymbol>() .SelectMany(block => block.GetMembers(memberName)) .SingleOrDefault() ?? throw new InvalidOperationException($"Cannot find '{containerName}.{memberName}'."); } private static Func<ModuleSymbol, Symbol> Overload(string qualifiedName, int parameterCount) { return module => module.GlobalNamespace .GetMembersByQualifiedName<MethodSymbol>(qualifiedName) .SingleOrDefault(m => m.Parameters.Length == parameterCount) ?? throw new InvalidOperationException($"Cannot find '{qualifiedName}' with {parameterCount} parameters."); } private static Func<ModuleSymbol, Symbol> OverloadByReturnType(string qualifiedName, string returnType) { return module => { var candidates = module.GlobalNamespace .GetMembersByQualifiedName<MethodSymbol>(qualifiedName); return candidates.SingleOrDefault(m => m.ReturnType.ToTestDisplayString() == returnType) ?? throw new InvalidOperationException($"Cannot find '{qualifiedName}' with return type '{returnType}'. " + $"Found {string.Join(", ", candidates.Select(static m => m.ReturnType.ToTestDisplayString()))}."); }; } private static Func<ModuleSymbol, Symbol> EventField(string qualifiedName) { return module => module.GlobalNamespace .GetMember<EventSymbol>(qualifiedName) .AssociatedField ?? throw new InvalidOperationException($"Cannot find event field '{qualifiedName}'."); } private static void VerifyMemorySafetyRulesAttribute( ModuleSymbol module, AttributeDefinition expectedDefinition, bool includesAttributeUse) { const string Name = "MemorySafetyRulesAttribute"; const string FullName = $"System.Runtime.CompilerServices.{Name}"; var type = (NamedTypeSymbol)module.GlobalNamespace.GetMember(FullName); var attribute = module.GetAttributes().SingleOrDefault(a => a.AttributeClass?.Name == Name); Assert.Equal(attribute, module.GetPublicSymbol().GetAttributes().SingleOrDefault(a => a.AttributeClass?.Name == Name)); if (expectedDefinition != AttributeDefinition.None) { Assert.NotNull(type); if (expectedDefinition == AttributeDefinition.Synthesized) { var attributeAttributes = type.GetAttributes() .Select(a => a.AttributeClass.ToTestDisplayString()) .OrderBy(StringComparer.Ordinal); Assert.Equal( [ "Microsoft.CodeAnalysis.EmbeddedAttribute", "System.AttributeUsageAttribute", "System.Runtime.CompilerServices.CompilerGeneratedAttribute", ], attributeAttributes); } else { Assert.Equal(AttributeDefinition.FromSource, expectedDefinition); } } else { Assert.Null(type); if (includesAttributeUse) { Assert.NotNull(attribute); type = attribute.AttributeClass; } } if (type is { }) { Assert.Equal(expectedDefinition == AttributeDefinition.Synthesized ? Accessibility.Internal : Accessibility.Public, type.DeclaredAccessibility); } else { Assert.Equal(AttributeDefinition.None, expectedDefinition); } if (includesAttributeUse) { Assert.NotNull(attribute); Assert.Equal(type, attribute.AttributeClass); Assert.Equal([2], attribute.ConstructorArguments.Select(a => a.Value)); Assert.Equal([], attribute.NamedArguments); } else { Assert.Null(attribute); } } /// <remarks> /// <paramref name="expectedUnsafeSymbols"/> (and <paramref name="expectedSafeSymbols"/>) should be symbol names (<see cref="string"/>) /// or symbol getters (<c><![CDATA[Func<ModuleSymbol, Symbol>]]></c>) of symbols that are expected to be unsafe (or safe, respectively). /// </remarks> private static void VerifyRequiresUnsafeAttribute( ModuleSymbol module, ReadOnlySpan<object> expectedUnsafeSymbols, ReadOnlySpan<object> expectedSafeSymbols, AttributeDefinition expectedDefinition, CallerUnsafeMode expectedUnsafeMode = CallerUnsafeMode.Explicit) { const string Name = "RequiresUnsafeAttribute"; const string FullName = $"System.Diagnostics.CodeAnalysis.{Name}"; var type = (NamedTypeSymbol)module.GlobalNamespace.GetMember(FullName); if (expectedDefinition != AttributeDefinition.None) { Assert.NotNull(type); if (expectedDefinition == AttributeDefinition.Synthesized) { var attributeAttributes = type.GetAttributes() .Select(a => a.AttributeClass.ToTestDisplayString()) .OrderBy(StringComparer.Ordinal); Assert.Equal( [ "Microsoft.CodeAnalysis.EmbeddedAttribute", "System.Runtime.CompilerServices.CompilerGeneratedAttribute", ], attributeAttributes); } else { Assert.Equal(AttributeDefinition.FromSource, expectedDefinition); } Assert.Equal(expectedDefinition == AttributeDefinition.Synthesized ? Accessibility.Internal : Accessibility.Public, type.DeclaredAccessibility); } else { Assert.Null(type); } var seenSymbols = new HashSet<Symbol>(); foreach (var symbol in expectedUnsafeSymbols) { verifySymbol(symbol, shouldBeUnsafe: true); } foreach (var symbol in expectedSafeSymbols) { verifySymbol(symbol, shouldBeUnsafe: false); } void verifySymbol(object symbolGetter, bool shouldBeUnsafe) { var symbol = getSymbol(symbolGetter); var symbolExpectedUnsafeMode = shouldBeUnsafe ? expectedUnsafeMode : CallerUnsafeMode.None; Assert.True(symbolExpectedUnsafeMode == symbol.GetCallerUnsafeMode(ConsList<FieldSymbol>.Empty), $"Expected {symbol.GetType().Name} '{symbol.ToTestDisplayString()}' to have {nameof(CallerUnsafeMode)}.{symbolExpectedUnsafeMode} (got {symbol.GetCallerUnsafeMode(ConsList<FieldSymbol>.Empty)})."); var publicSymbol = symbol.GetPublicSymbol(); Assert.Equal(symbolExpectedUnsafeMode != CallerUnsafeMode.None, publicSymbol.RequiresUnsafeContext); var hasAttributeInGetAttributes = symbol.GetAttributes().Any(a => a.AttributeClass?.Name == Name); Assert.False(hasAttributeInGetAttributes, $"Did not expect {symbol.GetType().Name} '{symbol.ToTestDisplayString()}' to have the attribute in GetAttributes()."); var hasAttributeInPublicGetAttributes = publicSymbol.GetAttributes().Any(a => a.AttributeClass?.Name == Name); Assert.False(hasAttributeInPublicGetAttributes, $"Did not expect {symbol.GetType().Name} '{symbol.ToTestDisplayString()}' to have the attribute in GetPublicSymbol().GetAttributes()."); // For PE symbols, the attribute should be present in raw metadata when expected. if (module is PEModuleSymbol peModule) { verifyAttributeInMetadata(symbol, shouldBeUnsafe && expectedUnsafeMode == CallerUnsafeMode.Explicit); if (symbol is PEMethodSymbol { AssociatedSymbol: Symbol associatedSymbol }) verifyAttributeInMetadata(associatedSymbol, associatedSymbol.GetCallerUnsafeMode(ConsList<FieldSymbol>.Empty) == CallerUnsafeMode.Explicit); void verifyAttributeInMetadata(Symbol s, bool shouldHave) { EntityHandle? handle = s switch { PEMethodSymbol m => (EntityHandle?)m.Handle, PEFieldSymbol f => f.Handle, PEPropertySymbol p => p.Handle, PEEventSymbol e => e.Handle, _ => null, }; if (handle is { } h) { var hasAttributeInMetadata = peModule.Module.HasAttribute(h, AttributeDescription.RequiresUnsafeAttribute); Assert.True(shouldHave == hasAttributeInMetadata, $"{(shouldHave ? "Expected" : "Did not expect")} {s.GetType().Name} '{s.ToTestDisplayString()}' to have the attribute in metadata."); } } } Assert.True(seenSymbols.Add(symbol), $"Symbol '{symbol.ToTestDisplayString()}' specified multiple times."); } Symbol getSymbol(object symbolGetter) { var symbol = symbolGetter switch { string symbolName => module.GlobalNamespace.GetMember(symbolName), Func<ModuleSymbol, Symbol> func => func(module), _ => throw ExceptionUtilities.UnexpectedValue(symbolGetter), }; Assert.False(symbol is null, $"Cannot find symbol '{symbolGetter}' in {module.GetType().Name}."); return symbol; } } [Fact] public void RulesAttribute_Synthesized() { var source = """ class C; """; CompileAndVerify(source, symbolValidator: m => VerifyMemorySafetyRulesAttribute(m, expectedDefinition: AttributeDefinition.None, includesAttributeUse: false)) .VerifyDiagnostics(); var ref1 = CompileAndVerify(source, options: TestOptions.ReleaseDll.WithUpdatedMemorySafetyRules(), symbolValidator: m => VerifyMemorySafetyRulesAttribute(m, expectedDefinition: AttributeDefinition.Synthesized, includesAttributeUse: true)) .VerifyDiagnostics() .GetImageReference(); CompileAndVerify("", [ref1], options: TestOptions.ReleaseDll.WithUpdatedMemorySafetyRules(), symbolValidator: m => VerifyMemorySafetyRulesAttribute(m, expectedDefinition: AttributeDefinition.Synthesized, includesAttributeUse: true)) .VerifyDiagnostics(); var source2 = """ class B; """; CompileAndVerify(source2, [ref1], options: TestOptions.ReleaseDll.WithUpdatedMemorySafetyRules(), symbolValidator: m => VerifyMemorySafetyRulesAttribute(m, expectedDefinition: AttributeDefinition.Synthesized, includesAttributeUse: true)) .VerifyDiagnostics(); CompileAndVerify(source, options: TestOptions.ReleaseModule, verify: Verification.Skipped, symbolValidator: m => VerifyMemorySafetyRulesAttribute(m, expectedDefinition: AttributeDefinition.None, includesAttributeUse: false)) .VerifyDiagnostics(); CreateCompilation(source, options: TestOptions.ReleaseModule.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // (1,1): error CS0518: Predefined type 'System.Runtime.CompilerServices.MemorySafetyRulesAttribute' is not defined or imported Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound).WithArguments("System.Runtime.CompilerServices.MemorySafetyRulesAttribute").WithLocation(1, 1)); // Script compilation. source = "System.Console.WriteLine();"; CompileAndVerify(source, parseOptions: TestOptions.Script, options: TestOptions.ReleaseModule, verify: Verification.Skipped, symbolValidator: m => VerifyMemorySafetyRulesAttribute(m, expectedDefinition: AttributeDefinition.None, includesAttributeUse: false)) .VerifyDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.Script, options: TestOptions.ReleaseModule.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // error CS8630: Invalid 'MemorySafetyRules' value: '2' for C# 14.0. Please use language version 'preview' or greater. Diagnostic(ErrorCode.ERR_CompilationOptionNotAvailable).WithArguments("MemorySafetyRules", "2", "14.0", "preview").WithLocation(1, 1), // error CS0518: Predefined type 'System.Runtime.CompilerServices.MemorySafetyRulesAttribute' is not defined or imported Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound).WithArguments("System.Runtime.CompilerServices.MemorySafetyRulesAttribute").WithLocation(1, 1)); // No types and members in the compilation, but the attribute is still synthesized if updated rules are enabled. source = """ [assembly: System.Reflection.AssemblyDescriptionAttribute(null)] """; CompileAndVerify(source, symbolValidator: m => VerifyMemorySafetyRulesAttribute(m, expectedDefinition: AttributeDefinition.None, includesAttributeUse: false)) .VerifyDiagnostics(); CompileAndVerify(source, options: TestOptions.ReleaseDll.WithUpdatedMemorySafetyRules(), symbolValidator: m => VerifyMemorySafetyRulesAttribute(m, expectedDefinition: AttributeDefinition.Synthesized, includesAttributeUse: true)) .VerifyDiagnostics(); CreateCompilation(source, options: TestOptions.ReleaseModule.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // (1,1): error CS0518: Predefined type 'System.Runtime.CompilerServices.MemorySafetyRulesAttribute' is not defined or imported Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound).WithArguments("System.Runtime.CompilerServices.MemorySafetyRulesAttribute").WithLocation(1, 1)); } [Theory, CombinatorialData] public void RulesAttribute_TypeForwardedTo( bool updatedRulesA, bool updatedRulesB, bool useCompilationReference) { var sourceA = """ public class A { } """; var comp = CreateCompilation(sourceA, options: TestOptions.ReleaseDll.WithUpdatedMemorySafetyRules(updatedRulesA)) .VerifyDiagnostics(); var refA = AsReference(comp, useCompilationReference); Assert.Equal(updatedRulesA, comp.SourceModule.UseUpdatedMemorySafetyRules); CompileAndVerify(comp, symbolValidator: m => VerifyMemorySafetyRulesAttribute(m, expectedDefinition: updatedRulesA ? AttributeDefinition.Synthesized : AttributeDefinition.None, includesAttributeUse: updatedRulesA)) .VerifyDiagnostics(); var sourceB = """ using System.Runtime.CompilerServices; [assembly: TypeForwardedTo(typeof(A))] """; comp = CreateCompilation(sourceB, [refA], options: TestOptions.ReleaseDll.WithUpdatedMemorySafetyRules(updatedRulesB)); Assert.Equal(updatedRulesB, comp.SourceModule.UseUpdatedMemorySafetyRules); CompileAndVerify(comp, symbolValidator: m => VerifyMemorySafetyRulesAttribute(m, expectedDefinition: updatedRulesB ? AttributeDefinition.Synthesized : AttributeDefinition.None, includesAttributeUse: updatedRulesB)) .VerifyDiagnostics(); } [Fact] public void RulesAttribute_Reflection() { var sourceA = """ using System; using System.Linq; public class A { public static int GetAttributeValue(Type type) { var module = type.Assembly.Modules.Single(); var attribute = module.GetCustomAttributes(inherit: false).Single(a => a.GetType().Name == "MemorySafetyRulesAttribute"); var prop = attribute.GetType().GetProperty("Version"); return (int)prop.GetValue(attribute); } } """; var refA = CreateCompilation(sourceA, options: TestOptions.ReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics() .EmitToImageReference(); var sourceB = """ using System; class B : A { static void Main() { Console.Write(GetAttributeValue(typeof(A))); Console.Write(" "); Console.Write(GetAttributeValue(typeof(B))); } } """; CompileAndVerify(sourceB, [refA], options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules(), expectedOutput: "2 2") .VerifyDiagnostics(); } [Fact] public void RulesAttribute_FromSource() { var source = """ class C; """; CompileAndVerify([source, MemorySafetyRulesAttributeDefinition], symbolValidator: m => VerifyMemorySafetyRulesAttribute(m, expectedDefinition: AttributeDefinition.FromSource, includesAttributeUse: false)) .VerifyDiagnostics(); CompileAndVerify([source, MemorySafetyRulesAttributeDefinition], options: TestOptions.ReleaseDll.WithUpdatedMemorySafetyRules(), symbolValidator: m => VerifyMemorySafetyRulesAttribute(m, expectedDefinition: AttributeDefinition.FromSource, includesAttributeUse: true)) .VerifyDiagnostics(); } [Theory, CombinatorialData] public void RulesAttribute_FromMetadata(bool useCompilationReference) { var comp = CreateCompilation(MemorySafetyRulesAttributeDefinition); CompileAndVerify(comp, symbolValidator: m => VerifyMemorySafetyRulesAttribute(m, expectedDefinition: AttributeDefinition.FromSource, includesAttributeUse: false)) .VerifyDiagnostics(); var ref1 = AsReference(comp, useCompilationReference); var source = """ class C; """; CompileAndVerify(source, [ref1], options: TestOptions.ReleaseDll.WithUpdatedMemorySafetyRules(), symbolValidator: m => VerifyMemorySafetyRulesAttribute(m, expectedDefinition: AttributeDefinition.None, includesAttributeUse: true)) .VerifyDiagnostics(); } [Theory, CombinatorialData] public void RulesAttribute_FromMetadata_Multiple(bool useCompilationReference) { var comp1 = CreateCompilation(MemorySafetyRulesAttributeDefinition).VerifyDiagnostics(); var ref1 = AsReference(comp1, useCompilationReference); var comp2 = CreateCompilation(MemorySafetyRulesAttributeDefinition).VerifyDiagnostics(); var ref2 = AsReference(comp2, useCompilationReference); var source = """ class C; """; // Ambiguous attribute definitions from references => synthesize our own. CompileAndVerify(source, [ref1, ref2], options: TestOptions.ReleaseDll.WithUpdatedMemorySafetyRules(), symbolValidator: m => VerifyMemorySafetyRulesAttribute(m, expectedDefinition: AttributeDefinition.Synthesized, includesAttributeUse: true)) .VerifyDiagnostics(); // Also defined in source. CompileAndVerify([source, MemorySafetyRulesAttributeDefinition], [ref1, ref2], options: TestOptions.ReleaseDll.WithUpdatedMemorySafetyRules(), symbolValidator: m => VerifyMemorySafetyRulesAttribute(m, expectedDefinition: AttributeDefinition.FromSource, includesAttributeUse: true)) .VerifyDiagnostics(); } [Theory, CombinatorialData] public void RulesAttribute_FromMetadata_Multiple_AndCorLib(bool useCompilationReference) { var corlibSource = """ namespace System { public class Object; public class ValueType; public class Attribute; public struct Void; public struct Int32; public struct Boolean; public class AttributeUsageAttribute : Attribute { public AttributeUsageAttribute(AttributeTargets t) { } public bool AllowMultiple { get; set; } public bool Inherited { get; set; } } public class Enum; public enum AttributeTargets { Module = 2 } } """; var corlib = CreateEmptyCompilation([corlibSource, MemorySafetyRulesAttributeDefinition]).VerifyDiagnostics(); var corlibRef = AsReference(corlib, useCompilationReference); var comp1 = CreateEmptyCompilation(MemorySafetyRulesAttributeDefinition, [corlibRef]).VerifyDiagnostics(); var ref1 = AsReference(comp1, useCompilationReference); var comp2 = CreateEmptyCompilation(MemorySafetyRulesAttributeDefinition, [corlibRef]).VerifyDiagnostics(); var ref2 = AsReference(comp2, useCompilationReference); var source = """ class C; """; // Using the attribute from corlib even if there are ambiguous definitions in other references. var verifier = CompileAndVerify(CreateEmptyCompilation(source, [ref1, ref2, corlibRef], options: TestOptions.ReleaseDll.WithUpdatedMemorySafetyRules()), verify: Verification.Skipped, symbolValidator: m => VerifyMemorySafetyRulesAttribute(m, expectedDefinition: AttributeDefinition.None, includesAttributeUse: true)); verifier.Diagnostics.WhereAsArray(d => d.Code != (int)ErrorCode.WRN_NoRuntimeMetadataVersion).Verify(); var comp = (CSharpCompilation)verifier.Compilation; Assert.Same(comp.Assembly.CorLibrary, comp.GetReferencedAssemblySymbol(corlibRef)); } [Theory] [InlineData(0)] [InlineData(-1)] [InlineData(1)] [InlineData(2, true)] [InlineData(3)] [InlineData(int.MinValue)] [InlineData(int.MaxValue)] public void RulesAttribute_FromMetadata_Version(int version, bool correctVersion = false) { // [module: MemorySafetyRules({version})] // public class A { public static void M() => throw null; } var sourceA = $$""" .assembly extern mscorlib { .ver 4:0:0:0 .publickeytoken = (B7 7A 5C 56 19 34 E0 89) } .assembly '<<GeneratedFileName>>' { } .module '<<GeneratedFileName>>.dll' .custom instance void System.Runtime.CompilerServices.MemorySafetyRulesAttribute::.ctor(int32) = { int32({{version}}) } .class private System.Runtime.CompilerServices.MemorySafetyRulesAttribute extends [mscorlib]System.Attribute { .method public hidebysig specialname rtspecialname instance void .ctor(int32 version) cil managed { ret } } .class public A { .method public static void M() { ldnull throw } } """; var refA = CompileIL(sourceA, prependDefaultHeader: false); var sourceB = """ class B { void M() => A.M(); } """; var comp = CreateCompilation(sourceB, [refA]); if (correctVersion) { comp.VerifyEmitDiagnostics(); } else { comp.VerifyDiagnostics( // (3,17): error CS9103: 'A.M()' is defined in a module with an unrecognized System.Runtime.CompilerServices.MemorySafetyRulesAttribute version, expecting '2'. // void M() => A.M(); Diagnostic(ErrorCode.ERR_UnrecognizedAttributeVersion, "A.M").WithArguments("A.M()", "System.Runtime.CompilerServices.MemorySafetyRulesAttribute", "2").WithLocation(3, 17)); } var method = comp.GetMember<MethodSymbol>("B.M"); Assert.False(method.ContainingModule.UseUpdatedMemorySafetyRules); // 'A.M' not used => no error. CreateCompilation("class C;", references: [refA]).VerifyEmitDiagnostics(); } [Theory] [InlineData(2, 0, true)] [InlineData(0, 2, false)] public void RulesAttribute_FromMetadata_Version_Multiple(int version1, int version2, bool correctVersion) { // [module: MemorySafetyRules({version1})] // [module: MemorySafetyRules({version2})] // public class A { public static void M() => throw null; } var sourceA = $$""" .assembly extern mscorlib { .ver 4:0:0:0 .publickeytoken = (B7 7A 5C 56 19 34 E0 89) } .assembly '<<GeneratedFileName>>' { } .module '<<GeneratedFileName>>.dll' .custom instance void System.Runtime.CompilerServices.MemorySafetyRulesAttribute::.ctor(int32) = { int32({{version1}}) } .custom instance void System.Runtime.CompilerServices.MemorySafetyRulesAttribute::.ctor(int32) = { int32({{version2}}) } .class private System.Runtime.CompilerServices.MemorySafetyRulesAttribute extends [mscorlib]System.Attribute { .method public hidebysig specialname rtspecialname instance void .ctor(int32 version) cil managed { ret } } .class public A { .method public static void M() { ldnull throw } } """; var refA = CompileIL(sourceA, prependDefaultHeader: false); var a = CreateCompilation("", [refA]).GetReferencedAssemblySymbol(refA); Assert.Equal(correctVersion, a.Modules.Single().UseUpdatedMemorySafetyRules); var sourceB = """ class B { void M() => A.M(); } """; var comp = CreateCompilation(sourceB, [refA]); if (correctVersion) { comp.VerifyEmitDiagnostics(); } else { comp.VerifyDiagnostics( // (3,17): error CS9103: 'A.M()' is defined in a module with an unrecognized System.Runtime.CompilerServices.MemorySafetyRulesAttribute version, expecting '2'. // void M() => A.M(); Diagnostic(ErrorCode.ERR_UnrecognizedAttributeVersion, "A.M").WithArguments("A.M()", "System.Runtime.CompilerServices.MemorySafetyRulesAttribute", "2").WithLocation(3, 17)); } var method = comp.GetMember<MethodSymbol>("B.M"); Assert.False(method.ContainingModule.UseUpdatedMemorySafetyRules); // 'A.M' not used => no error. CreateCompilation("class C;", references: [refA]).VerifyEmitDiagnostics(); } [Fact] public void RulesAttribute_FromMetadata_UnrecognizedConstructor_NoArguments() { // [module: MemorySafetyRules()] // public class A { public static void M() => throw null; } var sourceA = """ .assembly extern mscorlib { .ver 4:0:0:0 .publickeytoken = (B7 7A 5C 56 19 34 E0 89) } .assembly '<<GeneratedFileName>>' { } .module '<<GeneratedFileName>>.dll' .custom instance void System.Runtime.CompilerServices.MemorySafetyRulesAttribute::.ctor() = ( 01 00 00 00 ) .class private System.Runtime.CompilerServices.MemorySafetyRulesAttribute extends [mscorlib]System.Attribute { .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ret } } .class public A { .method public static void M() { ldnull throw } } """; var refA = CompileIL(sourceA, prependDefaultHeader: false); var sourceB = """ class B { void M() => A.M(); } """; var comp = CreateCompilation(sourceB, [refA]); comp.VerifyDiagnostics( // (3,17): error CS9103: 'A.M()' is defined in a module with an unrecognized System.Runtime.CompilerServices.MemorySafetyRulesAttribute version, expecting '2'. // void M() => A.M(); Diagnostic(ErrorCode.ERR_UnrecognizedAttributeVersion, "A.M").WithArguments("A.M()", "System.Runtime.CompilerServices.MemorySafetyRulesAttribute", "2").WithLocation(3, 17)); var method = comp.GetMember<MethodSymbol>("B.M"); Assert.False(method.ContainingModule.UseUpdatedMemorySafetyRules); // 'A.M' not used => no error. CreateCompilation("class C;", references: [refA]).VerifyEmitDiagnostics(); } [Fact] public void RulesAttribute_FromMetadata_UnrecognizedConstructor_StringArgument() { // [module: MemorySafetyRules("2")] // public class A { public static void M() => throw null; } var sourceA = """ .assembly extern mscorlib { .ver 4:0:0:0 .publickeytoken = (B7 7A 5C 56 19 34 E0 89) } .assembly '<<GeneratedFileName>>' { } .module '<<GeneratedFileName>>.dll' .custom instance void System.Runtime.CompilerServices.MemorySafetyRulesAttribute::.ctor(string) = {string('2')} .class private System.Runtime.CompilerServices.MemorySafetyRulesAttribute extends [mscorlib]System.Attribute { .method public hidebysig specialname rtspecialname instance void .ctor(string version) cil managed { ret } } .class public A { .method public static void M() { ldnull throw } } """; var refA = CompileIL(sourceA, prependDefaultHeader: false); var sourceB = """ class B { void M() => A.M(); } """; var comp = CreateCompilation(sourceB, [refA]); comp.VerifyDiagnostics( // (3,17): error CS9103: 'A.M()' is defined in a module with an unrecognized System.Runtime.CompilerServices.MemorySafetyRulesAttribute version, expecting '2'. // void M() => A.M(); Diagnostic(ErrorCode.ERR_UnrecognizedAttributeVersion, "A.M").WithArguments("A.M()", "System.Runtime.CompilerServices.MemorySafetyRulesAttribute", "2").WithLocation(3, 17)); var method = comp.GetMember<MethodSymbol>("B.M"); Assert.False(method.ContainingModule.UseUpdatedMemorySafetyRules); // 'A.M' not used => no error. CreateCompilation("class C;", references: [refA]).VerifyEmitDiagnostics(); } [Fact] public void RulesAttribute_MissingConstructor() { var source1 = """ namespace System.Runtime.CompilerServices { public sealed class MemorySafetyRulesAttribute : Attribute { } } """; var source2 = """ class C; """; CreateCompilation([source1, source2]).VerifyEmitDiagnostics(); CreateCompilation([source1, source2], options: TestOptions.ReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics() .VerifyEmitDiagnostics( // error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.MemorySafetyRulesAttribute..ctor' Diagnostic(ErrorCode.ERR_MissingPredefinedMember).WithArguments("System.Runtime.CompilerServices.MemorySafetyRulesAttribute", ".ctor").WithLocation(1, 1)); CreateCompilation([source1, source2], options: TestOptions.ReleaseModule.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // (1,1): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.MemorySafetyRulesAttribute..ctor' Diagnostic(ErrorCode.ERR_MissingPredefinedMember).WithArguments("System.Runtime.CompilerServices.MemorySafetyRulesAttribute", ".ctor").WithLocation(1, 1)); } [Theory, CombinatorialData] public void RulesAttribute_ReferencedInSource( bool updatedRules, bool useCompilationReference) { var comp = CreateCompilation(MemorySafetyRulesAttributeDefinition).VerifyDiagnostics(); var ref1 = AsReference(comp, useCompilationReference); var source = """ using System.Runtime.CompilerServices; [assembly: MemorySafetyRules(2)] [module: MemorySafetyRules(2)] """; comp = CreateCompilation(source, [ref1], options: TestOptions.ReleaseDll.WithUpdatedMemorySafetyRules(updatedRules)); comp.VerifyDiagnostics( // (2,12): error CS0592: Attribute 'MemorySafetyRules' is not valid on this declaration type. It is only valid on 'module' declarations. // [assembly: MemorySafetyRules(2)] Diagnostic(ErrorCode.ERR_AttributeOnBadSymbolType, "MemorySafetyRules").WithArguments("MemorySafetyRules", "module").WithLocation(2, 12), // (3,10): error CS8335: Do not use 'System.Runtime.CompilerServices.MemorySafetyRulesAttribute'. This is reserved for compiler usage. // [module: MemorySafetyRules(2)] Diagnostic(ErrorCode.ERR_ExplicitReservedAttr, "MemorySafetyRules(2)").WithArguments("System.Runtime.CompilerServices.MemorySafetyRulesAttribute").WithLocation(3, 10)); } [Theory, CombinatorialData] public void RulesAttribute_ReferencedInSource_Reserved(bool updatedRules) { var source = """ using System.Runtime.CompilerServices; [assembly: MemorySafetyRules(2)] [module: MemorySafetyRules(2)] [MemorySafetyRules(2)] class C { [MemorySafetyRules(2)] void M() { } [MemorySafetyRules(2)] int P1 { get; set; } [field: MemorySafetyRules(2)] int P2 { get; set; } int P3 { [MemorySafetyRules(2)] get; [MemorySafetyRules(2)] set; } #pragma warning disable CS0067 // unused event [MemorySafetyRules(2)] event System.Action E1; [field: MemorySafetyRules(2)] event System.Action E2; event System.Action E3 { [MemorySafetyRules(2)] add { } [MemorySafetyRules(2)] remove { } } [MemorySafetyRules(2)] int this[int i] { get => i; set { } } [MemorySafetyRules(2)] C() { } [MemorySafetyRules(2)] ~C() { } [MemorySafetyRules(2)] static C() { } [MemorySafetyRules(2)] public static C operator +(C c1, C c2) => c1; [MemorySafetyRules(2)] public void operator +=(C c) { } public void M([MemorySafetyRules(2)] int x) { } [return: MemorySafetyRules(2)] public int Func() => 0; public void M<[MemorySafetyRules(2)] T>() { } #pragma warning disable CS0169 // unused field [MemorySafetyRules(2)] int F; void L() { var lam = [MemorySafetyRules(2)] () => { }; } } [MemorySafetyRules(2)] delegate void D(); [MemorySafetyRules(2)] enum E { X } enum F { [MemorySafetyRules(2)] X } namespace System.Runtime.CompilerServices { public sealed class MemorySafetyRulesAttribute : Attribute { public MemorySafetyRulesAttribute(int version) { Version = version; } public int Version { get; } } } """; CreateCompilation([source, CompilerFeatureRequiredAttribute], options: TestOptions.ReleaseDll.WithUpdatedMemorySafetyRules(updatedRules)) .VerifyDiagnostics( // (2,12): error CS8335: Do not use 'System.Runtime.CompilerServices.MemorySafetyRulesAttribute'. This is reserved for compiler usage. // [assembly: MemorySafetyRules(2)] Diagnostic(ErrorCode.ERR_ExplicitReservedAttr, "MemorySafetyRules(2)").WithArguments("System.Runtime.CompilerServices.MemorySafetyRulesAttribute").WithLocation(2, 12), // (3,10): error CS8335: Do not use 'System.Runtime.CompilerServices.MemorySafetyRulesAttribute'. This is reserved for compiler usage. // [module: MemorySafetyRules(2)] Diagnostic(ErrorCode.ERR_ExplicitReservedAttr, "MemorySafetyRules(2)").WithArguments("System.Runtime.CompilerServices.MemorySafetyRulesAttribute").WithLocation(3, 10), // (4,2): error CS8335: Do not use 'System.Runtime.CompilerServices.MemorySafetyRulesAttribute'. This is reserved for compiler usage. // [MemorySafetyRules(2)] class C Diagnostic(ErrorCode.ERR_ExplicitReservedAttr, "MemorySafetyRules(2)").WithArguments("System.Runtime.CompilerServices.MemorySafetyRulesAttribute").WithLocation(4, 2), // (6,6): error CS8335: Do not use 'System.Runtime.CompilerServices.MemorySafetyRulesAttribute'. This is reserved for compiler usage. // [MemorySafetyRules(2)] void M() { } Diagnostic(ErrorCode.ERR_ExplicitReservedAttr, "MemorySafetyRules(2)").WithArguments("System.Runtime.CompilerServices.MemorySafetyRulesAttribute").WithLocation(6, 6), // (7,6): error CS8335: Do not use 'System.Runtime.CompilerServices.MemorySafetyRulesAttribute'. This is reserved for compiler usage. // [MemorySafetyRules(2)] int P1 { get; set; } Diagnostic(ErrorCode.ERR_ExplicitReservedAttr, "MemorySafetyRules(2)").WithArguments("System.Runtime.CompilerServices.MemorySafetyRulesAttribute").WithLocation(7, 6), // (8,13): error CS8335: Do not use 'System.Runtime.CompilerServices.MemorySafetyRulesAttribute'. This is reserved for compiler usage. // [field: MemorySafetyRules(2)] int P2 { get; set; } Diagnostic(ErrorCode.ERR_ExplicitReservedAttr, "MemorySafetyRules(2)").WithArguments("System.Runtime.CompilerServices.MemorySafetyRulesAttribute").WithLocation(8, 13), // (9,15): error CS8335: Do not use 'System.Runtime.CompilerServices.MemorySafetyRulesAttribute'. This is reserved for compiler usage. // int P3 { [MemorySafetyRules(2)] get; [MemorySafetyRules(2)] set; } Diagnostic(ErrorCode.ERR_ExplicitReservedAttr, "MemorySafetyRules(2)").WithArguments("System.Runtime.CompilerServices.MemorySafetyRulesAttribute").WithLocation(9, 15), // (9,43): error CS8335: Do not use 'System.Runtime.CompilerServices.MemorySafetyRulesAttribute'. This is reserved for compiler usage. // int P3 { [MemorySafetyRules(2)] get; [MemorySafetyRules(2)] set; } Diagnostic(ErrorCode.ERR_ExplicitReservedAttr, "MemorySafetyRules(2)").WithArguments("System.Runtime.CompilerServices.MemorySafetyRulesAttribute").WithLocation(9, 43), // (11,6): error CS8335: Do not use 'System.Runtime.CompilerServices.MemorySafetyRulesAttribute'. This is reserved for compiler usage. // [MemorySafetyRules(2)] event System.Action E1; Diagnostic(ErrorCode.ERR_ExplicitReservedAttr, "MemorySafetyRules(2)").WithArguments("System.Runtime.CompilerServices.MemorySafetyRulesAttribute").WithLocation(11, 6), // (12,13): error CS8335: Do not use 'System.Runtime.CompilerServices.MemorySafetyRulesAttribute'. This is reserved for compiler usage. // [field: MemorySafetyRules(2)] event System.Action E2; Diagnostic(ErrorCode.ERR_ExplicitReservedAttr, "MemorySafetyRules(2)").WithArguments("System.Runtime.CompilerServices.MemorySafetyRulesAttribute").WithLocation(12, 13), // (13,31): error CS8335: Do not use 'System.Runtime.CompilerServices.MemorySafetyRulesAttribute'. This is reserved for compiler usage. // event System.Action E3 { [MemorySafetyRules(2)] add { } [MemorySafetyRules(2)] remove { } } Diagnostic(ErrorCode.ERR_ExplicitReservedAttr, "MemorySafetyRules(2)").WithArguments("System.Runtime.CompilerServices.MemorySafetyRulesAttribute").WithLocation(13, 31), // (13,62): error CS8335: Do not use 'System.Runtime.CompilerServices.MemorySafetyRulesAttribute'. This is reserved for compiler usage. // event System.Action E3 { [MemorySafetyRules(2)] add { } [MemorySafetyRules(2)] remove { } } Diagnostic(ErrorCode.ERR_ExplicitReservedAttr, "MemorySafetyRules(2)").WithArguments("System.Runtime.CompilerServices.MemorySafetyRulesAttribute").WithLocation(13, 62), // (14,6): error CS8335: Do not use 'System.Runtime.CompilerServices.MemorySafetyRulesAttribute'. This is reserved for compiler usage. // [MemorySafetyRules(2)] int this[int i] { get => i; set { } } Diagnostic(ErrorCode.ERR_ExplicitReservedAttr, "MemorySafetyRules(2)").WithArguments("System.Runtime.CompilerServices.MemorySafetyRulesAttribute").WithLocation(14, 6), // (15,6): error CS8335: Do not use 'System.Runtime.CompilerServices.MemorySafetyRulesAttribute'. This is reserved for compiler usage. // [MemorySafetyRules(2)] C() { } Diagnostic(ErrorCode.ERR_ExplicitReservedAttr, "MemorySafetyRules(2)").WithArguments("System.Runtime.CompilerServices.MemorySafetyRulesAttribute").WithLocation(15, 6), // (16,6): error CS8335: Do not use 'System.Runtime.CompilerServices.MemorySafetyRulesAttribute'. This is reserved for compiler usage. // [MemorySafetyRules(2)] ~C() { } Diagnostic(ErrorCode.ERR_ExplicitReservedAttr, "MemorySafetyRules(2)").WithArguments("System.Runtime.CompilerServices.MemorySafetyRulesAttribute").WithLocation(16, 6), // (17,6): error CS8335: Do not use 'System.Runtime.CompilerServices.MemorySafetyRulesAttribute'. This is reserved for compiler usage. // [MemorySafetyRules(2)] static C() { } Diagnostic(ErrorCode.ERR_ExplicitReservedAttr, "MemorySafetyRules(2)").WithArguments("System.Runtime.CompilerServices.MemorySafetyRulesAttribute").WithLocation(17, 6), // (18,6): error CS8335: Do not use 'System.Runtime.CompilerServices.MemorySafetyRulesAttribute'. This is reserved for compiler usage. // [MemorySafetyRules(2)] public static C operator +(C c1, C c2) => c1; Diagnostic(ErrorCode.ERR_ExplicitReservedAttr, "MemorySafetyRules(2)").WithArguments("System.Runtime.CompilerServices.MemorySafetyRulesAttribute").WithLocation(18, 6), // (19,6): error CS8335: Do not use 'System.Runtime.CompilerServices.MemorySafetyRulesAttribute'. This is reserved for compiler usage. // [MemorySafetyRules(2)] public void operator +=(C c) { } Diagnostic(ErrorCode.ERR_ExplicitReservedAttr, "MemorySafetyRules(2)").WithArguments("System.Runtime.CompilerServices.MemorySafetyRulesAttribute").WithLocation(19, 6), // (20,20): error CS8335: Do not use 'System.Runtime.CompilerServices.MemorySafetyRulesAttribute'. This is reserved for compiler usage. // public void M([MemorySafetyRules(2)] int x) { } Diagnostic(ErrorCode.ERR_ExplicitReservedAttr, "MemorySafetyRules(2)").WithArguments("System.Runtime.CompilerServices.MemorySafetyRulesAttribute").WithLocation(20, 20), // (21,14): error CS8335: Do not use 'System.Runtime.CompilerServices.MemorySafetyRulesAttribute'. This is reserved for compiler usage. // [return: MemorySafetyRules(2)] public int Func() => 0; Diagnostic(ErrorCode.ERR_ExplicitReservedAttr, "MemorySafetyRules(2)").WithArguments("System.Runtime.CompilerServices.MemorySafetyRulesAttribute").WithLocation(21, 14), // (22,20): error CS8335: Do not use 'System.Runtime.CompilerServices.MemorySafetyRulesAttribute'. This is reserved for compiler usage. // public void M<[MemorySafetyRules(2)] T>() { } Diagnostic(ErrorCode.ERR_ExplicitReservedAttr, "MemorySafetyRules(2)").WithArguments("System.Runtime.CompilerServices.MemorySafetyRulesAttribute").WithLocation(22, 20), // (24,6): error CS8335: Do not use 'System.Runtime.CompilerServices.MemorySafetyRulesAttribute'. This is reserved for compiler usage. // [MemorySafetyRules(2)] int F; Diagnostic(ErrorCode.ERR_ExplicitReservedAttr, "MemorySafetyRules(2)").WithArguments("System.Runtime.CompilerServices.MemorySafetyRulesAttribute").WithLocation(24, 6), // (25,27): error CS8335: Do not use 'System.Runtime.CompilerServices.MemorySafetyRulesAttribute'. This is reserved for compiler usage. // void L() { var lam = [MemorySafetyRules(2)] () => { }; } Diagnostic(ErrorCode.ERR_ExplicitReservedAttr, "MemorySafetyRules(2)").WithArguments("System.Runtime.CompilerServices.MemorySafetyRulesAttribute").WithLocation(25, 27), // (27,2): error CS8335: Do not use 'System.Runtime.CompilerServices.MemorySafetyRulesAttribute'. This is reserved for compiler usage. // [MemorySafetyRules(2)] delegate void D(); Diagnostic(ErrorCode.ERR_ExplicitReservedAttr, "MemorySafetyRules(2)").WithArguments("System.Runtime.CompilerServices.MemorySafetyRulesAttribute").WithLocation(27, 2), // (28,2): error CS8335: Do not use 'System.Runtime.CompilerServices.MemorySafetyRulesAttribute'. This is reserved for compiler usage. // [MemorySafetyRules(2)] enum E { X } Diagnostic(ErrorCode.ERR_ExplicitReservedAttr, "MemorySafetyRules(2)").WithArguments("System.Runtime.CompilerServices.MemorySafetyRulesAttribute").WithLocation(28, 2), // (29,11): error CS8335: Do not use 'System.Runtime.CompilerServices.MemorySafetyRulesAttribute'. This is reserved for compiler usage. // enum F { [MemorySafetyRules(2)] X } Diagnostic(ErrorCode.ERR_ExplicitReservedAttr, "MemorySafetyRules(2)").WithArguments("System.Runtime.CompilerServices.MemorySafetyRulesAttribute").WithLocation(29, 11)); source = """ using System.Runtime.CompilerServices; [assembly: MemorySafetyRules] [module: MemorySafetyRules] [MemorySafetyRules] class C; namespace System.Runtime.CompilerServices { public sealed class MemorySafetyRulesAttribute : Attribute { public MemorySafetyRulesAttribute() { } public MemorySafetyRulesAttribute(int version) { Version = version; } public int Version { get; } } } """; CreateCompilation(source, options: TestOptions.ReleaseDll.WithUpdatedMemorySafetyRules(updatedRules)) .VerifyEmitDiagnostics(); } [Theory, CombinatorialData] public void Pointer_Variable_SafeContext(bool allowUnsafe) { var source = """ int* x = null; """; var expectedDiagnostics = new[] { // (1,1): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // int* x = null; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(1, 1), }; CreateCompilation(source, options: TestOptions.ReleaseExe.WithAllowUnsafe(allowUnsafe)).VerifyEmitDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.ReleaseExe.WithAllowUnsafe(allowUnsafe)).VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseExe.WithAllowUnsafe(allowUnsafe)).VerifyEmitDiagnostics(); CreateCompilation(source, options: TestOptions.ReleaseExe.WithAllowUnsafe(allowUnsafe).WithUpdatedMemorySafetyRules()).VerifyEmitDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseExe.WithAllowUnsafe(allowUnsafe).WithUpdatedMemorySafetyRules()).VerifyEmitDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.ReleaseExe.WithAllowUnsafe(allowUnsafe).WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // error CS8630: Invalid 'MemorySafetyRules' value: '2' for C# 14.0. Please use language version 'preview' or greater. Diagnostic(ErrorCode.ERR_CompilationOptionNotAvailable).WithArguments("MemorySafetyRules", "2", "14.0", "preview").WithLocation(1, 1), // (1,1): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // int* x = null; Diagnostic(ErrorCode.ERR_FeatureInPreview, "int*").WithArguments("updated memory safety rules").WithLocation(1, 1)); } [Fact] public void Pointer_Variable_SafeContext_Var() { var source = """ var x = GetPointer(); int* GetPointer() => null; """; CreateCompilation(source, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()).VerifyEmitDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()).VerifyEmitDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // error CS8630: Invalid 'MemorySafetyRules' value: '2' for C# 14.0. Please use language version 'preview' or greater. Diagnostic(ErrorCode.ERR_CompilationOptionNotAvailable).WithArguments("MemorySafetyRules", "2", "14.0", "preview").WithLocation(1, 1), // (1,9): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // var x = GetPointer(); Diagnostic(ErrorCode.ERR_FeatureInPreview, "GetPointer()").WithArguments("updated memory safety rules").WithLocation(1, 9), // (2,1): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // int* GetPointer() => null; Diagnostic(ErrorCode.ERR_FeatureInPreview, "int*").WithArguments("updated memory safety rules").WithLocation(2, 1)); } [Fact] public void Pointer_Variable_SafeContext_InIterator() { var source = """ unsafe { M(); System.Collections.Generic.IEnumerable<int> M() { int* p = null; yield return 1; } } """; CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics( // (6,9): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // int* p = null; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(6, 9)); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe).VerifyEmitDiagnostics(); CreateCompilation(source, options: TestOptions.UnsafeReleaseExe).VerifyEmitDiagnostics(); CreateCompilation(source, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()).VerifyEmitDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()).VerifyEmitDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // error CS8630: Invalid 'MemorySafetyRules' value: '2' for C# 14.0. Please use language version 'preview' or greater. Diagnostic(ErrorCode.ERR_CompilationOptionNotAvailable).WithArguments("MemorySafetyRules", "2", "14.0", "preview").WithLocation(1, 1), // (6,9): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // int* p = null; Diagnostic(ErrorCode.ERR_FeatureInPreview, "int*").WithArguments("updated memory safety rules").WithLocation(6, 9)); var expectedDiagnostics = new[] { // (6,9): error CS9202: Feature 'ref and unsafe in async and iterator methods' is not available in C# 12.0. Please use language version 13.0 or greater. // int* p = null; Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "int*").WithArguments("ref and unsafe in async and iterator methods", "13.0").WithLocation(6, 9), }; CreateCompilation(source, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseExe) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( [ // error CS8630: Invalid 'MemorySafetyRules' value: '2' for C# 12.0. Please use language version 'preview' or greater. Diagnostic(ErrorCode.ERR_CompilationOptionNotAvailable).WithArguments("MemorySafetyRules", "2", "12.0", "preview").WithLocation(1, 1), .. expectedDiagnostics, ]); } [Fact] public void Pointer_Variable_UnsafeContext() { var source = """ unsafe { int* x = null; } """; var expectedDiagnostics = new[] { // (1,1): error CS0227: Unsafe code may only appear if compiling with /unsafe // unsafe { int* x = null; } Diagnostic(ErrorCode.ERR_IllegalUnsafe, "unsafe").WithLocation(1, 1), }; CreateCompilation(source).VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, options: TestOptions.UnsafeReleaseExe).VerifyEmitDiagnostics(); CreateCompilation(source, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()).VerifyEmitDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()).VerifyEmitDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()).VerifyEmitDiagnostics( // error CS8630: Invalid 'MemorySafetyRules' value: '2' for C# 14.0. Please use language version 'preview' or greater. Diagnostic(ErrorCode.ERR_CompilationOptionNotAvailable).WithArguments("MemorySafetyRules", "2", "14.0", "preview").WithLocation(1, 1)); } [Fact] public void Pointer_Variable_UsingAlias_SafeContext() { var source = """ using X = int*; X x = null; """; var expectedDiagnostics = new[] { // (1,11): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // using X = int*; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(1, 11), // (2,1): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // X x = null; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "X").WithLocation(2, 1), }; CreateCompilation(source, options: TestOptions.ReleaseExe).VerifyEmitDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseExe).VerifyEmitDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.ReleaseExe).VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()).VerifyEmitDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()).VerifyEmitDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // error CS8630: Invalid 'MemorySafetyRules' value: '2' for C# 14.0. Please use language version 'preview' or greater. Diagnostic(ErrorCode.ERR_CompilationOptionNotAvailable).WithArguments("MemorySafetyRules", "2", "14.0", "preview").WithLocation(1, 1), // (1,11): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // using X = int*; Diagnostic(ErrorCode.ERR_FeatureInPreview, "int*").WithArguments("updated memory safety rules").WithLocation(1, 11), // (2,1): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // X x = null; Diagnostic(ErrorCode.ERR_FeatureInPreview, "X").WithArguments("updated memory safety rules").WithLocation(2, 1)); } [Fact] public void Pointer_Variable_UsingAlias_UnsafeContext() { var source = """ using unsafe X = int*; unsafe { X x = null; } """; var expectedDiagnostics = new[] { // (1,7): error CS0227: Unsafe code may only appear if compiling with /unsafe // using unsafe X = int*; Diagnostic(ErrorCode.ERR_IllegalUnsafe, "unsafe").WithLocation(1, 7), // (2,1): error CS0227: Unsafe code may only appear if compiling with /unsafe // unsafe { X x = null; } Diagnostic(ErrorCode.ERR_IllegalUnsafe, "unsafe").WithLocation(2, 1), }; CreateCompilation(source).VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, options: TestOptions.UnsafeReleaseExe).VerifyEmitDiagnostics(); CreateCompilation(source, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()).VerifyEmitDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()).VerifyEmitDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()).VerifyEmitDiagnostics( // error CS8630: Invalid 'MemorySafetyRules' value: '2' for C# 14.0. Please use language version 'preview' or greater. Diagnostic(ErrorCode.ERR_CompilationOptionNotAvailable).WithArguments("MemorySafetyRules", "2", "14.0", "preview").WithLocation(1, 1)); } [Theory, CombinatorialData] public void Pointer_Dereference_SafeContext(bool allowUnsafe) { var source = """ int* x = null; int y = *x; """; CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.ReleaseExe.WithAllowUnsafe(allowUnsafe)) .VerifyDiagnostics( // (1,1): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // int* x = null; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(1, 1), // (2,10): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // int y = *x; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "x").WithLocation(2, 10)); var expectedDiagnostics = new[] { // (2,9): error CS9360: This operation may only be used in an unsafe context // int y = *x; Diagnostic(ErrorCode.ERR_UnsafeOperation, "*").WithLocation(2, 9), }; CreateCompilation(source, options: TestOptions.ReleaseExe.WithAllowUnsafe(allowUnsafe)) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseExe.WithAllowUnsafe(allowUnsafe)) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, options: TestOptions.ReleaseExe.WithAllowUnsafe(allowUnsafe).WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseExe.WithAllowUnsafe(allowUnsafe).WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.ReleaseExe.WithAllowUnsafe(allowUnsafe).WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // error CS8630: Invalid 'MemorySafetyRules' value: '2' for C# 14.0. Please use language version 'preview' or greater. Diagnostic(ErrorCode.ERR_CompilationOptionNotAvailable).WithArguments("MemorySafetyRules", "2", "14.0", "preview").WithLocation(1, 1), // (1,1): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // int* x = null; Diagnostic(ErrorCode.ERR_FeatureInPreview, "int*").WithArguments("updated memory safety rules").WithLocation(1, 1), // (2,10): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // int y = *x; Diagnostic(ErrorCode.ERR_FeatureInPreview, "x").WithArguments("updated memory safety rules").WithLocation(2, 10), // (2,9): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // int y = *x; Diagnostic(ErrorCode.ERR_FeatureInPreview, "*").WithArguments("updated memory safety rules").WithLocation(2, 9)); } [Fact] public void Pointer_Dereference_SafeContext_InIterator() { var source = """ unsafe { M(); System.Collections.Generic.IEnumerable<int> M() { int* p = null; int y = *p; yield return 1; } } """; CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseExe) .VerifyDiagnostics( // (6,9): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // int* p = null; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(6, 9), // (7,18): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // int y = *p; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "p").WithLocation(7, 18)); var expectedDiagnostics = new[] { // (7,17): error CS9360: This operation may only be used in an unsafe context // int y = *p; Diagnostic(ErrorCode.ERR_UnsafeOperation, "*").WithLocation(7, 17), }; CreateCompilation(source, options: TestOptions.UnsafeReleaseExe) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // error CS8630: Invalid 'MemorySafetyRules' value: '2' for C# 14.0. Please use language version 'preview' or greater. Diagnostic(ErrorCode.ERR_CompilationOptionNotAvailable).WithArguments("MemorySafetyRules", "2", "14.0", "preview").WithLocation(1, 1), // (6,9): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // int* p = null; Diagnostic(ErrorCode.ERR_FeatureInPreview, "int*").WithArguments("updated memory safety rules").WithLocation(6, 9), // (7,18): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // int y = *p; Diagnostic(ErrorCode.ERR_FeatureInPreview, "p").WithArguments("updated memory safety rules").WithLocation(7, 18), // (7,17): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // int y = *p; Diagnostic(ErrorCode.ERR_FeatureInPreview, "*").WithArguments("updated memory safety rules").WithLocation(7, 17)); expectedDiagnostics = [ // (6,9): error CS9202: Feature 'ref and unsafe in async and iterator methods' is not available in C# 12.0. Please use language version 13.0 or greater. // int* p = null; Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "int*").WithArguments("ref and unsafe in async and iterator methods", "13.0").WithLocation(6, 9), // (7,18): error CS9202: Feature 'ref and unsafe in async and iterator methods' is not available in C# 12.0. Please use language version 13.0 or greater. // int y = *p; Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "p").WithArguments("ref and unsafe in async and iterator methods", "13.0").WithLocation(7, 18), ]; CreateCompilation(source, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseExe) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( [ // error CS8630: Invalid 'MemorySafetyRules' value: '2' for C# 12.0. Please use language version 'preview' or greater. Diagnostic(ErrorCode.ERR_CompilationOptionNotAvailable).WithArguments("MemorySafetyRules", "2", "12.0", "preview").WithLocation(1, 1), .. expectedDiagnostics, ]); } [Fact] public void Pointer_Dereference_SafeContext_Null() { var source = """ int x = *null; """; var expectedDiagnostics = new[] { // (1,9): error CS0193: The * or -> operator must be applied to a pointer // int x = *null; Diagnostic(ErrorCode.ERR_PtrExpected, "*null").WithLocation(1, 9), }; CreateCompilation(source, options: TestOptions.ReleaseExe).VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()).VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics([ // error CS8630: Invalid 'MemorySafetyRules' value: '2' for C# 14.0. Please use language version 'preview' or greater. Diagnostic(ErrorCode.ERR_CompilationOptionNotAvailable).WithArguments("MemorySafetyRules", "2", "14.0", "preview").WithLocation(1, 1), .. expectedDiagnostics, ]); } [Fact] public void Pointer_Dereference_UnsafeContext() { var source = """ int* x = null; unsafe { int y = *x; } """; CreateCompilation(source, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()).VerifyEmitDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()).VerifyEmitDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // error CS8630: Invalid 'MemorySafetyRules' value: '2' for C# 14.0. Please use language version 'preview' or greater. Diagnostic(ErrorCode.ERR_CompilationOptionNotAvailable).WithArguments("MemorySafetyRules", "2", "14.0", "preview").WithLocation(1, 1), // (1,1): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // int* x = null; Diagnostic(ErrorCode.ERR_FeatureInPreview, "int*").WithArguments("updated memory safety rules").WithLocation(1, 1)); } [Fact] public void Pointer_MemberAccess_SafeContext() { var source = """ int* x = null; string s = x->ToString(); """; CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.ReleaseExe) .VerifyDiagnostics( // (1,1): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // int* x = null; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(1, 1), // (2,12): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // string s = x->ToString(); Diagnostic(ErrorCode.ERR_UnsafeNeeded, "x").WithLocation(2, 12)); var expectedDiagnostics = new[] { // (2,13): error CS9360: This operation may only be used in an unsafe context // string s = x->ToString(); Diagnostic(ErrorCode.ERR_UnsafeOperation, "->").WithLocation(2, 13), }; CreateCompilation(source, options: TestOptions.ReleaseExe) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseExe) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // error CS8630: Invalid 'MemorySafetyRules' value: '2' for C# 14.0. Please use language version 'preview' or greater. Diagnostic(ErrorCode.ERR_CompilationOptionNotAvailable).WithArguments("MemorySafetyRules", "2", "14.0", "preview").WithLocation(1, 1), // (1,1): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // int* x = null; Diagnostic(ErrorCode.ERR_FeatureInPreview, "int*").WithArguments("updated memory safety rules").WithLocation(1, 1), // (2,12): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // string s = x->ToString(); Diagnostic(ErrorCode.ERR_FeatureInPreview, "x").WithArguments("updated memory safety rules").WithLocation(2, 12), // (2,13): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // string s = x->ToString(); Diagnostic(ErrorCode.ERR_FeatureInPreview, "->").WithArguments("updated memory safety rules").WithLocation(2, 13)); } [Fact] public void Pointer_MemberAccess_SafeContext_Null() { var source = """ string s = null->ToString(); """; var expectedDiagnostics = new[] { // (1,12): error CS0193: The * or -> operator must be applied to a pointer // string s = null->ToString(); Diagnostic(ErrorCode.ERR_PtrExpected, "null->ToString").WithLocation(1, 12), }; CreateCompilation(source, options: TestOptions.ReleaseExe).VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics([ // error CS8630: Invalid 'MemorySafetyRules' value: '2' for C# 14.0. Please use language version 'preview' or greater. Diagnostic(ErrorCode.ERR_CompilationOptionNotAvailable).WithArguments("MemorySafetyRules", "2", "14.0", "preview").WithLocation(1, 1), .. expectedDiagnostics, ]); } [Fact] public void Pointer_MemberAccess_UnsafeContext() { var source = """ int* x = null; unsafe { string s = x->ToString(); } """; CreateCompilation(source, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()).VerifyEmitDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()).VerifyEmitDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // error CS8630: Invalid 'MemorySafetyRules' value: '2' for C# 14.0. Please use language version 'preview' or greater. Diagnostic(ErrorCode.ERR_CompilationOptionNotAvailable).WithArguments("MemorySafetyRules", "2", "14.0", "preview").WithLocation(1, 1), // (1,1): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // int* x = null; Diagnostic(ErrorCode.ERR_FeatureInPreview, "int*").WithArguments("updated memory safety rules").WithLocation(1, 1)); } [Fact] public void Pointer_MemberAccessViaDereference_SafeContext() { var source = """ int* x = null; string s = (*x).ToString(); """; CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.ReleaseExe) .VerifyDiagnostics( // (1,1): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // int* x = null; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(1, 1), // (2,14): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // string s = (*x).ToString(); Diagnostic(ErrorCode.ERR_UnsafeNeeded, "x").WithLocation(2, 14)); var expectedDiagnostics = new[] { // (2,13): error CS9360: This operation may only be used in an unsafe context // string s = (*x).ToString(); Diagnostic(ErrorCode.ERR_UnsafeOperation, "*").WithLocation(2, 13), }; CreateCompilation(source, options: TestOptions.ReleaseExe) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseExe) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // error CS8630: Invalid 'MemorySafetyRules' value: '2' for C# 14.0. Please use language version 'preview' or greater. Diagnostic(ErrorCode.ERR_CompilationOptionNotAvailable).WithArguments("MemorySafetyRules", "2", "14.0", "preview").WithLocation(1, 1), // (1,1): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // int* x = null; Diagnostic(ErrorCode.ERR_FeatureInPreview, "int*").WithArguments("updated memory safety rules").WithLocation(1, 1), // (2,14): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // string s = (*x).ToString(); Diagnostic(ErrorCode.ERR_FeatureInPreview, "x").WithArguments("updated memory safety rules").WithLocation(2, 14), // (2,13): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // string s = (*x).ToString(); Diagnostic(ErrorCode.ERR_FeatureInPreview, "*").WithArguments("updated memory safety rules").WithLocation(2, 13)); } [Fact] public void Pointer_MemberAccessViaDereference_UnsafeContext() { var source = """ int* x = null; unsafe { string s = (*x).ToString(); } """; CreateCompilation(source, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()).VerifyEmitDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()).VerifyEmitDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // error CS8630: Invalid 'MemorySafetyRules' value: '2' for C# 14.0. Please use language version 'preview' or greater. Diagnostic(ErrorCode.ERR_CompilationOptionNotAvailable).WithArguments("MemorySafetyRules", "2", "14.0", "preview").WithLocation(1, 1), // (1,1): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // int* x = null; Diagnostic(ErrorCode.ERR_FeatureInPreview, "int*").WithArguments("updated memory safety rules").WithLocation(1, 1)); } [Fact] public void Pointer_ElementAccess_SafeContext() { var source = """ int* x = null; x[0] = 1; int y = x[1]; """; CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.ReleaseExe) .VerifyDiagnostics( // (1,1): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // int* x = null; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(1, 1), // (2,1): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // x[0] = 1; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "x").WithLocation(2, 1), // (3,9): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // int y = x[1]; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "x").WithLocation(3, 9)); var expectedDiagnostics = new[] { // (2,2): error CS9360: This operation may only be used in an unsafe context // x[0] = 1; Diagnostic(ErrorCode.ERR_UnsafeOperation, "[").WithLocation(2, 2), // (3,10): error CS9360: This operation may only be used in an unsafe context // int y = x[1]; Diagnostic(ErrorCode.ERR_UnsafeOperation, "[").WithLocation(3, 10), }; CreateCompilation(source, options: TestOptions.ReleaseExe) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseExe) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // error CS8630: Invalid 'MemorySafetyRules' value: '2' for C# 14.0. Please use language version 'preview' or greater. Diagnostic(ErrorCode.ERR_CompilationOptionNotAvailable).WithArguments("MemorySafetyRules", "2", "14.0", "preview").WithLocation(1, 1), // (1,1): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // int* x = null; Diagnostic(ErrorCode.ERR_FeatureInPreview, "int*").WithArguments("updated memory safety rules").WithLocation(1, 1), // (2,1): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // x[0] = 1; Diagnostic(ErrorCode.ERR_FeatureInPreview, "x").WithArguments("updated memory safety rules").WithLocation(2, 1), // (2,2): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // x[0] = 1; Diagnostic(ErrorCode.ERR_FeatureInPreview, "[").WithArguments("updated memory safety rules").WithLocation(2, 2), // (3,9): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // int y = x[1]; Diagnostic(ErrorCode.ERR_FeatureInPreview, "x").WithArguments("updated memory safety rules").WithLocation(3, 9), // (3,10): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // int y = x[1]; Diagnostic(ErrorCode.ERR_FeatureInPreview, "[").WithArguments("updated memory safety rules").WithLocation(3, 10)); } [Fact] public void Pointer_ElementAccess_SafeContext_MultipleIndices() { var source = """ int* x = null; x[0, 1] = 1; int y = x[2, 3]; """; CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.ReleaseExe) .VerifyDiagnostics( // (1,1): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // int* x = null; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(1, 1), // (2,1): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // x[0, 1] = 1; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "x").WithLocation(2, 1), // (2,1): error CS0196: A pointer must be indexed by only one value // x[0, 1] = 1; Diagnostic(ErrorCode.ERR_PtrIndexSingle, "x[0, 1]").WithLocation(2, 1), // (3,9): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // int y = x[2, 3]; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "x").WithLocation(3, 9), // (3,9): error CS0196: A pointer must be indexed by only one value // int y = x[2, 3]; Diagnostic(ErrorCode.ERR_PtrIndexSingle, "x[2, 3]").WithLocation(3, 9)); var expectedDiagnostics = new[] { // (2,1): error CS0196: A pointer must be indexed by only one value // x[0, 1] = 1; Diagnostic(ErrorCode.ERR_PtrIndexSingle, "x[0, 1]").WithLocation(2, 1), // (3,9): error CS0196: A pointer must be indexed by only one value // int y = x[2, 3]; Diagnostic(ErrorCode.ERR_PtrIndexSingle, "x[2, 3]").WithLocation(3, 9), }; CreateCompilation(source, options: TestOptions.ReleaseExe) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseExe) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // error CS8630: Invalid 'MemorySafetyRules' value: '2' for C# 14.0. Please use language version 'preview' or greater. Diagnostic(ErrorCode.ERR_CompilationOptionNotAvailable).WithArguments("MemorySafetyRules", "2", "14.0", "preview").WithLocation(1, 1), // (1,1): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // int* x = null; Diagnostic(ErrorCode.ERR_FeatureInPreview, "int*").WithArguments("updated memory safety rules").WithLocation(1, 1), // (2,1): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // x[0, 1] = 1; Diagnostic(ErrorCode.ERR_FeatureInPreview, "x").WithArguments("updated memory safety rules").WithLocation(2, 1), // (2,1): error CS0196: A pointer must be indexed by only one value // x[0, 1] = 1; Diagnostic(ErrorCode.ERR_PtrIndexSingle, "x[0, 1]").WithLocation(2, 1), // (3,9): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // int y = x[2, 3]; Diagnostic(ErrorCode.ERR_FeatureInPreview, "x").WithArguments("updated memory safety rules").WithLocation(3, 9), // (3,9): error CS0196: A pointer must be indexed by only one value // int y = x[2, 3]; Diagnostic(ErrorCode.ERR_PtrIndexSingle, "x[2, 3]").WithLocation(3, 9)); } [Fact] public void Pointer_ElementAccess_SafeContext_ArrayOfPointers() { var source = """ int*[] x = []; x[0] = null; _ = x[1]; """; CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.ReleaseExe) .VerifyDiagnostics( // (1,1): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // int*[] x = []; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(1, 1), // (2,1): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // x[0] = null; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "x").WithLocation(2, 1), // (2,1): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // x[0] = null; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "x[0]").WithLocation(2, 1), // (2,1): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // x[0] = null; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "x[0] = null").WithLocation(2, 1), // (3,5): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // _ = x[1]; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "x").WithLocation(3, 5), // (3,5): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // _ = x[1]; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "x[1]").WithLocation(3, 5), // (3,1): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // _ = x[1]; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "_ = x[1]").WithLocation(3, 1)); CreateCompilation(source, options: TestOptions.ReleaseExe).VerifyEmitDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseExe).VerifyEmitDiagnostics(); CreateCompilation(source, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()).VerifyEmitDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()).VerifyEmitDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // error CS8630: Invalid 'MemorySafetyRules' value: '2' for C# 14.0. Please use language version 'preview' or greater. Diagnostic(ErrorCode.ERR_CompilationOptionNotAvailable).WithArguments("MemorySafetyRules", "2", "14.0", "preview").WithLocation(1, 1), // (1,1): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // int*[] x = []; Diagnostic(ErrorCode.ERR_FeatureInPreview, "int*").WithArguments("updated memory safety rules").WithLocation(1, 1), // (2,1): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // x[0] = null; Diagnostic(ErrorCode.ERR_FeatureInPreview, "x").WithArguments("updated memory safety rules").WithLocation(2, 1), // (2,1): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // x[0] = null; Diagnostic(ErrorCode.ERR_FeatureInPreview, "x[0]").WithArguments("updated memory safety rules").WithLocation(2, 1), // (2,1): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // x[0] = null; Diagnostic(ErrorCode.ERR_FeatureInPreview, "x[0] = null").WithArguments("updated memory safety rules").WithLocation(2, 1), // (3,5): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // _ = x[1]; Diagnostic(ErrorCode.ERR_FeatureInPreview, "x").WithArguments("updated memory safety rules").WithLocation(3, 5), // (3,5): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // _ = x[1]; Diagnostic(ErrorCode.ERR_FeatureInPreview, "x[1]").WithArguments("updated memory safety rules").WithLocation(3, 5), // (3,1): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // _ = x[1]; Diagnostic(ErrorCode.ERR_FeatureInPreview, "_ = x[1]").WithArguments("updated memory safety rules").WithLocation(3, 1)); } [Fact] public void Pointer_ElementAccess_SafeContext_FunctionPointer() { var source = """ delegate*<void> x = null; x[0] = null; _ = x[1]; """; CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.ReleaseExe) .VerifyDiagnostics( // (1,1): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // delegate*<void> x = null; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "delegate*").WithLocation(1, 1), // (2,1): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // x[0] = null; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "x").WithLocation(2, 1), // (2,1): error CS0021: Cannot apply indexing with [] to an expression of type 'delegate*<void>' // x[0] = null; Diagnostic(ErrorCode.ERR_BadIndexLHS, "x[0]").WithArguments("delegate*<void>").WithLocation(2, 1), // (3,5): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // _ = x[1]; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "x").WithLocation(3, 5), // (3,5): error CS0021: Cannot apply indexing with [] to an expression of type 'delegate*<void>' // _ = x[1]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "x[1]").WithArguments("delegate*<void>").WithLocation(3, 5)); var expectedDiagnostics = new[] { // (2,1): error CS0021: Cannot apply indexing with [] to an expression of type 'delegate*<void>' // x[0] = null; Diagnostic(ErrorCode.ERR_BadIndexLHS, "x[0]").WithArguments("delegate*<void>").WithLocation(2, 1), // (3,5): error CS0021: Cannot apply indexing with [] to an expression of type 'delegate*<void>' // _ = x[1]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "x[1]").WithArguments("delegate*<void>").WithLocation(3, 5), }; CreateCompilation(source, options: TestOptions.ReleaseExe) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseExe) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // error CS8630: Invalid 'MemorySafetyRules' value: '2' for C# 14.0. Please use language version 'preview' or greater. Diagnostic(ErrorCode.ERR_CompilationOptionNotAvailable).WithArguments("MemorySafetyRules", "2", "14.0", "preview").WithLocation(1, 1), // (1,1): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // delegate*<void> x = null; Diagnostic(ErrorCode.ERR_FeatureInPreview, "delegate*").WithArguments("updated memory safety rules").WithLocation(1, 1), // (2,1): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // x[0] = null; Diagnostic(ErrorCode.ERR_FeatureInPreview, "x").WithArguments("updated memory safety rules").WithLocation(2, 1), // (2,1): error CS0021: Cannot apply indexing with [] to an expression of type 'delegate*<void>' // x[0] = null; Diagnostic(ErrorCode.ERR_BadIndexLHS, "x[0]").WithArguments("delegate*<void>").WithLocation(2, 1), // (3,5): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // _ = x[1]; Diagnostic(ErrorCode.ERR_FeatureInPreview, "x").WithArguments("updated memory safety rules").WithLocation(3, 5), // (3,5): error CS0021: Cannot apply indexing with [] to an expression of type 'delegate*<void>' // _ = x[1]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "x[1]").WithArguments("delegate*<void>").WithLocation(3, 5)); } [Fact] public void Pointer_ElementAccess_UnsafeContext() { var source = """ int* x = null; unsafe { x[0] = 1; int y = x[1]; } """; CreateCompilation(source, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()).VerifyEmitDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()).VerifyEmitDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // error CS8630: Invalid 'MemorySafetyRules' value: '2' for C# 14.0. Please use language version 'preview' or greater. Diagnostic(ErrorCode.ERR_CompilationOptionNotAvailable).WithArguments("MemorySafetyRules", "2", "14.0", "preview").WithLocation(1, 1), // (1,1): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // int* x = null; Diagnostic(ErrorCode.ERR_FeatureInPreview, "int*").WithArguments("updated memory safety rules").WithLocation(1, 1)); } [Fact] public void Pointer_Function_Variable_SafeContext() { var source = """ delegate*<void> f = null; """; var expectedDiagnostics = new[] { // (1,1): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // delegate*<void> f = null; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "delegate*").WithLocation(1, 1), }; CreateCompilation(source, options: TestOptions.ReleaseExe).VerifyEmitDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.ReleaseExe).VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()).VerifyEmitDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()).VerifyEmitDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // error CS8630: Invalid 'MemorySafetyRules' value: '2' for C# 14.0. Please use language version 'preview' or greater. Diagnostic(ErrorCode.ERR_CompilationOptionNotAvailable).WithArguments("MemorySafetyRules", "2", "14.0", "preview").WithLocation(1, 1), // (1,1): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // delegate*<void> f = null; Diagnostic(ErrorCode.ERR_FeatureInPreview, "delegate*").WithArguments("updated memory safety rules").WithLocation(1, 1)); } [Fact] public void Pointer_Function_Variable_UnsafeContext() { var source = """ unsafe { delegate*<void> f = null; } """; var expectedDiagnostics = new[] { // (1,1): error CS0227: Unsafe code may only appear if compiling with /unsafe // unsafe { delegate*<void> f = null; } Diagnostic(ErrorCode.ERR_IllegalUnsafe, "unsafe").WithLocation(1, 1), }; CreateCompilation(source).VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, options: TestOptions.UnsafeReleaseExe).VerifyEmitDiagnostics(); CreateCompilation(source, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()).VerifyEmitDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()).VerifyEmitDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()).VerifyEmitDiagnostics( // error CS8630: Invalid 'MemorySafetyRules' value: '2' for C# 14.0. Please use language version 'preview' or greater. Diagnostic(ErrorCode.ERR_CompilationOptionNotAvailable).WithArguments("MemorySafetyRules", "2", "14.0", "preview").WithLocation(1, 1)); } [Fact] public void Pointer_Function_Variable_UsingAlias_SafeContext() { var source = """ using X = delegate*<void>; X x = null; """; var expectedDiagnostics = new[] { // (1,11): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // using X = delegate*<void>; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "delegate*").WithLocation(1, 11), // (2,1): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // X x = null; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "X").WithLocation(2, 1), }; CreateCompilation(source, options: TestOptions.ReleaseExe).VerifyDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.ReleaseExe).VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseExe).VerifyDiagnostics(); // https://github.com/dotnet/roslyn/issues/77389 expectedDiagnostics = PlatformInformation.IsWindows ? [ // error CS8911: Using a function pointer type in this context is not supported. Diagnostic(ErrorCode.ERR_FunctionPointerTypesInAttributeNotSupported).WithLocation(1, 1), ] : []; CreateCompilation(source, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics() .VerifyEmitDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics() .VerifyEmitDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // error CS8630: Invalid 'MemorySafetyRules' value: '2' for C# 14.0. Please use language version 'preview' or greater. Diagnostic(ErrorCode.ERR_CompilationOptionNotAvailable).WithArguments("MemorySafetyRules", "2", "14.0", "preview").WithLocation(1, 1), // (1,11): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // using X = delegate*<void>; Diagnostic(ErrorCode.ERR_FeatureInPreview, "delegate*").WithArguments("updated memory safety rules").WithLocation(1, 11), // (2,1): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // X x = null; Diagnostic(ErrorCode.ERR_FeatureInPreview, "X").WithArguments("updated memory safety rules").WithLocation(2, 1)); } [Fact] public void Pointer_Function_Variable_UsingAlias_UnsafeContext() { var source = """ using unsafe X = delegate*<void>; unsafe { X x = null; } """; var expectedDiagnostics = new[] { // (1,7): error CS0227: Unsafe code may only appear if compiling with /unsafe // using unsafe X = delegate*<void>; Diagnostic(ErrorCode.ERR_IllegalUnsafe, "unsafe").WithLocation(1, 7), // (2,1): error CS0227: Unsafe code may only appear if compiling with /unsafe // unsafe { X x = null; } Diagnostic(ErrorCode.ERR_IllegalUnsafe, "unsafe").WithLocation(2, 1), }; CreateCompilation(source).VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(expectedDiagnostics); // https://github.com/dotnet/roslyn/issues/77389 expectedDiagnostics = PlatformInformation.IsWindows ? [ // error CS8911: Using a function pointer type in this context is not supported. Diagnostic(ErrorCode.ERR_FunctionPointerTypesInAttributeNotSupported).WithLocation(1, 1), ] : []; CreateCompilation(source, options: TestOptions.UnsafeReleaseExe) .VerifyDiagnostics() .VerifyEmitDiagnostics(expectedDiagnostics); CreateCompilation(source, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics() .VerifyEmitDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics() .VerifyEmitDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // error CS8630: Invalid 'MemorySafetyRules' value: '2' for C# 14.0. Please use language version 'preview' or greater. Diagnostic(ErrorCode.ERR_CompilationOptionNotAvailable).WithArguments("MemorySafetyRules", "2", "14.0", "preview").WithLocation(1, 1)); } [Fact] public void Pointer_Function_Call_SafeContext() { var source = """ delegate*<string> x = null; string s = x(); """; CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.ReleaseExe) .VerifyDiagnostics( // (1,1): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // delegate*<string> x = null; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "delegate*").WithLocation(1, 1), // (2,12): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // string s = x(); Diagnostic(ErrorCode.ERR_UnsafeNeeded, "x()").WithLocation(2, 12)); var expectedDiagnostics = new[] { // (2,12): error CS9360: This operation may only be used in an unsafe context // string s = x(); Diagnostic(ErrorCode.ERR_UnsafeOperation, "x()").WithLocation(2, 12), }; CreateCompilation(source, options: TestOptions.ReleaseExe) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseExe) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // error CS8630: Invalid 'MemorySafetyRules' value: '2' for C# 14.0. Please use language version 'preview' or greater. Diagnostic(ErrorCode.ERR_CompilationOptionNotAvailable).WithArguments("MemorySafetyRules", "2", "14.0", "preview").WithLocation(1, 1), // (1,1): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // delegate*<string> x = null; Diagnostic(ErrorCode.ERR_FeatureInPreview, "delegate*").WithArguments("updated memory safety rules").WithLocation(1, 1), // (2,12): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // string s = x(); Diagnostic(ErrorCode.ERR_FeatureInPreview, "x()").WithArguments("updated memory safety rules").WithLocation(2, 12)); } [Fact] public void Pointer_Function_Call_UnsafeContext() { var source = """ delegate*<string> x = null; unsafe { string s = x(); } """; CreateCompilation(source, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()).VerifyEmitDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()).VerifyEmitDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // error CS8630: Invalid 'MemorySafetyRules' value: '2' for C# 14.0. Please use language version 'preview' or greater. Diagnostic(ErrorCode.ERR_CompilationOptionNotAvailable).WithArguments("MemorySafetyRules", "2", "14.0", "preview").WithLocation(1, 1), // (1,1): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // delegate*<string> x = null; Diagnostic(ErrorCode.ERR_FeatureInPreview, "delegate*").WithArguments("updated memory safety rules").WithLocation(1, 1)); } [Fact] public void Pointer_Function_Call_UsingAlias() { var source = """ using X = delegate*<string>; X x = null; string s = x(); """; CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.ReleaseExe) .VerifyDiagnostics( // (1,11): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // using X = delegate*<string>; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "delegate*").WithLocation(1, 11), // (2,1): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // X x = null; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "X").WithLocation(2, 1), // (3,12): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // string s = x(); Diagnostic(ErrorCode.ERR_UnsafeNeeded, "x()").WithLocation(3, 12)); var expectedDiagnostics = new[] { // (3,12): error CS9360: This operation may only be used in an unsafe context // string s = x(); Diagnostic(ErrorCode.ERR_UnsafeOperation, "x()").WithLocation(3, 12), }; CreateCompilation(source, options: TestOptions.ReleaseExe) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseExe) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // error CS8630: Invalid 'MemorySafetyRules' value: '2' for C# 14.0. Please use language version 'preview' or greater. Diagnostic(ErrorCode.ERR_CompilationOptionNotAvailable).WithArguments("MemorySafetyRules", "2", "14.0", "preview").WithLocation(1, 1), // (1,11): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // using X = delegate*<string>; Diagnostic(ErrorCode.ERR_FeatureInPreview, "delegate*").WithArguments("updated memory safety rules").WithLocation(1, 11), // (2,1): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // X x = null; Diagnostic(ErrorCode.ERR_FeatureInPreview, "X").WithArguments("updated memory safety rules").WithLocation(2, 1), // (3,12): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // string s = x(); Diagnostic(ErrorCode.ERR_FeatureInPreview, "x()").WithArguments("updated memory safety rules").WithLocation(3, 12)); } [Fact] public void Pointer_AddressOf_SafeContext() { var source = """ int x; int* p = &x; """; var expectedDiagnostics = new[] { // (2,1): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // int* p = &x; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(2, 1), // (2,10): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // int* p = &x; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "&x").WithLocation(2, 10), }; CreateCompilation(source, options: TestOptions.ReleaseExe).VerifyEmitDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.ReleaseExe).VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseExe).VerifyEmitDiagnostics(); CreateCompilation(source, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()).VerifyEmitDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()).VerifyEmitDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // error CS8630: Invalid 'MemorySafetyRules' value: '2' for C# 14.0. Please use language version 'preview' or greater. Diagnostic(ErrorCode.ERR_CompilationOptionNotAvailable).WithArguments("MemorySafetyRules", "2", "14.0", "preview").WithLocation(1, 1), // (2,1): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // int* p = &x; Diagnostic(ErrorCode.ERR_FeatureInPreview, "int*").WithArguments("updated memory safety rules").WithLocation(2, 1), // (2,10): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // int* p = &x; Diagnostic(ErrorCode.ERR_FeatureInPreview, "&x").WithArguments("updated memory safety rules").WithLocation(2, 10)); } [Fact] public void Pointer_AddressOf_SafeContext_Const() { var source = """ const int x = 1; int* p = &x; """; CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.ReleaseExe) .VerifyDiagnostics( // (2,1): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // int* p = &x; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(2, 1), // (2,11): error CS0211: Cannot take the address of the given expression // int* p = &x; Diagnostic(ErrorCode.ERR_InvalidAddrOp, "x").WithLocation(2, 11)); var expectedDiagnostics = new[] { // (2,11): error CS0211: Cannot take the address of the given expression // int* p = &x; Diagnostic(ErrorCode.ERR_InvalidAddrOp, "x").WithLocation(2, 11), }; CreateCompilation(source, options: TestOptions.ReleaseExe) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseExe) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // error CS8630: Invalid 'MemorySafetyRules' value: '2' for C# 14.0. Please use language version 'preview' or greater. Diagnostic(ErrorCode.ERR_CompilationOptionNotAvailable).WithArguments("MemorySafetyRules", "2", "14.0", "preview").WithLocation(1, 1), // (2,1): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // int* p = &x; Diagnostic(ErrorCode.ERR_FeatureInPreview, "int*").WithArguments("updated memory safety rules").WithLocation(2, 1), // (2,11): error CS0211: Cannot take the address of the given expression // int* p = &x; Diagnostic(ErrorCode.ERR_InvalidAddrOp, "x").WithLocation(2, 11)); } [Fact] public void Pointer_AddressOf_UnsafeContext() { var source = """ int x; unsafe { int* p = &x; } """; var expectedDiagnostics = new[] { // (2,1): error CS0227: Unsafe code may only appear if compiling with /unsafe // unsafe { int* p = &x; } Diagnostic(ErrorCode.ERR_IllegalUnsafe, "unsafe").WithLocation(2, 1), }; CreateCompilation(source).VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, options: TestOptions.UnsafeReleaseExe).VerifyEmitDiagnostics(); CreateCompilation(source, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()).VerifyEmitDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()).VerifyEmitDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()).VerifyEmitDiagnostics( // error CS8630: Invalid 'MemorySafetyRules' value: '2' for C# 14.0. Please use language version 'preview' or greater. Diagnostic(ErrorCode.ERR_CompilationOptionNotAvailable).WithArguments("MemorySafetyRules", "2", "14.0", "preview").WithLocation(1, 1)); } [Fact] public void Pointer_AddressOfManaged_SafeContext() { var source = """ string s; string* p = &s; """; var expectedDiagnostics = new[] { // (2,1): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('string') // string* p = &s; Diagnostic(ErrorCode.WRN_ManagedAddr, "string*").WithArguments("string").WithLocation(2, 1), // (2,13): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('string') // string* p = &s; Diagnostic(ErrorCode.WRN_ManagedAddr, "&s").WithArguments("string").WithLocation(2, 13), }; CreateCompilation(source, options: TestOptions.ReleaseExe).VerifyEmitDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.ReleaseExe).VerifyDiagnostics( // (2,1): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // string* p = &s; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "string*").WithLocation(2, 1), // (2,1): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('string') // string* p = &s; Diagnostic(ErrorCode.WRN_ManagedAddr, "string*").WithArguments("string").WithLocation(2, 1), // (2,13): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // string* p = &s; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "&s").WithLocation(2, 13), // (2,13): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('string') // string* p = &s; Diagnostic(ErrorCode.WRN_ManagedAddr, "&s").WithArguments("string").WithLocation(2, 13)); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseExe).VerifyEmitDiagnostics(expectedDiagnostics); CreateCompilation(source, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()).VerifyEmitDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()).VerifyEmitDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // error CS8630: Invalid 'MemorySafetyRules' value: '2' for C# 14.0. Please use language version 'preview' or greater. Diagnostic(ErrorCode.ERR_CompilationOptionNotAvailable).WithArguments("MemorySafetyRules", "2", "14.0", "preview").WithLocation(1, 1), // (2,1): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // string* p = &s; Diagnostic(ErrorCode.ERR_FeatureInPreview, "string*").WithArguments("updated memory safety rules").WithLocation(2, 1), // (2,1): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('string') // string* p = &s; Diagnostic(ErrorCode.WRN_ManagedAddr, "string*").WithArguments("string").WithLocation(2, 1), // (2,13): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // string* p = &s; Diagnostic(ErrorCode.ERR_FeatureInPreview, "&s").WithArguments("updated memory safety rules").WithLocation(2, 13), // (2,13): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('string') // string* p = &s; Diagnostic(ErrorCode.WRN_ManagedAddr, "&s").WithArguments("string").WithLocation(2, 13)); } [Fact] public void Pointer_AddressOfManaged_UnsafeContext() { var source = """ string s; unsafe { string* p = &s; } """; var expectedWarnings = new[] { // (2,10): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('string') // unsafe { string* p = &s; } Diagnostic(ErrorCode.WRN_ManagedAddr, "string*").WithArguments("string").WithLocation(2, 10), // (2,22): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('string') // unsafe { string* p = &s; } Diagnostic(ErrorCode.WRN_ManagedAddr, "&s").WithArguments("string").WithLocation(2, 22), }; var expectedDiagnostics = new[] { // (2,1): error CS0227: Unsafe code may only appear if compiling with /unsafe // unsafe { string* p = &s; } Diagnostic(ErrorCode.ERR_IllegalUnsafe, "unsafe").WithLocation(2, 1), // (2,10): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('string') // unsafe { string* p = &s; } Diagnostic(ErrorCode.WRN_ManagedAddr, "string*").WithArguments("string").WithLocation(2, 10), // (2,22): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('string') // unsafe { string* p = &s; } Diagnostic(ErrorCode.WRN_ManagedAddr, "&s").WithArguments("string").WithLocation(2, 22), }; CreateCompilation(source).VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, options: TestOptions.UnsafeReleaseExe).VerifyEmitDiagnostics(expectedWarnings); CreateCompilation(source, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()).VerifyEmitDiagnostics(expectedWarnings); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()).VerifyEmitDiagnostics(expectedWarnings); CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()).VerifyEmitDiagnostics( // error CS8630: Invalid 'MemorySafetyRules' value: '2' for C# 14.0. Please use language version 'preview' or greater. Diagnostic(ErrorCode.ERR_CompilationOptionNotAvailable).WithArguments("MemorySafetyRules", "2", "14.0", "preview").WithLocation(1, 1), // (2,10): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('string') // unsafe { string* p = &s; } Diagnostic(ErrorCode.WRN_ManagedAddr, "string*").WithArguments("string").WithLocation(2, 10), // (2,22): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('string') // unsafe { string* p = &s; } Diagnostic(ErrorCode.WRN_ManagedAddr, "&s").WithArguments("string").WithLocation(2, 22)); } [Fact] public void Pointer_Fixed_SafeContext() { var source = """ class C { static int x; static void Main() { fixed (int* p = &x) { } } } """; var expectedDiagnostics = new[] { // (6,9): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // fixed (int* p = &x) { } Diagnostic(ErrorCode.ERR_UnsafeNeeded, "fixed (int* p = &x) { }").WithLocation(6, 9), // (6,16): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // fixed (int* p = &x) { } Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(6, 16), // (6,25): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // fixed (int* p = &x) { } Diagnostic(ErrorCode.ERR_UnsafeNeeded, "&x").WithLocation(6, 25), }; CreateCompilation(source, options: TestOptions.ReleaseExe).VerifyEmitDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.ReleaseExe).VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseExe).VerifyEmitDiagnostics(); CreateCompilation(source, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()).VerifyEmitDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()).VerifyEmitDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // error CS8630: Invalid 'MemorySafetyRules' value: '2' for C# 14.0. Please use language version 'preview' or greater. Diagnostic(ErrorCode.ERR_CompilationOptionNotAvailable).WithArguments("MemorySafetyRules", "2", "14.0", "preview").WithLocation(1, 1), // (6,9): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // fixed (int* p = &x) { } Diagnostic(ErrorCode.ERR_FeatureInPreview, "fixed (int* p = &x) { }").WithArguments("updated memory safety rules").WithLocation(6, 9), // (6,16): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // fixed (int* p = &x) { } Diagnostic(ErrorCode.ERR_FeatureInPreview, "int*").WithArguments("updated memory safety rules").WithLocation(6, 16), // (6,25): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // fixed (int* p = &x) { } Diagnostic(ErrorCode.ERR_FeatureInPreview, "&x").WithArguments("updated memory safety rules").WithLocation(6, 25)); } [Fact] public void Pointer_Fixed_PatternBased() { var source = """ class C { static void Main() { fixed (int* p = new S()) { } } } struct S { public ref readonly int GetPinnableReference() => throw null; } """; var expectedDiagnostics = new[] { // (5,9): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // fixed (int* p = new S()) { } Diagnostic(ErrorCode.ERR_UnsafeNeeded, "fixed (int* p = new S()) { }").WithLocation(5, 9), // (5,16): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // fixed (int* p = new S()) { } Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(5, 16), }; CreateCompilation(source, options: TestOptions.ReleaseExe).VerifyEmitDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.ReleaseExe).VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseExe).VerifyEmitDiagnostics(); CreateCompilation(source, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()).VerifyEmitDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()).VerifyEmitDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // error CS8630: Invalid 'MemorySafetyRules' value: '2' for C# 14.0. Please use language version 'preview' or greater. Diagnostic(ErrorCode.ERR_CompilationOptionNotAvailable).WithArguments("MemorySafetyRules", "2", "14.0", "preview").WithLocation(1, 1), // (5,9): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // fixed (int* p = new S()) { } Diagnostic(ErrorCode.ERR_FeatureInPreview, "fixed (int* p = new S()) { }").WithArguments("updated memory safety rules").WithLocation(5, 9), // (5,16): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // fixed (int* p = new S()) { } Diagnostic(ErrorCode.ERR_FeatureInPreview, "int*").WithArguments("updated memory safety rules").WithLocation(5, 16)); } [Fact] public void Pointer_Fixed_SafeContext_AlreadyFixed() { var source = """ int x; fixed (int* p = &x) { } """; CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.ReleaseExe) .VerifyDiagnostics( // (2,1): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // fixed (int* p = &x) { } Diagnostic(ErrorCode.ERR_UnsafeNeeded, "fixed (int* p = &x) { }").WithLocation(2, 1), // (2,8): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // fixed (int* p = &x) { } Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(2, 8), // (2,17): error CS0213: You cannot use the fixed statement to take the address of an already fixed expression // fixed (int* p = &x) { } Diagnostic(ErrorCode.ERR_FixedNotNeeded, "&x").WithLocation(2, 17)); var expectedDiagnostics = new[] { // (2,17): error CS0213: You cannot use the fixed statement to take the address of an already fixed expression // fixed (int* p = &x) { } Diagnostic(ErrorCode.ERR_FixedNotNeeded, "&x").WithLocation(2, 17), }; CreateCompilation(source, options: TestOptions.ReleaseExe) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseExe) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // error CS8630: Invalid 'MemorySafetyRules' value: '2' for C# 14.0. Please use language version 'preview' or greater. Diagnostic(ErrorCode.ERR_CompilationOptionNotAvailable).WithArguments("MemorySafetyRules", "2", "14.0", "preview").WithLocation(1, 1), // (2,1): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // fixed (int* p = &x) { } Diagnostic(ErrorCode.ERR_FeatureInPreview, "fixed (int* p = &x) { }").WithArguments("updated memory safety rules").WithLocation(2, 1), // (2,8): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // fixed (int* p = &x) { } Diagnostic(ErrorCode.ERR_FeatureInPreview, "int*").WithArguments("updated memory safety rules").WithLocation(2, 8), // (2,17): error CS0213: You cannot use the fixed statement to take the address of an already fixed expression // fixed (int* p = &x) { } Diagnostic(ErrorCode.ERR_FixedNotNeeded, "&x").WithLocation(2, 17)); } [Fact] public void Pointer_Fixed_UnsafeContext() { var source = """ class C { static int x; static void Main() { unsafe { fixed (int* p = &x) { } } } } """; var expectedDiagnostics = new[] { // (6,9): error CS0227: Unsafe code may only appear if compiling with /unsafe // unsafe { fixed (int* p = &x) { } } Diagnostic(ErrorCode.ERR_IllegalUnsafe, "unsafe").WithLocation(6, 9), }; CreateCompilation(source).VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, options: TestOptions.UnsafeReleaseExe).VerifyEmitDiagnostics(); CreateCompilation(source, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()).VerifyEmitDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()).VerifyEmitDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()).VerifyEmitDiagnostics( // error CS8630: Invalid 'MemorySafetyRules' value: '2' for C# 14.0. Please use language version 'preview' or greater. Diagnostic(ErrorCode.ERR_CompilationOptionNotAvailable).WithArguments("MemorySafetyRules", "2", "14.0", "preview").WithLocation(1, 1)); } [Fact] public void Pointer_Fixed_ManagedType() { var source = """ class C { static string x; static void Main() { fixed (string* p1 = &x) { } unsafe { fixed (string* p2 = &x) { } } } } """; CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseExe) .VerifyDiagnostics( // (6,9): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // fixed (string* p1 = &x) { } Diagnostic(ErrorCode.ERR_UnsafeNeeded, "fixed (string* p1 = &x) { }").WithLocation(6, 9), // (6,16): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // fixed (string* p1 = &x) { } Diagnostic(ErrorCode.ERR_UnsafeNeeded, "string*").WithLocation(6, 16), // (6,16): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('string') // fixed (string* p1 = &x) { } Diagnostic(ErrorCode.WRN_ManagedAddr, "string*").WithArguments("string").WithLocation(6, 16), // (6,29): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('string') // fixed (string* p1 = &x) { } Diagnostic(ErrorCode.WRN_ManagedAddr, "&x").WithArguments("string").WithLocation(6, 29), // (6,29): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // fixed (string* p1 = &x) { } Diagnostic(ErrorCode.ERR_UnsafeNeeded, "&x").WithLocation(6, 29), // (6,29): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('string') // fixed (string* p1 = &x) { } Diagnostic(ErrorCode.WRN_ManagedAddr, "&x").WithArguments("string").WithLocation(6, 29), // (7,25): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('string') // unsafe { fixed (string* p2 = &x) { } } Diagnostic(ErrorCode.WRN_ManagedAddr, "string*").WithArguments("string").WithLocation(7, 25), // (7,38): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('string') // unsafe { fixed (string* p2 = &x) { } } Diagnostic(ErrorCode.WRN_ManagedAddr, "&x").WithArguments("string").WithLocation(7, 38), // (7,38): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('string') // unsafe { fixed (string* p2 = &x) { } } Diagnostic(ErrorCode.WRN_ManagedAddr, "&x").WithArguments("string").WithLocation(7, 38)); var expectedDiagnostics = new[] { // (6,16): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('string') // fixed (string* p1 = &x) { } Diagnostic(ErrorCode.WRN_ManagedAddr, "string*").WithArguments("string").WithLocation(6, 16), // (6,29): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('string') // fixed (string* p1 = &x) { } Diagnostic(ErrorCode.WRN_ManagedAddr, "&x").WithArguments("string").WithLocation(6, 29), // (6,29): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('string') // fixed (string* p1 = &x) { } Diagnostic(ErrorCode.WRN_ManagedAddr, "&x").WithArguments("string").WithLocation(6, 29), // (7,25): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('string') // unsafe { fixed (string* p2 = &x) { } } Diagnostic(ErrorCode.WRN_ManagedAddr, "string*").WithArguments("string").WithLocation(7, 25), // (7,38): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('string') // unsafe { fixed (string* p2 = &x) { } } Diagnostic(ErrorCode.WRN_ManagedAddr, "&x").WithArguments("string").WithLocation(7, 38), // (7,38): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('string') // unsafe { fixed (string* p2 = &x) { } } Diagnostic(ErrorCode.WRN_ManagedAddr, "&x").WithArguments("string").WithLocation(7, 38), }; CreateCompilationWithSpan(source, options: TestOptions.UnsafeReleaseExe) .VerifyEmitDiagnostics(expectedDiagnostics); CreateCompilationWithSpan(source, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe) .VerifyEmitDiagnostics(expectedDiagnostics); CreateCompilationWithSpan(source, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyEmitDiagnostics(expectedDiagnostics); CreateCompilationWithSpan(source, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyEmitDiagnostics(expectedDiagnostics); CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // error CS8630: Invalid 'MemorySafetyRules' value: '2' for C# 14.0. Please use language version 'preview' or greater. Diagnostic(ErrorCode.ERR_CompilationOptionNotAvailable).WithArguments("MemorySafetyRules", "2", "14.0", "preview").WithLocation(1, 1), // (6,9): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // fixed (string* p1 = &x) { } Diagnostic(ErrorCode.ERR_FeatureInPreview, "fixed (string* p1 = &x) { }").WithArguments("updated memory safety rules").WithLocation(6, 9), // (6,16): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // fixed (string* p1 = &x) { } Diagnostic(ErrorCode.ERR_FeatureInPreview, "string*").WithArguments("updated memory safety rules").WithLocation(6, 16), // (6,16): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('string') // fixed (string* p1 = &x) { } Diagnostic(ErrorCode.WRN_ManagedAddr, "string*").WithArguments("string").WithLocation(6, 16), // (6,29): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('string') // fixed (string* p1 = &x) { } Diagnostic(ErrorCode.WRN_ManagedAddr, "&x").WithArguments("string").WithLocation(6, 29), // (6,29): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // fixed (string* p1 = &x) { } Diagnostic(ErrorCode.ERR_FeatureInPreview, "&x").WithArguments("updated memory safety rules").WithLocation(6, 29), // (6,29): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('string') // fixed (string* p1 = &x) { } Diagnostic(ErrorCode.WRN_ManagedAddr, "&x").WithArguments("string").WithLocation(6, 29), // (7,25): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('string') // unsafe { fixed (string* p2 = &x) { } } Diagnostic(ErrorCode.WRN_ManagedAddr, "string*").WithArguments("string").WithLocation(7, 25), // (7,38): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('string') // unsafe { fixed (string* p2 = &x) { } } Diagnostic(ErrorCode.WRN_ManagedAddr, "&x").WithArguments("string").WithLocation(7, 38), // (7,38): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('string') // unsafe { fixed (string* p2 = &x) { } } Diagnostic(ErrorCode.WRN_ManagedAddr, "&x").WithArguments("string").WithLocation(7, 38)); } [Fact] public void Pointer_Arithmetic_SafeContext() { var source = """ int* p = null; p++; int* p2 = p + 2; long x = p - p; bool b = p > p2; """; var expectedDiagnostics = new[] { // (1,1): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // int* p = null; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(1, 1), // (2,1): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // p++; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "p").WithLocation(2, 1), // (2,1): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // p++; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "p++").WithLocation(2, 1), // (3,1): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // int* p2 = p + 2; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(3, 1), // (3,11): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // int* p2 = p + 2; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "p").WithLocation(3, 11), // (3,11): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // int* p2 = p + 2; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "p + 2").WithLocation(3, 11), // (4,10): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // long x = p - p; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "p").WithLocation(4, 10), // (4,14): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // long x = p - p; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "p").WithLocation(4, 14), // (5,10): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // bool b = p > p2; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "p").WithLocation(5, 10), // (5,14): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // bool b = p > p2; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "p2").WithLocation(5, 14), }; CreateCompilation(source, options: TestOptions.ReleaseExe).VerifyEmitDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.ReleaseExe).VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseExe).VerifyEmitDiagnostics(); CreateCompilation(source, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()).VerifyEmitDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()).VerifyEmitDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // error CS8630: Invalid 'MemorySafetyRules' value: '2' for C# 14.0. Please use language version 'preview' or greater. Diagnostic(ErrorCode.ERR_CompilationOptionNotAvailable).WithArguments("MemorySafetyRules", "2", "14.0", "preview").WithLocation(1, 1), // (1,1): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // int* p = null; Diagnostic(ErrorCode.ERR_FeatureInPreview, "int*").WithArguments("updated memory safety rules").WithLocation(1, 1), // (2,1): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // p++; Diagnostic(ErrorCode.ERR_FeatureInPreview, "p").WithArguments("updated memory safety rules").WithLocation(2, 1), // (2,1): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // p++; Diagnostic(ErrorCode.ERR_FeatureInPreview, "p++").WithArguments("updated memory safety rules").WithLocation(2, 1), // (3,1): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // int* p2 = p + 2; Diagnostic(ErrorCode.ERR_FeatureInPreview, "int*").WithArguments("updated memory safety rules").WithLocation(3, 1), // (3,11): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // int* p2 = p + 2; Diagnostic(ErrorCode.ERR_FeatureInPreview, "p").WithArguments("updated memory safety rules").WithLocation(3, 11), // (3,11): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // int* p2 = p + 2; Diagnostic(ErrorCode.ERR_FeatureInPreview, "p + 2").WithArguments("updated memory safety rules").WithLocation(3, 11), // (4,10): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // long x = p - p; Diagnostic(ErrorCode.ERR_FeatureInPreview, "p").WithArguments("updated memory safety rules").WithLocation(4, 10), // (4,14): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // long x = p - p; Diagnostic(ErrorCode.ERR_FeatureInPreview, "p").WithArguments("updated memory safety rules").WithLocation(4, 14), // (5,10): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // bool b = p > p2; Diagnostic(ErrorCode.ERR_FeatureInPreview, "p").WithArguments("updated memory safety rules").WithLocation(5, 10), // (5,14): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // bool b = p > p2; Diagnostic(ErrorCode.ERR_FeatureInPreview, "p2").WithArguments("updated memory safety rules").WithLocation(5, 14)); } [Fact] public void SizeOf_SafeContext() { var source = """ _ = sizeof(int); _ = sizeof(nint); _ = sizeof(S); struct S; """; CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.ReleaseExe) .VerifyDiagnostics( // (2,5): error CS0233: 'nint' does not have a predefined size, therefore sizeof can only be used in an unsafe context // _ = sizeof(nint); Diagnostic(ErrorCode.ERR_SizeofUnsafe, "sizeof(nint)").WithArguments("nint").WithLocation(2, 5), // (3,5): error CS0233: 'S' does not have a predefined size, therefore sizeof can only be used in an unsafe context // _ = sizeof(S); Diagnostic(ErrorCode.ERR_SizeofUnsafe, "sizeof(S)").WithArguments("S").WithLocation(3, 5)); CreateCompilation(source, options: TestOptions.ReleaseExe).VerifyEmitDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseExe).VerifyEmitDiagnostics(); CreateCompilation(source, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()).VerifyEmitDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()).VerifyEmitDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // error CS8630: Invalid 'MemorySafetyRules' value: '2' for C# 14.0. Please use language version 'preview' or greater. Diagnostic(ErrorCode.ERR_CompilationOptionNotAvailable).WithArguments("MemorySafetyRules", "2", "14.0", "preview").WithLocation(1, 1), // (2,5): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // _ = sizeof(nint); Diagnostic(ErrorCode.ERR_FeatureInPreview, "sizeof(nint)").WithArguments("updated memory safety rules").WithLocation(2, 5), // (3,5): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // _ = sizeof(S); Diagnostic(ErrorCode.ERR_FeatureInPreview, "sizeof(S)").WithArguments("updated memory safety rules").WithLocation(3, 5)); } [Fact] public void SizeOf_ManagedType() { var source = """ _ = sizeof(string); unsafe { _ = sizeof(string); } """; CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseExe) .VerifyDiagnostics( // (1,5): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('string') // _ = sizeof(string); Diagnostic(ErrorCode.WRN_ManagedAddr, "sizeof(string)").WithArguments("string").WithLocation(1, 5), // (1,5): error CS0233: 'string' does not have a predefined size, therefore sizeof can only be used in an unsafe context // _ = sizeof(string); Diagnostic(ErrorCode.ERR_SizeofUnsafe, "sizeof(string)").WithArguments("string").WithLocation(1, 5), // (2,14): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('string') // unsafe { _ = sizeof(string); } Diagnostic(ErrorCode.WRN_ManagedAddr, "sizeof(string)").WithArguments("string").WithLocation(2, 14)); var expectedDiagnostics = new[] { // (1,5): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('string') // _ = sizeof(string); Diagnostic(ErrorCode.WRN_ManagedAddr, "sizeof(string)").WithArguments("string").WithLocation(1, 5), // (2,14): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('string') // unsafe { _ = sizeof(string); } Diagnostic(ErrorCode.WRN_ManagedAddr, "sizeof(string)").WithArguments("string").WithLocation(2, 14), }; CreateCompilationWithSpan(source, options: TestOptions.UnsafeReleaseExe) .VerifyEmitDiagnostics(expectedDiagnostics); CreateCompilationWithSpan(source, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe) .VerifyEmitDiagnostics(expectedDiagnostics); CreateCompilationWithSpan(source, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyEmitDiagnostics(expectedDiagnostics); CreateCompilationWithSpan(source, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyEmitDiagnostics(expectedDiagnostics); CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // error CS8630: Invalid 'MemorySafetyRules' value: '2' for C# 14.0. Please use language version 'preview' or greater. Diagnostic(ErrorCode.ERR_CompilationOptionNotAvailable).WithArguments("MemorySafetyRules", "2", "14.0", "preview").WithLocation(1, 1), // (1,5): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('string') // _ = sizeof(string); Diagnostic(ErrorCode.WRN_ManagedAddr, "sizeof(string)").WithArguments("string").WithLocation(1, 5), // (1,5): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // _ = sizeof(string); Diagnostic(ErrorCode.ERR_FeatureInPreview, "sizeof(string)").WithArguments("updated memory safety rules").WithLocation(1, 5), // (2,14): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('string') // unsafe { _ = sizeof(string); } Diagnostic(ErrorCode.WRN_ManagedAddr, "sizeof(string)").WithArguments("string").WithLocation(2, 14)); } [Fact] public void FixedSizeBuffer_SafeContext() { var source = """ var s = new S(); int* p = s.y; int z = s.x[100]; struct S { public fixed int x[5], y[10]; } """; CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.ReleaseExe) .VerifyDiagnostics( // (2,1): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // int* p = s.y; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(2, 1), // (2,10): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // int* p = s.y; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "s.y").WithLocation(2, 10), // (3,9): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // int z = s.x[100]; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "s.x").WithLocation(3, 9), // (7,22): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // public fixed int x[5], y[10]; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "x[5]").WithLocation(7, 22)); var expectedDiagnostics = new[] { // (3,12): error CS9360: This operation may only be used in an unsafe context // int z = s.x[100]; Diagnostic(ErrorCode.ERR_UnsafeOperation, "[").WithLocation(3, 12), }; CreateCompilation(source, options: TestOptions.ReleaseExe) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseExe) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // error CS8630: Invalid 'MemorySafetyRules' value: '2' for C# 14.0. Please use language version 'preview' or greater. Diagnostic(ErrorCode.ERR_CompilationOptionNotAvailable).WithArguments("MemorySafetyRules", "2", "14.0", "preview").WithLocation(1, 1), // (2,1): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // int* p = s.y; Diagnostic(ErrorCode.ERR_FeatureInPreview, "int*").WithArguments("updated memory safety rules").WithLocation(2, 1), // (2,10): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // int* p = s.y; Diagnostic(ErrorCode.ERR_FeatureInPreview, "s.y").WithArguments("updated memory safety rules").WithLocation(2, 10), // (3,9): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // int z = s.x[100]; Diagnostic(ErrorCode.ERR_FeatureInPreview, "s.x").WithArguments("updated memory safety rules").WithLocation(3, 9), // (3,12): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // int z = s.x[100]; Diagnostic(ErrorCode.ERR_FeatureInPreview, "[").WithArguments("updated memory safety rules").WithLocation(3, 12), // (7,22): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // public fixed int x[5], y[10]; Diagnostic(ErrorCode.ERR_FeatureInPreview, "x[5]").WithArguments("updated memory safety rules").WithLocation(7, 22)); } [Fact] public void FixedSizeBuffer_Unsafe() { var expectedFixedBufferDiagnosticsForLegacyCaller = new[] { // (3,12): error CS9360: This operation may only be used in an unsafe context // int z = s.x[0]; Diagnostic(ErrorCode.ERR_UnsafeOperation, "[").WithLocation(3, 12), }; CompileAndVerifyUnsafe( lib: """ public struct S { public unsafe fixed int x[5], y[10]; } """, caller: """ var s = new S(); int* p = s.y; int z = s.x[0]; unsafe { p = s.y; z = s.x[0]; } """, expectedUnsafeSymbols: ["S.x", "S.y"], expectedSafeSymbols: ["S"], expectedDiagnostics: [ // (2,10): error CS9362: 'S.y' must be used in an unsafe context because it is marked as 'unsafe' // int* p = s.y; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "s.y").WithArguments("S.y").WithLocation(2, 10), // (3,9): error CS9362: 'S.x' must be used in an unsafe context because it is marked as 'unsafe' // int z = s.x[0]; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "s.x").WithArguments("S.x").WithLocation(3, 9), // (3,12): error CS9360: This operation may only be used in an unsafe context // int z = s.x[0]; Diagnostic(ErrorCode.ERR_UnsafeOperation, "[").WithLocation(3, 12), ], expectedDiagnosticsWhenReferencingLegacyLib: expectedFixedBufferDiagnosticsForLegacyCaller, expectedDiagnosticsForLegacyCaller: expectedFixedBufferDiagnosticsForLegacyCaller); } [Fact] public void SkipLocalsInit_NeedsUnsafe() { var source = """ class C { [System.Runtime.CompilerServices.SkipLocalsInit] void M() { } } namespace System.Runtime.CompilerServices { public class SkipLocalsInitAttribute : Attribute; } """; var expectedDiagnostics = new[] { // (1,12): error CS0227: Unsafe code may only appear if compiling with /unsafe // class C { [System.Runtime.CompilerServices.SkipLocalsInit] void M() { } } Diagnostic(ErrorCode.ERR_IllegalUnsafe, "System.Runtime.CompilerServices.SkipLocalsInit").WithLocation(1, 12), }; CreateCompilation(source, options: TestOptions.ReleaseDll) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, options: TestOptions.ReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, options: TestOptions.UnsafeReleaseDll) .VerifyEmitDiagnostics(); CreateCompilation(source, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyEmitDiagnostics(); } [Fact] public void StackAlloc_SafeContext() { var source = """ int* x = stackalloc int[3]; System.Span<int> y = stackalloc int[5]; M(); [System.Runtime.CompilerServices.SkipLocalsInit] void M() { System.Span<int> a = stackalloc int[5]; System.Span<int> b = stackalloc int[] { 1 }; System.Span<int> d = stackalloc int[2] { 1, 2 }; System.Span<int> e = stackalloc int[3] { 1, 2 }; } namespace System.Runtime.CompilerServices { public class SkipLocalsInitAttribute : Attribute; } """; CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseExe) .VerifyDiagnostics( // (1,1): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // int* x = stackalloc int[3]; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(1, 1), // (1,10): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // int* x = stackalloc int[3]; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "stackalloc int[3]").WithLocation(1, 10), // (11,26): error CS0847: An array initializer of length '3' is expected // System.Span<int> e = stackalloc int[3] { 1, 2 }; Diagnostic(ErrorCode.ERR_ArrayInitializerIncorrectLength, "stackalloc int[3] { 1, 2 }").WithArguments("3").WithLocation(11, 26)); var expectedDiagnosticsWithoutOptIn = new[] { // (11,26): error CS0847: An array initializer of length '3' is expected // System.Span<int> e = stackalloc int[3] { 1, 2 }; Diagnostic(ErrorCode.ERR_ArrayInitializerIncorrectLength, "stackalloc int[3] { 1, 2 }").WithArguments("3").WithLocation(11, 26), }; CreateCompilationWithSpan(source, options: TestOptions.UnsafeReleaseExe) .VerifyDiagnostics(expectedDiagnosticsWithoutOptIn); CreateCompilationWithSpan(source, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe) .VerifyDiagnostics(expectedDiagnosticsWithoutOptIn); var expectedDiagnosticsWithOptIn = new[] { // (8,26): error CS9361: stackalloc expression without an initializer inside SkipLocalsInit may only be used in an unsafe context // System.Span<int> a = stackalloc int[5]; Diagnostic(ErrorCode.ERR_UnsafeUninitializedStackAlloc, "stackalloc int[5]").WithLocation(8, 26), // (11,26): error CS0847: An array initializer of length '3' is expected // System.Span<int> e = stackalloc int[3] { 1, 2 }; Diagnostic(ErrorCode.ERR_ArrayInitializerIncorrectLength, "stackalloc int[3] { 1, 2 }").WithArguments("3").WithLocation(11, 26), }; CreateCompilationWithSpan(source, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(expectedDiagnosticsWithOptIn); CreateCompilationWithSpan(source, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(expectedDiagnosticsWithOptIn); CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // error CS8630: Invalid 'MemorySafetyRules' value: '2' for C# 14.0. Please use language version 'preview' or greater. Diagnostic(ErrorCode.ERR_CompilationOptionNotAvailable).WithArguments("MemorySafetyRules", "2", "14.0", "preview").WithLocation(1, 1), // (1,1): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // int* x = stackalloc int[3]; Diagnostic(ErrorCode.ERR_FeatureInPreview, "int*").WithArguments("updated memory safety rules").WithLocation(1, 1), // (1,10): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // int* x = stackalloc int[3]; Diagnostic(ErrorCode.ERR_FeatureInPreview, "stackalloc int[3]").WithArguments("updated memory safety rules").WithLocation(1, 10), // (8,26): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // System.Span<int> a = stackalloc int[5]; Diagnostic(ErrorCode.ERR_FeatureInPreview, "stackalloc int[5]").WithArguments("updated memory safety rules").WithLocation(8, 26), // (11,26): error CS0847: An array initializer of length '3' is expected // System.Span<int> e = stackalloc int[3] { 1, 2 }; Diagnostic(ErrorCode.ERR_ArrayInitializerIncorrectLength, "stackalloc int[3] { 1, 2 }").WithArguments("3").WithLocation(11, 26)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/84034")] public void StackAlloc_SafeContext_WrappedExpressions() { var source = """ [module: System.Runtime.CompilerServices.SkipLocalsInit] class C { void M(bool b) { System.Span<int> a = (stackalloc int[5]); System.Span<int> c = b ? stackalloc int[5] : default; System.Span<int> d = (System.Span<int>)stackalloc int[5]; Consume(stackalloc int[5]); ConsumeRo(stackalloc int[5]); } void Consume(System.Span<int> span) { } void ConsumeRo(System.ReadOnlySpan<int> span) { } } namespace System.Runtime.CompilerServices { public class SkipLocalsInitAttribute : Attribute; } """; CreateCompilationWithSpan(source, options: TestOptions.UnsafeReleaseDll) .VerifyDiagnostics(); CreateCompilationWithSpan(source, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // (7,31): error CS9361: stackalloc expression without an initializer inside SkipLocalsInit may only be used in an unsafe context // System.Span<int> a = (stackalloc int[5]); Diagnostic(ErrorCode.ERR_UnsafeUninitializedStackAlloc, "stackalloc int[5]").WithLocation(7, 31), // (8,34): error CS9361: stackalloc expression without an initializer inside SkipLocalsInit may only be used in an unsafe context // System.Span<int> c = b ? stackalloc int[5] : default; Diagnostic(ErrorCode.ERR_UnsafeUninitializedStackAlloc, "stackalloc int[5]").WithLocation(8, 34), // (9,48): error CS9361: stackalloc expression without an initializer inside SkipLocalsInit may only be used in an unsafe context // System.Span<int> d = (System.Span<int>)stackalloc int[5]; Diagnostic(ErrorCode.ERR_UnsafeUninitializedStackAlloc, "stackalloc int[5]").WithLocation(9, 48), // (10,17): error CS9361: stackalloc expression without an initializer inside SkipLocalsInit may only be used in an unsafe context // Consume(stackalloc int[5]); Diagnostic(ErrorCode.ERR_UnsafeUninitializedStackAlloc, "stackalloc int[5]").WithLocation(10, 17), // (11,19): error CS9361: stackalloc expression without an initializer inside SkipLocalsInit may only be used in an unsafe context // ConsumeRo(stackalloc int[5]); Diagnostic(ErrorCode.ERR_UnsafeUninitializedStackAlloc, "stackalloc int[5]").WithLocation(11, 19)); } [Fact] public void StackAlloc_SafeContext_WrappedExpressions_WithInitializer() { var source = """ [module: System.Runtime.CompilerServices.SkipLocalsInit] class C { void M(bool b) { System.Span<int> a = (stackalloc int[] { 1 }); System.Span<int> c = b ? stackalloc int[] { 1 } : default; System.Span<int> d = (System.Span<int>)stackalloc int[] { 1 }; Consume(stackalloc int[] { 1 }); ConsumeRo(stackalloc int[] { 1 }); } void Consume(System.Span<int> span) { } void ConsumeRo(System.ReadOnlySpan<int> span) { } } namespace System.Runtime.CompilerServices { public class SkipLocalsInitAttribute : Attribute; } """; CreateCompilationWithSpan(source, options: TestOptions.UnsafeReleaseDll) .VerifyDiagnostics(); CreateCompilationWithSpan(source, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(); } [Fact] public void StackAlloc_SafeContext_TargetTyped_AttributeEffect_ImplicitAndExplicit() { var sourceWithoutAttribute = """ class C { void M() { System.Span<int> a = stackalloc int[5]; System.Span<int> b = stackalloc[] { 1 }; Consume(stackalloc int[5]); Consume(stackalloc[] { 1 }); } void Consume(System.Span<int> span) { } } """; CreateCompilationWithSpan(sourceWithoutAttribute, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(); var sourceWithAttribute = """ [module: System.Runtime.CompilerServices.SkipLocalsInit] class C { void M() { System.Span<int> a = stackalloc int[5]; System.Span<int> b = stackalloc[] { 1 }; Consume(stackalloc int[5]); Consume(stackalloc[] { 1 }); } void Consume(System.Span<int> span) { } } namespace System.Runtime.CompilerServices { public class SkipLocalsInitAttribute : Attribute; } """; CreateCompilationWithSpan(sourceWithAttribute, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // (6,30): error CS9361: stackalloc expression without an initializer inside SkipLocalsInit may only be used in an unsafe context // System.Span<int> a = stackalloc int[5]; Diagnostic(ErrorCode.ERR_UnsafeUninitializedStackAlloc, "stackalloc int[5]").WithLocation(6, 30), // (8,17): error CS9361: stackalloc expression without an initializer inside SkipLocalsInit may only be used in an unsafe context // Consume(stackalloc int[5]); Diagnostic(ErrorCode.ERR_UnsafeUninitializedStackAlloc, "stackalloc int[5]").WithLocation(8, 17)); } [Fact] public void StackAlloc_UnsafeContext() { var source = """ unsafe { System.Span<int> y = stackalloc int[5]; } M(); [System.Runtime.CompilerServices.SkipLocalsInit] void M() { unsafe { System.Span<int> a = stackalloc int[5]; } unsafe { System.Span<int> e = stackalloc int[3] { 1, 2 }; } } namespace System.Runtime.CompilerServices { public class SkipLocalsInitAttribute : Attribute; } """; var expectedDiagnostics = new[] { // (8,35): error CS0847: An array initializer of length '3' is expected // unsafe { System.Span<int> e = stackalloc int[3] { 1, 2 }; } Diagnostic(ErrorCode.ERR_ArrayInitializerIncorrectLength, "stackalloc int[3] { 1, 2 }").WithArguments("3").WithLocation(8, 35), }; CreateCompilationWithSpan(source, options: TestOptions.UnsafeReleaseExe) .VerifyDiagnostics(expectedDiagnostics); CreateCompilationWithSpan(source, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(expectedDiagnostics); CreateCompilationWithSpan(source, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(expectedDiagnostics); CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( [ // error CS8630: Invalid 'MemorySafetyRules' value: '2' for C# 14.0. Please use language version 'preview' or greater. Diagnostic(ErrorCode.ERR_CompilationOptionNotAvailable).WithArguments("MemorySafetyRules", "2", "14.0", "preview").WithLocation(1, 1), .. expectedDiagnostics, ]); } [Fact] public void StackAlloc_ManagedType() { var source = """ unsafe { System.Span<string> x = stackalloc string[5]; } unsafe { string* y = stackalloc[] { "a" }; } M(); [System.Runtime.CompilerServices.SkipLocalsInit] void M() { System.Span<string> a = stackalloc string[5]; } namespace System.Runtime.CompilerServices { public class SkipLocalsInitAttribute : Attribute; } """; var expectedDiagnostics = new[] { // (1,45): error CS0208: Cannot take the address of, get the size of, or declare a pointer to a managed type ('string') // unsafe { System.Span<string> x = stackalloc string[5]; } Diagnostic(ErrorCode.ERR_ManagedAddr, "string").WithArguments("string").WithLocation(1, 45), // (2,10): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('string') // unsafe { string* y = stackalloc[] { "a" }; } Diagnostic(ErrorCode.WRN_ManagedAddr, "string*").WithArguments("string").WithLocation(2, 10), // (2,22): error CS0208: Cannot take the address of, get the size of, or declare a pointer to a managed type ('string') // unsafe { string* y = stackalloc[] { "a" }; } Diagnostic(ErrorCode.ERR_ManagedAddr, @"stackalloc[] { ""a"" }").WithArguments("string").WithLocation(2, 22), // (8,40): error CS0208: Cannot take the address of, get the size of, or declare a pointer to a managed type ('string') // System.Span<string> a = stackalloc string[5]; Diagnostic(ErrorCode.ERR_ManagedAddr, "string").WithArguments("string").WithLocation(8, 40), }; CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseExe) .VerifyEmitDiagnostics(expectedDiagnostics); CreateCompilationWithSpan(source, options: TestOptions.UnsafeReleaseExe) .VerifyEmitDiagnostics(expectedDiagnostics); CreateCompilationWithSpan(source, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe) .VerifyEmitDiagnostics(expectedDiagnostics); CreateCompilationWithSpan(source, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyEmitDiagnostics(expectedDiagnostics); CreateCompilationWithSpan(source, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyEmitDiagnostics(expectedDiagnostics); CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( [ // error CS8630: Invalid 'MemorySafetyRules' value: '2' for C# 14.0. Please use language version 'preview' or greater. Diagnostic(ErrorCode.ERR_CompilationOptionNotAvailable).WithArguments("MemorySafetyRules", "2", "14.0", "preview").WithLocation(1, 1), .. expectedDiagnostics, ]); } [Fact] public void StackAlloc_Lambda() { var source = """ var lam = [System.Runtime.CompilerServices.SkipLocalsInit] () => { System.Span<int> a = stackalloc int[5]; int* b = stackalloc int[3]; unsafe { System.Span<int> c = stackalloc int[1]; } }; namespace System.Runtime.CompilerServices { public class SkipLocalsInitAttribute : Attribute; } """; CreateCompilationWithSpan(source, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // (3,26): error CS9361: stackalloc expression without an initializer inside SkipLocalsInit may only be used in an unsafe context // System.Span<int> a = stackalloc int[5]; Diagnostic(ErrorCode.ERR_UnsafeUninitializedStackAlloc, "stackalloc int[5]").WithLocation(3, 26)); CreateCompilationWithSpan(source, options: TestOptions.UnsafeReleaseExe) .VerifyEmitDiagnostics(); } [Fact] public void UnsafeExpression_RequiresAllowUnsafe() { var source = """ _ = unsafe(1); """; CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.ReleaseExe) .VerifyDiagnostics( // (1,5): error CS0227: Unsafe code may only appear if compiling with /unsafe // _ = unsafe(1); Diagnostic(ErrorCode.ERR_IllegalUnsafe, "unsafe").WithLocation(1, 5), // (1,5): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // _ = unsafe(1); Diagnostic(ErrorCode.ERR_FeatureInPreview, "unsafe").WithArguments("updated memory safety rules").WithLocation(1, 5)); var expectedDiagnostics = new[] { // (1,5): error CS0227: Unsafe code may only appear if compiling with /unsafe // _ = unsafe(1); Diagnostic(ErrorCode.ERR_IllegalUnsafe, "unsafe").WithLocation(1, 5), }; CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseExe) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.RegularPreview, options: TestOptions.ReleaseExe) .VerifyDiagnostics(expectedDiagnostics); } [Fact] public void UnsafeExpression_LangVersion() { var source = """ _ = unsafe(1); """; CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseExe) .VerifyDiagnostics( // (1,5): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // _ = unsafe(1); Diagnostic(ErrorCode.ERR_FeatureInPreview, "unsafe").WithArguments("updated memory safety rules").WithLocation(1, 5)); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe) .VerifyDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.RegularPreview, options: TestOptions.UnsafeReleaseExe) .VerifyDiagnostics(); } [Fact] public void UnsafeExpression_PointerDereference() { var source = """ class C { void M(int* p) { int x = unsafe(*p); int y = *p; } } """; CreateCompilation(source, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // (6,17): error CS9360: This operation may only be used in an unsafe context // int y = *p; Diagnostic(ErrorCode.ERR_UnsafeOperation, "*").WithLocation(6, 17)); } [Fact] public void UnsafeExpression_Context() { var source = """ class C { void M(int* p) { int x = unsafe(*p) + *p; } } """; CreateCompilation(source, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // (7,15): error CS9360: This operation may only be used in an unsafe context // + *p; Diagnostic(ErrorCode.ERR_UnsafeOperation, "*").WithLocation(7, 15)); } [Fact] public void UnsafeExpression_Lambda() { var source = """ class C { void M(int* p) { System.Func<int> f = unsafe(() => *p); System.Func<int> g = () => unsafe(*p); } } """; CreateCompilation(source, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(); } [Fact] public void UnsafeExpression_Emit() { var source = """ class C { int M1(int* p) => unsafe(*p); unsafe int M2(int* p) => *p; } """; var verifier = CompileAndVerify(source, options: TestOptions.UnsafeDebugDll, verify: Verification.Skipped) .VerifyDiagnostics(); var expectedIl = """ { // Code size 3 (0x3) .maxstack 1 IL_0000: ldarg.1 IL_0001: ldind.i4 IL_0002: ret } """; verifier.VerifyIL("C.M1", expectedIl); verifier.VerifyIL("C.M2", expectedIl); } [Fact] public void UnsafeExpression_CheckedUnchecked() { var source = """ class C { void M(int* p, int x) { _ = checked(unsafe(*p + x)); _ = unsafe(checked(*p + x)); _ = unchecked(unsafe(*p + x)); _ = unsafe(unchecked(*p + x)); } } """; var verifier = CompileAndVerify(source, options: TestOptions.UnsafeDebugDll.WithUpdatedMemorySafetyRules(), verify: Verification.Skipped) .VerifyDiagnostics(); verifier.VerifyIL("C.M", """ { // Code size 18 (0x12) .maxstack 2 IL_0000: nop IL_0001: ldarg.1 IL_0002: ldind.i4 IL_0003: ldarg.2 IL_0004: add.ovf IL_0005: pop IL_0006: ldarg.1 IL_0007: ldind.i4 IL_0008: ldarg.2 IL_0009: add.ovf IL_000a: pop IL_000b: ldarg.1 IL_000c: ldind.i4 IL_000d: pop IL_000e: ldarg.1 IL_000f: ldind.i4 IL_0010: pop IL_0011: ret } """); } [Fact] public void UnsafeExpression_IOperation_01() { var source = """ class C { int M(int i) { /*<bind>*/return unsafe(i);/*</bind>*/ } } """; var expectedOperationTree = """ IReturnOperation (OperationKind.Return, Type: null) (Syntax: 'return unsafe(i);') ReturnedValue: IParameterReferenceOperation: i (OperationKind.ParameterReference, Type: System.Int32) (Syntax: 'i') """; var expectedDiagnostics = DiagnosticDescription.None; VerifyOperationTreeAndDiagnosticsForTest<ReturnStatementSyntax>( source, expectedOperationTree, expectedDiagnostics, compilationOptions: TestOptions.UnsafeReleaseDll); } [Fact] public void UnsafeExpression_IOperation_02() { var source = """ class C { int M(int i) { return /*<bind>*/unsafe(i)/*</bind>*/; } } """; var comp = CreateCompilation(source, options: TestOptions.UnsafeReleaseDll); comp.VerifyDiagnostics(); var (operation, _) = GetOperationAndSyntaxForTest<UnsafeExpressionSyntax>(comp); Assert.Null(operation); } [Fact] public void UnsafeExpression_IOperation_03() { var source = """ class C { int M(int i) { return unsafe(/*<bind>*/i/*</bind>*/); } } """; var expectedOperationTree = """ IParameterReferenceOperation: i (OperationKind.ParameterReference, Type: System.Int32) (Syntax: 'i') """; var expectedDiagnostics = DiagnosticDescription.None; VerifyOperationTreeAndDiagnosticsForTest<IdentifierNameSyntax>( source, expectedOperationTree, expectedDiagnostics, compilationOptions: TestOptions.UnsafeReleaseDll); } [Fact] public void UnsafeExpression_FlowGraph_01() { var source = """ class C { int M(int i) /*<bind>*/{ return unsafe(i); }/*</bind>*/ } """; var expectedFlowGraph = """ Block[B0] - Entry Statements (0) Next (Regular) Block[B1] Block[B1] - Block Predecessors: [B0] Statements (0) Next (Return) Block[B2] IParameterReferenceOperation: i (OperationKind.ParameterReference, Type: System.Int32) (Syntax: 'i') Block[B2] - Exit Predecessors: [B1] Statements (0) """; var expectedDiagnostics = DiagnosticDescription.None; VerifyFlowGraphAndDiagnosticsForTest<BlockSyntax>( source, expectedFlowGraph, expectedDiagnostics, compilationOptions: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()); } [Fact] public void UnsafeExpression_FlowGraph_02() { var source = """ class C { int M(bool b, int x, int y) /*<bind>*/{ return unsafe(b ? x : y); }/*</bind>*/ } """; var expectedFlowGraph = """ Block[B0] - Entry Statements (0) Next (Regular) Block[B1] Entering: {R1} .locals {R1} { CaptureIds: [0] Block[B1] - Block Predecessors: [B0] Statements (0) Jump if False (Regular) to Block[B3] IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'b') Next (Regular) Block[B2] Block[B2] - Block Predecessors: [B1] Statements (1) IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'x') Value: IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: System.Int32) (Syntax: 'x') Next (Regular) Block[B4] Block[B3] - Block Predecessors: [B1] Statements (1) IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'y') Value: IParameterReferenceOperation: y (OperationKind.ParameterReference, Type: System.Int32) (Syntax: 'y') Next (Regular) Block[B4] Block[B4] - Block Predecessors: [B2] [B3] Statements (0) Next (Return) Block[B5] IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Int32, IsImplicit) (Syntax: 'b ? x : y') Leaving: {R1} } Block[B5] - Exit Predecessors: [B4] Statements (0) """; var expectedDiagnostics = DiagnosticDescription.None; VerifyFlowGraphAndDiagnosticsForTest<BlockSyntax>( source, expectedFlowGraph, expectedDiagnostics, compilationOptions: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()); } [Fact] public void UnsafeExpression_SemanticModel() { var source = """ #pragma warning disable CS0219 // unused constant class C { long M(int* p) { const int Constant = unsafe(1); _ = unsafe(F()); return unsafe(*p); } static unsafe int F() => 42; } """; var comp = CreateCompilation(source, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()); comp.VerifyDiagnostics(); var tree = comp.SyntaxTrees.Single(); var model = comp.GetSemanticModel(tree); var unsafeExpressions = tree.GetRoot().DescendantNodes().OfType<UnsafeExpressionSyntax>().ToArray(); Assert.Equal(3, unsafeExpressions.Length); var constantExpression = unsafeExpressions[0]; var invocationExpression = unsafeExpressions[1]; var returnExpression = unsafeExpressions[2]; Assert.Equal("System.Int32", model.GetTypeInfo(constantExpression).Type.ToTestDisplayString()); Assert.Equal("System.Int32", model.GetTypeInfo(constantExpression.Expression).Type.ToTestDisplayString()); Assert.Equal(1, model.GetConstantValue(constantExpression).Value); Assert.Equal(1, model.GetConstantValue(constantExpression.Expression).Value); Assert.Equal("System.Int32", model.GetTypeInfo(invocationExpression).Type.ToTestDisplayString()); Assert.Equal("System.Int32", model.GetTypeInfo(invocationExpression.Expression).Type.ToTestDisplayString()); Assert.Equal("System.Int32 C.F()", model.GetSymbolInfo(invocationExpression).Symbol.ToTestDisplayString()); Assert.Equal("System.Int32 C.F()", model.GetSymbolInfo(invocationExpression.Expression).Symbol.ToTestDisplayString()); var returnTypeInfo = model.GetTypeInfo(returnExpression); Assert.Equal("System.Int32", returnTypeInfo.Type.ToTestDisplayString()); Assert.Equal("System.Int64", returnTypeInfo.ConvertedType.ToTestDisplayString()); var conversion = comp.ClassifyConversion(returnTypeInfo.Type!, returnTypeInfo.ConvertedType!); Assert.True(conversion.Exists); Assert.True(conversion.IsImplicit); Assert.True(conversion.IsNumeric); } [Fact] public void UnsafeExpression_SemanticModel_EnclosingBinder() { var source = """ class C { void M() { int local = 1; _ = unsafe(local + 1); } } """; var comp = CreateCompilation(source, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()); comp.VerifyDiagnostics(); var tree = comp.SyntaxTrees.Single(); var model = comp.GetSemanticModel(tree); var unsafeExpression = tree.GetRoot().DescendantNodes().OfType<UnsafeExpressionSyntax>().Single(); var binder = ((CSharpSemanticModel)model).GetEnclosingBinder(unsafeExpression.Expression.SpanStart); Assert.True(binder.InUnsafeRegion); Assert.True(binder.IsSemanticModelBinder); } [Fact] public void UnsafeExpression_Await() { var source = """ using System.Threading.Tasks; class C { async Task M() { await GetTask(); await unsafe(GetTask()); _ = unsafe(await GetTask()); } unsafe Task<int> GetTask() => null; } """; CreateCompilation(source, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // (7,15): error CS9362: 'C.GetTask()' must be used in an unsafe context because it is marked as 'unsafe' // await GetTask(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "GetTask()").WithArguments("C.GetTask()").WithLocation(7, 15)); } [Fact] public void UnsafeExpression_Attribute() { var source = """ class A : System.Attribute { public unsafe A(int x) { } } [unsafe(A(1))] class C1; [A(unsafe(1))] class C2; """; CreateCompilation(source, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // (6,1): error CS8803: Top-level statements must precede namespace and type declarations. // [unsafe(A(1))] class C1; Diagnostic(ErrorCode.ERR_TopLevelStatementAfterNamespaceOrType, "[unsafe(A(1)").WithLocation(6, 1), // (6,1): error CS8805: Program using top-level statements must be an executable. // [unsafe(A(1))] class C1; Diagnostic(ErrorCode.ERR_SimpleProgramNotAnExecutable, "[unsafe(A(1)").WithLocation(6, 1), // (6,2): error CS1001: Identifier expected // [unsafe(A(1))] class C1; Diagnostic(ErrorCode.ERR_IdentifierExpected, "unsafe").WithLocation(6, 2), // (6,2): error CS1003: Syntax error, ']' expected // [unsafe(A(1))] class C1; Diagnostic(ErrorCode.ERR_SyntaxError, "unsafe").WithArguments("]").WithLocation(6, 2), // (6,10): error CS8124: Tuple must contain at least two elements. // [unsafe(A(1))] class C1; Diagnostic(ErrorCode.ERR_TupleTooFewElements, "(").WithLocation(6, 10), // (6,10): error CS1026: ) expected // [unsafe(A(1))] class C1; Diagnostic(ErrorCode.ERR_CloseParenExpected, "(").WithLocation(6, 10), // (6,10): error CS1001: Identifier expected // [unsafe(A(1))] class C1; Diagnostic(ErrorCode.ERR_IdentifierExpected, "(").WithLocation(6, 10), // (6,10): error CS8112: Local function '()' must declare a body because it is not marked 'static extern'. // [unsafe(A(1))] class C1; Diagnostic(ErrorCode.ERR_LocalFunctionMissingBody, "").WithArguments("()").WithLocation(6, 10), // (6,11): error CS1001: Identifier expected // [unsafe(A(1))] class C1; Diagnostic(ErrorCode.ERR_IdentifierExpected, "1").WithLocation(6, 11), // (6,13): error CS1002: ; expected // [unsafe(A(1))] class C1; Diagnostic(ErrorCode.ERR_SemicolonExpected, ")").WithLocation(6, 13), // (6,13): error CS1022: Type or namespace definition, or end-of-file expected // [unsafe(A(1))] class C1; Diagnostic(ErrorCode.ERR_EOFExpected, ")").WithLocation(6, 13), // (7,2): error CS9362: 'A.A(int)' must be used in an unsafe context because it is marked as 'unsafe' // [A(unsafe(1))] class C2; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "A(unsafe(1))").WithArguments("A.A(int)").WithLocation(7, 2)); } [Fact] public void UnsafeExpression_AttributeArgument() { var source = """ class A : System.Attribute { public A(int x) { } } [A(unsafe(C.U()))] [A(C.U())] class C { public static unsafe int U() => 0; } """; CreateCompilation(source, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // (6,11): error CS0182: An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type // [A(unsafe(C.U()))] Diagnostic(ErrorCode.ERR_BadAttributeArgument, "C.U()").WithLocation(6, 11), // (7,4): error CS9362: 'C.U()' must be used in an unsafe context because it is marked as 'unsafe' // [A(C.U())] Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "C.U()").WithArguments("C.U()").WithLocation(7, 4), // (7,4): error CS0182: An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type // [A(C.U())] Diagnostic(ErrorCode.ERR_BadAttributeArgument, "C.U()").WithLocation(7, 4)); } [Fact] public void UnsafeExpression_AttributeArgument_DefaultParameterValue() { var source = """ using System.Runtime.InteropServices; class C { void M([Optional, DefaultParameterValue(unsafe(1))] int x) { } } """; var comp = CreateCompilation(source, options: TestOptions.UnsafeReleaseDll); comp.VerifyEmitDiagnostics(); var parameter = comp.GetMember<MethodSymbol>("C.M").Parameters.Single(); Assert.True(parameter.HasExplicitDefaultValue); Assert.Equal(1, parameter.ExplicitDefaultValue); } [Fact] public void UnsafeExpression_FieldInitializer() { var source = """ class C { static int F1 = GetInt(); static int F2 = unsafe(GetInt()); static unsafe int GetInt() => 0; } """; CreateCompilation(source, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // (3,21): error CS9362: 'C.GetInt()' must be used in an unsafe context because it is marked as 'unsafe' // static int F1 = GetInt(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "GetInt()").WithArguments("C.GetInt()").WithLocation(3, 21)); } [Fact] public void UnsafeExpression_Constants() { var source = """ class A : System.Attribute { public A(int x) { } } [A(unsafe(1))] class C { const int X = unsafe(1); void M(int x = unsafe(1)) { } } """; CreateCompilation(source, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(); } [Fact] public void UnsafeExpression_ConstructorInitializer() { var source = """ class C { C(int i, int j) : this(unsafe(i)) { } C(int i, byte b) : unsafe(this(i)) { } unsafe C(int i) { } } """; CreateCompilation(source, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // (3,21): error CS9362: 'C.C(int)' must be used in an unsafe context because it is marked as 'unsafe' // C(int i, int j) : this(unsafe(i)) { } Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, ": this(unsafe(i))").WithArguments("C.C(int)").WithLocation(3, 21), // (4,5): error CS0501: 'C.C(int, byte)' must declare a body because it is not marked abstract, extern, or partial // C(int i, byte b) : unsafe(this(i)) { } Diagnostic(ErrorCode.ERR_ConcreteMissingBody, "C").WithArguments("C.C(int, byte)").WithLocation(4, 5), // (4,24): error CS1018: Keyword 'this' or 'base' expected // C(int i, byte b) : unsafe(this(i)) { } Diagnostic(ErrorCode.ERR_ThisOrBaseExpected, "unsafe").WithLocation(4, 24), // (4,24): error CS1002: ; expected // C(int i, byte b) : unsafe(this(i)) { } Diagnostic(ErrorCode.ERR_SemicolonExpected, "unsafe").WithLocation(4, 24), // (4,24): error CS1729: 'C' does not contain a constructor that takes 0 arguments // C(int i, byte b) : unsafe(this(i)) { } Diagnostic(ErrorCode.ERR_BadCtorArgCount, "").WithArguments("C", "0").WithLocation(4, 24), // (4,31): error CS1031: Type expected // C(int i, byte b) : unsafe(this(i)) { } Diagnostic(ErrorCode.ERR_TypeExpected, "this").WithLocation(4, 31), // (4,31): error CS8124: Tuple must contain at least two elements. // C(int i, byte b) : unsafe(this(i)) { } Diagnostic(ErrorCode.ERR_TupleTooFewElements, "this").WithLocation(4, 31), // (4,31): error CS1026: ) expected // C(int i, byte b) : unsafe(this(i)) { } Diagnostic(ErrorCode.ERR_CloseParenExpected, "this").WithLocation(4, 31), // (4,31): error CS0548: 'C.this[(i, ?)]': property or indexer must have at least one accessor // C(int i, byte b) : unsafe(this(i)) { } Diagnostic(ErrorCode.ERR_PropertyWithNoAccessors, "this").WithArguments("C.this[(i, ?)]").WithLocation(4, 31), // (4,35): error CS1003: Syntax error, '[' expected // C(int i, byte b) : unsafe(this(i)) { } Diagnostic(ErrorCode.ERR_SyntaxError, "(").WithArguments("[").WithLocation(4, 35), // (4,36): error CS0246: The type or namespace name 'i' could not be found (are you missing a using directive or an assembly reference?) // C(int i, byte b) : unsafe(this(i)) { } Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "i").WithArguments("i").WithLocation(4, 36), // (4,37): error CS8124: Tuple must contain at least two elements. // C(int i, byte b) : unsafe(this(i)) { } Diagnostic(ErrorCode.ERR_TupleTooFewElements, ")").WithLocation(4, 37), // (4,38): error CS1001: Identifier expected // C(int i, byte b) : unsafe(this(i)) { } Diagnostic(ErrorCode.ERR_IdentifierExpected, ")").WithLocation(4, 38), // (4,38): error CS1003: Syntax error, ']' expected // C(int i, byte b) : unsafe(this(i)) { } Diagnostic(ErrorCode.ERR_SyntaxError, ")").WithArguments("]").WithLocation(4, 38), // (4,38): error CS1514: { expected // C(int i, byte b) : unsafe(this(i)) { } Diagnostic(ErrorCode.ERR_LbraceExpected, ")").WithLocation(4, 38), // (4,38): error CS1014: A get or set accessor expected // C(int i, byte b) : unsafe(this(i)) { } Diagnostic(ErrorCode.ERR_GetOrSetExpected, ")").WithLocation(4, 38), // (4,40): error CS1014: A get or set accessor expected // C(int i, byte b) : unsafe(this(i)) { } Diagnostic(ErrorCode.ERR_GetOrSetExpected, "{").WithLocation(4, 40), // (4,43): error CS1513: } expected // C(int i, byte b) : unsafe(this(i)) { } Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(4, 43)); } [Fact] public void UnsafeExpression_ConstructorInitializerArgument() { var source = """ class C { C() : this(GetInt()) { } C(string s) : this(unsafe(GetInt())) { } C(int i) { } static unsafe int GetInt() => 0; } """; CreateCompilation(source, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // (3,16): error CS9362: 'C.GetInt()' must be used in an unsafe context because it is marked as 'unsafe' // C() : this(GetInt()) { } Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "GetInt()").WithArguments("C.GetInt()").WithLocation(3, 16)); } [Fact] public void UnsafeExpression_ExceptionFilter() { var source = """ using System; class C { void M() { try { } catch (Exception e) when (F(e)) { } try { } catch (Exception e) when (unsafe(F(e))) { } } static unsafe bool F(Exception e) => true; } """; CreateCompilation(source, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // (7,43): error CS9362: 'C.F(Exception)' must be used in an unsafe context because it is marked as 'unsafe' // try { } catch (Exception e) when (F(e)) { } Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "F(e)").WithArguments("C.F(System.Exception)").WithLocation(7, 43)); } [Fact] public void UnsafeExpression_Statement() { var source = """ int x = 1; unsafe(x); unsafe(x++); """; CreateCompilation(source, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // (2,1): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement // unsafe(x); Diagnostic(ErrorCode.ERR_IllegalStatement, "unsafe(x)").WithLocation(2, 1), // (3,1): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement // unsafe(x++); Diagnostic(ErrorCode.ERR_IllegalStatement, "unsafe(x++)").WithLocation(3, 1)); } [Fact] public void UnsafeExpression_Void() { var source = """ _ = unsafe(F()); void F() { } """; CreateCompilation(source, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // (1,1): error CS8209: A value of type 'void' may not be assigned. // _ = unsafe(F()); Diagnostic(ErrorCode.ERR_VoidAssignment, "_").WithLocation(1, 1)); } [Fact] public void UnsafeExpression_Assign() { var source = """ int x; x = 1; unsafe(x) = 2; """; CreateCompilation(source, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // (1,5): warning CS0219: The variable 'x' is assigned but its value is never used // int x; Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "x").WithArguments("x").WithLocation(1, 5)); } [Fact] public void UnsafeExpression_AssignToPointer() { var source = """ class C { void M(int* p) { *p = 1; unsafe(*p) = 2; } } """; CreateCompilation(source, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // (5,9): error CS9360: This operation may only be used in an unsafe context // *p = 1; Diagnostic(ErrorCode.ERR_UnsafeOperation, "*").WithLocation(5, 9)); } [Fact] public void UnsafeExpression_RefArgument() { var source = """ class C { void M(int* p) { F(ref unsafe(*p)); } void F(ref int x) { } } """; CreateCompilation(source, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(); } [Fact] public void UnsafeExpression_RefLocal() { var source = """ var local = 0; C.M(&local); System.Console.Write(local); class C { public static void M(int* p) { ref int r = ref unsafe(*p); r = 1; } } """; CompileAndVerify(source, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules(), expectedOutput: "1", verify: Verification.Skipped) .VerifyDiagnostics(); } [Fact] public void UnsafeExpression_Conditional() { var source = """ var local = 1; C.M(&local, true); C.M(&local, false); class C { public static void M(int* p, bool b) { int x = b ? unsafe(*p) : 2; System.Console.Write(x); } } """; CompileAndVerify(source, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules(), expectedOutput: "12", verify: Verification.Skipped) .VerifyDiagnostics(); } [Fact] public void UnsafeExpression_OutVar() { var source = """ var local = 1; _ = unsafe(C.F(out local)); System.Console.Write(local); class C { public static bool F(out int x) { x = 2; return true; } } """; CompileAndVerify(source, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules(), expectedOutput: "2", verify: Verification.Skipped) .VerifyDiagnostics(); } [Fact] public void UnsafeDeclarations() { var source = """ #pragma warning disable CS0169 // unused field #pragma warning disable CS8019 // unused using #pragma warning disable CS8321 // unused local function using static unsafe System.Collections.Generic.List<int*[]>; using unsafe U = int*; unsafe void F() { } unsafe class C; unsafe struct S; unsafe interface I; unsafe enum E { A } unsafe record R; unsafe delegate void D(); unsafe union N(int); class X { unsafe static X() { } unsafe X() { } unsafe ~X() { } unsafe int f; unsafe void M() { } unsafe int P { get; set; } unsafe event System.Action E { add { } remove { } } public static unsafe implicit operator int(X x) => 0; } unsafe partial class P1; partial class P1; partial class P2; unsafe partial class P2; """; CSharpTestSource sources = [source, IsExternalInitTypeDefinition, IUnionSource, UnionAttributeSource]; var expectedDiagnostics = new[] { // (10,13): error CS0106: The modifier 'unsafe' is not valid for this item // unsafe enum E { A } Diagnostic(ErrorCode.ERR_BadMemberFlag, "E").WithArguments("unsafe").WithLocation(10, 13), }; CreateCompilation(sources, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedDiagnostics); expectedDiagnostics = [ // (7,14): error CS9377: The 'unsafe' modifier does not have any effect here under the current memory safety rules. // unsafe class C; Diagnostic(ErrorCode.ERR_UnsafeMeaningless, "C").WithLocation(7, 14), // (8,15): error CS9377: The 'unsafe' modifier does not have any effect here under the current memory safety rules. // unsafe struct S; Diagnostic(ErrorCode.ERR_UnsafeMeaningless, "S").WithLocation(8, 15), // (9,18): error CS9377: The 'unsafe' modifier does not have any effect here under the current memory safety rules. // unsafe interface I; Diagnostic(ErrorCode.ERR_UnsafeMeaningless, "I").WithLocation(9, 18), // (10,13): error CS0106: The modifier 'unsafe' is not valid for this item // unsafe enum E { A } Diagnostic(ErrorCode.ERR_BadMemberFlag, "E").WithArguments("unsafe").WithLocation(10, 13), // (11,15): error CS9377: The 'unsafe' modifier does not have any effect here under the current memory safety rules. // unsafe record R; Diagnostic(ErrorCode.ERR_UnsafeMeaningless, "R").WithLocation(11, 15), // (12,22): error CS9377: The 'unsafe' modifier does not have any effect here under the current memory safety rules. // unsafe delegate void D(); Diagnostic(ErrorCode.ERR_UnsafeMeaningless, "D").WithLocation(12, 22), // (13,14): error CS9377: The 'unsafe' modifier does not have any effect here under the current memory safety rules. // unsafe union N(int); Diagnostic(ErrorCode.ERR_UnsafeMeaningless, "N").WithLocation(13, 14), // (16,5): error CS9377: The 'unsafe' modifier does not have any effect here under the current memory safety rules. // unsafe static X() { } Diagnostic(ErrorCode.ERR_UnsafeMeaningless, "unsafe").WithLocation(16, 5), // (18,5): error CS9377: The 'unsafe' modifier does not have any effect here under the current memory safety rules. // unsafe ~X() { } Diagnostic(ErrorCode.ERR_UnsafeMeaningless, "unsafe").WithLocation(18, 5), // (25,22): error CS9377: The 'unsafe' modifier does not have any effect here under the current memory safety rules. // unsafe partial class P1; Diagnostic(ErrorCode.ERR_UnsafeMeaningless, "P1").WithLocation(25, 22), // (27,15): error CS9377: The 'unsafe' modifier does not have any effect here under the current memory safety rules. // partial class P2; Diagnostic(ErrorCode.ERR_UnsafeMeaningless, "P2").WithLocation(27, 15), ]; CreateCompilation(sources, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()).VerifyDiagnostics(expectedDiagnostics); CreateCompilation(sources, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()).VerifyDiagnostics(expectedDiagnostics); CreateCompilation(sources, parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()).VerifyDiagnostics( // error CS8630: Invalid 'MemorySafetyRules' value: '2' for C# 14.0. Please use language version 'preview' or greater. Diagnostic(ErrorCode.ERR_CompilationOptionNotAvailable).WithArguments("MemorySafetyRules", "2", "14.0", "preview").WithLocation(1, 1), // (7,14): error CS9377: The 'unsafe' modifier does not have any effect here under the current memory safety rules. // unsafe class C; Diagnostic(ErrorCode.ERR_UnsafeMeaningless, "C").WithLocation(7, 14), // (8,15): error CS9377: The 'unsafe' modifier does not have any effect here under the current memory safety rules. // unsafe struct S; Diagnostic(ErrorCode.ERR_UnsafeMeaningless, "S").WithLocation(8, 15), // (9,18): error CS9377: The 'unsafe' modifier does not have any effect here under the current memory safety rules. // unsafe interface I; Diagnostic(ErrorCode.ERR_UnsafeMeaningless, "I").WithLocation(9, 18), // (10,13): error CS0106: The modifier 'unsafe' is not valid for this item // unsafe enum E { A } Diagnostic(ErrorCode.ERR_BadMemberFlag, "E").WithArguments("unsafe").WithLocation(10, 13), // (11,15): error CS9377: The 'unsafe' modifier does not have any effect here under the current memory safety rules. // unsafe record R; Diagnostic(ErrorCode.ERR_UnsafeMeaningless, "R").WithLocation(11, 15), // (12,22): error CS9377: The 'unsafe' modifier does not have any effect here under the current memory safety rules. // unsafe delegate void D(); Diagnostic(ErrorCode.ERR_UnsafeMeaningless, "D").WithLocation(12, 22), // (13,1): error CS8803: Top-level statements must precede namespace and type declarations. // unsafe union N(int); Diagnostic(ErrorCode.ERR_TopLevelStatementAfterNamespaceOrType, "unsafe union N(int);").WithLocation(13, 1), // (13,8): error CS0246: The type or namespace name 'union' could not be found (are you missing a using directive or an assembly reference?) // unsafe union N(int); Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "union").WithArguments("union").WithLocation(13, 8), // (13,14): error CS8112: Local function 'N(int)' must declare a body because it is not marked 'static extern'. // unsafe union N(int); Diagnostic(ErrorCode.ERR_LocalFunctionMissingBody, "N").WithArguments("N(int)").WithLocation(13, 14), // (13,19): error CS1001: Identifier expected // unsafe union N(int); Diagnostic(ErrorCode.ERR_IdentifierExpected, ")").WithLocation(13, 19), // (16,5): error CS9377: The 'unsafe' modifier does not have any effect here under the current memory safety rules. // unsafe static X() { } Diagnostic(ErrorCode.ERR_UnsafeMeaningless, "unsafe").WithLocation(16, 5), // (18,5): error CS9377: The 'unsafe' modifier does not have any effect here under the current memory safety rules. // unsafe ~X() { } Diagnostic(ErrorCode.ERR_UnsafeMeaningless, "unsafe").WithLocation(18, 5), // (25,22): error CS9377: The 'unsafe' modifier does not have any effect here under the current memory safety rules. // unsafe partial class P1; Diagnostic(ErrorCode.ERR_UnsafeMeaningless, "P1").WithLocation(25, 22), // (27,15): error CS9377: The 'unsafe' modifier does not have any effect here under the current memory safety rules. // partial class P2; Diagnostic(ErrorCode.ERR_UnsafeMeaningless, "P2").WithLocation(27, 15)); } [Fact] public void UnsafeDeclarations_NameOf() { var source = """ #pragma warning disable CS0649, CS8019, CS8321 // unused field, using, local function using unsafe U = int*; _ = nameof(U); _ = nameof(F); _ = nameof(C); _ = nameof(S); _ = nameof(I); _ = nameof(R); _ = nameof(D); _ = nameof(X.F); _ = nameof(X.M); _ = nameof(X.P); _ = nameof(X.Pg); _ = nameof(X.Ps); _ = nameof(X.E); unsafe void F() { } unsafe class C; unsafe struct S; unsafe interface I; unsafe record R; unsafe delegate void D(); class X { public unsafe int F; public unsafe void M() { } public unsafe int P { get; set; } public int Pg { unsafe get; set; } public int Ps { get; unsafe set; } public unsafe event System.Action E { add { } remove { } } } """; CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseExe).VerifyEmitDiagnostics( // (29,21): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // public int Pg { unsafe get; set; } Diagnostic(ErrorCode.ERR_FeatureInPreview, "unsafe").WithArguments("updated memory safety rules").WithLocation(29, 21), // (30,26): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // public int Ps { get; unsafe set; } Diagnostic(ErrorCode.ERR_FeatureInPreview, "unsafe").WithArguments("updated memory safety rules").WithLocation(30, 26)); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe).VerifyEmitDiagnostics(); CreateCompilation(source, options: TestOptions.UnsafeReleaseExe).VerifyEmitDiagnostics(); CreateCompilation(source, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()).VerifyEmitDiagnostics( // (19,14): error CS9377: The 'unsafe' modifier does not have any effect here under the current memory safety rules. // unsafe class C; Diagnostic(ErrorCode.ERR_UnsafeMeaningless, "C").WithLocation(19, 14), // (20,15): error CS9377: The 'unsafe' modifier does not have any effect here under the current memory safety rules. // unsafe struct S; Diagnostic(ErrorCode.ERR_UnsafeMeaningless, "S").WithLocation(20, 15), // (21,18): error CS9377: The 'unsafe' modifier does not have any effect here under the current memory safety rules. // unsafe interface I; Diagnostic(ErrorCode.ERR_UnsafeMeaningless, "I").WithLocation(21, 18), // (22,15): error CS9377: The 'unsafe' modifier does not have any effect here under the current memory safety rules. // unsafe record R; Diagnostic(ErrorCode.ERR_UnsafeMeaningless, "R").WithLocation(22, 15), // (23,22): error CS9377: The 'unsafe' modifier does not have any effect here under the current memory safety rules. // unsafe delegate void D(); Diagnostic(ErrorCode.ERR_UnsafeMeaningless, "D").WithLocation(23, 22)); } [Fact] public void UnsafeDeclarations_NameOf_CompatMode() { var source = """ #pragma warning disable CS0649, CS8321 // unused field, local function _ = nameof(F); _ = nameof(D); _ = nameof(X.F); _ = nameof(X.M); _ = nameof(X.P); _ = nameof(X.E); unsafe void F(int* p) { } unsafe delegate int* D(); class X { public unsafe int* F; public unsafe void M(int* p) { } public unsafe int* P { get; set; } public unsafe event System.Action<int*[]> E { add { } remove { } } } """; CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseExe).VerifyEmitDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe).VerifyEmitDiagnostics(); CreateCompilation(source, options: TestOptions.UnsafeReleaseExe).VerifyEmitDiagnostics(); CreateCompilation(source, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()).VerifyEmitDiagnostics( // (11,22): error CS9377: The 'unsafe' modifier does not have any effect here under the current memory safety rules. // unsafe delegate int* D(); Diagnostic(ErrorCode.ERR_UnsafeMeaningless, "D").WithLocation(11, 22)); } [Fact] public void UnsafeDeclarations_PointerToManagedType() { var source = """ _ = new X<string*[]>(); unsafe { _ = new X<string*[]>(); } class C { void M1(X<string*[]> x) { } unsafe void M2(X<string*[]> x) { } } class X<T>; """; CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseExe) .VerifyDiagnostics( // (1,1): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // _ = new X<string*[]>(); Diagnostic(ErrorCode.ERR_UnsafeNeeded, "_ = new X<string*[]>()").WithLocation(1, 1), // (1,5): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // _ = new X<string*[]>(); Diagnostic(ErrorCode.ERR_UnsafeNeeded, "new X<string*[]>()").WithLocation(1, 5), // (1,11): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // _ = new X<string*[]>(); Diagnostic(ErrorCode.ERR_UnsafeNeeded, "string*").WithLocation(1, 11), // (1,11): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('string') // _ = new X<string*[]>(); Diagnostic(ErrorCode.WRN_ManagedAddr, "string*").WithArguments("string").WithLocation(1, 11), // (2,20): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('string') // unsafe { _ = new X<string*[]>(); } Diagnostic(ErrorCode.WRN_ManagedAddr, "string*").WithArguments("string").WithLocation(2, 20), // (6,15): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // void M1(X<string*[]> x) { } Diagnostic(ErrorCode.ERR_UnsafeNeeded, "string*").WithLocation(6, 15), // (6,26): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('string') // void M1(X<string*[]> x) { } Diagnostic(ErrorCode.WRN_ManagedAddr, "x").WithArguments("string").WithLocation(6, 26), // (7,33): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('string') // unsafe void M2(X<string*[]> x) { } Diagnostic(ErrorCode.WRN_ManagedAddr, "x").WithArguments("string").WithLocation(7, 33)); var expectedDiagnostics = new[] { // (1,11): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('string') // _ = new X<string*[]>(); Diagnostic(ErrorCode.WRN_ManagedAddr, "string*").WithArguments("string").WithLocation(1, 11), // (2,20): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('string') // unsafe { _ = new X<string*[]>(); } Diagnostic(ErrorCode.WRN_ManagedAddr, "string*").WithArguments("string").WithLocation(2, 20), // (6,26): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('string') // void M1(X<string*[]> x) { } Diagnostic(ErrorCode.WRN_ManagedAddr, "x").WithArguments("string").WithLocation(6, 26), // (7,33): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('string') // unsafe void M2(X<string*[]> x) { } Diagnostic(ErrorCode.WRN_ManagedAddr, "x").WithArguments("string").WithLocation(7, 33), }; CreateCompilationWithSpan(source, options: TestOptions.UnsafeReleaseExe) .VerifyEmitDiagnostics(expectedDiagnostics); CreateCompilationWithSpan(source, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe) .VerifyEmitDiagnostics(expectedDiagnostics); CreateCompilationWithSpan([source], options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyEmitDiagnostics(expectedDiagnostics); CreateCompilationWithSpan([source], parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyEmitDiagnostics(expectedDiagnostics); CreateCompilationWithSpan([source], parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // error CS8630: Invalid 'MemorySafetyRules' value: '2' for C# 14.0. Please use language version 'preview' or greater. Diagnostic(ErrorCode.ERR_CompilationOptionNotAvailable).WithArguments("MemorySafetyRules", "2", "14.0", "preview").WithLocation(1, 1), // (1,1): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // _ = new X<string*[]>(); Diagnostic(ErrorCode.ERR_FeatureInPreview, "_ = new X<string*[]>()").WithArguments("updated memory safety rules").WithLocation(1, 1), // (1,5): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // _ = new X<string*[]>(); Diagnostic(ErrorCode.ERR_FeatureInPreview, "new X<string*[]>()").WithArguments("updated memory safety rules").WithLocation(1, 5), // (1,11): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // _ = new X<string*[]>(); Diagnostic(ErrorCode.ERR_FeatureInPreview, "string*").WithArguments("updated memory safety rules").WithLocation(1, 11), // (1,11): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('string') // _ = new X<string*[]>(); Diagnostic(ErrorCode.WRN_ManagedAddr, "string*").WithArguments("string").WithLocation(1, 11), // (2,20): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('string') // unsafe { _ = new X<string*[]>(); } Diagnostic(ErrorCode.WRN_ManagedAddr, "string*").WithArguments("string").WithLocation(2, 20), // (6,15): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // void M1(X<string*[]> x) { } Diagnostic(ErrorCode.ERR_FeatureInPreview, "string*").WithArguments("updated memory safety rules").WithLocation(6, 15), // (6,26): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('string') // void M1(X<string*[]> x) { } Diagnostic(ErrorCode.WRN_ManagedAddr, "x").WithArguments("string").WithLocation(6, 26), // (7,22): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // unsafe void M2(X<string*[]> x) { } Diagnostic(ErrorCode.ERR_FeatureInPreview, "string*").WithArguments("updated memory safety rules").WithLocation(7, 22), // (7,33): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('string') // unsafe void M2(X<string*[]> x) { } Diagnostic(ErrorCode.WRN_ManagedAddr, "x").WithArguments("string").WithLocation(7, 33)); } [Fact] public void UnsafeContext_ExteriorOnly() { var source = """ class C { unsafe int M1(int* p) { return *p; } unsafe int P1 { get { int* p = null; return *p; } } int M2(int* p) { unsafe { return *p; } } unsafe int f = *(default(int*)); } unsafe class D { int M(int* p) { return *p; } int f = *(default(int*)); } """; CreateCompilation(source, options: TestOptions.UnsafeReleaseDll).VerifyEmitDiagnostics(); CreateCompilation(source, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()).VerifyDiagnostics( // (3,36): error CS9360: This operation may only be used in an unsafe context // unsafe int M1(int* p) { return *p; } Diagnostic(ErrorCode.ERR_UnsafeOperation, "*").WithLocation(3, 36), // (4,49): error CS9360: This operation may only be used in an unsafe context // unsafe int P1 { get { int* p = null; return *p; } } Diagnostic(ErrorCode.ERR_UnsafeOperation, "*").WithLocation(4, 49), // (6,20): error CS9360: This operation may only be used in an unsafe context // unsafe int f = *(default(int*)); Diagnostic(ErrorCode.ERR_UnsafeOperation, "*").WithLocation(6, 20), // (8,14): error CS9377: The 'unsafe' modifier does not have any effect here under the current memory safety rules. // unsafe class D Diagnostic(ErrorCode.ERR_UnsafeMeaningless, "D").WithLocation(8, 14), // (10,28): error CS9360: This operation may only be used in an unsafe context // int M(int* p) { return *p; } Diagnostic(ErrorCode.ERR_UnsafeOperation, "*").WithLocation(10, 28), // (11,13): error CS9360: This operation may only be used in an unsafe context // int f = *(default(int*)); Diagnostic(ErrorCode.ERR_UnsafeOperation, "*").WithLocation(11, 13)); } [Fact] public void UnsafeContext_ExteriorOnly_LocalFunctions() { var source = """ #pragma warning disable CS8321 // unused local function class C { unsafe void M() { // Local function inside unsafe method: method's unsafe has no interior meaning, // but the local function inherits from the method's context which under updated rules is safe. void Local1(int* p) { _ = *p; } // Local function in unsafe block: has unsafe context. unsafe { void Local2(int* p) { _ = *p; } } // unsafe local function: no interior unsafe context under updated rules. unsafe void Local3() { int* p = null; _ = *p; } } } """; CreateCompilation(source, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); CreateCompilation(source, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()).VerifyDiagnostics( // (8,35): error CS9360: This operation may only be used in an unsafe context // void Local1(int* p) { _ = *p; } Diagnostic(ErrorCode.ERR_UnsafeOperation, "*").WithLocation(8, 35), // (14,51): error CS9360: This operation may only be used in an unsafe context // unsafe void Local3() { int* p = null; _ = *p; } Diagnostic(ErrorCode.ERR_UnsafeOperation, "*").WithLocation(14, 51)); } [Fact] public void UnsafeContext_ExteriorOnly_AwaitAndYield() { var source = """ using System.Collections.Generic; using System.Threading.Tasks; class C { // Await in an unsafe block is allowed under the unsafe evolution feature. async Task M1() { unsafe { await Task.Yield(); } } // Yield return in unsafe block is an error. IEnumerable<int> M2() { unsafe { yield return 1; } } // Under updated rules, unsafe on the method has no interior meaning, // so the body is NOT in unsafe context. Await and yield should be fine. unsafe async Task M3() { await Task.Yield(); } unsafe IEnumerable<int> M4() { yield return 1; } } """; CreateCompilation(source, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( // (15,18): error CS9238: Cannot use 'yield return' in an 'unsafe' block // unsafe { yield return 1; } Diagnostic(ErrorCode.ERR_BadYieldInUnsafe, "yield").WithLocation(15, 18)); // With updated rules: M3's body is NOT in unsafe context, so await is fine there. CreateCompilation(source, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()).VerifyDiagnostics( // (15,18): error CS9238: Cannot use 'yield return' in an 'unsafe' block // unsafe { yield return 1; } Diagnostic(ErrorCode.ERR_BadYieldInUnsafe, "yield").WithLocation(15, 18)); } [Fact] public void UnsafeContext_ExteriorOnly_IteratorMethodFeatureCheck() { var source = """ using System.Collections.Generic; class C { unsafe IEnumerable<int> M() { yield return 1; } } """; CreateCompilation(source, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( // (5,29): error CS9202: Feature 'ref and unsafe in async and iterator methods' is not available in C# 12.0. Please use language version 13.0 or greater. // unsafe IEnumerable<int> M() Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "M").WithArguments("ref and unsafe in async and iterator methods", "13.0").WithLocation(5, 29)); CreateCompilation(source, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()).VerifyDiagnostics( // error CS8630: Invalid 'MemorySafetyRules' value: '2' for C# 12.0. Please use language version 'preview' or greater. Diagnostic(ErrorCode.ERR_CompilationOptionNotAvailable).WithArguments("MemorySafetyRules", "2", "12.0", "preview").WithLocation(1, 1)); } [Fact] public void UnsafeContext_ExteriorOnly_CallerUnsafeInSignature_DefaultParameterValue() { var source = """ public class Lib { unsafe public static int UnsafeMethod() => 0; } class C { // Under updated rules, 'unsafe' on a method has no interior meaning. // In signatures (default parameter values, attributes), the unsafe context from // the member modifier was already not applied, so caller-unsafe members should error. unsafe void M(int x = Lib.UnsafeMethod()) { } // In an unsafe block, the call is fine. void M2(int x = Lib.UnsafeMethod()) { } } """; var commonDiagnostics = new[] { // (11,27): error CS1736: Default parameter value for 'x' must be a compile-time constant // unsafe void M(int x = Lib.UnsafeMethod()) { } Diagnostic(ErrorCode.ERR_DefaultValueMustBeConstant, "Lib.UnsafeMethod()").WithArguments("x").WithLocation(11, 27), // (14,21): error CS1736: Default parameter value for 'x' must be a compile-time constant // void M2(int x = Lib.UnsafeMethod()) { } Diagnostic(ErrorCode.ERR_DefaultValueMustBeConstant, "Lib.UnsafeMethod()").WithArguments("x").WithLocation(14, 21), }; // Without updated rules: default parameter values are not affected by method's unsafe modifier, // so caller-unsafe members in default values error the same way. CreateCompilation(source, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(commonDiagnostics); // With updated rules: additionally, caller-unsafe member diagnostics are reported. CreateCompilation(source, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()).VerifyDiagnostics([ .. commonDiagnostics, // (11,27): error CS9362: 'Lib.UnsafeMethod()' must be used in an unsafe context because it is marked as 'unsafe' // unsafe void M(int x = Lib.UnsafeMethod()) { } Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "Lib.UnsafeMethod()").WithArguments("Lib.UnsafeMethod()").WithLocation(11, 27), // (14,21): error CS9362: 'Lib.UnsafeMethod()' must be used in an unsafe context because it is marked as 'unsafe' // void M2(int x = Lib.UnsafeMethod()) { } Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "Lib.UnsafeMethod()").WithArguments("Lib.UnsafeMethod()").WithLocation(14, 21), ]); } [Fact] public void UnsafeContext_ExteriorOnly_CallerUnsafeInSignature() { var source = """ using System; class A : Attribute { unsafe public A() { } } interface I<T> where T : new(); class C { unsafe public C() { } } class Test { unsafe void M1(I<C> i) { } [A] unsafe void M2() { } } unsafe class UnsafeTest { void M1(I<C> i) { } [A] void M2() { } } """; CreateCompilation(source, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); CreateCompilation(source, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()).VerifyDiagnostics( // (19,6): error CS9362: 'A.A()' must be used in an unsafe context because it is marked as 'unsafe' // [A] unsafe void M2() { } Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "A").WithArguments("A.A()").WithLocation(19, 6), // (22,14): error CS9377: The 'unsafe' modifier does not have any effect here under the current memory safety rules. // unsafe class UnsafeTest Diagnostic(ErrorCode.ERR_UnsafeMeaningless, "UnsafeTest").WithLocation(22, 14), // (26,6): error CS9362: 'A.A()' must be used in an unsafe context because it is marked as 'unsafe' // [A] void M2() { } Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "A").WithArguments("A.A()").WithLocation(26, 6)); } [Fact] public void UnsafeContext_ExteriorOnly_PointerDereferenceInSignature() { // Optional parameter default values are part of the signature. Under updated rules, // unsafe on a method or containing type doesn't introduce unsafe context for them. var source = """ class C { unsafe void M1(int x = *(default(int*))) { } void M2(int x = *(default(int*))) { } } unsafe class D { void M(int x = *(default(int*))) { } } """; var commonDiagnostics = new[] { // (3,28): error CS1736: Default parameter value for 'x' must be a compile-time constant // unsafe void M1(int x = *(default(int*))) { } Diagnostic(ErrorCode.ERR_DefaultValueMustBeConstant, "*(default(int*))").WithArguments("x").WithLocation(3, 28), // (4,21): error CS1736: Default parameter value for 'x' must be a compile-time constant // void M2(int x = *(default(int*))) { } Diagnostic(ErrorCode.ERR_DefaultValueMustBeConstant, "*(default(int*))").WithArguments("x").WithLocation(4, 21), // (9,20): error CS1736: Default parameter value for 'x' must be a compile-time constant // void M(int x = *(default(int*))) { } Diagnostic(ErrorCode.ERR_DefaultValueMustBeConstant, "*(default(int*))").WithArguments("x").WithLocation(9, 20), }; CreateCompilation(source, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics([ .. commonDiagnostics, // (4,21): error CS9360: This operation may only be used in an unsafe context // void M2(int x = *(default(int*))) { } Diagnostic(ErrorCode.ERR_UnsafeOperation, "*").WithLocation(4, 21), ]); CreateCompilation(source, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()).VerifyDiagnostics([ .. commonDiagnostics, // (3,28): error CS9360: This operation may only be used in an unsafe context // unsafe void M1(int x = *(default(int*))) { } Diagnostic(ErrorCode.ERR_UnsafeOperation, "*").WithLocation(3, 28), // (4,21): error CS9360: This operation may only be used in an unsafe context // void M2(int x = *(default(int*))) { } Diagnostic(ErrorCode.ERR_UnsafeOperation, "*").WithLocation(4, 21), // (7,14): error CS9377: The 'unsafe' modifier does not have any effect here under the current memory safety rules. // unsafe class D Diagnostic(ErrorCode.ERR_UnsafeMeaningless, "D").WithLocation(7, 14), // (9,20): error CS9360: This operation may only be used in an unsafe context // void M(int x = *(default(int*))) { } Diagnostic(ErrorCode.ERR_UnsafeOperation, "*").WithLocation(9, 20), ]); } [Fact] public void UnsafeContext_ExteriorOnly_RefAnalysis() { var source = """ class C { // In an unsafe block, ref-return of a local is downgraded from error to warning. unsafe ref int M1() { int x = 0; return ref x; } // With unsafe block explicitly: warning. ref int M2() { int x = 0; unsafe { return ref x; } } // Without unsafe context: error. ref int M3() { int x = 0; return ref x; } } """; CSharpCompilation createCompilation(CSharpCompilationOptions options) => CreateEmptyCompilation(source, TargetFrameworkUtil.GetReferences(TargetFramework.Standard), options: options #if DEBUG // https://github.com/dotnet/roslyn/issues/83549 , skipExtraValidation: true #endif ); // Without updated rules: M1 is in unsafe context, so it's a warning. createCompilation(TestOptions.UnsafeReleaseDll).VerifyDiagnostics( // (7,20): warning CS9077: Use of variable 'x' in this context may expose referenced variables outside of their declaration scope // return ref x; Diagnostic(ErrorCode.WRN_RefReturnLocal, "x").WithArguments("x").WithLocation(7, 20), // (14,29): warning CS9077: Use of variable 'x' in this context may expose referenced variables outside of their declaration scope // unsafe { return ref x; } Diagnostic(ErrorCode.WRN_RefReturnLocal, "x").WithArguments("x").WithLocation(14, 29), // (21,20): error CS8168: Cannot return local 'x' by reference because it is not a ref local // return ref x; Diagnostic(ErrorCode.ERR_RefReturnLocal, "x").WithArguments("x").WithLocation(21, 20)); // With updated rules: M1's body should NOT be in unsafe context (unsafe on method has no interior meaning), // so ref-return of local should be an error (not downgraded to warning). createCompilation(TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()).VerifyDiagnostics( // (7,20): error CS8168: Cannot return local 'x' by reference because it is not a ref local // return ref x; Diagnostic(ErrorCode.ERR_RefReturnLocal, "x").WithArguments("x").WithLocation(7, 20), // (14,29): warning CS9077: Use of variable 'x' in this context may expose referenced variables outside of their declaration scope // unsafe { return ref x; } Diagnostic(ErrorCode.WRN_RefReturnLocal, "x").WithArguments("x").WithLocation(14, 29), // (21,20): error CS8168: Cannot return local 'x' by reference because it is not a ref local // return ref x; Diagnostic(ErrorCode.ERR_RefReturnLocal, "x").WithArguments("x").WithLocation(21, 20)); } [Fact] public void UnsafeContext_ExteriorOnly_RefAnalysis_LocalFunction() { var source = """ #pragma warning disable CS8321 // unused local function class C { void M() { unsafe ref int Local1() { int x = 0; return ref x; } ref int Local2() { int x = 0; unsafe { return ref x; } } ref int Local3() { int x = 0; return ref x; } } } """; CSharpCompilation createCompilation(CSharpCompilationOptions options) => CreateEmptyCompilation(source, TargetFrameworkUtil.GetReferences(TargetFramework.Standard), options: options, skipExtraValidation: true); // Without updated rules: Local1 is in unsafe context, so it's a warning. createCompilation(TestOptions.UnsafeReleaseDll).VerifyDiagnostics( // (9,24): warning CS9077: Use of variable 'x' in this context may expose referenced variables outside of their declaration scope // return ref x; Diagnostic(ErrorCode.WRN_RefReturnLocal, "x").WithArguments("x").WithLocation(9, 24), // (15,33): warning CS9077: Use of variable 'x' in this context may expose referenced variables outside of their declaration scope // unsafe { return ref x; } Diagnostic(ErrorCode.WRN_RefReturnLocal, "x").WithArguments("x").WithLocation(15, 33), // (21,24): error CS8168: Cannot return local 'x' by reference because it is not a ref local // return ref x; Diagnostic(ErrorCode.ERR_RefReturnLocal, "x").WithArguments("x").WithLocation(21, 24)); // With updated rules: Local1's body should NOT be in unsafe context (unsafe on local function has no interior meaning), // so ref-return of local should be an error (not downgraded to warning). createCompilation(TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()).VerifyDiagnostics( // (9,24): error CS8168: Cannot return local 'x' by reference because it is not a ref local // return ref x; Diagnostic(ErrorCode.ERR_RefReturnLocal, "x").WithArguments("x").WithLocation(9, 24), // (15,33): warning CS9077: Use of variable 'x' in this context may expose referenced variables outside of their declaration scope // unsafe { return ref x; } Diagnostic(ErrorCode.WRN_RefReturnLocal, "x").WithArguments("x").WithLocation(15, 33), // (21,24): error CS8168: Cannot return local 'x' by reference because it is not a ref local // return ref x; Diagnostic(ErrorCode.ERR_RefReturnLocal, "x").WithArguments("x").WithLocation(21, 24)); } [Fact] public void Member_UnsafeDeclarations() { var declarationsSource = """ #pragma warning disable CS8321 // unused local function class C { void M0() { unsafe void F() { } } unsafe void M() { } unsafe int P { get; set; } unsafe event System.Action E { add { } remove { } } unsafe int this[int i] { get => i; set { } } unsafe C() { } unsafe public static C operator +(C c1, C c2) => c1; unsafe public void operator +=(C c) { } #pragma warning disable CS0169 // unused field unsafe int F; } """; CSharpTestSource source = [ declarationsSource, CompilerFeatureRequiredAttribute, RequiresUnsafeAttributeDefinition, ]; string[] safeSymbols = ["C"]; string[] unsafeSymbols = [ "C.<M0>g__F|0_0", "C.M", "C.P", "C.get_P", "C.set_P", "C.E", "C.add_E", "C.remove_E", "C.this[]", "C.get_Item", "C.set_Item", "C..ctor", "C.op_Addition", "C.op_AdditionAssignment", "C.F", ]; foreach (var parseOptions in new[] { TestOptions.RegularPreview, TestOptions.RegularNext, TestOptions.Regular14 }) { CompileAndVerify(source, parseOptions: parseOptions, options: TestOptions.UnsafeReleaseDll.WithMetadataImportOptions(MetadataImportOptions.All), symbolValidator: m => { VerifyMemorySafetyRulesAttribute(m, expectedDefinition: AttributeDefinition.None, includesAttributeUse: false); VerifyRequiresUnsafeAttribute( m, expectedUnsafeSymbols: [], expectedSafeSymbols: [.. safeSymbols, .. unsafeSymbols], expectedDefinition: AttributeDefinition.FromSource); }) .VerifyDiagnostics(); } foreach (var parseOptions in new[] { TestOptions.RegularPreview, TestOptions.RegularNext }) { CompileAndVerify(source, parseOptions: parseOptions, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules().WithMetadataImportOptions(MetadataImportOptions.All), symbolValidator: m => { VerifyMemorySafetyRulesAttribute(m, expectedDefinition: AttributeDefinition.Synthesized, includesAttributeUse: true); VerifyRequiresUnsafeAttribute( m, expectedUnsafeSymbols: [.. unsafeSymbols], expectedSafeSymbols: [.. safeSymbols], expectedDefinition: AttributeDefinition.FromSource); }) .VerifyDiagnostics(); } CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // error CS8630: Invalid 'MemorySafetyRules' value: '2' for C# 14.0. Please use language version 'preview' or greater. Diagnostic(ErrorCode.ERR_CompilationOptionNotAvailable).WithArguments("MemorySafetyRules", "2", "14.0", "preview").WithLocation(1, 1)); source = [ declarationsSource, CompilerFeatureRequiredAttribute, MemorySafetyRulesAttributeDefinition, ]; CreateCompilation(source, options: TestOptions.ReleaseModule.WithAllowUnsafe(true).WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // (4,17): error CS0518: Predefined type 'System.Diagnostics.CodeAnalysis.RequiresUnsafeAttribute' is not defined or imported // void M0() { unsafe void F() { } } Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "unsafe").WithArguments("System.Diagnostics.CodeAnalysis.RequiresUnsafeAttribute").WithLocation(4, 17), // (5,5): error CS0518: Predefined type 'System.Diagnostics.CodeAnalysis.RequiresUnsafeAttribute' is not defined or imported // unsafe void M() { } Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "unsafe").WithArguments("System.Diagnostics.CodeAnalysis.RequiresUnsafeAttribute").WithLocation(5, 5), // (6,5): error CS0518: Predefined type 'System.Diagnostics.CodeAnalysis.RequiresUnsafeAttribute' is not defined or imported // unsafe int P { get; set; } Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "unsafe").WithArguments("System.Diagnostics.CodeAnalysis.RequiresUnsafeAttribute").WithLocation(6, 5), // (6,20): error CS0518: Predefined type 'System.Diagnostics.CodeAnalysis.RequiresUnsafeAttribute' is not defined or imported // unsafe int P { get; set; } Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "get").WithArguments("System.Diagnostics.CodeAnalysis.RequiresUnsafeAttribute").WithLocation(6, 20), // (6,25): error CS0518: Predefined type 'System.Diagnostics.CodeAnalysis.RequiresUnsafeAttribute' is not defined or imported // unsafe int P { get; set; } Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "set").WithArguments("System.Diagnostics.CodeAnalysis.RequiresUnsafeAttribute").WithLocation(6, 25), // (8,5): error CS0518: Predefined type 'System.Diagnostics.CodeAnalysis.RequiresUnsafeAttribute' is not defined or imported // unsafe event System.Action E { add { } remove { } } Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "unsafe").WithArguments("System.Diagnostics.CodeAnalysis.RequiresUnsafeAttribute").WithLocation(8, 5), // (8,36): error CS0518: Predefined type 'System.Diagnostics.CodeAnalysis.RequiresUnsafeAttribute' is not defined or imported // unsafe event System.Action E { add { } remove { } } Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "add").WithArguments("System.Diagnostics.CodeAnalysis.RequiresUnsafeAttribute").WithLocation(8, 36), // (8,44): error CS0518: Predefined type 'System.Diagnostics.CodeAnalysis.RequiresUnsafeAttribute' is not defined or imported // unsafe event System.Action E { add { } remove { } } Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "remove").WithArguments("System.Diagnostics.CodeAnalysis.RequiresUnsafeAttribute").WithLocation(8, 44), // (9,5): error CS0518: Predefined type 'System.Diagnostics.CodeAnalysis.RequiresUnsafeAttribute' is not defined or imported // unsafe int this[int i] { get => i; set { } } Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "unsafe").WithArguments("System.Diagnostics.CodeAnalysis.RequiresUnsafeAttribute").WithLocation(9, 5), // (9,30): error CS0518: Predefined type 'System.Diagnostics.CodeAnalysis.RequiresUnsafeAttribute' is not defined or imported // unsafe int this[int i] { get => i; set { } } Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "get").WithArguments("System.Diagnostics.CodeAnalysis.RequiresUnsafeAttribute").WithLocation(9, 30), // (9,40): error CS0518: Predefined type 'System.Diagnostics.CodeAnalysis.RequiresUnsafeAttribute' is not defined or imported // unsafe int this[int i] { get => i; set { } } Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "set").WithArguments("System.Diagnostics.CodeAnalysis.RequiresUnsafeAttribute").WithLocation(9, 40), // (10,5): error CS0518: Predefined type 'System.Diagnostics.CodeAnalysis.RequiresUnsafeAttribute' is not defined or imported // unsafe C() { } Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "unsafe").WithArguments("System.Diagnostics.CodeAnalysis.RequiresUnsafeAttribute").WithLocation(10, 5), // (11,5): error CS0518: Predefined type 'System.Diagnostics.CodeAnalysis.RequiresUnsafeAttribute' is not defined or imported // unsafe public static C operator +(C c1, C c2) => c1; Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "unsafe").WithArguments("System.Diagnostics.CodeAnalysis.RequiresUnsafeAttribute").WithLocation(11, 5), // (12,5): error CS0518: Predefined type 'System.Diagnostics.CodeAnalysis.RequiresUnsafeAttribute' is not defined or imported // unsafe public void operator +=(C c) { } Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "unsafe").WithArguments("System.Diagnostics.CodeAnalysis.RequiresUnsafeAttribute").WithLocation(12, 5), // (14,5): error CS0518: Predefined type 'System.Diagnostics.CodeAnalysis.RequiresUnsafeAttribute' is not defined or imported // unsafe int F; Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "unsafe").WithArguments("System.Diagnostics.CodeAnalysis.RequiresUnsafeAttribute").WithLocation(14, 5)); } [Theory, CombinatorialData] public void Member_Method_Invocation( bool apiUpdatedRules, bool apiUnsafe, [CombinatorialValues(LanguageVersion.CSharp14, LanguageVersionFacts.CSharpNext, LanguageVersion.Preview)] LanguageVersion callerLangVersion, bool callerAllowUnsafe, bool callerUpdatedRules, bool callerUnsafeBlock, bool? compilationReference) { var unsafeModifier = apiUnsafe && apiUpdatedRules ? "unsafe" : ""; var api = $$""" public class C { {{unsafeModifier}} public void M() => System.Console.Write(111); } """; var caller = $""" var c = new C(); {(callerUnsafeBlock ? "unsafe { c.M(); }" : "c.M();")} """; var expectedOutput = "111"; CSharpCompilation comp; List<DiagnosticDescription> expectedDiagnostics = []; if (compilationReference is { } useCompilationReference) { var apiCompilation = CreateCompilation( [api], options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules(apiUpdatedRules)) .VerifyDiagnostics(); var apiReference = AsReference(apiCompilation, useCompilationReference); comp = CreateCompilation(caller, [apiReference], parseOptions: TestOptions.Regular.WithLanguageVersion(callerLangVersion), options: TestOptions.ReleaseExe.WithAllowUnsafe(callerAllowUnsafe).WithUpdatedMemorySafetyRules(callerUpdatedRules)); } else { if (apiUpdatedRules != callerUpdatedRules) { return; } comp = CreateCompilation( [api, caller], parseOptions: TestOptions.Regular.WithLanguageVersion(callerLangVersion), options: TestOptions.ReleaseExe.WithAllowUnsafe(callerAllowUnsafe).WithUpdatedMemorySafetyRules(callerUpdatedRules)); } if (!callerAllowUnsafe && callerUnsafeBlock) { expectedDiagnostics.Add( // (2,1): error CS0227: Unsafe code may only appear if compiling with /unsafe // unsafe { c.M(); } Diagnostic(ErrorCode.ERR_IllegalUnsafe, "unsafe").WithLocation(2, 1)); } if (!callerAllowUnsafe && compilationReference is null && unsafeModifier != "") { expectedDiagnostics.Add( // (3,24): error CS0227: Unsafe code may only appear if compiling with /unsafe // unsafe public void M() => System.Console.Write(111); Diagnostic(ErrorCode.ERR_IllegalUnsafe, "M").WithLocation(3, 24)); } if (apiUnsafe && apiUpdatedRules && callerUpdatedRules && !callerUnsafeBlock) { if (callerLangVersion >= LanguageVersionFacts.CSharpNext) { expectedDiagnostics.Add( // (2,1): error CS9362: 'C.M()' must be used in an unsafe context because it is marked as 'unsafe' // c.M(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c.M()").WithArguments("C.M()").WithLocation(2, 1)); } else { expectedDiagnostics.Add( // (2,1): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // c.M(); Diagnostic(ErrorCode.ERR_FeatureInPreview, "c.M()").WithArguments("updated memory safety rules").WithLocation(2, 1)); } } if (callerUpdatedRules && callerLangVersion < LanguageVersionFacts.CSharpNext) { expectedDiagnostics.Add( // error CS8630: Invalid 'MemorySafetyRules' value: '2' for C# 14.0. Please use language version 'preview' or greater. Diagnostic(ErrorCode.ERR_CompilationOptionNotAvailable).WithArguments("MemorySafetyRules", "2", "14.0", "preview").WithLocation(1, 1)); } comp.VerifyDiagnostics([.. expectedDiagnostics]); if (!comp.GetDiagnostics().HasAnyErrors()) { CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); } } [Fact] public void Member_Method_OverloadResolution() { CompileAndVerifyUnsafe( lib: """ public class C { public static void M() { } unsafe public static void M(int x) { } } """, caller: """ C.M(); C.M(1); _ = nameof(C.M); unsafe { C.M(1); } """, expectedUnsafeSymbols: [Overload("C.M", 1)], expectedSafeSymbols: ["C", Overload("C.M", 0)], expectedDiagnostics: [ // (2,1): error CS9362: 'C.M(int)' must be used in an unsafe context because it is marked as 'unsafe' // C.M(1); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "C.M(1)").WithArguments("C.M(int)").WithLocation(2, 1), ]); } [Fact] public void Member_Method_SafeBoundary() { CompileAndVerifyUnsafe( lib: """ public class C { public void M1() { unsafe { M2(); } } unsafe public void M2() { } } """, caller: """ var c = new C(); c.M1(); c.M2(); """, expectedUnsafeSymbols: ["C.M2"], expectedSafeSymbols: ["C.M1"], expectedDiagnostics: [ // (3,1): error CS9362: 'C.M2()' must be used in an unsafe context because it is marked as 'unsafe' // c.M2(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c.M2()").WithArguments("C.M2()").WithLocation(3, 1), ]); } [Fact] public void Member_Method_NameOf() { CompileAndVerifyUnsafe( lib: """ public class C { unsafe public static void M() { } } """, caller: """ _ = nameof(C.M); """, expectedUnsafeSymbols: ["C.M"], expectedSafeSymbols: ["C"], expectedDiagnostics: []); } [Fact] public void Member_Method_Extension() { CompileAndVerifyUnsafe( lib: """ public static class E { unsafe public static void M1(this int x) { } extension(int x) { unsafe public void M2() { } } } """, caller: """ 123.M1(); 123.M2(); E.M1(123); E.M2(123); unsafe { 123.M1(); } unsafe { 123.M2(); } unsafe { E.M1(123); } unsafe { E.M2(123); } """, expectedUnsafeSymbols: ["E.M1", "E.M2", ExtensionMember("E", "M2")], expectedSafeSymbols: [], expectedDiagnostics: [ // (1,1): error CS9362: 'E.M1(int)' must be used in an unsafe context because it is marked as 'unsafe' // 123.M1(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "123.M1()").WithArguments("E.M1(int)").WithLocation(1, 1), // (2,1): error CS9362: 'E.extension(int).M2()' must be used in an unsafe context because it is marked as 'unsafe' // 123.M2(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "123.M2()").WithArguments("E.extension(int).M2()").WithLocation(2, 1), // (3,1): error CS9362: 'E.M1(int)' must be used in an unsafe context because it is marked as 'unsafe' // E.M1(123); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "E.M1(123)").WithArguments("E.M1(int)").WithLocation(3, 1), // (4,1): error CS9362: 'E.M2(int)' must be used in an unsafe context because it is marked as 'unsafe' // E.M2(123); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "E.M2(123)").WithArguments("E.M2(int)").WithLocation(4, 1), ]); } [Fact] public void Member_Method_InUnsafeClass() { CompileAndVerifyUnsafe( lib: """ using System.Collections.Generic; public unsafe class C { public void M1() { } public IEnumerable<int> M2() { yield return 1; } unsafe public void M3() { } } """, caller: """ var c = new C(); c.M1(); c.M2(); c.M3(); unsafe { c.M3(); } """, expectedUnsafeSymbols: ["C.M3"], expectedSafeSymbols: ["C.M1", "C.M2"], expectedDiagnostics: [ // (4,1): error CS9362: 'C.M3()' must be used in an unsafe context because it is marked as 'unsafe' // c.M3(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c.M3()").WithArguments("C.M3()").WithLocation(4, 1), ], expectedLibDiagnostics: [ // (2,21): error CS9377: The 'unsafe' modifier does not have any effect here under the current memory safety rules. // public unsafe class C Diagnostic(ErrorCode.ERR_UnsafeMeaningless, "C").WithLocation(2, 21), ]); } [Fact] public void UnsafeClass_OtherModifierErrors() { var source = """ unsafe virtual class C; """; CreateCompilation(source, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()).VerifyDiagnostics( // (1,22): error CS0106: The modifier 'virtual' is not valid for this item // unsafe virtual class C; Diagnostic(ErrorCode.ERR_BadMemberFlag, "C").WithArguments("virtual").WithLocation(1, 22), // (1,22): error CS9377: The 'unsafe' modifier does not have any effect here under the current memory safety rules. // unsafe virtual class C; Diagnostic(ErrorCode.ERR_UnsafeMeaningless, "C").WithLocation(1, 22)); } [Fact] public void Member_Method_ConvertToFunctionPointer() { CompileAndVerifyUnsafe( lib: """ public static class C { unsafe public static void M() { } } """, caller: """ delegate*<void> p1 = &C.M; unsafe { delegate*<void> p2 = &C.M; } """, expectedUnsafeSymbols: ["C.M"], expectedSafeSymbols: ["C"], expectedDiagnostics: [ // (1,22): error CS9362: 'C.M()' must be used in an unsafe context because it is marked as 'unsafe' // delegate*<void> p1 = &C.M; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "&C.M").WithArguments("C.M()").WithLocation(1, 22), ], expectedDiagnosticsWithOldLangVersion: [ // (1,1): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // delegate*<void> p1 = &C.M; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "delegate*").WithLocation(1, 1), ]); } [Fact] public void Member_Method_ConvertToDelegate() { CompileAndVerifyUnsafe( lib: """ public static class C { unsafe public static void M() { } } """, caller: """ D1 a = C.M; D2 b = C.M; unsafe { D1 c = C.M; } unsafe { D1 d = C.M; } delegate void D1(); unsafe delegate void D2(); """, expectedUnsafeSymbols: ["C.M"], expectedSafeSymbols: ["C"], expectedDiagnostics: [ // (1,8): error CS9362: 'C.M()' must be used in an unsafe context because it is marked as 'unsafe' // D1 a = C.M; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "C.M").WithArguments("C.M()").WithLocation(1, 8), // (2,8): error CS9362: 'C.M()' must be used in an unsafe context because it is marked as 'unsafe' // D2 b = C.M; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "C.M").WithArguments("C.M()").WithLocation(2, 8), // (7,22): error CS9377: The 'unsafe' modifier does not have any effect here under the current memory safety rules. // unsafe delegate void D2(); Diagnostic(ErrorCode.ERR_UnsafeMeaningless, "D2").WithLocation(7, 22), ], expectedDiagnosticsWhenReferencingLegacyLib: [ // (7,22): error CS9377: The 'unsafe' modifier does not have any effect here under the current memory safety rules. // unsafe delegate void D2(); Diagnostic(ErrorCode.ERR_UnsafeMeaningless, "D2").WithLocation(7, 22), ]); } [Fact] public void Member_Method_ConvertToDelegate_Inferred() { CompileAndVerifyUnsafe( lib: """ public static class C { unsafe public static void M() { } } """, caller: """ var a = C.M; unsafe { var b = C.M; } """, expectedUnsafeSymbols: ["C.M"], expectedSafeSymbols: ["C"], expectedDiagnostics: [ // (1,9): error CS9362: 'C.M()' must be used in an unsafe context because it is marked as 'unsafe' // var a = C.M; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "C.M").WithArguments("C.M()").WithLocation(1, 9), ]); CompileAndVerify( [ """ System.Action a; unsafe { var d = C.M; System.Console.WriteLine(d.GetType()); a = d; } a(); static class C { unsafe public static void M() => System.Console.WriteLine("ran"); } """, ], options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules(), expectedOutput: """ System.Action ran """) .VerifyDiagnostics(); } [Fact] public void Member_Method_ExpressionTree() { CompileAndVerifyUnsafe( lib: """ public static class C { unsafe public static void M() { } } """, caller: """ using System; using System.Linq.Expressions; Expression<Action> e1 = () => C.M(); unsafe { Expression<Action> e2 = () => C.M(); } """, expectedUnsafeSymbols: ["C.M"], expectedSafeSymbols: ["C"], expectedDiagnostics: [ // (4,31): error CS9362: 'C.M()' must be used in an unsafe context because it is marked as 'unsafe' // Expression<Action> e1 = () => C.M(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "C.M()").WithArguments("C.M()").WithLocation(4, 31), ]); } [Fact] public void Member_Method_Attribute() { var commonDiagnostics = new[] { // (3,4): error CS0617: 'F' is not a valid named attribute argument. Named attribute arguments must be fields which are not readonly, static, or const, or read-write properties which are public and not static. // [A(F = 0)] void M2() { } Diagnostic(ErrorCode.ERR_BadNamedAttributeArgument, "F").WithArguments("F").WithLocation(3, 4), // (4,13): error CS0617: 'F' is not a valid named attribute argument. Named attribute arguments must be fields which are not readonly, static, or const, or read-write properties which are public and not static. // unsafe { [A(F = 0)] void M3() { } } Diagnostic(ErrorCode.ERR_BadNamedAttributeArgument, "F").WithArguments("F").WithLocation(4, 13), // (5,4): error CS0617: 'F' is not a valid named attribute argument. Named attribute arguments must be fields which are not readonly, static, or const, or read-write properties which are public and not static. // [A(F = 0)] unsafe void M4() { } Diagnostic(ErrorCode.ERR_BadNamedAttributeArgument, "F").WithArguments("F").WithLocation(5, 4), }; CompileAndVerifyUnsafe( lib: """ public class A : System.Attribute { unsafe public void F(int x) { } } """, caller: """ #pragma warning disable CS8321 // unused local function [A] void M1() { } [A(F = 0)] void M2() { } unsafe { [A(F = 0)] void M3() { } } [A(F = 0)] unsafe void M4() { } """, expectedUnsafeSymbols: ["A.F"], expectedSafeSymbols: ["A"], expectedDiagnostics: [ // (3,4): error CS9362: 'A.F(int)' must be used in an unsafe context because it is marked as 'unsafe' // [A(F = 0)] void M2() { } Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "F = 0").WithArguments("A.F(int)").WithLocation(3, 4), .. commonDiagnostics, // (5,4): error CS9362: 'A.F(int)' must be used in an unsafe context because it is marked as 'unsafe' // [A(F = 0)] unsafe void M4() { } Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "F = 0").WithArguments("A.F(int)").WithLocation(5, 4), ], expectedDiagnosticsWhenReferencingLegacyLib: commonDiagnostics, expectedDiagnosticsForLegacyCaller: commonDiagnostics); } [Fact] public void Member_Method_Interface() { CompileAndVerifyUnsafe( lib: """ public interface I { void M1(); unsafe void M2(); } """, caller: """ I i = null; i.M1(); i.M2(); unsafe { i.M2(); } """, expectedUnsafeSymbols: ["I.M2"], expectedSafeSymbols: ["I", "I.M1"], expectedDiagnostics: [ // (3,1): error CS9362: 'I.M2()' must be used in an unsafe context because it is marked as 'unsafe' // i.M2(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "i.M2()").WithArguments("I.M2()").WithLocation(3, 1), ]); } [Fact] public void Member_Method_Override_LangVersion() { var lib = """ public class B { public virtual void M1() { } unsafe public virtual void M2() { } } """; var caller = """ new C().M1(); class C : B { unsafe public override void M1() { } public override void M2() { } } """; var expectedDiagnostics = new[] { // (1,1): error CS9362: 'C.M1()' must be used in an unsafe context because it is marked as 'unsafe' // new C().M1(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "new C().M1()").WithArguments("C.M1()").WithLocation(1, 1), // (5,33): error CS9364: Unsafe member 'C.M1()' cannot override safe member 'B.M1()' // unsafe public override void M1() { } Diagnostic(ErrorCode.ERR_CallerUnsafeOverridingSafe, "M1").WithArguments("C.M1()", "B.M1()").WithLocation(5, 33), }; CompileAndVerifyUnsafe( lib: lib, caller: caller, expectedUnsafeSymbols: ["B.M2"], expectedSafeSymbols: ["B.M1"], expectedDiagnostics: expectedDiagnostics, expectedDiagnosticsWhenReferencingLegacyLib: expectedDiagnostics); CreateCompilation([lib, caller], parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation([lib, caller], parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // error CS8630: Invalid 'MemorySafetyRules' value: '2' for C# 14.0. Please use language version 'preview' or greater. Diagnostic(ErrorCode.ERR_CompilationOptionNotAvailable).WithArguments("MemorySafetyRules", "2", "14.0", "preview").WithLocation(1, 1), // (5,33): error CS9364: Unsafe member 'C.M1()' cannot override safe member 'B.M1()' // unsafe public override void M1() { } Diagnostic(ErrorCode.ERR_CallerUnsafeOverridingSafe, "M1").WithArguments("C.M1()", "B.M1()").WithLocation(5, 33), // (1,1): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // new C().M1(); Diagnostic(ErrorCode.ERR_FeatureInPreview, "new C().M1()").WithArguments("updated memory safety rules").WithLocation(1, 1)); } [Fact] public void Member_Method_Override() { CompileAndVerifyUnsafe( lib: """ public class B { unsafe public virtual void M1() { } unsafe public virtual void M2() { } unsafe public virtual void M3() { } public virtual void M4() { } public virtual void M5() { } public virtual void M6() { } } public class C : B { unsafe public override void M1() { } public new virtual void M2() { } public override void M3() { } public override void M4() { } unsafe public new virtual void M5() { } } """, caller: """ var d1 = new D1(); d1.M1(); d1.M2(); d1.M3(); d1.M4(); d1.M5(); d1.M6(); var d2 = new D2(); d2.M1(); d2.M2(); d2.M3(); d2.M4(); d2.M5(); d2.M6(); var d3 = new D3(); d3.M1(); d3.M2(); d3.M3(); d3.M4(); d3.M5(); d3.M6(); C c = d1; c.M1(); c.M2(); c.M3(); c.M4(); c.M5(); c.M6(); class D1 : C { public override void M1() { } public override void M2() { } public override void M3() { } public override void M4() { } public override void M5() { } public override void M6() { } public void BaseCalls() { base.M1(); base.M2(); base.M3(); base.M4(); base.M6(); } } class D2 : C { unsafe public override void M1() { } unsafe public override void M2() { } unsafe public override void M3() { } unsafe public override void M4() { } unsafe public override void M5() { } unsafe public override void M6() { } } class D3 : C; """, expectedUnsafeSymbols: ["B.M1", "B.M2", "B.M3", "C.M1", "C.M5"], expectedSafeSymbols: ["B.M4", "B.M5", "B.M6", "C.M2", "C.M3", "C.M4"], expectedDiagnostics: [ // (2,20): error CS9362: 'D2.M1()' must be used in an unsafe context because it is marked as 'unsafe' // var d2 = new D2(); d2.M1(); d2.M2(); d2.M3(); d2.M4(); d2.M5(); d2.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "d2.M1()").WithArguments("D2.M1()").WithLocation(2, 20), // (2,29): error CS9362: 'D2.M2()' must be used in an unsafe context because it is marked as 'unsafe' // var d2 = new D2(); d2.M1(); d2.M2(); d2.M3(); d2.M4(); d2.M5(); d2.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "d2.M2()").WithArguments("D2.M2()").WithLocation(2, 29), // (2,38): error CS9362: 'D2.M3()' must be used in an unsafe context because it is marked as 'unsafe' // var d2 = new D2(); d2.M1(); d2.M2(); d2.M3(); d2.M4(); d2.M5(); d2.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "d2.M3()").WithArguments("D2.M3()").WithLocation(2, 38), // (2,47): error CS9362: 'D2.M4()' must be used in an unsafe context because it is marked as 'unsafe' // var d2 = new D2(); d2.M1(); d2.M2(); d2.M3(); d2.M4(); d2.M5(); d2.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "d2.M4()").WithArguments("D2.M4()").WithLocation(2, 47), // (2,56): error CS9362: 'D2.M5()' must be used in an unsafe context because it is marked as 'unsafe' // var d2 = new D2(); d2.M1(); d2.M2(); d2.M3(); d2.M4(); d2.M5(); d2.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "d2.M5()").WithArguments("D2.M5()").WithLocation(2, 56), // (2,65): error CS9362: 'D2.M6()' must be used in an unsafe context because it is marked as 'unsafe' // var d2 = new D2(); d2.M1(); d2.M2(); d2.M3(); d2.M4(); d2.M5(); d2.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "d2.M6()").WithArguments("D2.M6()").WithLocation(2, 65), // (3,20): error CS9362: 'C.M1()' must be used in an unsafe context because it is marked as 'unsafe' // var d3 = new D3(); d3.M1(); d3.M2(); d3.M3(); d3.M4(); d3.M5(); d3.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "d3.M1()").WithArguments("C.M1()").WithLocation(3, 20), // (3,56): error CS9362: 'C.M5()' must be used in an unsafe context because it is marked as 'unsafe' // var d3 = new D3(); d3.M1(); d3.M2(); d3.M3(); d3.M4(); d3.M5(); d3.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "d3.M5()").WithArguments("C.M5()").WithLocation(3, 56), // (4,11): error CS9362: 'C.M1()' must be used in an unsafe context because it is marked as 'unsafe' // C c = d1; c.M1(); c.M2(); c.M3(); c.M4(); c.M5(); c.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c.M1()").WithArguments("C.M1()").WithLocation(4, 11), // (4,43): error CS9362: 'C.M5()' must be used in an unsafe context because it is marked as 'unsafe' // C c = d1; c.M1(); c.M2(); c.M3(); c.M4(); c.M5(); c.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c.M5()").WithArguments("C.M5()").WithLocation(4, 43), // (14,31): error CS9362: 'C.M1()' must be used in an unsafe context because it is marked as 'unsafe' // public void BaseCalls() { base.M1(); base.M2(); base.M3(); base.M4(); base.M6(); } Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "base.M1()").WithArguments("C.M1()").WithLocation(14, 31), // (20,33): error CS9364: Unsafe member 'D2.M2()' cannot override safe member 'C.M2()' // unsafe public override void M2() { } Diagnostic(ErrorCode.ERR_CallerUnsafeOverridingSafe, "M2").WithArguments("D2.M2()", "C.M2()").WithLocation(20, 33), // (22,33): error CS9364: Unsafe member 'D2.M4()' cannot override safe member 'B.M4()' // unsafe public override void M4() { } Diagnostic(ErrorCode.ERR_CallerUnsafeOverridingSafe, "M4").WithArguments("D2.M4()", "B.M4()").WithLocation(22, 33), // (24,33): error CS9364: Unsafe member 'D2.M6()' cannot override safe member 'B.M6()' // unsafe public override void M6() { } Diagnostic(ErrorCode.ERR_CallerUnsafeOverridingSafe, "M6").WithArguments("D2.M6()", "B.M6()").WithLocation(24, 33), ], expectedDiagnosticsWhenReferencingLegacyLib: [ // (2,20): error CS9362: 'D2.M1()' must be used in an unsafe context because it is marked as 'unsafe' // var d2 = new D2(); d2.M1(); d2.M2(); d2.M3(); d2.M4(); d2.M5(); d2.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "d2.M1()").WithArguments("D2.M1()").WithLocation(2, 20), // (2,29): error CS9362: 'D2.M2()' must be used in an unsafe context because it is marked as 'unsafe' // var d2 = new D2(); d2.M1(); d2.M2(); d2.M3(); d2.M4(); d2.M5(); d2.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "d2.M2()").WithArguments("D2.M2()").WithLocation(2, 29), // (2,38): error CS9362: 'D2.M3()' must be used in an unsafe context because it is marked as 'unsafe' // var d2 = new D2(); d2.M1(); d2.M2(); d2.M3(); d2.M4(); d2.M5(); d2.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "d2.M3()").WithArguments("D2.M3()").WithLocation(2, 38), // (2,47): error CS9362: 'D2.M4()' must be used in an unsafe context because it is marked as 'unsafe' // var d2 = new D2(); d2.M1(); d2.M2(); d2.M3(); d2.M4(); d2.M5(); d2.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "d2.M4()").WithArguments("D2.M4()").WithLocation(2, 47), // (2,56): error CS9362: 'D2.M5()' must be used in an unsafe context because it is marked as 'unsafe' // var d2 = new D2(); d2.M1(); d2.M2(); d2.M3(); d2.M4(); d2.M5(); d2.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "d2.M5()").WithArguments("D2.M5()").WithLocation(2, 56), // (2,65): error CS9362: 'D2.M6()' must be used in an unsafe context because it is marked as 'unsafe' // var d2 = new D2(); d2.M1(); d2.M2(); d2.M3(); d2.M4(); d2.M5(); d2.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "d2.M6()").WithArguments("D2.M6()").WithLocation(2, 65), // (19,33): error CS9364: Unsafe member 'D2.M1()' cannot override safe member 'B.M1()' // unsafe public override void M1() { } Diagnostic(ErrorCode.ERR_CallerUnsafeOverridingSafe, "M1").WithArguments("D2.M1()", "B.M1()").WithLocation(19, 33), // (20,33): error CS9364: Unsafe member 'D2.M2()' cannot override safe member 'C.M2()' // unsafe public override void M2() { } Diagnostic(ErrorCode.ERR_CallerUnsafeOverridingSafe, "M2").WithArguments("D2.M2()", "C.M2()").WithLocation(20, 33), // (21,33): error CS9364: Unsafe member 'D2.M3()' cannot override safe member 'B.M3()' // unsafe public override void M3() { } Diagnostic(ErrorCode.ERR_CallerUnsafeOverridingSafe, "M3").WithArguments("D2.M3()", "B.M3()").WithLocation(21, 33), // (22,33): error CS9364: Unsafe member 'D2.M4()' cannot override safe member 'B.M4()' // unsafe public override void M4() { } Diagnostic(ErrorCode.ERR_CallerUnsafeOverridingSafe, "M4").WithArguments("D2.M4()", "B.M4()").WithLocation(22, 33), // (23,33): error CS9364: Unsafe member 'D2.M5()' cannot override safe member 'C.M5()' // unsafe public override void M5() { } Diagnostic(ErrorCode.ERR_CallerUnsafeOverridingSafe, "M5").WithArguments("D2.M5()", "C.M5()").WithLocation(23, 33), // (24,33): error CS9364: Unsafe member 'D2.M6()' cannot override safe member 'B.M6()' // unsafe public override void M6() { } Diagnostic(ErrorCode.ERR_CallerUnsafeOverridingSafe, "M6").WithArguments("D2.M6()", "B.M6()").WithLocation(24, 33), ]); } [Fact] public void Member_Method_Override_Metadata() { // [module: MemorySafetyRules(updated)] // public class A // { // public virtual void M() { } // } // public class B : A // { // [RequiresUnsafe] // public override void M() { } // } var refA = CompileIL($$""" .assembly extern mscorlib { .ver 4:0:0:0 .publickeytoken = (B7 7A 5C 56 19 34 E0 89) } .assembly '<<GeneratedFileName>>' { } .module '<<GeneratedFileName>>.dll' .custom instance void System.Runtime.CompilerServices.MemorySafetyRulesAttribute::.ctor(int32) = { int32({{CSharpCompilationOptions.UpdatedMemorySafetyRulesVersion}}) } .class public System.Runtime.CompilerServices.MemorySafetyRulesAttribute extends [mscorlib]System.Attribute { .method public hidebysig specialname rtspecialname instance void .ctor(int32 version) cil managed { ret } } .class public System.Diagnostics.CodeAnalysis.RequiresUnsafeAttribute extends [mscorlib]System.Attribute { .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ret } } .class public A { .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ret } .method public hidebysig newslot virtual instance void M() cil managed { ret } } .class public B extends A { .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ret } .method public hidebysig virtual instance void M() cil managed { .custom instance void System.Diagnostics.CodeAnalysis.RequiresUnsafeAttribute::.ctor() ret } } """, prependDefaultHeader: false); var moduleA = CreateCompilation("", [refA]).GetReferencedAssemblySymbol(refA).Modules.Single(); VerifyMemorySafetyRulesAttribute( moduleA, expectedDefinition: AttributeDefinition.FromSource, includesAttributeUse: true); VerifyRequiresUnsafeAttribute( moduleA, expectedUnsafeSymbols: ["B.M"], expectedSafeSymbols: ["A", "A.M", "B"], expectedDefinition: AttributeDefinition.FromSource); CreateCompilation(""" var c1 = new C1(); c1.M(); var c2 = new C2(); c2.M(); B b = c2; b.M(); A a = b; a.M(); class C1 : B { public override void M() { } } class C2 : B { unsafe public override void M() { } } """, [refA], options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // (4,1): error CS9362: 'C2.M()' must be used in an unsafe context because it is marked as 'unsafe' // c2.M(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c2.M()").WithArguments("C2.M()").WithLocation(4, 1), // (6,1): error CS9362: 'B.M()' must be used in an unsafe context because it is marked as 'unsafe' // b.M(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "b.M()").WithArguments("B.M()").WithLocation(6, 1), // (16,33): error CS9364: Unsafe member 'C2.M()' cannot override safe member 'A.M()' // unsafe public override void M() { } Diagnostic(ErrorCode.ERR_CallerUnsafeOverridingSafe, "M").WithArguments("C2.M()", "A.M()").WithLocation(16, 33)); } [Fact] public void Member_Method_Implementation() { CompileAndVerifyUnsafe( lib: """ public interface I1 { unsafe void M1(); void M2(); } public interface I2 { void M1(); unsafe void M2(); } public class C1 : I1 { public void M1() { } public void M2() { } } public class C2 : I1 { void I1.M1() { } void I1.M2() { } } """, caller: """ var c5 = new C5(); I1 i1 = c5; i1.M1(); i1.M2(); I2 i2 = c5; i2.M1(); i2.M2(); public class C3 : I1 { unsafe public void M1() { } unsafe public void M2() { } } public class C4 : I1 { unsafe void I1.M1() { } unsafe void I1.M2() { } } public class C5 : I1, I2 { unsafe public void M1() { } unsafe public void M2() { } } public class C6 : I2, I1 { unsafe public void M1() { } unsafe public void M2() { } } public class C7 : I1, I2 { public void M1() { } public void M2() { } } public class C8 : I2, I1 { public void M1() { } public void M2() { } } """, expectedUnsafeSymbols: ["I1.M1", "I2.M2"], expectedSafeSymbols: ["I1.M2", "I2.M1", "C1.M1", "C1.M2", "C2.I1.M1", "C2.I1.M2"], expectedDiagnostics: [ // (3,1): error CS9362: 'I1.M1()' must be used in an unsafe context because it is marked as 'unsafe' // i1.M1(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "i1.M1()").WithArguments("I1.M1()").WithLocation(3, 1), // (7,1): error CS9362: 'I2.M2()' must be used in an unsafe context because it is marked as 'unsafe' // i2.M2(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "i2.M2()").WithArguments("I2.M2()").WithLocation(7, 1), // (12,24): error CS9365: Unsafe member 'C3.M2()' cannot implicitly implement safe member 'I1.M2()' // unsafe public void M2() { } Diagnostic(ErrorCode.ERR_CallerUnsafeImplicitlyImplementingSafe, "M2").WithArguments("C3.M2()", "I1.M2()").WithLocation(12, 24), // (18,20): error CS9366: Unsafe member 'C4.I1.M2()' cannot implement safe member 'I1.M2()' // unsafe void I1.M2() { } Diagnostic(ErrorCode.ERR_CallerUnsafeExplicitlyImplementingSafe, "M2").WithArguments("C4.I1.M2()", "I1.M2()").WithLocation(18, 20), // (23,24): error CS9365: Unsafe member 'C5.M1()' cannot implicitly implement safe member 'I2.M1()' // unsafe public void M1() { } Diagnostic(ErrorCode.ERR_CallerUnsafeImplicitlyImplementingSafe, "M1").WithArguments("C5.M1()", "I2.M1()").WithLocation(23, 24), // (24,24): error CS9365: Unsafe member 'C5.M2()' cannot implicitly implement safe member 'I1.M2()' // unsafe public void M2() { } Diagnostic(ErrorCode.ERR_CallerUnsafeImplicitlyImplementingSafe, "M2").WithArguments("C5.M2()", "I1.M2()").WithLocation(24, 24), // (29,24): error CS9365: Unsafe member 'C6.M1()' cannot implicitly implement safe member 'I2.M1()' // unsafe public void M1() { } Diagnostic(ErrorCode.ERR_CallerUnsafeImplicitlyImplementingSafe, "M1").WithArguments("C6.M1()", "I2.M1()").WithLocation(29, 24), // (30,24): error CS9365: Unsafe member 'C6.M2()' cannot implicitly implement safe member 'I1.M2()' // unsafe public void M2() { } Diagnostic(ErrorCode.ERR_CallerUnsafeImplicitlyImplementingSafe, "M2").WithArguments("C6.M2()", "I1.M2()").WithLocation(30, 24), ], expectedDiagnosticsWhenReferencingLegacyLib: [ // (11,24): error CS9365: Unsafe member 'C3.M1()' cannot implicitly implement safe member 'I1.M1()' // unsafe public void M1() { } Diagnostic(ErrorCode.ERR_CallerUnsafeImplicitlyImplementingSafe, "M1").WithArguments("C3.M1()", "I1.M1()").WithLocation(11, 24), // (12,24): error CS9365: Unsafe member 'C3.M2()' cannot implicitly implement safe member 'I1.M2()' // unsafe public void M2() { } Diagnostic(ErrorCode.ERR_CallerUnsafeImplicitlyImplementingSafe, "M2").WithArguments("C3.M2()", "I1.M2()").WithLocation(12, 24), // (17,20): error CS9366: Unsafe member 'C4.I1.M1()' cannot implement safe member 'I1.M1()' // unsafe void I1.M1() { } Diagnostic(ErrorCode.ERR_CallerUnsafeExplicitlyImplementingSafe, "M1").WithArguments("C4.I1.M1()", "I1.M1()").WithLocation(17, 20), // (18,20): error CS9366: Unsafe member 'C4.I1.M2()' cannot implement safe member 'I1.M2()' // unsafe void I1.M2() { } Diagnostic(ErrorCode.ERR_CallerUnsafeExplicitlyImplementingSafe, "M2").WithArguments("C4.I1.M2()", "I1.M2()").WithLocation(18, 20), // (23,24): error CS9365: Unsafe member 'C5.M1()' cannot implicitly implement safe member 'I1.M1()' // unsafe public void M1() { } Diagnostic(ErrorCode.ERR_CallerUnsafeImplicitlyImplementingSafe, "M1").WithArguments("C5.M1()", "I1.M1()").WithLocation(23, 24), // (23,24): error CS9365: Unsafe member 'C5.M1()' cannot implicitly implement safe member 'I2.M1()' // unsafe public void M1() { } Diagnostic(ErrorCode.ERR_CallerUnsafeImplicitlyImplementingSafe, "M1").WithArguments("C5.M1()", "I2.M1()").WithLocation(23, 24), // (24,24): error CS9365: Unsafe member 'C5.M2()' cannot implicitly implement safe member 'I1.M2()' // unsafe public void M2() { } Diagnostic(ErrorCode.ERR_CallerUnsafeImplicitlyImplementingSafe, "M2").WithArguments("C5.M2()", "I1.M2()").WithLocation(24, 24), // (24,24): error CS9365: Unsafe member 'C5.M2()' cannot implicitly implement safe member 'I2.M2()' // unsafe public void M2() { } Diagnostic(ErrorCode.ERR_CallerUnsafeImplicitlyImplementingSafe, "M2").WithArguments("C5.M2()", "I2.M2()").WithLocation(24, 24), // (29,24): error CS9365: Unsafe member 'C6.M1()' cannot implicitly implement safe member 'I2.M1()' // unsafe public void M1() { } Diagnostic(ErrorCode.ERR_CallerUnsafeImplicitlyImplementingSafe, "M1").WithArguments("C6.M1()", "I2.M1()").WithLocation(29, 24), // (29,24): error CS9365: Unsafe member 'C6.M1()' cannot implicitly implement safe member 'I1.M1()' // unsafe public void M1() { } Diagnostic(ErrorCode.ERR_CallerUnsafeImplicitlyImplementingSafe, "M1").WithArguments("C6.M1()", "I1.M1()").WithLocation(29, 24), // (30,24): error CS9365: Unsafe member 'C6.M2()' cannot implicitly implement safe member 'I2.M2()' // unsafe public void M2() { } Diagnostic(ErrorCode.ERR_CallerUnsafeImplicitlyImplementingSafe, "M2").WithArguments("C6.M2()", "I2.M2()").WithLocation(30, 24), // (30,24): error CS9365: Unsafe member 'C6.M2()' cannot implicitly implement safe member 'I1.M2()' // unsafe public void M2() { } Diagnostic(ErrorCode.ERR_CallerUnsafeImplicitlyImplementingSafe, "M2").WithArguments("C6.M2()", "I1.M2()").WithLocation(30, 24), ]); } [Fact] public void Member_Method_Implementation_Synthesized() { CompileAndVerifyUnsafe( lib: """ public class B<T> { public void M1(T x) { } unsafe public void M2(T x) { } } """, caller: """ var c = new C(); c.M1(null); c.M2(null); class C : B<D>, I; class D; interface I { unsafe void M1(D x); } """, expectedUnsafeSymbols: ["B.M2"], expectedSafeSymbols: ["B.M1"], expectedDiagnostics: [ // (3,1): error CS9362: 'B<D>.M2(D)' must be used in an unsafe context because it is marked as 'unsafe' // c.M2(null); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c.M2(null)").WithArguments("B<D>.M2(D)").WithLocation(3, 1), ]); } [Fact] public void Member_Method_OverrideOfImplementation() { CompileAndVerifyUnsafe( lib: """ public interface I { void M1(); unsafe void M2(); } public class B1 : I { public virtual void M1() { } unsafe public virtual void M2() { } } """, caller: """ var c1 = new C1(); c1.M1(); c1.M2(); B1 b1 = c1; b1.M1(); b1.M2(); I i = b1; i.M1(); i.M2(); class B2 : I { unsafe public virtual void M1() { } public virtual void M2() { } } class C1 : B1 { unsafe public override void M1() { } public override void M2() { } } class C2 : B1, I { unsafe public override void M1() { } public override void M2() { } } """, expectedUnsafeSymbols: ["I.M2", "B1.M2"], expectedSafeSymbols: ["I.M1", "B1.M1"], expectedDiagnostics: [ // (2,1): error CS9362: 'C1.M1()' must be used in an unsafe context because it is marked as 'unsafe' // c1.M1(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c1.M1()").WithArguments("C1.M1()").WithLocation(2, 1), // (6,1): error CS9362: 'B1.M2()' must be used in an unsafe context because it is marked as 'unsafe' // b1.M2(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "b1.M2()").WithArguments("B1.M2()").WithLocation(6, 1), // (9,1): error CS9362: 'I.M2()' must be used in an unsafe context because it is marked as 'unsafe' // i.M2(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "i.M2()").WithArguments("I.M2()").WithLocation(9, 1), // (13,32): error CS9365: Unsafe member 'B2.M1()' cannot implicitly implement safe member 'I.M1()' // unsafe public virtual void M1() { } Diagnostic(ErrorCode.ERR_CallerUnsafeImplicitlyImplementingSafe, "M1").WithArguments("B2.M1()", "I.M1()").WithLocation(13, 32), // (19,33): error CS9364: Unsafe member 'C1.M1()' cannot override safe member 'B1.M1()' // unsafe public override void M1() { } Diagnostic(ErrorCode.ERR_CallerUnsafeOverridingSafe, "M1").WithArguments("C1.M1()", "B1.M1()").WithLocation(19, 33), // (25,33): error CS9364: Unsafe member 'C2.M1()' cannot override safe member 'B1.M1()' // unsafe public override void M1() { } Diagnostic(ErrorCode.ERR_CallerUnsafeOverridingSafe, "M1").WithArguments("C2.M1()", "B1.M1()").WithLocation(25, 33), // (25,33): error CS9365: Unsafe member 'C2.M1()' cannot implicitly implement safe member 'I.M1()' // unsafe public override void M1() { } Diagnostic(ErrorCode.ERR_CallerUnsafeImplicitlyImplementingSafe, "M1").WithArguments("C2.M1()", "I.M1()").WithLocation(25, 33), ], expectedDiagnosticsWhenReferencingLegacyLib: [ // (2,1): error CS9362: 'C1.M1()' must be used in an unsafe context because it is marked as 'unsafe' // c1.M1(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c1.M1()").WithArguments("C1.M1()").WithLocation(2, 1), // (13,32): error CS9365: Unsafe member 'B2.M1()' cannot implicitly implement safe member 'I.M1()' // unsafe public virtual void M1() { } Diagnostic(ErrorCode.ERR_CallerUnsafeImplicitlyImplementingSafe, "M1").WithArguments("B2.M1()", "I.M1()").WithLocation(13, 32), // (19,33): error CS9364: Unsafe member 'C1.M1()' cannot override safe member 'B1.M1()' // unsafe public override void M1() { } Diagnostic(ErrorCode.ERR_CallerUnsafeOverridingSafe, "M1").WithArguments("C1.M1()", "B1.M1()").WithLocation(19, 33), // (25,33): error CS9364: Unsafe member 'C2.M1()' cannot override safe member 'B1.M1()' // unsafe public override void M1() { } Diagnostic(ErrorCode.ERR_CallerUnsafeOverridingSafe, "M1").WithArguments("C2.M1()", "B1.M1()").WithLocation(25, 33), // (25,33): error CS9365: Unsafe member 'C2.M1()' cannot implicitly implement safe member 'I.M1()' // unsafe public override void M1() { } Diagnostic(ErrorCode.ERR_CallerUnsafeImplicitlyImplementingSafe, "M1").WithArguments("C2.M1()", "I.M1()").WithLocation(25, 33), ]); } /// <summary> /// Caller-unsafety should not count as part of the signature for hiding purposes. /// </summary> [Fact] public void Member_Method_Hiding() { DiagnosticDescription[] commonDiagnostics = [ // (7,17): warning CS0108: 'C1.M1()' hides inherited member 'B.M1()'. Use the new keyword if hiding was intended. // public void M1() { } Diagnostic(ErrorCode.WRN_NewRequired, "M1").WithArguments("C1.M1()", "B.M1()").WithLocation(7, 17), // (8,24): warning CS0108: 'C1.M2()' hides inherited member 'B.M2()'. Use the new keyword if hiding was intended. // unsafe public void M2() { } Diagnostic(ErrorCode.WRN_NewRequired, "M2").WithArguments("C1.M2()", "B.M2()").WithLocation(8, 24), // (14,24): error CS0111: Type 'C2' already defines a member called 'M1' with the same parameter types // unsafe public void M1() { } Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "M1").WithArguments("M1", "C2").WithLocation(14, 24), ]; DiagnosticDescription[] updatedCallerDiagnostics = [ // (3,1): error CS9362: 'C1.M2()' must be used in an unsafe context because it is marked as 'unsafe' // c.M2(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c.M2()").WithArguments("C1.M2()").WithLocation(3, 1), .. commonDiagnostics, ]; CompileAndVerifyUnsafe( lib: """ public class B { unsafe public void M1() { } public void M2() { } } """, caller: """ var c = new C1(); c.M1(); c.M2(); class C1 : B { public void M1() { } unsafe public void M2() { } } class C2 { public void M1() { } unsafe public void M1() { } } """, expectedUnsafeSymbols: ["B.M1"], expectedSafeSymbols: ["B.M2"], expectedDiagnostics: updatedCallerDiagnostics, expectedDiagnosticsWhenReferencingLegacyLib: updatedCallerDiagnostics, expectedDiagnosticsForLegacyCaller: commonDiagnostics); } [Fact] public void Member_Await() { static string getLib(string onCompletedAttribute) => $$""" using System; using System.Runtime.CompilerServices; public class C : INotifyCompletion { unsafe public bool IsCompleted => false; unsafe public C GetAwaiter() => this; unsafe public void GetResult() { } {{onCompletedAttribute}} public void OnCompleted(Action continuation) { } } """; CreateCompilation( [getLib("unsafe")], options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // (10,17): error CS9365: Unsafe member 'C.OnCompleted(Action)' cannot implicitly implement safe member 'INotifyCompletion.OnCompleted(Action)' // unsafe public void OnCompleted(Action continuation) { } Diagnostic(ErrorCode.ERR_CallerUnsafeImplicitlyImplementingSafe, "OnCompleted").WithArguments("C.OnCompleted(System.Action)", "System.Runtime.CompilerServices.INotifyCompletion.OnCompleted(System.Action)").WithLocation(10, 17)); var lib = getLib(""); CompileAndVerifyUnsafe( lib: lib, caller: """ await new C(); """, expectedUnsafeSymbols: ["C.IsCompleted", "C.GetAwaiter", "C.GetResult"], expectedSafeSymbols: ["C", "C.OnCompleted"], expectedDiagnostics: [ // (1,1): error CS9362: 'C.GetAwaiter()' must be used in an unsafe context because it is marked as 'unsafe' // await new C(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "await new C()").WithArguments("C.GetAwaiter()").WithLocation(1, 1), // (1,1): error CS9362: 'C.IsCompleted.get' must be used in an unsafe context because it is marked as 'unsafe' // await new C(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "await new C()").WithArguments("C.IsCompleted.get").WithLocation(1, 1), // (1,1): error CS9362: 'C.GetResult()' must be used in an unsafe context because it is marked as 'unsafe' // await new C(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "await new C()").WithArguments("C.GetResult()").WithLocation(1, 1), ]); CreateCompilation( [ lib, """ unsafe { await new C(); } """, ], options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(); } [Fact] public void Member_AsyncHelpers() { var corlib = CreateEmptyCompilation( """ namespace System { public class Object; public class ValueType; public class Attribute; public struct Void; public struct Int32; public struct Boolean; public class AttributeUsageAttribute { public AttributeUsageAttribute(AttributeTargets t) { } public bool AllowMultiple { get; set; } public bool Inherited { get; set; } } public class Enum; public enum AttributeTargets; public class String; public class Action; } namespace System.Diagnostics.CodeAnalysis { public class RequiresUnsafeAttribute : Attribute { } } namespace System.Runtime.CompilerServices { public interface INotifyCompletion; public static class AsyncHelpers { unsafe public static void AwaitAwaiter<TAwaiter>(TAwaiter awaiter) where TAwaiter : INotifyCompletion { } } } namespace System.Threading.Tasks { public class Task : System.Runtime.CompilerServices.INotifyCompletion { public bool IsCompleted => false; public Task GetAwaiter() => this; public void GetResult() { } public void OnCompleted(Action continuation) { } } } """, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics() .EmitToImageReference(); CreateEmptyCompilation(""" using System.Threading.Tasks; class Test { public static async Task F() { await new Task(); } } """, [corlib], parseOptions: WithRuntimeAsync(TestOptions.RegularPreview), options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // (7,15): error CS9362: 'AsyncHelpers.AwaitAwaiter<Task>(Task)' must be used in an unsafe context because it is marked as 'unsafe' // await new Task(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "new Task()").WithArguments("System.Runtime.CompilerServices.AsyncHelpers.AwaitAwaiter<System.Threading.Tasks.Task>(System.Threading.Tasks.Task)").WithLocation(7, 15)); CreateEmptyCompilation(""" using System.Threading.Tasks; class Test { public static async Task F() { unsafe { await new Task(); } } } """, [corlib], parseOptions: WithRuntimeAsync(TestOptions.RegularPreview), options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(); } [Fact] public void Member_CollectionBuilder_Create() { CompileAndVerifyUnsafe( lib: """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(C), nameof(Create))] public class C : IEnumerable<int> { unsafe public static C Create(ReadOnlySpan<int> s) => new C(); public IEnumerator<int> GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; } """, caller: """ C c1 = [1, 2, 3]; M(1, 2, 3); static void M(params C c) { } unsafe { C c2 = [1, 2, 3]; M2(1, 2, 3); static void M2(params C c) { } } M3(1, 2, 3); static unsafe void M3(params C c) { } """, additionalSources: [TestSources.Span, CollectionBuilderAttributeDefinition], verify: Verification.Skipped, expectedUnsafeSymbols: ["C.Create"], expectedSafeSymbols: ["C"], expectedDiagnostics: [ // (1,8): error CS9362: 'C.Create(ReadOnlySpan<int>)' must be used in an unsafe context because it is marked as 'unsafe' // C c1 = [1, 2, 3]; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "[1, 2, 3]").WithArguments("C.Create(System.ReadOnlySpan<int>)").WithLocation(1, 8), // (2,1): error CS9362: 'C.Create(ReadOnlySpan<int>)' must be used in an unsafe context because it is marked as 'unsafe' // M(1, 2, 3); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "M(1, 2, 3)").WithArguments("C.Create(System.ReadOnlySpan<int>)").WithLocation(2, 1), // (12,1): error CS9362: 'C.Create(ReadOnlySpan<int>)' must be used in an unsafe context because it is marked as 'unsafe' // M3(1, 2, 3); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "M3(1, 2, 3)").WithArguments("C.Create(System.ReadOnlySpan<int>)").WithLocation(12, 1), // (12,1): error CS9362: 'M3(params C)' must be used in an unsafe context because it is marked as 'unsafe' // M3(1, 2, 3); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "M3(1, 2, 3)").WithArguments("M3(params C)").WithLocation(12, 1), ], expectedDiagnosticsWhenReferencingLegacyLib: [ // (12,1): error CS9362: 'M3(params C)' must be used in an unsafe context because it is marked as 'unsafe' // M3(1, 2, 3); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "M3(1, 2, 3)").WithArguments("M3(params C)").WithLocation(12, 1), ]); } [Fact] public void Member_CollectionBuilder_GetEnumerator() { CompileAndVerifyUnsafe( lib: """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(C), nameof(Create))] public class C : IEnumerable<int> { public static C Create(ReadOnlySpan<int> s) => new C(); unsafe public IEnumerator<int> GetEnumerator() => null; IEnumerator<int> IEnumerable<int>.GetEnumerator() => null; IEnumerator IEnumerable.GetEnumerator() => null; } """, caller: """ C c1 = [1, 2, 3]; M(1, 2, 3); static void M(params C c) { } """, additionalSources: [TestSources.Span, CollectionBuilderAttributeDefinition], verify: Verification.Skipped, expectedUnsafeSymbols: ["C.GetEnumerator"], expectedSafeSymbols: ["C", "C.Create"], expectedDiagnostics: []); } [Fact] public void Member_CollectionConstructor() { CompileAndVerifyUnsafe( lib: """ using System.Collections; using System.Collections.Generic; public class C : IEnumerable<int> { unsafe public C() { } public void Add(int x) { } public IEnumerator<int> GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; } """, caller: """ C c1 = [1, 2, 3]; M1(1, 2, 3); static void M1(params C c) { } unsafe { C c2 = [1, 2, 3]; M2(1, 2, 3); static void M2(params C c) { } } M3(1, 2, 3); static unsafe void M3(params C c) { } """, expectedUnsafeSymbols: ["C..ctor"], expectedSafeSymbols: ["C"], expectedDiagnostics: [ // (1,8): error CS9362: 'C.C()' must be used in an unsafe context because it is marked as 'unsafe' // C c1 = [1, 2, 3]; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "[1, 2, 3]").WithArguments("C.C()").WithLocation(1, 8), // (2,1): error CS9362: 'C.C()' must be used in an unsafe context because it is marked as 'unsafe' // M1(1, 2, 3); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "M1(1, 2, 3)").WithArguments("C.C()").WithLocation(2, 1), // (12,1): error CS9362: 'C.C()' must be used in an unsafe context because it is marked as 'unsafe' // M3(1, 2, 3); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "M3(1, 2, 3)").WithArguments("C.C()").WithLocation(12, 1), // (12,1): error CS9362: 'M3(params C)' must be used in an unsafe context because it is marked as 'unsafe' // M3(1, 2, 3); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "M3(1, 2, 3)").WithArguments("M3(params C)").WithLocation(12, 1), ], expectedDiagnosticsWhenReferencingLegacyLib: [ // (12,1): error CS9362: 'M3(params C)' must be used in an unsafe context because it is marked as 'unsafe' // M3(1, 2, 3); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "M3(1, 2, 3)").WithArguments("M3(params C)").WithLocation(12, 1), ]); } [Fact] public void Member_CollectionConstructor_With() { CompileAndVerifyUnsafe( lib: """ using System.Collections; using System.Collections.Generic; public class C : IEnumerable<int> { unsafe public C(int i) { } public void Add(int x) { } public IEnumerator<int> GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; } """, caller: """ C c1 = [with(0), 1, 2, 3]; unsafe { C c2 = [with(0), 1, 2, 3]; } """, expectedUnsafeSymbols: ["C..ctor"], expectedSafeSymbols: ["C"], expectedDiagnostics: [ // (1,9): error CS9362: 'C.C(int)' must be used in an unsafe context because it is marked as 'unsafe' // C c1 = [with(0), 1, 2, 3]; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "with(0)").WithArguments("C.C(int)").WithLocation(1, 9), ]); } [Fact] public void Member_CollectionAddMethod() { CompileAndVerifyUnsafe( lib: """ using System.Collections; using System.Collections.Generic; public class C : IEnumerable<int> { unsafe public void Add(int x) { } public IEnumerator<int> GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; } """, caller: """ C c1 = [1, 2, 3]; C c2 = new() { 1, 2, 3 }; C c3 = [.. new C()]; X x1 = new() { F = { 1, 2, 3 } }; M1(1, 2, 3); static void M1(params C c) { } unsafe { C c4 = [1, 2, 3]; C c5 = new() { 1, 2, 3 }; C c6 = [.. new C()]; X x2 = new() { F = { 1, 2, 3 } }; M2(1, 2, 3); static void M2(params C c) { } } M3(1, 2, 3); static unsafe void M3(params C c) { } class X { public C F; } """, expectedUnsafeSymbols: ["C.Add"], expectedSafeSymbols: ["C"], expectedDiagnostics: [ // (1,9): error CS9362: 'C.Add(int)' must be used in an unsafe context because it is marked as 'unsafe' // C c1 = [1, 2, 3]; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "1").WithArguments("C.Add(int)").WithLocation(1, 9), // (1,12): error CS9362: 'C.Add(int)' must be used in an unsafe context because it is marked as 'unsafe' // C c1 = [1, 2, 3]; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "2").WithArguments("C.Add(int)").WithLocation(1, 12), // (1,15): error CS9362: 'C.Add(int)' must be used in an unsafe context because it is marked as 'unsafe' // C c1 = [1, 2, 3]; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "3").WithArguments("C.Add(int)").WithLocation(1, 15), // (2,16): error CS9362: 'C.Add(int)' must be used in an unsafe context because it is marked as 'unsafe' // C c2 = new() { 1, 2, 3 }; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "1").WithArguments("C.Add(int)").WithLocation(2, 16), // (2,19): error CS9362: 'C.Add(int)' must be used in an unsafe context because it is marked as 'unsafe' // C c2 = new() { 1, 2, 3 }; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "2").WithArguments("C.Add(int)").WithLocation(2, 19), // (2,22): error CS9362: 'C.Add(int)' must be used in an unsafe context because it is marked as 'unsafe' // C c2 = new() { 1, 2, 3 }; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "3").WithArguments("C.Add(int)").WithLocation(2, 22), // (3,12): error CS9362: 'C.Add(int)' must be used in an unsafe context because it is marked as 'unsafe' // C c3 = [.. new C()]; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "new C()").WithArguments("C.Add(int)").WithLocation(3, 12), // (4,22): error CS9362: 'C.Add(int)' must be used in an unsafe context because it is marked as 'unsafe' // X x1 = new() { F = { 1, 2, 3 } }; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "1").WithArguments("C.Add(int)").WithLocation(4, 22), // (4,25): error CS9362: 'C.Add(int)' must be used in an unsafe context because it is marked as 'unsafe' // X x1 = new() { F = { 1, 2, 3 } }; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "2").WithArguments("C.Add(int)").WithLocation(4, 25), // (4,28): error CS9362: 'C.Add(int)' must be used in an unsafe context because it is marked as 'unsafe' // X x1 = new() { F = { 1, 2, 3 } }; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "3").WithArguments("C.Add(int)").WithLocation(4, 28), // (5,4): error CS9362: 'C.Add(int)' must be used in an unsafe context because it is marked as 'unsafe' // M1(1, 2, 3); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "1").WithArguments("C.Add(int)").WithLocation(5, 4), // (5,7): error CS9362: 'C.Add(int)' must be used in an unsafe context because it is marked as 'unsafe' // M1(1, 2, 3); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "2").WithArguments("C.Add(int)").WithLocation(5, 7), // (5,10): error CS9362: 'C.Add(int)' must be used in an unsafe context because it is marked as 'unsafe' // M1(1, 2, 3); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "3").WithArguments("C.Add(int)").WithLocation(5, 10), // (18,1): error CS9362: 'M3(params C)' must be used in an unsafe context because it is marked as 'unsafe' // M3(1, 2, 3); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "M3(1, 2, 3)").WithArguments("M3(params C)").WithLocation(18, 1), // (18,4): error CS9362: 'C.Add(int)' must be used in an unsafe context because it is marked as 'unsafe' // M3(1, 2, 3); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "1").WithArguments("C.Add(int)").WithLocation(18, 4), // (18,7): error CS9362: 'C.Add(int)' must be used in an unsafe context because it is marked as 'unsafe' // M3(1, 2, 3); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "2").WithArguments("C.Add(int)").WithLocation(18, 7), // (18,10): error CS9362: 'C.Add(int)' must be used in an unsafe context because it is marked as 'unsafe' // M3(1, 2, 3); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "3").WithArguments("C.Add(int)").WithLocation(18, 10), ], expectedDiagnosticsWhenReferencingLegacyLib: [ // (18,1): error CS9362: 'M3(params C)' must be used in an unsafe context because it is marked as 'unsafe' // M3(1, 2, 3); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "M3(1, 2, 3)").WithArguments("M3(params C)").WithLocation(18, 1), ]); } [Fact] public void Member_GetEnumerator() { CompileAndVerifyUnsafe( lib: """ using System.Collections; using System.Collections.Generic; public class C : IEnumerable<int> { unsafe public IEnumerator<int> GetEnumerator() => null; IEnumerator<int> IEnumerable<int>.GetEnumerator() => null; IEnumerator IEnumerable.GetEnumerator() => null; } """, caller: """ foreach (var c in new C()) { } unsafe { foreach (var c in new C()) { } } """, expectedUnsafeSymbols: ["C.GetEnumerator"], expectedSafeSymbols: ["C"], expectedDiagnostics: [ // (1,1): error CS9362: 'C.GetEnumerator()' must be used in an unsafe context because it is marked as 'unsafe' // foreach (var c in new C()) { } Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "foreach").WithArguments("C.GetEnumerator()").WithLocation(1, 1), ]); } [Fact] public void Member_GetEnumerator_Spread() { CompileAndVerifyUnsafe( lib: """ using System.Collections; using System.Collections.Generic; public class C : IEnumerable<int> { public void Add(int x) { } unsafe public C GetEnumerator() => this; unsafe public bool MoveNext() => false; unsafe public int Current => 0; IEnumerator<int> IEnumerable<int>.GetEnumerator() => null; IEnumerator IEnumerable.GetEnumerator() => null; } """, caller: """ C c1 = [.. new C()]; unsafe { C c2 = [.. new C()]; } """, expectedUnsafeSymbols: ["C.GetEnumerator", "C.MoveNext", "C.Current", "C.get_Current"], expectedSafeSymbols: ["C"], expectedDiagnostics: [ // (1,9): error CS9362: 'C.GetEnumerator()' must be used in an unsafe context because it is marked as 'unsafe' // C c1 = [.. new C()]; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "..").WithArguments("C.GetEnumerator()").WithLocation(1, 9), // (1,9): error CS9362: 'C.MoveNext()' must be used in an unsafe context because it is marked as 'unsafe' // C c1 = [.. new C()]; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "..").WithArguments("C.MoveNext()").WithLocation(1, 9), // (1,9): error CS9362: 'C.Current.get' must be used in an unsafe context because it is marked as 'unsafe' // C c1 = [.. new C()]; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "..").WithArguments("C.Current.get").WithLocation(1, 9), ]); } [Fact] public void Member_MoveNext() { CompileAndVerifyUnsafe( lib: """ public class C { public C GetEnumerator() => this; unsafe public bool MoveNext() => false; public int Current => 0; } """, caller: """ foreach (var x in new C()) { } unsafe { foreach (var x in new C()) { } } """, expectedUnsafeSymbols: ["C.MoveNext"], expectedSafeSymbols: ["C"], expectedDiagnostics: [ // (1,1): error CS9362: 'C.MoveNext()' must be used in an unsafe context because it is marked as 'unsafe' // foreach (var x in new C()) { } Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "foreach").WithArguments("C.MoveNext()").WithLocation(1, 1), ]); } [Theory] [InlineData("=> 0;")] [InlineData("{ get => 0; }")] public void Member_Current_Property(string getter) { CompileAndVerifyUnsafe( lib: $$""" public class C { public C GetEnumerator() => this; public bool MoveNext() => false; unsafe public int Current {{getter}} } """, caller: """ foreach (var x in new C()) { } unsafe { foreach (var x in new C()) { } } """, expectedUnsafeSymbols: ["C.Current", "C.get_Current"], expectedSafeSymbols: ["C"], expectedDiagnostics: [ // (1,1): error CS9362: 'C.Current.get' must be used in an unsafe context because it is marked as 'unsafe' // foreach (var x in new C()) { } Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "foreach").WithArguments("C.Current.get").WithLocation(1, 1), ]); } [Fact] public void Member_Current_Getter() { CreateCompilation(""" public class C { public C GetEnumerator() => this; public bool MoveNext() => false; public int Current { unsafe get => 0; } } """, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // (5,16): error CS9397: Cannot specify the same 'unsafe' or 'safe' modifier on all accessors of property or indexer 'C.Current'. Instead, put that modifier on the property itself. // public int Current { unsafe get => 0; } Diagnostic(ErrorCode.ERR_SamePropertyUnsafeAccessorMods, "Current").WithArguments("C.Current").WithLocation(5, 16)); } [Fact] public void Member_Current_Setter() { CompileAndVerifyUnsafe( lib: """ public class C { public C GetEnumerator() => this; public bool MoveNext() => false; public int Current { get => 0; unsafe set { } } } """, caller: """ foreach (var x in new C()) { } unsafe { foreach (var x in new C()) { } } """, expectedUnsafeSymbols: ["C.set_Current"], expectedSafeSymbols: ["C", "C.Current", "C.get_Current"], expectedDiagnostics: []); } [Fact] public void Member_Dispose_Class() { CreateCompilation([""" public class C : System.IDisposable { public C GetEnumerator() => this; public bool MoveNext() => false; public int Current => 0; unsafe public void Dispose() { } } """], options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // (6,24): error CS9365: Unsafe member 'C.Dispose()' cannot implicitly implement safe member 'IDisposable.Dispose()' // unsafe public void Dispose() { } Diagnostic(ErrorCode.ERR_CallerUnsafeImplicitlyImplementingSafe, "Dispose").WithArguments("C.Dispose()", "System.IDisposable.Dispose()").WithLocation(6, 24)); } [Fact] public void Member_Dispose_Interface() { CompileAndVerifyUnsafe( lib: """ public class C : System.IDisposable { public C GetEnumerator() => this; public bool MoveNext() => false; public int Current => 0; public void Dispose() { } } namespace System { public class Object; public class ValueType; public class Attribute; public struct Void; public struct Int32; public struct Boolean; public class AttributeUsageAttribute { public AttributeUsageAttribute(AttributeTargets t) { } public bool AllowMultiple { get; set; } public bool Inherited { get; set; } } public class String; public class Enum; public enum AttributeTargets; public interface IDisposable { unsafe void Dispose(); } } namespace System.Diagnostics.CodeAnalysis { public class RequiresUnsafeAttribute : Attribute; } namespace System.Runtime.CompilerServices { public class CompilerGeneratedAttribute; } namespace System.Collections { public interface IEnumerable; } namespace System.Collections.Generic { public interface IEnumerable<T> : IEnumerable; public class List<T> : IEnumerable<T> { public void Add(T element) { } } } """, caller: """ foreach (var x in new C()) { } using (var c = new C()) { } System.Collections.Generic.List<int> l = [.. new C()]; unsafe { foreach (var x in new C()) { } } unsafe { using (var c = new C()) { } } unsafe { System.Collections.Generic.List<int> l2 = [.. new C()]; } """, targetFramework: TargetFramework.Empty, optionsDll: TestOptions.UnsafeDebugDll // warning CS8021: No value for RuntimeMetadataVersion found .WithSpecificDiagnosticOptions(GetIdForErrorCode(ErrorCode.WRN_NoRuntimeMetadataVersion), ReportDiagnostic.Suppress), expectedUnsafeSymbols: ["System.IDisposable.Dispose"], expectedSafeSymbols: ["C", "C.Dispose", "System.IDisposable"], verify: Verification.FailsPEVerify, expectedRequiresUnsafeAttribute: AttributeDefinition.FromSource, expectedDiagnostics: [ // (1,1): error CS9362: 'IDisposable.Dispose()' must be used in an unsafe context because it is marked as 'unsafe' // foreach (var x in new C()) { } Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "foreach").WithArguments("System.IDisposable.Dispose()").WithLocation(1, 1), // (2,8): error CS9362: 'IDisposable.Dispose()' must be used in an unsafe context because it is marked as 'unsafe' // using (var c = new C()) { } Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "var c = new C()").WithArguments("System.IDisposable.Dispose()").WithLocation(2, 8), // (3,43): error CS9362: 'IDisposable.Dispose()' must be used in an unsafe context because it is marked as 'unsafe' // System.Collections.Generic.List<int> l = [.. new C()]; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "..").WithArguments("System.IDisposable.Dispose()").WithLocation(3, 43), ]); } [Fact] public void Member_Dispose_RefStruct() { CompileAndVerifyUnsafe( lib: """ public ref struct S { public S GetEnumerator() => this; public bool MoveNext() => false; public int Current => 0; unsafe public void Dispose() { } } """, caller: """ foreach (var x in new S()) { } using (var s = new S()) { } unsafe { foreach (var y in new S()) { } } unsafe { using (var s = new S()) { } } """, verify: Verification.Skipped, expectedUnsafeSymbols: ["S.Dispose"], expectedSafeSymbols: ["S"], expectedDiagnostics: [ // (1,1): error CS9362: 'S.Dispose()' must be used in an unsafe context because it is marked as 'unsafe' // foreach (var x in new S()) { } Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "foreach (var x in new S()) { }").WithArguments("S.Dispose()").WithLocation(1, 1), // (2,8): error CS9362: 'S.Dispose()' must be used in an unsafe context because it is marked as 'unsafe' // using (var s = new S()) { } Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "var s = new S()").WithArguments("S.Dispose()").WithLocation(2, 8), ]); } [Fact] public void Member_DisposeAsync() { CompileAndVerifyUnsafe( lib: """ using System.Threading.Tasks; public class C { public C GetAsyncEnumerator() => this; public Task<bool> MoveNextAsync() => null; public int Current => 0; unsafe public Task DisposeAsync() => null; } """, caller: """ await foreach (var x in new C()) { } await using (var c = new C()) { } """, expectedUnsafeSymbols: ["C.DisposeAsync"], expectedSafeSymbols: ["C"], expectedDiagnostics: [ // (1,1): error CS9362: 'C.DisposeAsync()' must be used in an unsafe context because it is marked as 'unsafe' // await foreach (var x in new C()) { } Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "await foreach (var x in new C()) { }").WithArguments("C.DisposeAsync()").WithLocation(1, 1), // (2,14): error CS9362: 'C.DisposeAsync()' must be used in an unsafe context because it is marked as 'unsafe' // await using (var c = new C()) { } Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "var c = new C()").WithArguments("C.DisposeAsync()").WithLocation(2, 14), ]); } [Fact] public void Member_GetPinnableReference() { CompileAndVerifyUnsafe( lib: """ public class C1 { public ref int GetPinnableReference() => throw null; } public class C2 { unsafe public ref int GetPinnableReference() => throw null; } """, caller: """ fixed (int* p = new C1()) { } fixed (int* p = new C2()) { } unsafe { fixed (int* p = new C2()) { } } """, expectedUnsafeSymbols: ["C2.GetPinnableReference"], expectedSafeSymbols: ["C1", "C1.GetPinnableReference", "C2"], expectedDiagnostics: [ // (2,17): error CS9362: 'C2.GetPinnableReference()' must be used in an unsafe context because it is marked as 'unsafe' // fixed (int* p = new C2()) { } Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "new C2()").WithArguments("C2.GetPinnableReference()").WithLocation(2, 17), ], expectedDiagnosticsWithOldLangVersion: [ // (1,1): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // fixed (int* p = new C1()) { } Diagnostic(ErrorCode.ERR_UnsafeNeeded, "fixed (int* p = new C1()) { }").WithLocation(1, 1), // (1,8): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // fixed (int* p = new C1()) { } Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(1, 8), // (2,1): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // fixed (int* p = new C2()) { } Diagnostic(ErrorCode.ERR_UnsafeNeeded, "fixed (int* p = new C2()) { }").WithLocation(2, 1), // (2,8): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // fixed (int* p = new C2()) { } Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(2, 8), ]); } [Fact] public void Member_Deconstruct() { CompileAndVerifyUnsafe( lib: """ public class C { unsafe public void Deconstruct(out int x, out int y) { x = y = 0; } } """, caller: """ var (x, y) = new C(); unsafe { var (a, b) = new C(); } """, expectedUnsafeSymbols: ["C.Deconstruct"], expectedSafeSymbols: ["C"], expectedDiagnostics: [ // (1,14): error CS9362: 'C.Deconstruct(out int, out int)' must be used in an unsafe context because it is marked as 'unsafe' // var (x, y) = new C(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "new C()").WithArguments("C.Deconstruct(out int, out int)").WithLocation(1, 14), ]); } [Fact] public void Member_LockObject() { CompileAndVerifyUnsafe( lib: """ namespace System.Threading; public class Lock { unsafe public Scope EnterScope() => new(); public ref struct Scope { unsafe public void Dispose() { } } } """, caller: """ lock (new System.Threading.Lock()) { } unsafe { lock (new System.Threading.Lock()) { } } """, verify: Verification.Skipped, expectedUnsafeSymbols: ["System.Threading.Lock.EnterScope", "System.Threading.Lock.Scope.Dispose"], expectedSafeSymbols: ["System.Threading.Lock", "System.Threading.Lock.Scope"], expectedDiagnostics: [ // (1,7): error CS9362: 'Lock.EnterScope()' must be used in an unsafe context because it is marked as 'unsafe' // lock (new System.Threading.Lock()) { } Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "new System.Threading.Lock()").WithArguments("System.Threading.Lock.EnterScope()").WithLocation(1, 7), // (1,7): error CS9362: 'Lock.Scope.Dispose()' must be used in an unsafe context because it is marked as 'unsafe' // lock (new System.Threading.Lock()) { } Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "new System.Threading.Lock()").WithArguments("System.Threading.Lock.Scope.Dispose()").WithLocation(1, 7), ]); } [Fact] public void Member_ITuple() { CompileAndVerifyUnsafe( lib: """ namespace System.Runtime.CompilerServices; public interface ITuple { unsafe int Length { get; } unsafe object this[int index] { get; } } """, caller: """ object o = null; _ = o is (int x, string y); unsafe { _ = o is (int a, string b); } """, expectedUnsafeSymbols: ["System.Runtime.CompilerServices.ITuple.Length", "System.Runtime.CompilerServices.ITuple.get_Length", "System.Runtime.CompilerServices.ITuple.this[]", "System.Runtime.CompilerServices.ITuple.get_Item"], expectedSafeSymbols: ["System.Runtime.CompilerServices.ITuple"], expectedDiagnostics: [ // (2,10): error CS9362: 'ITuple.Length.get' must be used in an unsafe context because it is marked as 'unsafe' // _ = o is (int x, string y); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "(int x, string y)").WithArguments("System.Runtime.CompilerServices.ITuple.Length.get").WithLocation(2, 10), // (2,10): error CS9362: 'ITuple.this[int].get' must be used in an unsafe context because it is marked as 'unsafe' // _ = o is (int x, string y); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "(int x, string y)").WithArguments("System.Runtime.CompilerServices.ITuple.this[int].get").WithLocation(2, 10), ]); } [Fact] public void Member_InterpolatedStringHandler() { CompileAndVerifyUnsafe( lib: """ [System.Runtime.CompilerServices.InterpolatedStringHandler] public struct C { unsafe public C(int literalLength, int formattedCount) { } unsafe public void AppendLiteral(string s) { } unsafe public void AppendFormatted<T>(T t) { } } """, caller: """ log($"a{0}"); unsafe { log($"a{0}"); }; void log(C c) { } """, additionalSources: [InterpolatedStringHandlerAttribute], expectedUnsafeSymbols: [Overload("C..ctor", parameterCount: 2), "C.AppendLiteral", "C.AppendFormatted"], expectedSafeSymbols: ["C", Overload("C..ctor", parameterCount: 0)], expectedDiagnostics: [ // (1,7): error CS9362: 'C.AppendLiteral(string)' must be used in an unsafe context because it is marked as 'unsafe' // log($"a{0}"); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "a").WithArguments("C.AppendLiteral(string)").WithLocation(1, 7), // (1,8): error CS9362: 'C.AppendFormatted<int>(int)' must be used in an unsafe context because it is marked as 'unsafe' // log($"a{0}"); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "{0}").WithArguments("C.AppendFormatted<int>(int)").WithLocation(1, 8), // (1,5): error CS9362: 'C.C(int, int)' must be used in an unsafe context because it is marked as 'unsafe' // log($"a{0}"); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, @"$""a{0}""").WithArguments("C.C(int, int)").WithLocation(1, 5), ]); } [Theory, CombinatorialData] public void Member_Interceptor( [CombinatorialValues("unsafe", "")] string unsafe1, [CombinatorialValues("unsafe", "")] string unsafe2) { var source = ($$""" C.M(); class C { {{unsafe1}} public static void M() { } } """, "Program.cs"); var comp = CreateCompilation(source); var tree = comp.SyntaxTrees.Single(); var model = comp.GetSemanticModel(tree); var interceptableLocation = tree.GetRoot().DescendantNodes().OfType<InvocationExpressionSyntax>().Select(node => model.GetInterceptableLocation(node)).Single()!; var interceptor = ($$""" class D { {{interceptableLocation.GetInterceptsLocationAttributeSyntax()}} {{unsafe2}} public static void M() => System.Console.Write(1); } """, "Interceptor.cs"); CreateCompilation([source, interceptor, (TestSources.InterceptsLocationAttribute, "Attribute.cs")], parseOptions: TestOptions.RegularPreview.WithFeature(Feature.InterceptorsNamespaces, "global"), options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(unsafe1.Length > 0 ? [ // Program.cs(1,1): error CS9362: 'C.M()' must be used in an unsafe context because it is marked as 'unsafe' // C.M(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "C.M()").WithArguments("C.M()").WithLocation(1, 1), ] : []); } [Fact] public void Member_Iterator() { var lib = """ using System.Collections.Generic; public static class C { unsafe public static IEnumerable<int> M() { yield return 1; } } """; CompileAndVerifyUnsafe( lib: lib, caller: """ foreach (var x in C.M()) { } unsafe { foreach (var y in C.M()) { } } """, expectedUnsafeSymbols: ["C.M"], expectedSafeSymbols: ["C"], expectedDiagnostics: [ // (1,19): error CS9362: 'C.M()' must be used in an unsafe context because it is marked as 'unsafe' // foreach (var x in C.M()) { } Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "C.M()").WithArguments("C.M()").WithLocation(1, 19), ]); // Test symbols that are only in PE image (hence cannot test them via the helper above). var libRef = CreateCompilation( [lib], options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules().WithMetadataImportOptions(MetadataImportOptions.All)) .EmitToImageReference(); var libAssemblySymbol = CreateCompilation("", [libRef]).GetReferencedAssemblySymbol(libRef); VerifyRequiresUnsafeAttribute( libAssemblySymbol.Modules.Single(), expectedUnsafeSymbols: ["C.M"], expectedSafeSymbols: ["C", "C.<M>d__0", "C.<M>d__0.MoveNext"], expectedDefinition: AttributeDefinition.Synthesized); } [Fact] public void Member_LocalFunction() { var source = """ M1(); M2(); unsafe { M1(); } unsafe static void M1() { } static void M2() { } """; CreateCompilation([source], options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // (1,1): error CS9362: 'M1()' must be used in an unsafe context because it is marked as 'unsafe' // M1(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "M1()").WithArguments("M1()").WithLocation(1, 1)); } [Fact] public void Member_Lambda() { var source = """ System.Action lam = unsafe () => { }; lam(); """; CreateCompilation(source, options: TestOptions.UnsafeReleaseExe) .VerifyDiagnostics( // (1,29): error CS1525: Invalid expression term ')' // System.Action lam = unsafe () => { }; Diagnostic(ErrorCode.ERR_InvalidExprTerm, ")").WithArguments(")").WithLocation(1, 29), // (1,31): error CS1003: Syntax error, ',' expected // System.Action lam = unsafe () => { }; Diagnostic(ErrorCode.ERR_SyntaxError, "=>").WithArguments(",").WithLocation(1, 31), // (1,36): error CS1002: ; expected // System.Action lam = unsafe () => { }; Diagnostic(ErrorCode.ERR_SemicolonExpected, "}").WithLocation(1, 36), // (1,36): error CS1022: Type or namespace definition, or end-of-file expected // System.Action lam = unsafe () => { }; Diagnostic(ErrorCode.ERR_EOFExpected, "}").WithLocation(1, 36)); } [Fact] public void Member_Lambda_InUnsafeContext() { var source = """ unsafe { D lam = () => { }; } delegate void D(); """; var metadata = CreateCompilation(source, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics() .EmitToImageReference(); var assemblySymbol = CreateCompilation("", [metadata], options: TestOptions.ReleaseDll.WithMetadataImportOptions(MetadataImportOptions.All)) .GetReferencedAssemblySymbol(metadata); VerifyRequiresUnsafeAttribute( assemblySymbol.Modules.Single(), expectedUnsafeSymbols: [], expectedSafeSymbols: [ "Program", "Program.<Main>$", "Program.<>c", "Program.<>c.<<Main>$>b__0_0", "D", "D..ctor", "D.Invoke", "D.BeginInvoke", "D.EndInvoke", ], expectedDefinition: AttributeDefinition.Synthesized); } [Fact] public void Member_Property() { CompileAndVerifyUnsafe( lib: """ public class C { public int P1 { get; set; } unsafe public int P2 { get; set; } } """, caller: """ var c = new C(); c.P1 = c.P1 + 123; c.P2 = c.P2 + 123; unsafe { c.P2 = c.P2 + 123; } """, optionsDll: TestOptions.UnsafeReleaseDll.WithMetadataImportOptions(MetadataImportOptions.All), expectedUnsafeSymbols: ["C.P2", "C.get_P2", "C.set_P2"], expectedSafeSymbols: ["C.P1", "C.get_P1", "C.set_P1", "C.<P1>k__BackingField", "C.<P2>k__BackingField"], expectedDiagnostics: [ // (3,1): error CS9362: 'C.P2.set' must be used in an unsafe context because it is marked as 'unsafe' // c.P2 = c.P2 + 123; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c.P2").WithArguments("C.P2.set").WithLocation(3, 1), // (3,8): error CS9362: 'C.P2.get' must be used in an unsafe context because it is marked as 'unsafe' // c.P2 = c.P2 + 123; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c.P2").WithArguments("C.P2.get").WithLocation(3, 8) ]); } [Fact] public void Member_Property_CompoundAssignment() { CompileAndVerifyUnsafe( lib: """ public class C { unsafe public int P { get; set; } } """, caller: """ var c = new C(); c.P += 123; unsafe { c.P += 123; } """, expectedUnsafeSymbols: ["C.P", "C.get_P", "C.set_P"], expectedSafeSymbols: ["C"], expectedDiagnostics: [ // (2,1): error CS9362: 'C.P.set' must be used in an unsafe context because it is marked as 'unsafe' // c.P += 123; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c.P").WithArguments("C.P.set").WithLocation(2, 1), // (2,1): error CS9362: 'C.P.get' must be used in an unsafe context because it is marked as 'unsafe' // c.P += 123; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c.P").WithArguments("C.P.get").WithLocation(2, 1), ]); } [Fact] public void Member_Property_ObjectInitializer() { CompileAndVerifyUnsafe( lib: """ public class C { unsafe public C P1 { get; set; } public C P2 { unsafe get; set; } public C P3 { get; unsafe set; } public C P4 { get; set; } } """, caller: """ _ = new C { P1 = null, P2 = null, P3 = null, P4 = null, }; _ = new C { P1 = { }, P2 = { }, P3 = { }, P4 = { }, }; unsafe { _ = new C { P1 = null, P2 = null, P3 = null, P4 = null }; } unsafe { _ = new C { P1 = { }, P2 = { }, P3 = { }, P4 = { } }; } """, expectedUnsafeSymbols: ["C.P1", "C.get_P1", "C.set_P1", "C.get_P2", "C.set_P3"], expectedSafeSymbols: ["C", "C.P2", "C.set_P2", "C.P3", "C.get_P3", "C.P4", "C.get_P4", "C.set_P4"], expectedDiagnostics: [ // (3,5): error CS9362: 'C.P1.set' must be used in an unsafe context because it is marked as 'unsafe' // P1 = null, Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "P1").WithArguments("C.P1.set").WithLocation(3, 5), // (5,5): error CS9362: 'C.P3.set' must be used in an unsafe context because it is marked as 'unsafe' // P3 = null, Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "P3").WithArguments("C.P3.set").WithLocation(5, 5), // (10,5): error CS9362: 'C.P1.get' must be used in an unsafe context because it is marked as 'unsafe' // P1 = { }, Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "P1").WithArguments("C.P1.get").WithLocation(10, 5), // (11,5): error CS9362: 'C.P2.get' must be used in an unsafe context because it is marked as 'unsafe' // P2 = { }, Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "P2").WithArguments("C.P2.get").WithLocation(11, 5), ]); } [Fact] public void Member_Property_Pattern() { CompileAndVerifyUnsafe( lib: """ public class C { unsafe public int this[int i] => 0; unsafe public int Length => 0; } """, caller: """ var c = new C(); _ = c is { Length: 0 }; _ = c is []; unsafe { _ = c is { Length: 0 }; } unsafe { _ = c is []; } """, additionalSources: [TestSources.Index], expectedUnsafeSymbols: ["C.this[]", "C.get_Item", "C.Length", "C.get_Length"], expectedSafeSymbols: ["C"], expectedDiagnostics: [ // (2,12): error CS9362: 'C.Length.get' must be used in an unsafe context because it is marked as 'unsafe' // _ = c is { Length: 0 }; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "Length:").WithArguments("C.Length.get").WithLocation(2, 12), // (3,10): error CS9362: 'C.Length.get' must be used in an unsafe context because it is marked as 'unsafe' // _ = c is []; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "[]").WithArguments("C.Length.get").WithLocation(3, 10), // (3,10): error CS9362: 'C.Length.get' must be used in an unsafe context because it is marked as 'unsafe' // _ = c is []; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "[]").WithArguments("C.Length.get").WithLocation(3, 10), // (3,10): error CS9362: 'C.this[int].get' must be used in an unsafe context because it is marked as 'unsafe' // _ = c is []; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "[]").WithArguments("C.this[int].get").WithLocation(3, 10), ]); } [Fact] public void Member_Property_Extension() { CompileAndVerifyUnsafe( lib: """ public static class E { extension(int x) { public int P1 { get => x; set { } } unsafe public int P2 { get => x; set { } } } } """, caller: """ var x = 111; x.P1 = x.P1 + 222; x.P2 = x.P2 + 333; E.get_P1(x); E.set_P1(x, 0); E.get_P2(x); E.set_P2(x, 0); unsafe { x.P2 = x.P2 + 444; } unsafe { E.get_P2(x); E.set_P2(x, 0); } """, expectedUnsafeSymbols: [ExtensionMember("E", "P2"), "E.get_P2", ExtensionMember("E", "get_P2"), "E.set_P2", ExtensionMember("E", "set_P2")], expectedSafeSymbols: [ExtensionMember("E", "P1"), "E.get_P1", ExtensionMember("E", "get_P1"), "E.set_P1", ExtensionMember("E", "set_P1")], expectedDiagnostics: [ // (3,1): error CS9362: 'E.extension(int).P2.set' must be used in an unsafe context because it is marked as 'unsafe' // x.P2 = x.P2 + 333; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "x.P2").WithArguments("E.extension(int).P2.set").WithLocation(3, 1), // (3,8): error CS9362: 'E.extension(int).P2.get' must be used in an unsafe context because it is marked as 'unsafe' // x.P2 = x.P2 + 333; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "x.P2").WithArguments("E.extension(int).P2.get").WithLocation(3, 8), // (5,1): error CS9362: 'E.get_P2(int)' must be used in an unsafe context because it is marked as 'unsafe' // E.get_P2(x); E.set_P2(x, 0); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "E.get_P2(x)").WithArguments("E.get_P2(int)").WithLocation(5, 1), // (5,14): error CS9362: 'E.set_P2(int, int)' must be used in an unsafe context because it is marked as 'unsafe' // E.get_P2(x); E.set_P2(x, 0); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "E.set_P2(x, 0)").WithArguments("E.set_P2(int, int)").WithLocation(5, 14), ]); } [Fact] public void Member_Property_Record() { CompileAndVerifyUnsafe( lib: """ public record C(int P1, int P2) { unsafe public int P2 { get; set; } = P2; } """, caller: """ var c = new C(1, 2); c.P2 = c.P1 + c.P2; unsafe { c.P2 = c.P1 + c.P2; } """, additionalSources: [IsExternalInitTypeDefinition], verify: Verification.Skipped, expectedUnsafeSymbols: ["C.P2", "C.get_P2", "C.set_P2"], expectedSafeSymbols: ["C.P1", "C.get_P1", "C.set_P1"], expectedDiagnostics: [ // (2,1): error CS9362: 'C.P2.set' must be used in an unsafe context because it is marked as 'unsafe' // c.P2 = c.P1 + c.P2; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c.P2").WithArguments("C.P2.set").WithLocation(2, 1), // (2,15): error CS9362: 'C.P2.get' must be used in an unsafe context because it is marked as 'unsafe' // c.P2 = c.P1 + c.P2; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c.P2").WithArguments("C.P2.get").WithLocation(2, 15), ]); } [Fact] public void Member_Property_Accessors() { var lib = """ public class C { public int P1 { unsafe get; set; } public int P2 { get; unsafe set; } } """; CompileAndVerifyUnsafe( lib: lib, caller: """ var c = new C(); c.P1 = c.P1 + 123; c.P2 = c.P2 + 123; unsafe { c.P1 = c.P1 + 123; } unsafe { c.P2 = c.P2 + 123; } """, expectedUnsafeSymbols: ["C.get_P1", "C.set_P2"], expectedSafeSymbols: ["C.P1", "C.P2", "C.get_P2", "C.set_P1"], expectedDiagnostics: [ // (2,8): error CS9362: 'C.P1.get' must be used in an unsafe context because it is marked as 'unsafe' // c.P1 = c.P1 + 123; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c.P1").WithArguments("C.P1.get").WithLocation(2, 8), // (3,1): error CS9362: 'C.P2.set' must be used in an unsafe context because it is marked as 'unsafe' // c.P2 = c.P2 + 123; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c.P2").WithArguments("C.P2.set").WithLocation(3, 1), ]); CreateCompilation([lib], parseOptions: TestOptions.Regular14).VerifyEmitDiagnostics( // (3,21): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // public int P1 { unsafe get; set; } Diagnostic(ErrorCode.ERR_FeatureInPreview, "unsafe").WithArguments("updated memory safety rules").WithLocation(3, 21), // (4,26): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // public int P2 { get; unsafe set; } Diagnostic(ErrorCode.ERR_FeatureInPreview, "unsafe").WithArguments("updated memory safety rules").WithLocation(4, 26)); CreateCompilation([lib], parseOptions: TestOptions.RegularNext).VerifyEmitDiagnostics(); CreateCompilation([lib], parseOptions: TestOptions.RegularPreview).VerifyEmitDiagnostics(); } [Fact] public void Member_Property_Accessors_UnsafeKeyword() { CreateCompilation(""" public class C { public int P1 { unsafe get { int* p = null; return *p; } set { long* q = null; *q = value; } } public int P2 { get { long* p = null; return (int)*p; } unsafe set { int* q = null; *q = value; } } } """, options: TestOptions.UnsafeReleaseDll) .VerifyDiagnostics( // (3,84): error CS9360: This operation may only be used in an unsafe context // public int P1 { unsafe get { int* p = null; return *p; } set { long* q = null; *q = value; } } Diagnostic(ErrorCode.ERR_UnsafeOperation, "*").WithLocation(3, 84), // (4,55): error CS9360: This operation may only be used in an unsafe context // public int P2 { get { long* p = null; return (int)*p; } unsafe set { int* q = null; *q = value; } } Diagnostic(ErrorCode.ERR_UnsafeOperation, "*").WithLocation(4, 55)); } [Fact] public void Member_Property_Accessors_UnsafeKeyword_Partial() { CreateCompilation(""" public partial class C { public partial int P1 { unsafe get; set; } public partial int P1 { unsafe get { int* p = null; return *p; } set { long* q = null; *q = value; } } public partial int P2 { get; unsafe set; } public partial int P2 { get { long* p = null; return (int)*p; } unsafe set { int* q = null; *q = value; } } } """, options: TestOptions.UnsafeReleaseDll) .VerifyDiagnostics( // (4,92): error CS9360: This operation may only be used in an unsafe context // public partial int P1 { unsafe get { int* p = null; return *p; } set { long* q = null; *q = value; } } Diagnostic(ErrorCode.ERR_UnsafeOperation, "*").WithLocation(4, 92), // (7,63): error CS9360: This operation may only be used in an unsafe context // public partial int P2 { get { long* p = null; return (int)*p; } unsafe set { int* q = null; *q = value; } } Diagnostic(ErrorCode.ERR_UnsafeOperation, "*").WithLocation(7, 63)); // Mismatch: definition has unsafe, implementation doesn't CreateCompilation(""" partial class C { public partial int P1 { unsafe get; set; } public partial int P1 { get { int* p = null; return *p; } set { } } } """, options: TestOptions.UnsafeReleaseDll) .VerifyDiagnostics( // (4,29): error CS0764: Both partial member declarations must be unsafe or neither may be unsafe // public partial int P1 { get { int* p = null; return *p; } set { } } Diagnostic(ErrorCode.ERR_PartialMemberUnsafeDifference, "get").WithLocation(4, 29), // (4,57): error CS9360: This operation may only be used in an unsafe context // public partial int P1 { get { int* p = null; return *p; } set { } } Diagnostic(ErrorCode.ERR_UnsafeOperation, "*").WithLocation(4, 57)); // Property has unsafe on both, accessor inherits from property (no explicit accessor unsafe) // Pointer operations work because property is unsafe CreateCompilation(""" partial class C { public unsafe partial int P { get; set; } public unsafe partial int P { get { int* p = null; return *p; } set { int* q = null; *q = value; } } } """, options: TestOptions.UnsafeReleaseDll) .VerifyEmitDiagnostics(); // Both property and both accessors have unsafe - this is disallowed CreateCompilation(""" partial class C { public unsafe partial int P { unsafe get; set; } public unsafe partial int P { unsafe get { int* p = null; return *p; } set { int* q = null; *q = value; } } } """, options: TestOptions.UnsafeReleaseDll) .VerifyDiagnostics( // (3,31): error CS9396: Cannot specify 'unsafe' or 'safe' modifiers on both property or indexer 'C.P' and its accessor. Remove one of them. // public unsafe partial int P { unsafe get; set; } Diagnostic(ErrorCode.ERR_InvalidPropertyUnsafeMods, "P").WithArguments("C.P").WithLocation(3, 31)); // Property has unsafe on both, but only one accessor has explicit unsafe - mismatch on accessor CreateCompilation(""" partial class C { public unsafe partial int P { get; set; } public unsafe partial int P { unsafe get => 0; set { } } } """, options: TestOptions.UnsafeReleaseDll) .VerifyDiagnostics( // (4,42): error CS0764: Both partial member declarations must be unsafe or neither may be unsafe // public unsafe partial int P { unsafe get => 0; set { } } Diagnostic(ErrorCode.ERR_PartialMemberUnsafeDifference, "get").WithLocation(4, 42)); // Property has unsafe on definition, implementation has unsafe on accessor - mismatch on both CreateCompilation(""" partial class C { public unsafe partial int P { get; set; } public partial int P { unsafe get => 0; set { } } } """, options: TestOptions.UnsafeReleaseDll) .VerifyDiagnostics( // (4,24): error CS0764: Both partial member declarations must be unsafe or neither may be unsafe // public partial int P { unsafe get => 0; set { } } Diagnostic(ErrorCode.ERR_PartialMemberUnsafeDifference, "P").WithLocation(4, 24), // (4,35): error CS0764: Both partial member declarations must be unsafe or neither may be unsafe // public partial int P { unsafe get => 0; set { } } Diagnostic(ErrorCode.ERR_PartialMemberUnsafeDifference, "get").WithLocation(4, 35)); // Property has unsafe on implementation, definition has unsafe on accessor - mismatch on both CreateCompilation(""" partial class C { public partial int P { unsafe get; set; } public unsafe partial int P { get => 0; set { } } } """, options: TestOptions.UnsafeReleaseDll) .VerifyDiagnostics( // (4,31): error CS0764: Both partial member declarations must be unsafe or neither may be unsafe // public unsafe partial int P { get => 0; set { } } Diagnostic(ErrorCode.ERR_PartialMemberUnsafeDifference, "P").WithLocation(4, 31), // (4,35): error CS0764: Both partial member declarations must be unsafe or neither may be unsafe // public unsafe partial int P { get => 0; set { } } Diagnostic(ErrorCode.ERR_PartialMemberUnsafeDifference, "get").WithLocation(4, 35)); } [Fact] public void Member_Accessors_ConflictingUnsafeSafeModifiers() { CreateCompilation(""" class C { unsafe int P1 { unsafe get => 0; set { } } unsafe int P2 { get => 0; unsafe set { } } safe int P3 { safe get => 0; set { } } safe int P4 { get => 0; safe set { } } unsafe int P5 { safe get => 0; set { } } safe int P6 { unsafe get => 0; set { } } unsafe int P7 { safe get => 0; unsafe set { } } } """, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // (3,16): error CS9396: Cannot specify 'unsafe' or 'safe' modifiers on both property or indexer 'C.P1' and its accessor. Remove one of them. // unsafe int P1 { unsafe get => 0; set { } } Diagnostic(ErrorCode.ERR_InvalidPropertyUnsafeMods, "P1").WithArguments("C.P1").WithLocation(3, 16), // (4,16): error CS9396: Cannot specify 'unsafe' or 'safe' modifiers on both property or indexer 'C.P2' and its accessor. Remove one of them. // unsafe int P2 { get => 0; unsafe set { } } Diagnostic(ErrorCode.ERR_InvalidPropertyUnsafeMods, "P2").WithArguments("C.P2").WithLocation(4, 16), // (5,14): error CS9396: Cannot specify 'unsafe' or 'safe' modifiers on both property or indexer 'C.P3' and its accessor. Remove one of them. // safe int P3 { safe get => 0; set { } } Diagnostic(ErrorCode.ERR_InvalidPropertyUnsafeMods, "P3").WithArguments("C.P3").WithLocation(5, 14), // (6,14): error CS9396: Cannot specify 'unsafe' or 'safe' modifiers on both property or indexer 'C.P4' and its accessor. Remove one of them. // safe int P4 { get => 0; safe set { } } Diagnostic(ErrorCode.ERR_InvalidPropertyUnsafeMods, "P4").WithArguments("C.P4").WithLocation(6, 14), // (7,16): error CS9396: Cannot specify 'unsafe' or 'safe' modifiers on both property or indexer 'C.P5' and its accessor. Remove one of them. // unsafe int P5 { safe get => 0; set { } } Diagnostic(ErrorCode.ERR_InvalidPropertyUnsafeMods, "P5").WithArguments("C.P5").WithLocation(7, 16), // (8,14): error CS9396: Cannot specify 'unsafe' or 'safe' modifiers on both property or indexer 'C.P6' and its accessor. Remove one of them. // safe int P6 { unsafe get => 0; set { } } Diagnostic(ErrorCode.ERR_InvalidPropertyUnsafeMods, "P6").WithArguments("C.P6").WithLocation(8, 14), // (9,16): error CS9396: Cannot specify 'unsafe' or 'safe' modifiers on both property or indexer 'C.P7' and its accessor. Remove one of them. // unsafe int P7 { safe get => 0; unsafe set { } } Diagnostic(ErrorCode.ERR_InvalidPropertyUnsafeMods, "P7").WithArguments("C.P7").WithLocation(9, 16)); CreateCompilation(""" class C { int Q1 { unsafe get => 0; unsafe set { } } int Q2 { safe get => 0; safe set { } } int this[int i] { unsafe get => 0; unsafe set { } } } """, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // (3,9): error CS9397: Cannot specify the same 'unsafe' or 'safe' modifier on all accessors of property or indexer 'C.Q1'. Instead, put that modifier on the property itself. // int Q1 { unsafe get => 0; unsafe set { } } Diagnostic(ErrorCode.ERR_SamePropertyUnsafeAccessorMods, "Q1").WithArguments("C.Q1").WithLocation(3, 9), // (4,9): error CS9397: Cannot specify the same 'unsafe' or 'safe' modifier on all accessors of property or indexer 'C.Q2'. Instead, put that modifier on the property itself. // int Q2 { safe get => 0; safe set { } } Diagnostic(ErrorCode.ERR_SamePropertyUnsafeAccessorMods, "Q2").WithArguments("C.Q2").WithLocation(4, 9), // (5,9): error CS9397: Cannot specify the same 'unsafe' or 'safe' modifier on all accessors of property or indexer 'C.this[int]'. Instead, put that modifier on the property itself. // int this[int i] { unsafe get => 0; unsafe set { } } Diagnostic(ErrorCode.ERR_SamePropertyUnsafeAccessorMods, "this").WithArguments("C.this[int]").WithLocation(5, 9)); CreateCompilation(""" class C { int S1 { unsafe get => 0; } int S2 { safe get => 0; } int S3 { unsafe set { } } int this[int i] { safe get => 0; } int S4 { unsafe safe get => 0; } safe int S5 { get => 0; } safe int S6 => 0; safe int S7 { safe get => 0; } safe int S8 { unsafe get => 0; } } """, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // (3,9): error CS9397: Cannot specify the same 'unsafe' or 'safe' modifier on all accessors of property or indexer 'C.S1'. Instead, put that modifier on the property itself. // int S1 { unsafe get => 0; } Diagnostic(ErrorCode.ERR_SamePropertyUnsafeAccessorMods, "S1").WithArguments("C.S1").WithLocation(3, 9), // (4,9): error CS9397: Cannot specify the same 'unsafe' or 'safe' modifier on all accessors of property or indexer 'C.S2'. Instead, put that modifier on the property itself. // int S2 { safe get => 0; } Diagnostic(ErrorCode.ERR_SamePropertyUnsafeAccessorMods, "S2").WithArguments("C.S2").WithLocation(4, 9), // (5,9): error CS9397: Cannot specify the same 'unsafe' or 'safe' modifier on all accessors of property or indexer 'C.S3'. Instead, put that modifier on the property itself. // int S3 { unsafe set { } } Diagnostic(ErrorCode.ERR_SamePropertyUnsafeAccessorMods, "S3").WithArguments("C.S3").WithLocation(5, 9), // (6,9): error CS9397: Cannot specify the same 'unsafe' or 'safe' modifier on all accessors of property or indexer 'C.this[int]'. Instead, put that modifier on the property itself. // int this[int i] { safe get => 0; } Diagnostic(ErrorCode.ERR_SamePropertyUnsafeAccessorMods, "this").WithArguments("C.this[int]").WithLocation(6, 9), // (7,9): error CS9397: Cannot specify the same 'unsafe' or 'safe' modifier on all accessors of property or indexer 'C.S4'. Instead, put that modifier on the property itself. // int S4 { unsafe safe get => 0; } Diagnostic(ErrorCode.ERR_SamePropertyUnsafeAccessorMods, "S4").WithArguments("C.S4").WithLocation(7, 9), // (7,21): error CS9388: The 'safe' and 'unsafe' modifiers cannot be used together. // int S4 { unsafe safe get => 0; } Diagnostic(ErrorCode.ERR_SafeModifierCannotBeUsedWithUnsafe, "safe").WithLocation(7, 21), // (10,14): error CS9396: Cannot specify 'unsafe' or 'safe' modifiers on both property or indexer 'C.S7' and its accessor. Remove one of them. // safe int S7 { safe get => 0; } Diagnostic(ErrorCode.ERR_InvalidPropertyUnsafeMods, "S7").WithArguments("C.S7").WithLocation(10, 14), // (11,14): error CS9396: Cannot specify 'unsafe' or 'safe' modifiers on both property or indexer 'C.S8' and its accessor. Remove one of them. // safe int S8 { unsafe get => 0; } Diagnostic(ErrorCode.ERR_InvalidPropertyUnsafeMods, "S8").WithArguments("C.S8").WithLocation(11, 14)); CreateCompilation(""" class C { unsafe int R1 { get => 0; set { } } int R2 { safe get => 0; unsafe set { } } int R3 { unsafe get => 0; safe set { } } unsafe int R4 { get => 0; } safe int R5 { get => 0; } } """, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyEmitDiagnostics(); } [Fact] public void Member_Accessors_ConflictingUnsafeSafeModifiers_Override() { CreateCompilation(""" class B { public virtual int P1 { get => 0; unsafe set { } } } class C : B { public override int P1 { unsafe set { } } } """, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyEmitDiagnostics(); } [Fact] public void Member_Property_Accessors_UnsafeKeyword_LangVersion() { var source = """ public class C { public int P1 { unsafe get; set; } public int P2 { get; unsafe set; } } """; CreateCompilation(source, options: TestOptions.UnsafeReleaseDll) .VerifyEmitDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll) .VerifyEmitDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseDll) .VerifyDiagnostics( // (3,21): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // public int P1 { unsafe get; set; } Diagnostic(ErrorCode.ERR_FeatureInPreview, "unsafe").WithArguments("updated memory safety rules").WithLocation(3, 21), // (4,26): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // public int P2 { get; unsafe set; } Diagnostic(ErrorCode.ERR_FeatureInPreview, "unsafe").WithArguments("updated memory safety rules").WithLocation(4, 26)); } [Fact] public void Member_Property_Field() { CreateCompilation( """ class C { unsafe int P => field; } """, options: TestOptions.ReleaseDll) .VerifyDiagnostics( // (3,16): error CS0227: Unsafe code may only appear if compiling with /unsafe // unsafe int P => field; Diagnostic(ErrorCode.ERR_IllegalUnsafe, "P").WithLocation(3, 16)); } [Fact] public void Member_Property_Attribute() { CompileAndVerifyUnsafe( lib: """ public class A : System.Attribute { public int P1 { get; set; } unsafe public int P2 { get; set; } public int P3 { unsafe get; set; } public int P4 { get; unsafe set; } public unsafe int F; public int G; } """, caller: """ var c = new C1(); [A(P1 = 0, P2 = 0, P3 = 0, P4 = 0, F = 0, G = 0)] class C1; [A(P1 = 0, P2 = 0, P3 = 0, P4 = 0, F = 0, G = 0)] unsafe class C2; partial class C3 { [A(P1 = 0, P2 = 0, P3 = 0, P4 = 0, F = 0, G = 0)] void M1() { } } unsafe partial class C3 { [A(P1 = 0, P2 = 0, P3 = 0, P4 = 0, F = 0, G = 0)] void M2() { } } """, expectedUnsafeSymbols: ["A.P2", "A.get_P2", "A.set_P2", "A.get_P3", "A.set_P4", "A.F"], expectedSafeSymbols: ["A.P1", "A.get_P1", "A.set_P1", "A.set_P3", "A.get_P4", "A.G"], expectedDiagnostics: [ // (2,12): error CS9362: 'A.P2.set' must be used in an unsafe context because it is marked as 'unsafe' // [A(P1 = 0, P2 = 0, P3 = 0, P4 = 0, F = 0, G = 0)] class C1; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "P2 = 0").WithArguments("A.P2.set").WithLocation(2, 12), // (2,28): error CS9362: 'A.P4.set' must be used in an unsafe context because it is marked as 'unsafe' // [A(P1 = 0, P2 = 0, P3 = 0, P4 = 0, F = 0, G = 0)] class C1; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "P4 = 0").WithArguments("A.P4.set").WithLocation(2, 28), // (2,36): error CS9362: 'A.F' must be used in an unsafe context because it is marked as 'unsafe' // [A(P1 = 0, P2 = 0, P3 = 0, P4 = 0, F = 0, G = 0)] class C1; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "F = 0").WithArguments("A.F").WithLocation(2, 36), // (3,12): error CS9362: 'A.P2.set' must be used in an unsafe context because it is marked as 'unsafe' // [A(P1 = 0, P2 = 0, P3 = 0, P4 = 0, F = 0, G = 0)] unsafe class C2; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "P2 = 0").WithArguments("A.P2.set").WithLocation(3, 12), // (3,28): error CS9362: 'A.P4.set' must be used in an unsafe context because it is marked as 'unsafe' // [A(P1 = 0, P2 = 0, P3 = 0, P4 = 0, F = 0, G = 0)] unsafe class C2; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "P4 = 0").WithArguments("A.P4.set").WithLocation(3, 28), // (3,36): error CS9362: 'A.F' must be used in an unsafe context because it is marked as 'unsafe' // [A(P1 = 0, P2 = 0, P3 = 0, P4 = 0, F = 0, G = 0)] unsafe class C2; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "F = 0").WithArguments("A.F").WithLocation(3, 36), // (3,64): error CS9377: The 'unsafe' modifier does not have any effect here under the current memory safety rules. // [A(P1 = 0, P2 = 0, P3 = 0, P4 = 0, F = 0, G = 0)] unsafe class C2; Diagnostic(ErrorCode.ERR_UnsafeMeaningless, "C2").WithLocation(3, 64), // (4,15): error CS9377: The 'unsafe' modifier does not have any effect here under the current memory safety rules. // partial class C3 Diagnostic(ErrorCode.ERR_UnsafeMeaningless, "C3").WithLocation(4, 15), // (6,16): error CS9362: 'A.P2.set' must be used in an unsafe context because it is marked as 'unsafe' // [A(P1 = 0, P2 = 0, P3 = 0, P4 = 0, F = 0, G = 0)] void M1() { } Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "P2 = 0").WithArguments("A.P2.set").WithLocation(6, 16), // (6,32): error CS9362: 'A.P4.set' must be used in an unsafe context because it is marked as 'unsafe' // [A(P1 = 0, P2 = 0, P3 = 0, P4 = 0, F = 0, G = 0)] void M1() { } Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "P4 = 0").WithArguments("A.P4.set").WithLocation(6, 32), // (6,40): error CS9362: 'A.F' must be used in an unsafe context because it is marked as 'unsafe' // [A(P1 = 0, P2 = 0, P3 = 0, P4 = 0, F = 0, G = 0)] void M1() { } Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "F = 0").WithArguments("A.F").WithLocation(6, 40), // (10,16): error CS9362: 'A.P2.set' must be used in an unsafe context because it is marked as 'unsafe' // [A(P1 = 0, P2 = 0, P3 = 0, P4 = 0, F = 0, G = 0)] void M2() { } Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "P2 = 0").WithArguments("A.P2.set").WithLocation(10, 16), // (10,32): error CS9362: 'A.P4.set' must be used in an unsafe context because it is marked as 'unsafe' // [A(P1 = 0, P2 = 0, P3 = 0, P4 = 0, F = 0, G = 0)] void M2() { } Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "P4 = 0").WithArguments("A.P4.set").WithLocation(10, 32), // (10,40): error CS9362: 'A.F' must be used in an unsafe context because it is marked as 'unsafe' // [A(P1 = 0, P2 = 0, P3 = 0, P4 = 0, F = 0, G = 0)] void M2() { } Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "F = 0").WithArguments("A.F").WithLocation(10, 40), ], expectedDiagnosticsWhenReferencingLegacyLib: [ // (3,64): error CS9377: The 'unsafe' modifier does not have any effect here under the current memory safety rules. // [A(P1 = 0, P2 = 0, P3 = 0, P4 = 0, F = 0, G = 0)] unsafe class C2; Diagnostic(ErrorCode.ERR_UnsafeMeaningless, "C2").WithLocation(3, 64), // (4,15): error CS9377: The 'unsafe' modifier does not have any effect here under the current memory safety rules. // partial class C3 Diagnostic(ErrorCode.ERR_UnsafeMeaningless, "C3").WithLocation(4, 15), ]); } [Fact] public void Member_Property_Override() { CompileAndVerifyUnsafe( lib: """ public class B { unsafe public virtual int P1 { get; set; } public virtual int P2 { unsafe get; set; } public virtual int P3 { get; set; } } """, caller: """ var c = new C1(); c.P1 = c.P1 + 123; c.P2 = c.P2 + 123; c.P3 = c.P3 + 123; class C1 : B { public override int P1 { get; set; } unsafe public override int P2 { get; set; } public override int P3 { unsafe get; set; } } class C2 : B { unsafe public override int P1 { get; set; } public override int P2 { unsafe get; set; } unsafe public override int P3 { get; set; } } """, expectedUnsafeSymbols: ["B.P1", "B.get_P1", "B.set_P1", "B.get_P2"], expectedSafeSymbols: ["B.P2", "B.set_P2", "B.P3", "B.get_P3", "B.set_P3"], expectedDiagnostics: [ // (3,1): error CS9362: 'C1.P2.set' must be used in an unsafe context because it is marked as 'unsafe' // c.P2 = c.P2 + 123; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c.P2").WithArguments("C1.P2.set").WithLocation(3, 1), // (3,8): error CS9362: 'C1.P2.get' must be used in an unsafe context because it is marked as 'unsafe' // c.P2 = c.P2 + 123; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c.P2").WithArguments("C1.P2.get").WithLocation(3, 8), // (4,8): error CS9362: 'C1.P3.get' must be used in an unsafe context because it is marked as 'unsafe' // c.P3 = c.P3 + 123; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c.P3").WithArguments("C1.P3.get").WithLocation(4, 8), // (9,42): error CS9364: Unsafe member 'C1.P2.set' cannot override safe member 'B.P2.set' // unsafe public override int P2 { get; set; } Diagnostic(ErrorCode.ERR_CallerUnsafeOverridingSafe, "set").WithArguments("C1.P2.set", "B.P2.set").WithLocation(9, 42), // (10,37): error CS9364: Unsafe member 'C1.P3.get' cannot override safe member 'B.P3.get' // public override int P3 { unsafe get; set; } Diagnostic(ErrorCode.ERR_CallerUnsafeOverridingSafe, "get").WithArguments("C1.P3.get", "B.P3.get").WithLocation(10, 37), // (17,37): error CS9364: Unsafe member 'C2.P3.get' cannot override safe member 'B.P3.get' // unsafe public override int P3 { get; set; } Diagnostic(ErrorCode.ERR_CallerUnsafeOverridingSafe, "get").WithArguments("C2.P3.get", "B.P3.get").WithLocation(17, 37), // (17,42): error CS9364: Unsafe member 'C2.P3.set' cannot override safe member 'B.P3.set' // unsafe public override int P3 { get; set; } Diagnostic(ErrorCode.ERR_CallerUnsafeOverridingSafe, "set").WithArguments("C2.P3.set", "B.P3.set").WithLocation(17, 42), ], expectedDiagnosticsWhenReferencingLegacyLib: [ // (3,1): error CS9362: 'C1.P2.set' must be used in an unsafe context because it is marked as 'unsafe' // c.P2 = c.P2 + 123; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c.P2").WithArguments("C1.P2.set").WithLocation(3, 1), // (3,8): error CS9362: 'C1.P2.get' must be used in an unsafe context because it is marked as 'unsafe' // c.P2 = c.P2 + 123; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c.P2").WithArguments("C1.P2.get").WithLocation(3, 8), // (4,8): error CS9362: 'C1.P3.get' must be used in an unsafe context because it is marked as 'unsafe' // c.P3 = c.P3 + 123; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c.P3").WithArguments("C1.P3.get").WithLocation(4, 8), // (9,37): error CS9364: Unsafe member 'C1.P2.get' cannot override safe member 'B.P2.get' // unsafe public override int P2 { get; set; } Diagnostic(ErrorCode.ERR_CallerUnsafeOverridingSafe, "get").WithArguments("C1.P2.get", "B.P2.get").WithLocation(9, 37), // (9,42): error CS9364: Unsafe member 'C1.P2.set' cannot override safe member 'B.P2.set' // unsafe public override int P2 { get; set; } Diagnostic(ErrorCode.ERR_CallerUnsafeOverridingSafe, "set").WithArguments("C1.P2.set", "B.P2.set").WithLocation(9, 42), // (10,37): error CS9364: Unsafe member 'C1.P3.get' cannot override safe member 'B.P3.get' // public override int P3 { unsafe get; set; } Diagnostic(ErrorCode.ERR_CallerUnsafeOverridingSafe, "get").WithArguments("C1.P3.get", "B.P3.get").WithLocation(10, 37), // (15,37): error CS9364: Unsafe member 'C2.P1.get' cannot override safe member 'B.P1.get' // unsafe public override int P1 { get; set; } Diagnostic(ErrorCode.ERR_CallerUnsafeOverridingSafe, "get").WithArguments("C2.P1.get", "B.P1.get").WithLocation(15, 37), // (15,42): error CS9364: Unsafe member 'C2.P1.set' cannot override safe member 'B.P1.set' // unsafe public override int P1 { get; set; } Diagnostic(ErrorCode.ERR_CallerUnsafeOverridingSafe, "set").WithArguments("C2.P1.set", "B.P1.set").WithLocation(15, 42), // (16,37): error CS9364: Unsafe member 'C2.P2.get' cannot override safe member 'B.P2.get' // public override int P2 { unsafe get; set; } Diagnostic(ErrorCode.ERR_CallerUnsafeOverridingSafe, "get").WithArguments("C2.P2.get", "B.P2.get").WithLocation(16, 37), // (17,37): error CS9364: Unsafe member 'C2.P3.get' cannot override safe member 'B.P3.get' // unsafe public override int P3 { get; set; } Diagnostic(ErrorCode.ERR_CallerUnsafeOverridingSafe, "get").WithArguments("C2.P3.get", "B.P3.get").WithLocation(17, 37), // (17,42): error CS9364: Unsafe member 'C2.P3.set' cannot override safe member 'B.P3.set' // unsafe public override int P3 { get; set; } Diagnostic(ErrorCode.ERR_CallerUnsafeOverridingSafe, "set").WithArguments("C2.P3.set", "B.P3.set").WithLocation(17, 42), ]); } [Fact] public void Member_Property_Implementation() { CompileAndVerifyUnsafe( lib: """ public interface I { unsafe int P1 { get; set; } int P2 { unsafe get; set; } int P3 { get; set; } } """, caller: """ I i = new C1(); i.P1 = i.P1 + 123; i.P2 = i.P2 + 123; i.P3 = i.P3 + 123; class C1 : I { public int P1 { get; set; } unsafe public int P2 { get; set; } public int P3 { unsafe get; set; } } class C2 : I { unsafe public int P1 { get; set; } public int P2 { unsafe get; set; } unsafe public int P3 { get; set; } } """, expectedUnsafeSymbols: ["I.P1", "I.get_P1", "I.set_P1", "I.get_P2"], expectedSafeSymbols: ["I.P2", "I.set_P2", "I.P3", "I.get_P3", "I.set_P3"], expectedDiagnostics: [ // (2,1): error CS9362: 'I.P1.set' must be used in an unsafe context because it is marked as 'unsafe' // i.P1 = i.P1 + 123; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "i.P1").WithArguments("I.P1.set").WithLocation(2, 1), // (2,8): error CS9362: 'I.P1.get' must be used in an unsafe context because it is marked as 'unsafe' // i.P1 = i.P1 + 123; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "i.P1").WithArguments("I.P1.get").WithLocation(2, 8), // (3,8): error CS9362: 'I.P2.get' must be used in an unsafe context because it is marked as 'unsafe' // i.P2 = i.P2 + 123; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "i.P2").WithArguments("I.P2.get").WithLocation(3, 8), // (9,33): error CS9365: Unsafe member 'C1.P2.set' cannot implicitly implement safe member 'I.P2.set' // unsafe public int P2 { get; set; } Diagnostic(ErrorCode.ERR_CallerUnsafeImplicitlyImplementingSafe, "set").WithArguments("C1.P2.set", "I.P2.set").WithLocation(9, 33), // (10,28): error CS9365: Unsafe member 'C1.P3.get' cannot implicitly implement safe member 'I.P3.get' // public int P3 { unsafe get; set; } Diagnostic(ErrorCode.ERR_CallerUnsafeImplicitlyImplementingSafe, "get").WithArguments("C1.P3.get", "I.P3.get").WithLocation(10, 28), // (17,28): error CS9365: Unsafe member 'C2.P3.get' cannot implicitly implement safe member 'I.P3.get' // unsafe public int P3 { get; set; } Diagnostic(ErrorCode.ERR_CallerUnsafeImplicitlyImplementingSafe, "get").WithArguments("C2.P3.get", "I.P3.get").WithLocation(17, 28), // (17,33): error CS9365: Unsafe member 'C2.P3.set' cannot implicitly implement safe member 'I.P3.set' // unsafe public int P3 { get; set; } Diagnostic(ErrorCode.ERR_CallerUnsafeImplicitlyImplementingSafe, "set").WithArguments("C2.P3.set", "I.P3.set").WithLocation(17, 33), ], expectedDiagnosticsWhenReferencingLegacyLib: [ // (9,28): error CS9365: Unsafe member 'C1.P2.get' cannot implicitly implement safe member 'I.P2.get' // unsafe public int P2 { get; set; } Diagnostic(ErrorCode.ERR_CallerUnsafeImplicitlyImplementingSafe, "get").WithArguments("C1.P2.get", "I.P2.get").WithLocation(9, 28), // (9,33): error CS9365: Unsafe member 'C1.P2.set' cannot implicitly implement safe member 'I.P2.set' // unsafe public int P2 { get; set; } Diagnostic(ErrorCode.ERR_CallerUnsafeImplicitlyImplementingSafe, "set").WithArguments("C1.P2.set", "I.P2.set").WithLocation(9, 33), // (10,28): error CS9365: Unsafe member 'C1.P3.get' cannot implicitly implement safe member 'I.P3.get' // public int P3 { unsafe get; set; } Diagnostic(ErrorCode.ERR_CallerUnsafeImplicitlyImplementingSafe, "get").WithArguments("C1.P3.get", "I.P3.get").WithLocation(10, 28), // (15,28): error CS9365: Unsafe member 'C2.P1.get' cannot implicitly implement safe member 'I.P1.get' // unsafe public int P1 { get; set; } Diagnostic(ErrorCode.ERR_CallerUnsafeImplicitlyImplementingSafe, "get").WithArguments("C2.P1.get", "I.P1.get").WithLocation(15, 28), // (15,33): error CS9365: Unsafe member 'C2.P1.set' cannot implicitly implement safe member 'I.P1.set' // unsafe public int P1 { get; set; } Diagnostic(ErrorCode.ERR_CallerUnsafeImplicitlyImplementingSafe, "set").WithArguments("C2.P1.set", "I.P1.set").WithLocation(15, 33), // (16,28): error CS9365: Unsafe member 'C2.P2.get' cannot implicitly implement safe member 'I.P2.get' // public int P2 { unsafe get; set; } Diagnostic(ErrorCode.ERR_CallerUnsafeImplicitlyImplementingSafe, "get").WithArguments("C2.P2.get", "I.P2.get").WithLocation(16, 28), // (17,28): error CS9365: Unsafe member 'C2.P3.get' cannot implicitly implement safe member 'I.P3.get' // unsafe public int P3 { get; set; } Diagnostic(ErrorCode.ERR_CallerUnsafeImplicitlyImplementingSafe, "get").WithArguments("C2.P3.get", "I.P3.get").WithLocation(17, 28), // (17,33): error CS9365: Unsafe member 'C2.P3.set' cannot implicitly implement safe member 'I.P3.set' // unsafe public int P3 { get; set; } Diagnostic(ErrorCode.ERR_CallerUnsafeImplicitlyImplementingSafe, "set").WithArguments("C2.P3.set", "I.P3.set").WithLocation(17, 33), ]); } [Fact] public void Member_Indexer() { CompileAndVerifyUnsafe( lib: """ public class C1 { public int this[int i] { get => i; set { } } } public class C2 { unsafe public int this[int i] { get => i; set { } } } """, caller: """ var c1 = new C1(); c1[0] = c1[0] + 123; var c2 = new C2(); c2[0] = c2[0] + 123; unsafe { c2[0] = c2[0] + 123; } """, expectedUnsafeSymbols: ["C2.this[]", "C2.get_Item", "C2.set_Item"], expectedSafeSymbols: ["C1.this[]", "C1.get_Item", "C1.set_Item"], expectedDiagnostics: [ // (4,1): error CS9362: 'C2.this[int].set' must be used in an unsafe context because it is marked as 'unsafe' // c2[0] = c2[0] + 123; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c2[0]").WithArguments("C2.this[int].set").WithLocation(4, 1), // (4,9): error CS9362: 'C2.this[int].get' must be used in an unsafe context because it is marked as 'unsafe' // c2[0] = c2[0] + 123; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c2[0]").WithArguments("C2.this[int].get").WithLocation(4, 9), ]); } [Fact] public void Member_Indexer_CompoundAssignment() { CompileAndVerifyUnsafe( lib: """ public class C { unsafe public int this[int i] { get => i; set { } } } """, caller: """ var c = new C(); c[0] += 123; unsafe { c[0] += 123; } """, expectedUnsafeSymbols: ["C.this[]", "C.get_Item", "C.set_Item"], expectedSafeSymbols: ["C"], expectedDiagnostics: [ // (2,1): error CS9362: 'C.this[int].set' must be used in an unsafe context because it is marked as 'unsafe' // c[0] += 123; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c[0]").WithArguments("C.this[int].set").WithLocation(2, 1), // (2,1): error CS9362: 'C.this[int].get' must be used in an unsafe context because it is marked as 'unsafe' // c[0] += 123; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c[0]").WithArguments("C.this[int].get").WithLocation(2, 1), ]); } [Fact] public void Member_Indexer_ObjectInitializer() { CompileAndVerifyUnsafe( lib: """ public class C1 { unsafe public object this[int i] { get => null; set { } } } public class C2 { public object this[int i] { unsafe get => null; set { } } } public class C3 { public object this[int i] { get => null; unsafe set { } } } public class C4 { public object this[int i] { get => null; set { } } } """, caller: """ _ = new C1 { [0] = null, [0] = { } }; _ = new C2 { [0] = null, [0] = { } }; _ = new C3 { [0] = null, [0] = { } }; _ = new C4 { [0] = null, [0] = { } }; unsafe { _ = new C1 { [0] = null, [0] = { } }; } unsafe { _ = new C2 { [0] = null, [0] = { } }; } unsafe { _ = new C3 { [0] = null, [0] = { } }; } """, expectedUnsafeSymbols: ["C1.this[]", "C1.get_Item", "C1.set_Item", "C2.get_Item", "C3.set_Item"], expectedSafeSymbols: ["C1", "C2", "C2.this[]", "C2.set_Item", "C3", "C3.this[]", "C3.get_Item", "C4", "C4.this[]", "C4.get_Item", "C4.set_Item"], expectedDiagnostics: [ // (1,14): error CS9362: 'C1.this[int].set' must be used in an unsafe context because it is marked as 'unsafe' // _ = new C1 { [0] = null, [0] = { } }; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "[0]").WithArguments("C1.this[int].set").WithLocation(1, 14), // (1,26): error CS9362: 'C1.this[int].get' must be used in an unsafe context because it is marked as 'unsafe' // _ = new C1 { [0] = null, [0] = { } }; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "[0]").WithArguments("C1.this[int].get").WithLocation(1, 26), // (2,26): error CS9362: 'C2.this[int].get' must be used in an unsafe context because it is marked as 'unsafe' // _ = new C2 { [0] = null, [0] = { } }; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "[0]").WithArguments("C2.this[int].get").WithLocation(2, 26), // (3,14): error CS9362: 'C3.this[int].set' must be used in an unsafe context because it is marked as 'unsafe' // _ = new C3 { [0] = null, [0] = { } }; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "[0]").WithArguments("C3.this[int].set").WithLocation(3, 14), ]); } [Fact] public void Member_Indexer_Accessors() { var lib = """ public class C1 { public int this[int i] { unsafe get => i; set { } } } public class C2 { public int this[int i] { get => i; unsafe set { } } } """; CompileAndVerifyUnsafe( lib: lib, caller: """ var c1 = new C1(); c1[0] = c1[0] + 123; var c2 = new C2(); c2[0] = c2[0] + 123; unsafe { c1[0] = c1[0] + 123; } unsafe { c2[0] = c2[0] + 123; } """, expectedUnsafeSymbols: ["C1.get_Item", "C2.set_Item"], expectedSafeSymbols: ["C1.this[]", "C2.this[]", "C2.get_Item", "C1.set_Item"], expectedDiagnostics: [ // (2,9): error CS9362: 'C1.this[int].get' must be used in an unsafe context because it is marked as 'unsafe' // c1[0] = c1[0] + 123; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c1[0]").WithArguments("C1.this[int].get").WithLocation(2, 9), // (4,1): error CS9362: 'C2.this[int].set' must be used in an unsafe context because it is marked as 'unsafe' // c2[0] = c2[0] + 123; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c2[0]").WithArguments("C2.this[int].set").WithLocation(4, 1), ]); CreateCompilation([lib], parseOptions: TestOptions.Regular14).VerifyEmitDiagnostics( // (3,30): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // public int this[int i] { unsafe get => i; set { } } Diagnostic(ErrorCode.ERR_FeatureInPreview, "unsafe").WithArguments("updated memory safety rules").WithLocation(3, 30), // (7,40): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // public int this[int i] { get => i; unsafe set { } } Diagnostic(ErrorCode.ERR_FeatureInPreview, "unsafe").WithArguments("updated memory safety rules").WithLocation(7, 40)); CreateCompilation([lib], parseOptions: TestOptions.RegularNext).VerifyEmitDiagnostics(); CreateCompilation([lib], parseOptions: TestOptions.RegularPreview).VerifyEmitDiagnostics(); } [Fact] public void Member_Indexer_Accessors_UnsafeKeyword() { CreateCompilation(""" public class C { public int this[int i] { unsafe get { int* p = null; return *p; } set { long* q = null; *q = value; } } public int this[int i, int j] { get { long* p = null; return (int)*p; } unsafe set { int* q = null; *q = value; } } } """, options: TestOptions.UnsafeReleaseDll) .VerifyDiagnostics( // (3,93): error CS9360: This operation may only be used in an unsafe context // public int this[int i] { unsafe get { int* p = null; return *p; } set { long* q = null; *q = value; } } Diagnostic(ErrorCode.ERR_UnsafeOperation, "*").WithLocation(3, 93), // (4,71): error CS9360: This operation may only be used in an unsafe context // public int this[int i, int j] { get { long* p = null; return (int)*p; } unsafe set { int* q = null; *q = value; } } Diagnostic(ErrorCode.ERR_UnsafeOperation, "*").WithLocation(4, 71)); } [Fact] public void Member_Indexer_Accessors_UnsafeKeyword_Partial() { CreateCompilation(""" public partial class C { public partial int this[int i] { unsafe get; set; } public partial int this[int i] { unsafe get { int* p = null; return *p; } set { long* q = null; *q = value; } } public partial int this[int i, int j] { get; unsafe set; } public partial int this[int i, int j] { get { long* p = null; return (int)*p; } unsafe set { int* q = null; *q = value; } } } """, options: TestOptions.UnsafeReleaseDll) .VerifyDiagnostics( // (4,101): error CS9360: This operation may only be used in an unsafe context // public partial int this[int i] { unsafe get { int* p = null; return *p; } set { long* q = null; *q = value; } } Diagnostic(ErrorCode.ERR_UnsafeOperation, "*").WithLocation(4, 101), // (7,79): error CS9360: This operation may only be used in an unsafe context // public partial int this[int i, int j] { get { long* p = null; return (int)*p; } unsafe set { int* q = null; *q = value; } } Diagnostic(ErrorCode.ERR_UnsafeOperation, "*").WithLocation(7, 79)); // Mismatch: definition has unsafe, implementation doesn't CreateCompilation(""" partial class C { public partial int this[int i] { unsafe get; set; } public partial int this[int i] { get { int* p = null; return *p; } set { } } } """, options: TestOptions.UnsafeReleaseDll) .VerifyDiagnostics( // (4,38): error CS0764: Both partial member declarations must be unsafe or neither may be unsafe // public partial int this[int i] { get { int* p = null; return *p; } set { } } Diagnostic(ErrorCode.ERR_PartialMemberUnsafeDifference, "get").WithLocation(4, 38), // (4,66): error CS9360: This operation may only be used in an unsafe context // public partial int this[int i] { get { int* p = null; return *p; } set { } } Diagnostic(ErrorCode.ERR_UnsafeOperation, "*").WithLocation(4, 66)); // Indexer has unsafe on both, accessor inherits from indexer (no explicit accessor unsafe) // Pointer operations work because indexer is unsafe CreateCompilation(""" partial class C { public unsafe partial int this[int i] { get; set; } public unsafe partial int this[int i] { get { int* p = null; return *p; } set { int* q = null; *q = value; } } } """, options: TestOptions.UnsafeReleaseDll) .VerifyEmitDiagnostics(); // Both indexer and both accessors have unsafe - this is disallowed CreateCompilation(""" partial class C { public unsafe partial int this[int i] { unsafe get; set; } public unsafe partial int this[int i] { unsafe get { int* p = null; return *p; } set { int* q = null; *q = value; } } } """, options: TestOptions.UnsafeReleaseDll) .VerifyDiagnostics( // (3,31): error CS9396: Cannot specify 'unsafe' or 'safe' modifiers on both property or indexer 'C.this[int]' and its accessor. Remove one of them. // public unsafe partial int this[int i] { unsafe get; set; } Diagnostic(ErrorCode.ERR_InvalidPropertyUnsafeMods, "this").WithArguments("C.this[int]").WithLocation(3, 31)); // Indexer has unsafe on both, but only one accessor has explicit unsafe - mismatch on accessor CreateCompilation(""" partial class C { public unsafe partial int this[int i] { get; set; } public unsafe partial int this[int i] { unsafe get => i; set { } } } """, options: TestOptions.UnsafeReleaseDll) .VerifyDiagnostics( // (4,52): error CS0764: Both partial member declarations must be unsafe or neither may be unsafe // public unsafe partial int this[int i] { unsafe get => i; set { } } Diagnostic(ErrorCode.ERR_PartialMemberUnsafeDifference, "get").WithLocation(4, 52)); // Indexer has unsafe on definition, implementation has unsafe on accessor - mismatch on both CreateCompilation(""" partial class C { public unsafe partial int this[int i] { get; set; } public partial int this[int i] { unsafe get => i; set { } } } """, options: TestOptions.UnsafeReleaseDll) .VerifyDiagnostics( // (4,24): error CS0764: Both partial member declarations must be unsafe or neither may be unsafe // public partial int this[int i] { unsafe get => i; set { } } Diagnostic(ErrorCode.ERR_PartialMemberUnsafeDifference, "this").WithLocation(4, 24), // (4,45): error CS0764: Both partial member declarations must be unsafe or neither may be unsafe // public partial int this[int i] { unsafe get => i; set { } } Diagnostic(ErrorCode.ERR_PartialMemberUnsafeDifference, "get").WithLocation(4, 45)); // Indexer has unsafe on implementation, definition has unsafe on accessor - mismatch on both CreateCompilation(""" partial class C { public partial int this[int i] { unsafe get; set; } public unsafe partial int this[int i] { get => i; set { } } } """, options: TestOptions.UnsafeReleaseDll) .VerifyDiagnostics( // (4,31): error CS0764: Both partial member declarations must be unsafe or neither may be unsafe // public unsafe partial int this[int i] { get => i; set { } } Diagnostic(ErrorCode.ERR_PartialMemberUnsafeDifference, "this").WithLocation(4, 31), // (4,45): error CS0764: Both partial member declarations must be unsafe or neither may be unsafe // public unsafe partial int this[int i] { get => i; set { } } Diagnostic(ErrorCode.ERR_PartialMemberUnsafeDifference, "get").WithLocation(4, 45)); } [Fact] public void Member_Indexer_Accessors_UnsafeKeyword_LangVersion() { var source = """ public class C { public int this[int i] { unsafe get => i; set { } } public int this[int i, int j] { get => i + j; unsafe set { } } } """; CreateCompilation(source, options: TestOptions.UnsafeReleaseDll) .VerifyEmitDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll) .VerifyEmitDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseDll) .VerifyDiagnostics( // (3,30): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // public int this[int i] { unsafe get => i; set { } } Diagnostic(ErrorCode.ERR_FeatureInPreview, "unsafe").WithArguments("updated memory safety rules").WithLocation(3, 30), // (4,51): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // public int this[int i, int j] { get => i + j; unsafe set { } } Diagnostic(ErrorCode.ERR_FeatureInPreview, "unsafe").WithArguments("updated memory safety rules").WithLocation(4, 51)); } [Fact] public void Member_Event() { CompileAndVerifyUnsafe( lib: """ public class C { public event System.Action E1 { add { } remove { } } unsafe public event System.Action E2 { add { } remove { } } } """, caller: """ var c = new C(); c.E1 += null; c.E2 += null; unsafe { c.E2 += null; } """, expectedUnsafeSymbols: ["C.E2", "C.add_E2", "C.remove_E2"], expectedSafeSymbols: ["C.E1", "C.add_E1", "C.remove_E1"], expectedDiagnostics: [ // (3,6): error CS9362: 'C.E2.add' must be used in an unsafe context because it is marked as 'unsafe' // c.E2 += null; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "+=").WithArguments("C.E2.add").WithLocation(3, 6), ]); var source = """ class C { event System.Action E1, E2; unsafe event System.Action E3, E4; void M() { E1(); E2(); E3(); E4(); E1 = null; E2 = null; E3 = null; E4 = null; unsafe { E3(); } unsafe { E4(); } unsafe { E3 = null; } unsafe { E4 = null; } } } """; CreateCompilation([source], options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // (9,9): error CS9362: 'C.E3' must be used in an unsafe context because it is marked as 'unsafe' // E3(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "E3").WithArguments("C.E3").WithLocation(9, 9), // (10,9): error CS9362: 'C.E4' must be used in an unsafe context because it is marked as 'unsafe' // E4(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "E4").WithArguments("C.E4").WithLocation(10, 9), // (14,9): error CS9362: 'C.E3' must be used in an unsafe context because it is marked as 'unsafe' // E3 = null; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "E3").WithArguments("C.E3").WithLocation(14, 9), // (15,9): error CS9362: 'C.E4' must be used in an unsafe context because it is marked as 'unsafe' // E4 = null; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "E4").WithArguments("C.E4").WithLocation(15, 9)); } [Theory, CombinatorialData] public void Member_Event_Accessors(bool updatedRules) { CreateCompilation(""" public class C { public event System.Action E1 { unsafe add { } remove { } } public event System.Action E2 { add { } unsafe remove { } } public event System.Action E3 { safe add { } remove { } } public event System.Action E4 { add { } safe remove { } } } """, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules(updatedRules)) .VerifyDiagnostics( // (3,37): error CS1609: Modifiers cannot be placed on event accessor declarations // public event System.Action E1 { unsafe add { } remove { } } Diagnostic(ErrorCode.ERR_NoModifiersOnAccessor, "unsafe").WithLocation(3, 37), // (4,45): error CS1609: Modifiers cannot be placed on event accessor declarations // public event System.Action E2 { add { } unsafe remove { } } Diagnostic(ErrorCode.ERR_NoModifiersOnAccessor, "unsafe").WithLocation(4, 45), // (5,37): error CS1609: Modifiers cannot be placed on event accessor declarations // public event System.Action E3 { safe add { } remove { } } Diagnostic(ErrorCode.ERR_NoModifiersOnAccessor, "safe").WithLocation(5, 37), // (6,45): error CS1609: Modifiers cannot be placed on event accessor declarations // public event System.Action E4 { add { } safe remove { } } Diagnostic(ErrorCode.ERR_NoModifiersOnAccessor, "safe").WithLocation(6, 45)); } [Fact] public void Member_Event_Accessors_UnsafeKeyword() { // unsafe on event accessors is not allowed (like other modifiers on event accessors) CreateCompilation(""" public class C { public event System.Action E { unsafe add { int* p = null; } unsafe remove { int* p = null; } } } """, options: TestOptions.UnsafeReleaseDll) .VerifyDiagnostics( // (3,36): error CS1609: Modifiers cannot be placed on event accessor declarations // public event System.Action E { unsafe add { int* p = null; } unsafe remove { int* p = null; } } Diagnostic(ErrorCode.ERR_NoModifiersOnAccessor, "unsafe").WithLocation(3, 36), // (3,66): error CS1609: Modifiers cannot be placed on event accessor declarations // public event System.Action E { unsafe add { int* p = null; } unsafe remove { int* p = null; } } Diagnostic(ErrorCode.ERR_NoModifiersOnAccessor, "unsafe").WithLocation(3, 66)); } [Fact] public void Member_Event_Override() { CompileAndVerifyUnsafe( lib: """ #pragma warning disable CS0067 // unused event public class B { unsafe public virtual event System.Action E1; unsafe public virtual event System.Action E2 { add { } remove { } } public virtual event System.Action E3; public virtual event System.Action E4 { add { } remove { } } } """, caller: """ var c = new C1(); c.E1 += null; c.E2 += null; c.E3 += null; c.E4 += null; #pragma warning disable CS0067 // unused event class C1 : B { public override event System.Action E1; public override event System.Action E2 { add { } remove { } } unsafe public override event System.Action E3; unsafe public override event System.Action E4 { add { } remove { } } } class C2 : B { unsafe public override event System.Action E1; unsafe public override event System.Action E2 { add { } remove { } } public override event System.Action E3; public override event System.Action E4 { add { } remove { } } } """, expectedUnsafeSymbols: ["B.E1", "B.add_E1", "B.remove_E1", "B.E2", "B.add_E2", "B.remove_E2"], expectedSafeSymbols: ["B.E3", "B.add_E3", "B.remove_E3", "B.E4", "B.add_E4", "B.remove_E4"], expectedDiagnostics: [ // (4,6): error CS9362: 'C1.E3.add' must be used in an unsafe context because it is marked as 'unsafe' // c.E3 += null; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "+=").WithArguments("C1.E3.add").WithLocation(4, 6), // (5,6): error CS9362: 'C1.E4.add' must be used in an unsafe context because it is marked as 'unsafe' // c.E4 += null; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "+=").WithArguments("C1.E4.add").WithLocation(5, 6), // (13,48): error CS9364: Unsafe member 'C1.E3' cannot override safe member 'B.E3' // unsafe public override event System.Action E3; Diagnostic(ErrorCode.ERR_CallerUnsafeOverridingSafe, "E3").WithArguments("C1.E3", "B.E3").WithLocation(13, 48), // (14,48): error CS9364: Unsafe member 'C1.E4' cannot override safe member 'B.E4' // unsafe public override event System.Action E4 { add { } remove { } } Diagnostic(ErrorCode.ERR_CallerUnsafeOverridingSafe, "E4").WithArguments("C1.E4", "B.E4").WithLocation(14, 48), ], expectedDiagnosticsWhenReferencingLegacyLib: [ // (4,6): error CS9362: 'C1.E3.add' must be used in an unsafe context because it is marked as 'unsafe' // c.E3 += null; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "+=").WithArguments("C1.E3.add").WithLocation(4, 6), // (5,6): error CS9362: 'C1.E4.add' must be used in an unsafe context because it is marked as 'unsafe' // c.E4 += null; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "+=").WithArguments("C1.E4.add").WithLocation(5, 6), // (13,48): error CS9364: Unsafe member 'C1.E3' cannot override safe member 'B.E3' // unsafe public override event System.Action E3; Diagnostic(ErrorCode.ERR_CallerUnsafeOverridingSafe, "E3").WithArguments("C1.E3", "B.E3").WithLocation(13, 48), // (14,48): error CS9364: Unsafe member 'C1.E4' cannot override safe member 'B.E4' // unsafe public override event System.Action E4 { add { } remove { } } Diagnostic(ErrorCode.ERR_CallerUnsafeOverridingSafe, "E4").WithArguments("C1.E4", "B.E4").WithLocation(14, 48), // (19,48): error CS9364: Unsafe member 'C2.E1' cannot override safe member 'B.E1' // unsafe public override event System.Action E1; Diagnostic(ErrorCode.ERR_CallerUnsafeOverridingSafe, "E1").WithArguments("C2.E1", "B.E1").WithLocation(19, 48), // (20,48): error CS9364: Unsafe member 'C2.E2' cannot override safe member 'B.E2' // unsafe public override event System.Action E2 { add { } remove { } } Diagnostic(ErrorCode.ERR_CallerUnsafeOverridingSafe, "E2").WithArguments("C2.E2", "B.E2").WithLocation(20, 48), ]); } [Fact] public void Member_Event_Implementation() { CompileAndVerifyUnsafe( lib: """ public interface I { unsafe event System.Action E1; event System.Action E2; } """, caller: """ I i = new C1(); i.E1 += null; i.E2 += null; #pragma warning disable CS0067 // unused event class C1 : I { public event System.Action E1; unsafe public event System.Action E2; } class C2 : I { public event System.Action E1 { add { } remove { } } unsafe public event System.Action E2 { add { } remove { } } } class C3 : I { unsafe public event System.Action E1; public event System.Action E2; } class C4 : I { unsafe public event System.Action E1 { add { } remove { } } public event System.Action E2 { add { } remove { } } } """, expectedUnsafeSymbols: ["I.E1", "I.add_E1", "I.remove_E1"], expectedSafeSymbols: ["I.E2", "I.add_E2", "I.remove_E2"], expectedDiagnostics: [ // (2,6): error CS9362: 'I.E1.add' must be used in an unsafe context because it is marked as 'unsafe' // i.E1 += null; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "+=").WithArguments("I.E1.add").WithLocation(2, 6), // (10,39): error CS9365: Unsafe member 'C1.E2' cannot implicitly implement safe member 'I.E2' // unsafe public event System.Action E2; Diagnostic(ErrorCode.ERR_CallerUnsafeImplicitlyImplementingSafe, "E2").WithArguments("C1.E2", "I.E2").WithLocation(10, 39), // (16,39): error CS9365: Unsafe member 'C2.E2' cannot implicitly implement safe member 'I.E2' // unsafe public event System.Action E2 { add { } remove { } } Diagnostic(ErrorCode.ERR_CallerUnsafeImplicitlyImplementingSafe, "E2").WithArguments("C2.E2", "I.E2").WithLocation(16, 39), ], expectedDiagnosticsWhenReferencingLegacyLib: [ // (10,39): error CS9365: Unsafe member 'C1.E2' cannot implicitly implement safe member 'I.E2' // unsafe public event System.Action E2; Diagnostic(ErrorCode.ERR_CallerUnsafeImplicitlyImplementingSafe, "E2").WithArguments("C1.E2", "I.E2").WithLocation(10, 39), // (16,39): error CS9365: Unsafe member 'C2.E2' cannot implicitly implement safe member 'I.E2' // unsafe public event System.Action E2 { add { } remove { } } Diagnostic(ErrorCode.ERR_CallerUnsafeImplicitlyImplementingSafe, "E2").WithArguments("C2.E2", "I.E2").WithLocation(16, 39), // (21,39): error CS9365: Unsafe member 'C3.E1' cannot implicitly implement safe member 'I.E1' // unsafe public event System.Action E1; Diagnostic(ErrorCode.ERR_CallerUnsafeImplicitlyImplementingSafe, "E1").WithArguments("C3.E1", "I.E1").WithLocation(21, 39), // (27,39): error CS9365: Unsafe member 'C4.E1' cannot implicitly implement safe member 'I.E1' // unsafe public event System.Action E1 { add { } remove { } } Diagnostic(ErrorCode.ERR_CallerUnsafeImplicitlyImplementingSafe, "E1").WithArguments("C4.E1", "I.E1").WithLocation(27, 39), ]); } [Fact] public void Member_Event_Field_UnsafeInitializer() { CompileAndVerifyUnsafe( lib: """ public class C { unsafe public static System.Action M() => null; } """, caller: """ var d = new D(); d.E1 += null; d.E2 += null; d.U1 += null; d.U2 += null; unsafe { d.U1 += null; } unsafe { d.U2 += null; } var u = new U(); u.E1 += null; u.E2 += null; class D { public event System.Action E1 = default(delegate*<System.Action>)(); public event System.Action E2 = C.M(); unsafe public event System.Action U1 = default(delegate*<System.Action>)(); unsafe public event System.Action U2 = C.M(); } unsafe class U { public event System.Action E1 = default(delegate*<System.Action>)(); public event System.Action E2 = C.M(); } """, expectedUnsafeSymbols: ["C.M"], expectedSafeSymbols: ["C"], expectedDiagnostics: [ // (4,6): error CS9362: 'D.U1.add' must be used in an unsafe context because it is marked as 'unsafe' // d.U1 += null; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "+=").WithArguments("D.U1.add").WithLocation(4, 6), // (5,6): error CS9362: 'D.U2.add' must be used in an unsafe context because it is marked as 'unsafe' // d.U2 += null; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "+=").WithArguments("D.U2.add").WithLocation(5, 6), // (13,37): error CS9360: This operation may only be used in an unsafe context // public event System.Action E1 = default(delegate*<System.Action>)(); Diagnostic(ErrorCode.ERR_UnsafeOperation, "default(delegate*<System.Action>)()").WithLocation(13, 37), // (14,37): error CS9362: 'C.M()' must be used in an unsafe context because it is marked as 'unsafe' // public event System.Action E2 = C.M(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "C.M()").WithArguments("C.M()").WithLocation(14, 37), // (16,44): error CS9360: This operation may only be used in an unsafe context // unsafe public event System.Action U1 = default(delegate*<System.Action>)(); Diagnostic(ErrorCode.ERR_UnsafeOperation, "default(delegate*<System.Action>)()").WithLocation(16, 44), // (17,44): error CS9362: 'C.M()' must be used in an unsafe context because it is marked as 'unsafe' // unsafe public event System.Action U2 = C.M(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "C.M()").WithArguments("C.M()").WithLocation(17, 44), // (19,14): error CS9377: The 'unsafe' modifier does not have any effect here under the current memory safety rules. // unsafe class U Diagnostic(ErrorCode.ERR_UnsafeMeaningless, "U").WithLocation(19, 14), // (21,37): error CS9360: This operation may only be used in an unsafe context // public event System.Action E1 = default(delegate*<System.Action>)(); Diagnostic(ErrorCode.ERR_UnsafeOperation, "default(delegate*<System.Action>)()").WithLocation(21, 37), // (22,37): error CS9362: 'C.M()' must be used in an unsafe context because it is marked as 'unsafe' // public event System.Action E2 = C.M(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "C.M()").WithArguments("C.M()").WithLocation(22, 37), ], expectedDiagnosticsForLegacyCaller: [ // (13,37): error CS9360: This operation may only be used in an unsafe context // public event System.Action E1 = default(delegate*<System.Action>)(); Diagnostic(ErrorCode.ERR_UnsafeOperation, "default(delegate*<System.Action>)()").WithLocation(13, 37), ], expectedDiagnosticsWithOldLangVersion: [ // (13,45): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // public event System.Action E1 = default(delegate*<System.Action>)(); Diagnostic(ErrorCode.ERR_UnsafeNeeded, "delegate*").WithLocation(13, 45), // (13,37): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // public event System.Action E1 = default(delegate*<System.Action>)(); Diagnostic(ErrorCode.ERR_UnsafeNeeded, "default(delegate*<System.Action>)").WithLocation(13, 37), // (13,37): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // public event System.Action E1 = default(delegate*<System.Action>)(); Diagnostic(ErrorCode.ERR_UnsafeNeeded, "default(delegate*<System.Action>)()").WithLocation(13, 37), ], expectedDiagnosticsWhenReferencingLegacyLib: [ // (4,6): error CS9362: 'D.U1.add' must be used in an unsafe context because it is marked as 'unsafe' // d.U1 += null; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "+=").WithArguments("D.U1.add").WithLocation(4, 6), // (5,6): error CS9362: 'D.U2.add' must be used in an unsafe context because it is marked as 'unsafe' // d.U2 += null; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "+=").WithArguments("D.U2.add").WithLocation(5, 6), // (13,37): error CS9360: This operation may only be used in an unsafe context // public event System.Action E1 = default(delegate*<System.Action>)(); Diagnostic(ErrorCode.ERR_UnsafeOperation, "default(delegate*<System.Action>)()").WithLocation(13, 37), // (16,44): error CS9360: This operation may only be used in an unsafe context // unsafe public event System.Action U1 = default(delegate*<System.Action>)(); Diagnostic(ErrorCode.ERR_UnsafeOperation, "default(delegate*<System.Action>)()").WithLocation(16, 44), // (19,14): error CS9377: The 'unsafe' modifier does not have any effect here under the current memory safety rules. // unsafe class U Diagnostic(ErrorCode.ERR_UnsafeMeaningless, "U").WithLocation(19, 14), // (21,37): error CS9360: This operation may only be used in an unsafe context // public event System.Action E1 = default(delegate*<System.Action>)(); Diagnostic(ErrorCode.ERR_UnsafeOperation, "default(delegate*<System.Action>)()").WithLocation(21, 37), ]); } [Fact] public void Member_Constructor() { CompileAndVerifyUnsafe( lib: """ public class C { public C(int i) { } unsafe public C() { } } public unsafe class C2(int x) { int _x = x; } public class C3 { unsafe public C3() { } } """, caller: """ _ = new C(0); _ = new C(); unsafe { _ = new C(); } _ = new C2(0); _ = new C3(); unsafe { _ = new C3(); } """, expectedUnsafeSymbols: [Overload("C..ctor", parameterCount: 0)], expectedSafeSymbols: ["C", Overload("C..ctor", parameterCount: 1), "C2", "C2..ctor"], expectedDiagnostics: [ // (2,5): error CS9362: 'C.C()' must be used in an unsafe context because it is marked as 'unsafe' // _ = new C(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "new C()").WithArguments("C.C()").WithLocation(2, 5), // (5,5): error CS9362: 'C3.C3()' must be used in an unsafe context because it is marked as 'unsafe' // _ = new C3(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "new C3()").WithArguments("C3.C3()").WithLocation(5, 5), ], expectedLibDiagnostics: [ // (6,21): error CS9377: The 'unsafe' modifier does not have any effect here under the current memory safety rules. // public unsafe class C2(int x) Diagnostic(ErrorCode.ERR_UnsafeMeaningless, "C2").WithLocation(6, 21), ]); } [Fact] public void Member_Constructor_Base() { CompileAndVerifyUnsafe( lib: """ public class B { unsafe public B() { } } """, caller: """ _ = new C1(); _ = new C2(); _ = new C3(); _ = new C4(); _ = new C5(); _ = new D1(); _ = new D2(); unsafe { _ = new C5(); } class C1 : B; class C2 : B { public C2() { } public C2(int x) : base() { } } class D1() : B(); unsafe class C3 : B; unsafe class C4 : B { public C4() { } public C4(int x) : base() { } } unsafe class D2() : B(); class C5 : B { unsafe public C5() { } unsafe public C5(int x) : base() { } } """, expectedUnsafeSymbols: ["B..ctor"], expectedSafeSymbols: ["B"], expectedDiagnostics: [ // (5,5): error CS9362: 'C5.C5()' must be used in an unsafe context because it is marked as 'unsafe' // _ = new C5(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "new C5()").WithArguments("C5.C5()").WithLocation(5, 5), // (10,1): error CS9362: 'B.B()' must be used in an unsafe context because it is marked as 'unsafe' // class C1 : B; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "class C1 : B;").WithArguments("B.B()").WithLocation(10, 1), // (13,5): error CS9362: 'B.B()' must be used in an unsafe context because it is marked as 'unsafe' // public C2() { } Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "public C2() { }").WithArguments("B.B()").WithLocation(13, 5), // (14,22): error CS9362: 'B.B()' must be used in an unsafe context because it is marked as 'unsafe' // public C2(int x) : base() { } Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, ": base()").WithArguments("B.B()").WithLocation(14, 22), // (16,14): error CS9362: 'B.B()' must be used in an unsafe context because it is marked as 'unsafe' // class D1() : B(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "B()").WithArguments("B.B()").WithLocation(16, 14), // (18,1): error CS9362: 'B.B()' must be used in an unsafe context because it is marked as 'unsafe' // unsafe class C3 : B; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "unsafe class C3 : B;").WithArguments("B.B()").WithLocation(18, 1), // (18,14): error CS9377: The 'unsafe' modifier does not have any effect here under the current memory safety rules. // unsafe class C3 : B; Diagnostic(ErrorCode.ERR_UnsafeMeaningless, "C3").WithLocation(18, 14), // (19,14): error CS9377: The 'unsafe' modifier does not have any effect here under the current memory safety rules. // unsafe class C4 : B Diagnostic(ErrorCode.ERR_UnsafeMeaningless, "C4").WithLocation(19, 14), // (21,5): error CS9362: 'B.B()' must be used in an unsafe context because it is marked as 'unsafe' // public C4() { } Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "public C4() { }").WithArguments("B.B()").WithLocation(21, 5), // (22,22): error CS9362: 'B.B()' must be used in an unsafe context because it is marked as 'unsafe' // public C4(int x) : base() { } Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, ": base()").WithArguments("B.B()").WithLocation(22, 22), // (24,14): error CS9377: The 'unsafe' modifier does not have any effect here under the current memory safety rules. // unsafe class D2() : B(); Diagnostic(ErrorCode.ERR_UnsafeMeaningless, "D2").WithLocation(24, 14), // (24,21): error CS9362: 'B.B()' must be used in an unsafe context because it is marked as 'unsafe' // unsafe class D2() : B(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "B()").WithArguments("B.B()").WithLocation(24, 21), ], expectedDiagnosticsWhenReferencingLegacyLib: [ // (5,5): error CS9362: 'C5.C5()' must be used in an unsafe context because it is marked as 'unsafe' // _ = new C5(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "new C5()").WithArguments("C5.C5()").WithLocation(5, 5), // (18,14): error CS9377: The 'unsafe' modifier does not have any effect here under the current memory safety rules. // unsafe class C3 : B; Diagnostic(ErrorCode.ERR_UnsafeMeaningless, "C3").WithLocation(18, 14), // (19,14): error CS9377: The 'unsafe' modifier does not have any effect here under the current memory safety rules. // unsafe class C4 : B Diagnostic(ErrorCode.ERR_UnsafeMeaningless, "C4").WithLocation(19, 14), // (24,14): error CS9377: The 'unsafe' modifier does not have any effect here under the current memory safety rules. // unsafe class D2() : B(); Diagnostic(ErrorCode.ERR_UnsafeMeaningless, "D2").WithLocation(24, 14), ]); } [Fact] public void Member_Constructor_This() { var source = """ class C { public C() { } unsafe public C(int x) { } public C(string s) : this() { } public C(C c) : this(0) { } unsafe public C(int[] a) : this(0) { } } """; CreateCompilation([source], options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // (6,19): error CS9362: 'C.C(int)' must be used in an unsafe context because it is marked as 'unsafe' // public C(C c) : this(0) { } Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, ": this(0)").WithArguments("C.C(int)").WithLocation(6, 19)); CreateCompilation([source], options: TestOptions.UnsafeReleaseDll) .VerifyDiagnostics(); } [Theory, CombinatorialData] public void Member_Constructor_UnsafeContextInInitializer(bool updatedRules) { var source = """ class B { protected B(int x) { } } class C : B { C(int x) : base(*default(int*)) { } unsafe C(long x) : base(*default(int*)) { } C(short x) : this(*default(int*)) { } unsafe C(byte x) : this(*default(int*)) { } } """; CreateCompilation([source], options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules(updatedRules)) .VerifyDiagnostics( // (7,21): error CS9360: This operation may only be used in an unsafe context // C(int x) : base(*default(int*)) { } Diagnostic(ErrorCode.ERR_UnsafeOperation, "*").WithLocation(7, 21), // (9,23): error CS9360: This operation may only be used in an unsafe context // C(short x) : this(*default(int*)) { } Diagnostic(ErrorCode.ERR_UnsafeOperation, "*").WithLocation(9, 23)); } [Fact] public void Member_Constructor_Static() { CompileAndVerifyUnsafe( lib: """ public class C { public static readonly int F = 42; unsafe static C() { } } """, caller: """ _ = C.F; """, optionsDll: TestOptions.UnsafeReleaseDll.WithMetadataImportOptions(MetadataImportOptions.All), expectedUnsafeSymbols: [], expectedSafeSymbols: ["C", "C..cctor"], expectedDiagnostics: [], expectedLibDiagnostics: [ // (4,5): error CS9377: The 'unsafe' modifier does not have any effect here under the current memory safety rules. // unsafe static C() { } Diagnostic(ErrorCode.ERR_UnsafeMeaningless, "unsafe").WithLocation(4, 5), ]); } [Fact] public void Member_Constructor_Static_OtherModifierErrors() { var source = """ class C { unsafe virtual static C() { } } """; CreateCompilation(source, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()).VerifyDiagnostics( // (3,5): error CS9377: The 'unsafe' modifier does not have any effect here under the current memory safety rules. // unsafe virtual static C() { } Diagnostic(ErrorCode.ERR_UnsafeMeaningless, "unsafe").WithLocation(3, 5), // (3,27): error CS0106: The modifier 'virtual' is not valid for this item // unsafe virtual static C() { } Diagnostic(ErrorCode.ERR_BadMemberFlag, "C").WithArguments("virtual").WithLocation(3, 27)); } [Fact] public void Member_Constructor_Attribute() { CompileAndVerifyUnsafe( lib: """ public class A : System.Attribute { unsafe public A() { } public A(int x) { } } """, caller: """ #pragma warning disable CS8321 // unused local function [A] void M1() { } [A(0)] void M2() { } unsafe { [A] void M3() { } } [A] unsafe void M4() { } """, expectedUnsafeSymbols: [Overload("A..ctor", parameterCount: 0)], expectedSafeSymbols: ["A", Overload("A..ctor", parameterCount: 1)], expectedDiagnostics: [ // (2,2): error CS9362: 'A.A()' must be used in an unsafe context because it is marked as 'unsafe' // [A] void M1() { } Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "A").WithArguments("A.A()").WithLocation(2, 2), // (5,2): error CS9362: 'A.A()' must be used in an unsafe context because it is marked as 'unsafe' // [A] unsafe void M4() { } Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "A").WithArguments("A.A()").WithLocation(5, 2), ]); } [Fact] public void Member_Constructor_NewConstraint() { CompileAndVerifyUnsafe( lib: """ public class C { unsafe public C() { } public static void M<T>() where T : new() { } } public class D<T> where T : new(); """, caller: """ using X = D<C>; C.M<C>(); _ = new D<C>(); _ = new X(); unsafe { C.M<C>(); } unsafe { _ = new D<C>(); } unsafe { _ = new X(); } """, expectedUnsafeSymbols: ["C..ctor"], expectedSafeSymbols: ["C", "C.M", "D", "D..ctor"], expectedDiagnostics: [ // (1,7): error CS9376: An unsafe context is required for constructor 'C.C()' marked as 'unsafe' to satisfy the 'new()' constraint of type parameter 'T' in 'D<T>' // using X = D<C>; Diagnostic(ErrorCode.ERR_UnsafeConstructorConstraint, "X").WithArguments("C.C()", "T", "D<T>").WithLocation(1, 7), // (2,1): error CS9376: An unsafe context is required for constructor 'C.C()' marked as 'unsafe' to satisfy the 'new()' constraint of type parameter 'T' in 'C.M<T>()' // C.M<C>(); Diagnostic(ErrorCode.ERR_UnsafeConstructorConstraint, "C.M<C>()").WithArguments("C.C()", "T", "C.M<T>()").WithLocation(2, 1), // (3,9): error CS9376: An unsafe context is required for constructor 'C.C()' marked as 'unsafe' to satisfy the 'new()' constraint of type parameter 'T' in 'D<T>' // _ = new D<C>(); Diagnostic(ErrorCode.ERR_UnsafeConstructorConstraint, "D<C>").WithArguments("C.C()", "T", "D<T>").WithLocation(3, 9), ]); } [Fact] public void Member_Constructor_NewConstraint_ExtensionMember() { CompileAndVerifyUnsafe( lib: """ public class C { unsafe public C() { } } """, caller: """ var c = new C(); _ = c.P1; _ = C.P2; static class E { extension<T>(T t) where T : new() { public int P1 => new T().GetHashCode(); } extension<T>(T) where T : new() { public static int P2 => new T().GetHashCode(); } } """, expectedUnsafeSymbols: ["C..ctor"], expectedSafeSymbols: ["C"], expectedDiagnostics: [ // (1,9): error CS9502: 'C.C()' must be used in an unsafe context because it is marked as 'unsafe' // var c = new C(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "new C()").WithArguments("C.C()").WithLocation(1, 9), // (2,5): error CS9376: An unsafe context is required for constructor 'C.C()' marked as 'unsafe' to satisfy the 'new()' constraint of type parameter 'T' in 'E.extension<T>(T).P1.get' // _ = c.P1; Diagnostic(ErrorCode.ERR_UnsafeConstructorConstraint, "c.P1").WithArguments("C.C()", "T", "E.extension<T>(T).P1.get").WithLocation(2, 5), // (3,5): error CS9376: An unsafe context is required for constructor 'C.C()' marked as 'unsafe' to satisfy the 'new()' constraint of type parameter 'T' in 'E.extension<T>(T).P2.get' // _ = C.P2; Diagnostic(ErrorCode.ERR_UnsafeConstructorConstraint, "C.P2").WithArguments("C.C()", "T", "E.extension<T>(T).P2.get").WithLocation(3, 5), ]); } [Fact] public void Member_Constructor_NewConstraint_MoreArguments() { CompileAndVerifyUnsafe( lib: """ public class C { unsafe public C() { } } """, caller: """ M1<C, int, C>(); M1<int, C, int>(); M2<C, C, C>(null, null, null); C c = null; M2(c, c, c); static void M1<T1, T2, T3>() where T2 : new() { } static void M2<T1, T2, T3>(T1 t1, T2 t2, T3 t3) where T1 : new() where T3 : new() { } """, expectedUnsafeSymbols: ["C..ctor"], expectedSafeSymbols: ["C"], expectedDiagnostics: [ // (2,1): error CS9376: An unsafe context is required for constructor 'C.C()' marked as 'unsafe' to satisfy the 'new()' constraint of type parameter 'T2' in 'M1<T1, T2, T3>()' // M1<int, C, int>(); Diagnostic(ErrorCode.ERR_UnsafeConstructorConstraint, "M1<int, C, int>()").WithArguments("C.C()", "T2", "M1<T1, T2, T3>()").WithLocation(2, 1), // (3,1): error CS9376: An unsafe context is required for constructor 'C.C()' marked as 'unsafe' to satisfy the 'new()' constraint of type parameter 'T1' in 'M2<T1, T2, T3>(T1, T2, T3)' // M2<C, C, C>(null, null, null); Diagnostic(ErrorCode.ERR_UnsafeConstructorConstraint, "M2<C, C, C>(null, null, null)").WithArguments("C.C()", "T1", "M2<T1, T2, T3>(T1, T2, T3)").WithLocation(3, 1), // (3,1): error CS9376: An unsafe context is required for constructor 'C.C()' marked as 'unsafe' to satisfy the 'new()' constraint of type parameter 'T3' in 'M2<T1, T2, T3>(T1, T2, T3)' // M2<C, C, C>(null, null, null); Diagnostic(ErrorCode.ERR_UnsafeConstructorConstraint, "M2<C, C, C>(null, null, null)").WithArguments("C.C()", "T3", "M2<T1, T2, T3>(T1, T2, T3)").WithLocation(3, 1), // (6,1): error CS9376: An unsafe context is required for constructor 'C.C()' marked as 'unsafe' to satisfy the 'new()' constraint of type parameter 'T1' in 'M2<T1, T2, T3>(T1, T2, T3)' // M2(c, c, c); Diagnostic(ErrorCode.ERR_UnsafeConstructorConstraint, "M2(c, c, c)").WithArguments("C.C()", "T1", "M2<T1, T2, T3>(T1, T2, T3)").WithLocation(6, 1), // (6,1): error CS9376: An unsafe context is required for constructor 'C.C()' marked as 'unsafe' to satisfy the 'new()' constraint of type parameter 'T3' in 'M2<T1, T2, T3>(T1, T2, T3)' // M2(c, c, c); Diagnostic(ErrorCode.ERR_UnsafeConstructorConstraint, "M2(c, c, c)").WithArguments("C.C()", "T3", "M2<T1, T2, T3>(T1, T2, T3)").WithLocation(6, 1), ]); } [Fact] public void Member_Constructor_NewConstraint_MoreConstructors() { CompileAndVerifyUnsafe( lib: """ public class C1 { unsafe public C1(int x) { } public C1() { } } public class C2 { unsafe public C2() { } } """, caller: """ M<C1>(); M<C2>(); static void M<T>() where T : new() { } """, expectedUnsafeSymbols: [Overload("C1..ctor", parameterCount: 1), "C2..ctor"], expectedSafeSymbols: ["C1", Overload("C1..ctor", parameterCount: 0), "C2"], expectedDiagnostics: [ // (2,1): error CS9376: An unsafe context is required for constructor 'C2.C2()' marked as 'unsafe' to satisfy the 'new()' constraint of type parameter 'T' in 'M<T>()' // M<C2>(); Diagnostic(ErrorCode.ERR_UnsafeConstructorConstraint, "M<C2>()").WithArguments("C2.C2()", "T", "M<T>()").WithLocation(2, 1), ]); } [Fact] public void Member_Constructor_NewConstraint_Using() { CompileAndVerifyUnsafe( lib: """ public class C { unsafe public C() { } } """, caller: """ using static D1<C>; using static unsafe D2<C>; using X1 = D1<C>; using unsafe X2 = D2<C>; _ = new X1(); X1 x1 = new(); X1.M1(); M1(); D1<C>.M1(); _ = new X2(); X2 x2 = new(); X2.M2(); M2(); D2<C>.M2(); class D1<T> where T : new() { public static void M1() { } } class D2<T> where T : new() { public static void M2() { } } """, expectedUnsafeSymbols: ["C..ctor"], expectedSafeSymbols: ["C"], expectedDiagnostics: [ // (1,14): error CS9376: An unsafe context is required for constructor 'C.C()' marked as 'unsafe' to satisfy the 'new()' constraint of type parameter 'T' in 'D1<T>' // using static D1<C>; Diagnostic(ErrorCode.ERR_UnsafeConstructorConstraint, "D1<C>").WithArguments("C.C()", "T", "D1<T>").WithLocation(1, 14), // (3,7): error CS9376: An unsafe context is required for constructor 'C.C()' marked as 'unsafe' to satisfy the 'new()' constraint of type parameter 'T' in 'D1<T>' // using X1 = D1<C>; Diagnostic(ErrorCode.ERR_UnsafeConstructorConstraint, "X1").WithArguments("C.C()", "T", "D1<T>").WithLocation(3, 7), // (10,1): error CS9376: An unsafe context is required for constructor 'C.C()' marked as 'unsafe' to satisfy the 'new()' constraint of type parameter 'T' in 'D1<T>' // D1<C>.M1(); Diagnostic(ErrorCode.ERR_UnsafeConstructorConstraint, "D1<C>").WithArguments("C.C()", "T", "D1<T>").WithLocation(10, 1), // (16,1): error CS9376: An unsafe context is required for constructor 'C.C()' marked as 'unsafe' to satisfy the 'new()' constraint of type parameter 'T' in 'D2<T>' // D2<C>.M2(); Diagnostic(ErrorCode.ERR_UnsafeConstructorConstraint, "D2<C>").WithArguments("C.C()", "T", "D2<T>").WithLocation(16, 1), ]); } [Fact] public void Member_Constructor_NewConstraint_Namespace() { CompileAndVerifyUnsafe( lib: """ public class C { unsafe public C() { } } namespace N { public class D<T> where T : new(); } """, caller: """ N.D<C> x = new N.D<C>(); unsafe { N.D<C> y = new N.D<C>(); } """, expectedUnsafeSymbols: ["C..ctor"], expectedSafeSymbols: ["C", "N.D", "N.D..ctor"], expectedDiagnostics: [ // (1,1): error CS9376: An unsafe context is required for constructor 'C.C()' marked as 'unsafe' to satisfy the 'new()' constraint of type parameter 'T' in 'D<T>' // N.D<C> x = new N.D<C>(); Diagnostic(ErrorCode.ERR_UnsafeConstructorConstraint, "N.D<C>").WithArguments("C.C()", "T", "N.D<T>").WithLocation(1, 1), // (1,16): error CS9376: An unsafe context is required for constructor 'C.C()' marked as 'unsafe' to satisfy the 'new()' constraint of type parameter 'T' in 'D<T>' // N.D<C> x = new N.D<C>(); Diagnostic(ErrorCode.ERR_UnsafeConstructorConstraint, "N.D<C>").WithArguments("C.C()", "T", "N.D<T>").WithLocation(1, 16), ]); } [Fact] public void Member_Constructor_NewConstraint_NestedType_01() { CompileAndVerifyUnsafe( lib: """ public class C { unsafe public C() { } } public class D<T> where T : new() { public class Nested; } """, caller: """ D<C>.Nested x = new D<C>.Nested(); unsafe { D<C>.Nested y = new D<C>.Nested(); } """, expectedUnsafeSymbols: ["C..ctor"], expectedSafeSymbols: ["C", "D", "D..ctor", "D.Nested", "D.Nested..ctor"], expectedDiagnostics: [ // (1,1): error CS9376: An unsafe context is required for constructor 'C.C()' marked as 'unsafe' to satisfy the 'new()' constraint of type parameter 'T' in 'D<T>' // D<C>.Nested x = new D<C>.Nested(); Diagnostic(ErrorCode.ERR_UnsafeConstructorConstraint, "D<C>").WithArguments("C.C()", "T", "D<T>").WithLocation(1, 1), // (1,21): error CS9376: An unsafe context is required for constructor 'C.C()' marked as 'unsafe' to satisfy the 'new()' constraint of type parameter 'T' in 'D<T>' // D<C>.Nested x = new D<C>.Nested(); Diagnostic(ErrorCode.ERR_UnsafeConstructorConstraint, "D<C>").WithArguments("C.C()", "T", "D<T>").WithLocation(1, 21), ]); } [Fact] public void Member_Constructor_NewConstraint_NestedType_02() { CompileAndVerifyUnsafe( lib: """ public class C { unsafe public C() { } } public class D { public class Nested<T> where T : new(); } """, caller: """ D.Nested<C> x = new D.Nested<C>(); unsafe { D.Nested<C> y = new D.Nested<C>(); } """, expectedUnsafeSymbols: ["C..ctor"], expectedSafeSymbols: ["C", "D", "D..ctor", "D.Nested", "D.Nested..ctor"], expectedDiagnostics: [ // (1,1): error CS9376: An unsafe context is required for constructor 'C.C()' marked as 'unsafe' to satisfy the 'new()' constraint of type parameter 'T' in 'D.Nested<T>' // D.Nested<C> x = new D.Nested<C>(); Diagnostic(ErrorCode.ERR_UnsafeConstructorConstraint, "D.Nested<C>").WithArguments("C.C()", "T", "D.Nested<T>").WithLocation(1, 1), // (1,21): error CS9376: An unsafe context is required for constructor 'C.C()' marked as 'unsafe' to satisfy the 'new()' constraint of type parameter 'T' in 'D.Nested<T>' // D.Nested<C> x = new D.Nested<C>(); Diagnostic(ErrorCode.ERR_UnsafeConstructorConstraint, "D.Nested<C>").WithArguments("C.C()", "T", "D.Nested<T>").WithLocation(1, 21), ]); } [Fact] public void Member_Constructor_NewConstraint_ParameterlessStruct() { CompileAndVerifyUnsafe( lib: """ public class C { unsafe public C() { } } public struct S<T> where T : new(); """, caller: """ #pragma warning disable CS0219, CS0169 // unused variable, field var s = M(new S<C>()); M(new()); M(default); M(default(S<C>)); M(s with { }); X x = new(); unsafe S<C> M(S<C> s) => s; class X { S<C> f; } """, expectedUnsafeSymbols: ["C..ctor"], expectedSafeSymbols: ["C", "S", "S..ctor"], expectedDiagnostics: [ // (2,9): error CS9362: 'M(S<C>)' must be used in an unsafe context because it is marked as 'unsafe' // var s = M(new S<C>()); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "M(new S<C>())").WithArguments("M(S<C>)").WithLocation(2, 9), // (2,15): error CS9376: An unsafe context is required for constructor 'C.C()' marked as 'unsafe' to satisfy the 'new()' constraint of type parameter 'T' in 'S<T>' // var s = M(new S<C>()); Diagnostic(ErrorCode.ERR_UnsafeConstructorConstraint, "S<C>").WithArguments("C.C()", "T", "S<T>").WithLocation(2, 15), // (3,1): error CS9362: 'M(S<C>)' must be used in an unsafe context because it is marked as 'unsafe' // M(new()); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "M(new())").WithArguments("M(S<C>)").WithLocation(3, 1), // (4,1): error CS9362: 'M(S<C>)' must be used in an unsafe context because it is marked as 'unsafe' // M(default); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "M(default)").WithArguments("M(S<C>)").WithLocation(4, 1), // (5,1): error CS9362: 'M(S<C>)' must be used in an unsafe context because it is marked as 'unsafe' // M(default(S<C>)); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "M(default(S<C>))").WithArguments("M(S<C>)").WithLocation(5, 1), // (5,11): error CS9376: An unsafe context is required for constructor 'C.C()' marked as 'unsafe' to satisfy the 'new()' constraint of type parameter 'T' in 'S<T>' // M(default(S<C>)); Diagnostic(ErrorCode.ERR_UnsafeConstructorConstraint, "S<C>").WithArguments("C.C()", "T", "S<T>").WithLocation(5, 11), // (6,1): error CS9362: 'M(S<C>)' must be used in an unsafe context because it is marked as 'unsafe' // M(s with { }); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "M(s with { })").WithArguments("M(S<C>)").WithLocation(6, 1), // (9,8): error CS9376: An unsafe context is required for constructor 'C.C()' marked as 'unsafe' to satisfy the 'new()' constraint of type parameter 'T' in 'S<T>' // unsafe S<C> M(S<C> s) => s; Diagnostic(ErrorCode.ERR_UnsafeConstructorConstraint, "S<C>").WithArguments("C.C()", "T", "S<T>").WithLocation(9, 8), // (9,15): error CS9376: An unsafe context is required for constructor 'C.C()' marked as 'unsafe' to satisfy the 'new()' constraint of type parameter 'T' in 'S<T>' // unsafe S<C> M(S<C> s) => s; Diagnostic(ErrorCode.ERR_UnsafeConstructorConstraint, "S<C>").WithArguments("C.C()", "T", "S<T>").WithLocation(9, 15), ], expectedDiagnosticsWhenReferencingLegacyLib: [ // (2,9): error CS9362: 'M(S<C>)' must be used in an unsafe context because it is marked as 'unsafe' // var s = M(new S<C>()); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "M(new S<C>())").WithArguments("M(S<C>)").WithLocation(2, 9), // (3,1): error CS9362: 'M(S<C>)' must be used in an unsafe context because it is marked as 'unsafe' // M(new()); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "M(new())").WithArguments("M(S<C>)").WithLocation(3, 1), // (4,1): error CS9362: 'M(S<C>)' must be used in an unsafe context because it is marked as 'unsafe' // M(default); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "M(default)").WithArguments("M(S<C>)").WithLocation(4, 1), // (5,1): error CS9362: 'M(S<C>)' must be used in an unsafe context because it is marked as 'unsafe' // M(default(S<C>)); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "M(default(S<C>))").WithArguments("M(S<C>)").WithLocation(5, 1), // (6,1): error CS9362: 'M(S<C>)' must be used in an unsafe context because it is marked as 'unsafe' // M(s with { }); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "M(s with { })").WithArguments("M(S<C>)").WithLocation(6, 1), ]); } [Fact] public void Member_Destructor() { var comp = CreateCompilation( [ """ class C { unsafe ~C() { } void M() { Finalize(); } } class D : C { ~D() { } // implicitly calls base finalizer } """, ], options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // (3,5): error CS9377: The 'unsafe' modifier does not have any effect here under the current memory safety rules. // unsafe ~C() { } Diagnostic(ErrorCode.ERR_UnsafeMeaningless, "unsafe").WithLocation(3, 5), // (4,16): error CS0245: Destructors and object.Finalize cannot be called directly. Consider calling IDisposable.Dispose if available. // void M() { Finalize(); } Diagnostic(ErrorCode.ERR_CallingFinalizeDeprecated, "Finalize()").WithLocation(4, 16)); VerifyRequiresUnsafeAttribute( comp.SourceModule, expectedUnsafeSymbols: [], expectedSafeSymbols: ["C.Finalize"], expectedDefinition: AttributeDefinition.None); } [Fact] public void Member_Destructor_OtherModifierErrors() { var source = """ class C { unsafe virtual ~C() { } } """; CreateCompilation(source, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()).VerifyDiagnostics( // (3,5): error CS9377: The 'unsafe' modifier does not have any effect here under the current memory safety rules. // unsafe virtual ~C() { } Diagnostic(ErrorCode.ERR_UnsafeMeaningless, "unsafe").WithLocation(3, 5), // (3,21): error CS0106: The modifier 'virtual' is not valid for this item // unsafe virtual ~C() { } Diagnostic(ErrorCode.ERR_BadMemberFlag, "C").WithArguments("virtual").WithLocation(3, 21)); } [Fact] public void Member_Operator_Static() { CompileAndVerifyUnsafe( lib: """ public class C { public static C operator +(C c1, C c2) => c1; unsafe public static C operator -(C c1, C c2) => c1; } """, caller: """ var c = new C(); _ = c + c; _ = c - c; unsafe { _ = c - c; } """, expectedUnsafeSymbols: ["C.op_Subtraction"], expectedSafeSymbols: ["C.op_Addition"], expectedDiagnostics: [ // (3,5): error CS9362: 'C.operator -(C, C)' must be used in an unsafe context because it is marked as 'unsafe' // _ = c - c; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c - c").WithArguments("C.operator -(C, C)").WithLocation(3, 5), ]); } [Fact] public void Member_Operator_Static_Extension() { CompileAndVerifyUnsafe( lib: """ public class C; public static class E { extension(C) { public static C operator +(C c1, C c2) => c1; unsafe public static C operator -(C c1, C c2) => c1; } } """, caller: """ var c = new C(); _ = c + c; _ = c - c; E.op_Addition(c, c); E.op_Subtraction(c, c); unsafe { _ = c - c; } unsafe { E.op_Subtraction(c, c); } """, expectedUnsafeSymbols: ["E.op_Subtraction", ExtensionMember("E", "op_Subtraction")], expectedSafeSymbols: ["E.op_Addition", ExtensionMember("E", "op_Addition")], expectedDiagnostics: [ // (3,5): error CS9362: 'E.extension(C).operator -(C, C)' must be used in an unsafe context because it is marked as 'unsafe' // _ = c - c; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c - c").WithArguments("E.extension(C).operator -(C, C)").WithLocation(3, 5), // (5,1): error CS9362: 'E.op_Subtraction(C, C)' must be used in an unsafe context because it is marked as 'unsafe' // E.op_Subtraction(c, c); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "E.op_Subtraction(c, c)").WithArguments("E.op_Subtraction(C, C)").WithLocation(5, 1), ]); } [Fact] public void Member_Operator_Instance() { CompileAndVerifyUnsafe( lib: """ public class C { public void operator +=(C c) { } unsafe public void operator -=(C c) { } } """, caller: """ var c = new C(); c += c; c -= c; unsafe { c -= c; } """, additionalSources: [CompilerFeatureRequiredAttribute], expectedUnsafeSymbols: ["C.op_SubtractionAssignment"], expectedSafeSymbols: ["C.op_AdditionAssignment"], expectedDiagnostics: [ // (3,1): error CS9362: 'C.operator -=(C)' must be used in an unsafe context because it is marked as 'unsafe' // c -= c; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c -= c").WithArguments("C.operator -=(C)").WithLocation(3, 1), ]); } [Fact] public void Member_Operator_Instance_Extension() { CompileAndVerifyUnsafe( lib: """ public class C; public static class E { extension(C c1) { public void operator +=(C c2) { } unsafe public void operator -=(C c2) { } } } """, caller: """ var c = new C(); c += c; c -= c; E.op_AdditionAssignment(c, c); E.op_SubtractionAssignment(c, c); unsafe { c -= c; } unsafe { E.op_SubtractionAssignment(c, c); } """, additionalSources: [CompilerFeatureRequiredAttribute], expectedUnsafeSymbols: ["E.op_SubtractionAssignment", ExtensionMember("E", "op_SubtractionAssignment")], expectedSafeSymbols: ["E.op_AdditionAssignment", ExtensionMember("E", "op_AdditionAssignment")], expectedDiagnostics: [ // (3,1): error CS9362: 'E.extension(C).operator -=(C)' must be used in an unsafe context because it is marked as 'unsafe' // c -= c; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c -= c").WithArguments("E.extension(C).operator -=(C)").WithLocation(3, 1), // (5,1): error CS9362: 'E.op_SubtractionAssignment(C, C)' must be used in an unsafe context because it is marked as 'unsafe' // E.op_SubtractionAssignment(c, c); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "E.op_SubtractionAssignment(c, c)").WithArguments("E.op_SubtractionAssignment(C, C)").WithLocation(5, 1), ]); } [Fact] public void Member_Operator_Instance_Unary() { CompileAndVerifyUnsafe( lib: """ public class C { public void operator ++() { } unsafe public void operator --() { } } """, caller: """ var c = new C(); c++; c--; unsafe { c--; } """, additionalSources: [CompilerFeatureRequiredAttribute], expectedUnsafeSymbols: ["C.op_DecrementAssignment"], expectedSafeSymbols: ["C.op_IncrementAssignment"], expectedDiagnostics: [ // (3,1): error CS9362: 'C.operator --()' must be used in an unsafe context because it is marked as 'unsafe' // c--; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c--").WithArguments("C.operator --()").WithLocation(3, 1), ]); } [Fact] public void Member_Operator_Conversion() { CompileAndVerifyUnsafe( lib: """ public class C { public static implicit operator int(C c) => 0; unsafe public static implicit operator string(C c) => ""; } """, caller: """ var c = new C(); int i = c; string s = c; unsafe { string s2 = c; } """, expectedUnsafeSymbols: [OverloadByReturnType("C.op_Implicit", "System.String")], expectedSafeSymbols: [OverloadByReturnType("C.op_Implicit", "System.Int32")], expectedDiagnostics: [ // (3,12): error CS9362: 'C.implicit operator string(C)' must be used in an unsafe context because it is marked as 'unsafe' // string s = c; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c").WithArguments("C.implicit operator string(C)").WithLocation(3, 12), ]); } [Theory, CombinatorialData] public void Member_FunctionPointer(bool useCompilationReference) { var lib = CreateCompilation(""" public unsafe class C { public delegate*<string> F; } """, options: TestOptions.UnsafeReleaseDll, assemblyName: "lib") .VerifyDiagnostics(); var libRef = AsReference(lib, useCompilationReference); var source = """ var c = new C(); string s = c.F(); """; CreateCompilation(source, [libRef], options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // (2,12): error CS9363: 'C.F' must be used in an unsafe context because it has pointers in its signature // string s = c.F(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperationCompat, "c.F").WithArguments("C.F").WithLocation(2, 12), // (2,12): error CS9360: This operation may only be used in an unsafe context // string s = c.F(); Diagnostic(ErrorCode.ERR_UnsafeOperation, "c.F()").WithLocation(2, 12)); CompileAndVerify(""" var c = new C(); unsafe { string s = c.F(); } """, [libRef], options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules(), verify: Verification.Skipped, symbolValidator: m => VerifyRequiresUnsafeAttribute( m.ReferencedAssemblySymbols.Single(a => a.Name == "lib").Modules.Single(), expectedUnsafeSymbols: ["C.F"], expectedSafeSymbols: ["C", (object)getFunctionPointerType, (object)getFunctionPointerMethod], expectedUnsafeMode: CallerUnsafeMode.Implicit, expectedDefinition: AttributeDefinition.None)) .VerifyDiagnostics(); var expectedDiagnostics = new[] { // (2,12): error CS9363: 'C.F' must be used in an unsafe context because it has pointers in its signature // string s = c.F(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperationCompat, "c.F").WithArguments("C.F").WithLocation(2, 12), // (2,12): error CS9360: This operation may only be used in an unsafe context // string s = c.F(); Diagnostic(ErrorCode.ERR_UnsafeOperation, "c.F()").WithLocation(2, 12), }; CreateCompilation(source, [libRef], options: TestOptions.UnsafeReleaseExe) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, [libRef], parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseExe) .VerifyDiagnostics( // (2,12): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // string s = c.F(); Diagnostic(ErrorCode.ERR_UnsafeNeeded, "c.F()").WithLocation(2, 12)); CreateCompilation(source, [libRef], parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe) .VerifyDiagnostics(expectedDiagnostics); static Symbol getFunctionPointerType(ModuleSymbol module) { return module.GlobalNamespace.GetMember("C.F").GetTypeOrReturnType().Type; } static Symbol getFunctionPointerMethod(ModuleSymbol module) { var functionPointerType = (FunctionPointerTypeSymbol)getFunctionPointerType(module); return functionPointerType.Signature; } } [Fact] public void Member_Field() { CompileAndVerifyUnsafe( lib: """ public class C { public int F1; unsafe public int F2; public static int F3; unsafe public static int F4; public const int F5 = 0; } """, caller: """ var c = new C(); _ = c.F1; _ = c.F2; _ = C.F3; _ = C.F4; _ = C.F5; c.F1 = 1; c.F2 = 2; C.F3 = 3; C.F4 = 4; _ = new C { F1 = 1, F2 = 2 }; unsafe { _ = c.F2; _ = C.F4; c.F2 = 2; C.F4 = 4; _ = new C { F2 = 2 }; } """, expectedUnsafeSymbols: ["C.F2", "C.F4"], expectedSafeSymbols: ["C", "C.F1", "C.F3", "C.F5"], expectedDiagnostics: [ // (3,5): error CS9362: 'C.F2' must be used in an unsafe context because it is marked as 'unsafe' // _ = c.F2; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c.F2").WithArguments("C.F2").WithLocation(3, 5), // (5,5): error CS9362: 'C.F4' must be used in an unsafe context because it is marked as 'unsafe' // _ = C.F4; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "C.F4").WithArguments("C.F4").WithLocation(5, 5), // (8,1): error CS9362: 'C.F2' must be used in an unsafe context because it is marked as 'unsafe' // c.F2 = 2; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c.F2").WithArguments("C.F2").WithLocation(8, 1), // (10,1): error CS9362: 'C.F4' must be used in an unsafe context because it is marked as 'unsafe' // C.F4 = 4; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "C.F4").WithArguments("C.F4").WithLocation(10, 1), // (11,21): error CS9362: 'C.F2' must be used in an unsafe context because it is marked as 'unsafe' // _ = new C { F1 = 1, F2 = 2 }; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "F2").WithArguments("C.F2").WithLocation(11, 21), ]); CreateCompilation(""" public class C { unsafe public const int F = 0; } """, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // (3,29): error CS0106: The modifier 'unsafe' is not valid for this item // unsafe public const int F = 0; Diagnostic(ErrorCode.ERR_BadMemberFlag, "F").WithArguments("unsafe").WithLocation(3, 29)); } [Theory, CombinatorialData] public void Member_Field_ExplicitLayout([CombinatorialValues("struct", "class ")] string kind) { CompileAndVerifyUnsafe( lib: $$""" using System.Runtime.InteropServices; [StructLayout(LayoutKind.Explicit)] public {{kind}} S { [FieldOffset(0)] safe public int F1; [FieldOffset(4)] unsafe public int F2; [field: FieldOffset(0)] safe public int P1 { get; set; } [field: FieldOffset(0)] unsafe public int P2 { get; set; } [field: FieldOffset(0)] safe public event System.Action E1; [field: FieldOffset(0)] unsafe public event System.Action E2; } """, caller: """ var s = new S(); _ = s.F1; _ = s.F2; _ = s.P1; _ = s.P2; s.E1 += null; s.E2 += null; unsafe { _ = s.F2; _ = s.P2; s.E2 += null; } """, expectedUnsafeSymbols: ["S.F2", "S.P2", "S.get_P2", "S.set_P2", "S.E2", "S.add_E2", "S.remove_E2"], expectedSafeSymbols: ["S", "S.F1", "S.P1", "S.get_P1", "S.set_P1", "S.<P1>k__BackingField", "S.<P2>k__BackingField", "S.E1", "S.add_E1", "S.remove_E1", EventField("S.E1"), EventField("S.E2")], optionsDll: TestOptions.UnsafeReleaseDll.WithMetadataImportOptions(MetadataImportOptions.All), verify: Verification.FailsPEVerify, expectedDiagnostics: [ // (3,5): error CS9362: 'S.F2' must be used in an unsafe context because it is marked as 'unsafe' // _ = s.F2; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "s.F2").WithArguments("S.F2").WithLocation(3, 5), // (5,5): error CS9362: 'S.P2.get' must be used in an unsafe context because it is marked as 'unsafe' // _ = s.P2; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "s.P2").WithArguments("S.P2.get").WithLocation(5, 5), // (7,6): error CS9362: 'S.E2.add' must be used in an unsafe context because it is marked as 'unsafe' // s.E2 += null; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "+=").WithArguments("S.E2.add").WithLocation(7, 6), ]); var source = $$""" using System.Runtime.InteropServices; [StructLayout(LayoutKind.Explicit)] {{kind}} S { [FieldOffset(0)] public int F1; public const int F2 = 0; public static int F3 = 0; [field: FieldOffset(0)] public int P1 { get; set; } [field: FieldOffset(0)] public int P2 => field; public int P3 => 0; public static int P4 { get; set; } [field: FieldOffset(0)] public event System.Action E1; public static event System.Action E2; public event System.Action E3 { add { } remove { } } } [StructLayout(LayoutKind.Explicit)] public record {{kind}} R([field: FieldOffset(0)] int X); [StructLayout(LayoutKind.Explicit)] public {{kind}} P([field: FieldOffset(0)] int x) { public int X => x; } [StructLayout(LayoutKind.Explicit)] {{kind}} S2 { safe public const int F1 = 0; safe public static int F2 = 0; safe public static int P { get; set; } safe public static event System.Action E; } """; CreateCompilation([source, IsExternalInitTypeDefinition], options: TestOptions.ReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // (6,33): error CS9392: Field in an explicit or extended layout type must be marked 'unsafe' or 'safe'. // [FieldOffset(0)] public int F1; Diagnostic(ErrorCode.ERR_ExplicitOrExtendedLayoutFieldRequiresUnsafeOrSafe, "F1").WithLocation(6, 33), // (9,40): error CS9392: Field in an explicit or extended layout type must be marked 'unsafe' or 'safe'. // [field: FieldOffset(0)] public int P1 { get; set; } Diagnostic(ErrorCode.ERR_ExplicitOrExtendedLayoutFieldRequiresUnsafeOrSafe, "P1").WithLocation(9, 40), // (10,40): error CS9392: Field in an explicit or extended layout type must be marked 'unsafe' or 'safe'. // [field: FieldOffset(0)] public int P2 => field; Diagnostic(ErrorCode.ERR_ExplicitOrExtendedLayoutFieldRequiresUnsafeOrSafe, "P2").WithLocation(10, 40), // (13,56): error CS9392: Field in an explicit or extended layout type must be marked 'unsafe' or 'safe'. // [field: FieldOffset(0)] public event System.Action E1; Diagnostic(ErrorCode.ERR_ExplicitOrExtendedLayoutFieldRequiresUnsafeOrSafe, "E1").WithLocation(13, 56), // (19,52): error CS9392: Field in an explicit or extended layout type must be marked 'unsafe' or 'safe'. // public record class R([field: FieldOffset(0)] int X); Diagnostic(ErrorCode.ERR_ExplicitOrExtendedLayoutFieldRequiresUnsafeOrSafe, "X").WithLocation(19, 52), // (22,18): warning CS0657: 'field' is not a valid attribute location for this declaration. Valid attribute locations for this declaration are 'param'. All attributes in this block will be ignored. // public class P([field: FieldOffset(0)] int x) Diagnostic(ErrorCode.WRN_AttributeLocationOnBadDeclaration, "field").WithArguments("field", "param").WithLocation(22, 18), // (22,45): error CS0625: 'P.<x>P': instance field in types marked with StructLayout(LayoutKind.Explicit) must have a FieldOffset attribute // public class P([field: FieldOffset(0)] int x) Diagnostic(ErrorCode.ERR_MissingStructOffset, "x").WithArguments("P.<x>P").WithLocation(22, 45), // (22,45): error CS9392: Field in an explicit or extended layout type must be marked 'unsafe' or 'safe'. // public class P([field: FieldOffset(0)] int x) Diagnostic(ErrorCode.ERR_ExplicitOrExtendedLayoutFieldRequiresUnsafeOrSafe, "x").WithLocation(22, 45), // (30,27): error CS0106: The modifier 'safe' is not valid for this item // safe public const int F1 = 0; Diagnostic(ErrorCode.ERR_BadMemberFlag, "F1").WithArguments("safe").WithLocation(30, 27)); CreateCompilation([source, IsExternalInitTypeDefinition], options: TestOptions.ReleaseDll) .VerifyDiagnostics( // (22,18): warning CS0657: 'field' is not a valid attribute location for this declaration. Valid attribute locations for this declaration are 'param'. All attributes in this block will be ignored. // public class P([field: FieldOffset(0)] int x) Diagnostic(ErrorCode.WRN_AttributeLocationOnBadDeclaration, "field").WithArguments("field", "param").WithLocation(22, 18), // (22,45): error CS0625: 'P.<x>P': instance field in types marked with StructLayout(LayoutKind.Explicit) must have a FieldOffset attribute // public class P([field: FieldOffset(0)] int x) Diagnostic(ErrorCode.ERR_MissingStructOffset, "x").WithArguments("P.<x>P").WithLocation(22, 45), // (30,27): error CS0106: The modifier 'safe' is not valid for this item // safe public const int F1 = 0; Diagnostic(ErrorCode.ERR_BadMemberFlag, "F1").WithArguments("safe").WithLocation(30, 27)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/84564")] public void Member_Field_ExplicitLayout_SemanticModelDiagnostics() { var source = """ using System.Runtime.InteropServices; [StructLayout(LayoutKind.Explicit)] struct S { [FieldOffset(0)] public int F1; [FieldOffset(4)] public int F2; } """; var compilation = CreateCompilation(source, options: TestOptions.ReleaseDll.WithUpdatedMemorySafetyRules()); var tree = compilation.SyntaxTrees.Single(); var field = tree.GetRoot().DescendantNodes().OfType<FieldDeclarationSyntax>().First(); compilation.GetSemanticModel(tree).GetDiagnostics(field.Span).Verify( // (6,33): error CS9392: Field in an explicit or extended layout type must be marked 'unsafe' or 'safe'. // [FieldOffset(0)] public int F1; Diagnostic(ErrorCode.ERR_ExplicitOrExtendedLayoutFieldRequiresUnsafeOrSafe, "F1").WithLocation(6, 33)); } [Fact] public void Member_Field_ExtendedLayout() { CompileAndVerifyUnsafe( lib: """ using System.Runtime.InteropServices; [ExtendedLayout(ExtendedLayoutKind.CStruct)] public struct S { safe public int F1; unsafe public int F2; safe public int P1 { get; set; } unsafe public int P2 { get; set; } safe public event System.Action E1; unsafe public event System.Action E2; } """, caller: """ var s = new S(); _ = s.F1; _ = s.F2; _ = s.P1; _ = s.P2; s.E1 += null; s.E2 += null; unsafe { _ = s.F2; _ = s.P2; s.E2 += null; } """, expectedUnsafeSymbols: ["S.F2", "S.P2", "S.get_P2", "S.set_P2", "S.E2", "S.add_E2", "S.remove_E2"], expectedSafeSymbols: ["S", "S.F1", "S.P1", "S.get_P1", "S.set_P1", "S.<P1>k__BackingField", "S.<P2>k__BackingField", "S.E1", "S.add_E1", "S.remove_E1", EventField("S.E1"), EventField("S.E2")], optionsDll: TestOptions.UnsafeReleaseDll.WithMetadataImportOptions(MetadataImportOptions.All), targetFramework: TargetFramework.Net110, verify: Verification.Skipped, expectedRulesAttribute: AttributeDefinition.None, expectedRequiresUnsafeAttribute: AttributeDefinition.None, expectedDiagnostics: [ // (3,5): error CS9362: 'S.F2' must be used in an unsafe context because it is marked as 'unsafe' // _ = s.F2; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "s.F2").WithArguments("S.F2").WithLocation(3, 5), // (5,5): error CS9362: 'S.P2.get' must be used in an unsafe context because it is marked as 'unsafe' // _ = s.P2; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "s.P2").WithArguments("S.P2.get").WithLocation(5, 5), // (7,6): error CS9362: 'S.E2.add' must be used in an unsafe context because it is marked as 'unsafe' // s.E2 += null; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "+=").WithArguments("S.E2.add").WithLocation(7, 6), ]); var source = """ using System.Runtime.InteropServices; [ExtendedLayout(ExtendedLayoutKind.CStruct)] struct S { public int F1; public const int F2 = 0; public static int F3 = 0; public int P1 { get; set; } public int P2 => field; public int P3 => 0; public static int P4 { get; set; } public event System.Action E1; public static event System.Action E2; public event System.Action E3 { add { } remove { } } } [ExtendedLayout(ExtendedLayoutKind.CStruct)] public record struct R(int X); [ExtendedLayout(ExtendedLayoutKind.CStruct)] public struct P(int x) { public int X => x; } [ExtendedLayout(ExtendedLayoutKind.CStruct)] struct S2 { safe public const int F1 = 0; safe public static int F2 = 0; safe public static int P { get; set; } safe public static event System.Action E; } """; CreateCompilation(source, targetFramework: TargetFramework.Net110, options: TestOptions.ReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // (6,16): error CS9392: Field in an explicit or extended layout type must be marked 'unsafe' or 'safe'. // public int F1; Diagnostic(ErrorCode.ERR_ExplicitOrExtendedLayoutFieldRequiresUnsafeOrSafe, "F1").WithLocation(6, 16), // (9,16): error CS9392: Field in an explicit or extended layout type must be marked 'unsafe' or 'safe'. // public int P1 { get; set; } Diagnostic(ErrorCode.ERR_ExplicitOrExtendedLayoutFieldRequiresUnsafeOrSafe, "P1").WithLocation(9, 16), // (10,16): error CS9392: Field in an explicit or extended layout type must be marked 'unsafe' or 'safe'. // public int P2 => field; Diagnostic(ErrorCode.ERR_ExplicitOrExtendedLayoutFieldRequiresUnsafeOrSafe, "P2").WithLocation(10, 16), // (13,32): error CS9392: Field in an explicit or extended layout type must be marked 'unsafe' or 'safe'. // public event System.Action E1; Diagnostic(ErrorCode.ERR_ExplicitOrExtendedLayoutFieldRequiresUnsafeOrSafe, "E1").WithLocation(13, 32), // (19,28): error CS9392: Field in an explicit or extended layout type must be marked 'unsafe' or 'safe'. // public record struct R(int X); Diagnostic(ErrorCode.ERR_ExplicitOrExtendedLayoutFieldRequiresUnsafeOrSafe, "X").WithLocation(19, 28), // (22,21): error CS9392: Field in an explicit or extended layout type must be marked 'unsafe' or 'safe'. // public struct P(int x) Diagnostic(ErrorCode.ERR_ExplicitOrExtendedLayoutFieldRequiresUnsafeOrSafe, "x").WithLocation(22, 21), // (30,27): error CS0106: The modifier 'safe' is not valid for this item // safe public const int F1 = 0; Diagnostic(ErrorCode.ERR_BadMemberFlag, "F1").WithArguments("safe").WithLocation(30, 27)); CreateCompilation(source, targetFramework: TargetFramework.Net110, options: TestOptions.ReleaseDll) .VerifyDiagnostics( // (30,27): error CS0106: The modifier 'safe' is not valid for this item // safe public const int F1 = 0; Diagnostic(ErrorCode.ERR_BadMemberFlag, "F1").WithArguments("safe").WithLocation(30, 27)); } [Theory, CombinatorialData] public void Member_Field_ExtendedLayout_Class(bool updatedRules) { var source = """ #pragma warning disable CS0649, CS0067 // unused field, event using System.Runtime.InteropServices; [ExtendedLayout(ExtendedLayoutKind.CStruct)] class S { public int F1; public const int F2 = 0; public static int F3 = 0; public int P1 { get; set; } public int P2 => field; public int P3 => 0; public static int P4 { get; set; } public event System.Action E1; public static event System.Action E2; public event System.Action E3 { add { } remove { } } } [ExtendedLayout(ExtendedLayoutKind.CStruct)] public record R(int X); [ExtendedLayout(ExtendedLayoutKind.CStruct)] public class P(int x) { public int X => x; } """; CreateCompilation(source, targetFramework: TargetFramework.Net110, options: TestOptions.ReleaseDll.WithUpdatedMemorySafetyRules(updatedRules)) .VerifyDiagnostics( // (5,2): error CS0592: Attribute 'ExtendedLayout' is not valid on this declaration type. It is only valid on 'struct' declarations. // [ExtendedLayout(ExtendedLayoutKind.CStruct)] class S Diagnostic(ErrorCode.ERR_AttributeOnBadSymbolType, "ExtendedLayout").WithArguments("ExtendedLayout", "struct").WithLocation(5, 2), // (19,2): error CS0592: Attribute 'ExtendedLayout' is not valid on this declaration type. It is only valid on 'struct' declarations. // [ExtendedLayout(ExtendedLayoutKind.CStruct)] public record R(int X); Diagnostic(ErrorCode.ERR_AttributeOnBadSymbolType, "ExtendedLayout").WithArguments("ExtendedLayout", "struct").WithLocation(19, 2), // (21,2): error CS0592: Attribute 'ExtendedLayout' is not valid on this declaration type. It is only valid on 'struct' declarations. // [ExtendedLayout(ExtendedLayoutKind.CStruct)] public class P(int x) Diagnostic(ErrorCode.ERR_AttributeOnBadSymbolType, "ExtendedLayout").WithArguments("ExtendedLayout", "struct").WithLocation(21, 2)); } [Theory, CombinatorialData] public void Member_Field_OtherLayout( bool updatedRules, [CombinatorialValues("struct", "class")] string kind, [CombinatorialValues("LayoutKind.Sequential", "LayoutKind.Auto")] string layoutKind) { var source = $$""" using System.Runtime.InteropServices; [StructLayout({{layoutKind}})] public {{kind}} S { safe public int F; safe public int P { get; set; } safe public event System.Action E; } """; CreateCompilation(source, options: TestOptions.ReleaseDll.WithUpdatedMemorySafetyRules(updatedRules)) .VerifyEmitDiagnostics(); } [Fact] public void Member_Field_PointerType() { var source = """ var c = new C(); _ = c.F1; _ = c.F2; c.F2 = default; _ = c.F3; c.F3 = default; _ = new C { F2 = default, F3 = default }; public class C { public int F1; public int* F2; public delegate*<void> F3; } """; var comp = CreateCompilation(source, options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()); comp.VerifyDiagnostics(); VerifyRequiresUnsafeAttribute( comp.SourceModule, expectedUnsafeSymbols: [], expectedSafeSymbols: ["C", "C.F1", "C.F2", "C.F3"], expectedDefinition: AttributeDefinition.None); } [Fact] public void Member_Field_Enum() { CompileAndVerifyUnsafe( lib: """ public enum E { A, B, } """, caller: """ _ = E.A; _ = E.B; """, expectedUnsafeSymbols: [], expectedSafeSymbols: ["E", "E.A", "E.B"], expectedDiagnostics: []); CreateCompilation(""" public enum E { unsafe A, } """, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // (2,2): error CS1513: } expected // { Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(2, 2), // (3,12): error CS0116: A namespace cannot directly contain members such as fields, methods or statements // unsafe A, Diagnostic(ErrorCode.ERR_NamespaceUnexpected, "A").WithLocation(3, 12), // (3,13): error CS1022: Type or namespace definition, or end-of-file expected // unsafe A, Diagnostic(ErrorCode.ERR_EOFExpected, ",").WithLocation(3, 13), // (4,1): error CS1022: Type or namespace definition, or end-of-file expected // } Diagnostic(ErrorCode.ERR_EOFExpected, "}").WithLocation(4, 1)); } /// <summary> /// The compiler does not actually use the inline array's field, hence there is no diagnostic. /// </summary> [Fact] public void Member_Field_InlineArray() { var source = """ using System; class Program { static void M(Buffer buffer, int index) { _ = buffer[index]; _ = buffer[..]; Span<int> span = buffer; ReadOnlySpan<int> readOnlySpan = buffer; unsafe { _ = buffer[index]; _ = buffer[..]; Span<int> unsafeSpan = buffer; ReadOnlySpan<int> unsafeReadOnlySpan = buffer; } } } [System.Runtime.CompilerServices.InlineArray(3)] public struct Buffer { unsafe private int _element0; } """; CreateCompilation( source, targetFramework: TargetFramework.Net100, options: TestOptions.UnsafeReleaseDll) .VerifyEmitDiagnostics(); CreateCompilation( source, targetFramework: TargetFramework.Net100, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyEmitDiagnostics(); } [Fact] public void Member_Field_UnsafeInitializer() { CompileAndVerifyUnsafe( lib: """ public class C { unsafe public static int M() => 0; } """, caller: """ var d = new D(); class D { int F1 = *default(int*); int F2 = C.M(); unsafe int U1 = *default(int*); unsafe int U2 = C.M(); } unsafe class U { int F1 = *default(int*); int F2 = C.M(); } """, expectedUnsafeSymbols: ["C.M"], expectedSafeSymbols: ["C"], expectedDiagnostics: [ // (4,14): error CS9360: This operation may only be used in an unsafe context // int F1 = *default(int*); Diagnostic(ErrorCode.ERR_UnsafeOperation, "*").WithLocation(4, 14), // (5,14): error CS9362: 'C.M()' must be used in an unsafe context because it is marked as 'unsafe' // int F2 = C.M(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "C.M()").WithArguments("C.M()").WithLocation(5, 14), // (7,21): error CS9360: This operation may only be used in an unsafe context // unsafe int U1 = *default(int*); Diagnostic(ErrorCode.ERR_UnsafeOperation, "*").WithLocation(7, 21), // (8,21): error CS9362: 'C.M()' must be used in an unsafe context because it is marked as 'unsafe' // unsafe int U2 = C.M(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "C.M()").WithArguments("C.M()").WithLocation(8, 21), // (10,14): error CS9377: The 'unsafe' modifier does not have any effect here under the current memory safety rules. // unsafe class U Diagnostic(ErrorCode.ERR_UnsafeMeaningless, "U").WithLocation(10, 14), // (12,14): error CS9360: This operation may only be used in an unsafe context // int F1 = *default(int*); Diagnostic(ErrorCode.ERR_UnsafeOperation, "*").WithLocation(12, 14), // (13,14): error CS9362: 'C.M()' must be used in an unsafe context because it is marked as 'unsafe' // int F2 = C.M(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "C.M()").WithArguments("C.M()").WithLocation(13, 14), ], expectedDiagnosticsForLegacyCaller: [ // (4,14): error CS9360: This operation may only be used in an unsafe context // int F1 = *default(int*); Diagnostic(ErrorCode.ERR_UnsafeOperation, "*").WithLocation(4, 14), ], expectedDiagnosticsWithOldLangVersion: [ // (4,15): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // int F1 = *default(int*); Diagnostic(ErrorCode.ERR_UnsafeNeeded, "default(int*)").WithLocation(4, 15), // (4,23): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // int F1 = *default(int*); Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(4, 23), ], expectedDiagnosticsWhenReferencingLegacyLib: [ // (4,14): error CS9360: This operation may only be used in an unsafe context // int F1 = *default(int*); Diagnostic(ErrorCode.ERR_UnsafeOperation, "*").WithLocation(4, 14), // (7,21): error CS9360: This operation may only be used in an unsafe context // unsafe int U1 = *default(int*); Diagnostic(ErrorCode.ERR_UnsafeOperation, "*").WithLocation(7, 21), // (10,14): error CS9377: The 'unsafe' modifier does not have any effect here under the current memory safety rules. // unsafe class U Diagnostic(ErrorCode.ERR_UnsafeMeaningless, "U").WithLocation(10, 14), // (12,14): error CS9360: This operation may only be used in an unsafe context // int F1 = *default(int*); Diagnostic(ErrorCode.ERR_UnsafeOperation, "*").WithLocation(12, 14), ]); } [Theory, CombinatorialData] public void CompatMode_Field( [CombinatorialValues("int*", "int*[]", "delegate*<void>")] string type, bool useCompilationReference) { var lib = CreateCompilation($$""" public class C { public unsafe int F1; public unsafe {{type}} F2; } """, options: TestOptions.UnsafeReleaseDll, assemblyName: "lib") .VerifyDiagnostics(); var libRef = AsReference(lib, useCompilationReference); var source = """ var c = new C(); _ = c.F1; _ = c.F2; c.F2 = default; _ = new C { F2 = default }; unsafe { _ = c.F2; c.F2 = default; _ = new C { F2 = default }; } """; var expectedDiagnostics = new[] { // (3,5): error CS9363: 'C.F2' must be used in an unsafe context because it has pointers in its signature // _ = c.F2; Diagnostic(ErrorCode.ERR_UnsafeMemberOperationCompat, "c.F2").WithArguments("C.F2").WithLocation(3, 5), // (4,1): error CS9363: 'C.F2' must be used in an unsafe context because it has pointers in its signature // c.F2 = default; Diagnostic(ErrorCode.ERR_UnsafeMemberOperationCompat, "c.F2").WithArguments("C.F2").WithLocation(4, 1), // (5,13): error CS9363: 'C.F2' must be used in an unsafe context because it has pointers in its signature // _ = new C { F2 = default }; Diagnostic(ErrorCode.ERR_UnsafeMemberOperationCompat, "F2").WithArguments("C.F2").WithLocation(5, 13), }; CreateCompilation(source, [libRef], options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(expectedDiagnostics); CompileAndVerify(""" var c = new C(); _ = c.F1; unsafe { _ = c.F2; c.F2 = default; _ = new C { F2 = default }; } """, [libRef], options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules(), verify: Verification.Skipped, symbolValidator: m => VerifyRequiresUnsafeAttribute( m.ReferencedAssemblySymbols.Single(a => a.Name == "lib").Modules.Single(), expectedUnsafeSymbols: ["C.F2"], expectedSafeSymbols: ["C", "C.F1"], expectedUnsafeMode: CallerUnsafeMode.Implicit, expectedDefinition: AttributeDefinition.None)) .VerifyDiagnostics(); CreateCompilation(source, [libRef], parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseExe) .VerifyDiagnostics( // (3,1): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // _ = c.F2; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "_ = c.F2").WithLocation(3, 1), // (3,5): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // _ = c.F2; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "c.F2").WithLocation(3, 5), // (4,1): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // c.F2 = default; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "c.F2").WithLocation(4, 1), // (4,1): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // c.F2 = default; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "c.F2 = default").WithLocation(4, 1)); CreateCompilation(source, [libRef], options: TestOptions.UnsafeReleaseExe) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, [libRef], parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe) .VerifyDiagnostics(expectedDiagnostics); } [Theory, CombinatorialData] public void CompatMode_Method_ParameterType( [CombinatorialValues("int*", "int*[]", "delegate*<void>")] string parameterType, bool unsafeOnType, bool useCompilationReference) { var (typeModifier, methodModifier) = unsafeOnType ? ("unsafe", "") : ("", "unsafe"); var lib = CreateCompilation($$""" public {{typeModifier}} class C { public {{methodModifier}} void M1(int x) { } public {{methodModifier}} void M2({{parameterType}} y) { } } """, options: TestOptions.UnsafeReleaseDll, assemblyName: "lib") .VerifyDiagnostics(); var libRef = AsReference(lib, useCompilationReference); var source = """ var c = new C(); c.M1(0); c.M2(null); unsafe { c.M2(null); } """; var expectedDiagnostics = new[] { // (3,1): error CS9363: 'C.M2(int*)' must be used in an unsafe context because it has pointers in its signature // c.M2(null); Diagnostic(ErrorCode.ERR_UnsafeMemberOperationCompat, "c.M2(null)").WithArguments($"C.M2({parameterType})").WithLocation(3, 1), }; CreateCompilation(source, [libRef], options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(expectedDiagnostics); CompileAndVerify(""" var c = new C(); c.M1(0); unsafe { c.M2(null); } """, [libRef], options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules(), verify: Verification.Skipped, symbolValidator: m => VerifyRequiresUnsafeAttribute( m.ReferencedAssemblySymbols.Single(a => a.Name == "lib").Modules.Single(), expectedUnsafeSymbols: ["C.M2"], expectedSafeSymbols: ["C", "C.M1"], expectedUnsafeMode: CallerUnsafeMode.Implicit, expectedDefinition: AttributeDefinition.None)) .VerifyDiagnostics(); CreateCompilation(source, [libRef], parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseExe) .VerifyDiagnostics( // (3,6): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // c.M2(null); Diagnostic(ErrorCode.ERR_UnsafeNeeded, "null").WithLocation(3, 6), // (3,1): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // c.M2(null); Diagnostic(ErrorCode.ERR_UnsafeNeeded, "c.M2(null)").WithLocation(3, 1)); CreateCompilation(source, [libRef], options: TestOptions.UnsafeReleaseExe) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, [libRef], parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe) .VerifyDiagnostics(expectedDiagnostics); } [Theory, CombinatorialData] public void CompatMode_Method_ReturnType( [CombinatorialValues("int*", "int*[]", "delegate*<void>")] string returnType, bool useCompilationReference) { var lib = CreateCompilation($$""" public class C { public unsafe int M1(int i) => i; public unsafe {{returnType}} M2(string s) => null; } """, options: TestOptions.UnsafeReleaseDll, assemblyName: "lib") .VerifyDiagnostics(); var libRef = AsReference(lib, useCompilationReference); var source = """ var c = new C(); c.M1(0); c.M2(null); unsafe { c.M2(null); } """; var expectedDiagnostics = new[] { // (3,1): error CS9363: 'C.M2(string)' must be used in an unsafe context because it has pointers in its signature // c.M2(null); Diagnostic(ErrorCode.ERR_UnsafeMemberOperationCompat, "c.M2(null)").WithArguments("C.M2(string)").WithLocation(3, 1), }; CreateCompilation(source, [libRef], options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(expectedDiagnostics); CompileAndVerify(""" var c = new C(); c.M1(0); unsafe { c.M2(null); } """, [libRef], options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules(), symbolValidator: m => VerifyRequiresUnsafeAttribute( m.ReferencedAssemblySymbols.Single(a => a.Name == "lib").Modules.Single(), expectedUnsafeSymbols: ["C.M2"], expectedSafeSymbols: ["C", "C.M1"], expectedUnsafeMode: CallerUnsafeMode.Implicit, expectedDefinition: AttributeDefinition.None)) .VerifyDiagnostics(); CreateCompilation(source, [libRef], parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseExe) .VerifyDiagnostics( // (3,1): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // c.M2(null); Diagnostic(ErrorCode.ERR_UnsafeNeeded, "c.M2(null)").WithLocation(3, 1)); CreateCompilation(source, [libRef], options: TestOptions.UnsafeReleaseExe) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, [libRef], parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe) .VerifyDiagnostics(expectedDiagnostics); } [Theory, CombinatorialData] public void CompatMode_Method_ConstraintType(bool useCompilationReference) { var lib = CreateCompilation(""" public class C { public unsafe void M<T>(T t) where T : I<int*[]> { } } public interface I<T>; public unsafe class D : I<int*[]>; """, options: TestOptions.UnsafeReleaseDll, assemblyName: "lib") .VerifyDiagnostics(); var libRef = AsReference(lib, useCompilationReference); var source = """ var c = new C(); c.M<D>(null); """; CompileAndVerify(source, [libRef], options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules(), symbolValidator: validate) .VerifyDiagnostics(); CompileAndVerify(source, [libRef], options: TestOptions.UnsafeReleaseExe, symbolValidator: validate) .VerifyDiagnostics(); static void validate(ModuleSymbol module) { VerifyRequiresUnsafeAttribute( module.ReferencedAssemblySymbols.Single(a => a.Name == "lib").Modules.Single(), expectedUnsafeSymbols: [], expectedSafeSymbols: ["C", "I", "C.M", "D"], expectedUnsafeMode: CallerUnsafeMode.Implicit, expectedDefinition: AttributeDefinition.None); } } [Theory, CombinatorialData] public void CompatMode_Method_DefaultParameterValue(bool useCompilationReference) { var lib = CreateCompilation(""" public class C { public unsafe void M(string s = nameof(I<int*[]>)) { } } public interface I<T>; """, options: TestOptions.UnsafeReleaseDll, assemblyName: "lib") .VerifyDiagnostics(); var libRef = AsReference(lib, useCompilationReference); var source = """ var c = new C(); c.M(s: null); """; CompileAndVerify(source, [libRef], options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules(), symbolValidator: validate) .VerifyDiagnostics(); CompileAndVerify(source, [libRef], options: TestOptions.UnsafeReleaseExe, symbolValidator: validate) .VerifyDiagnostics(); static void validate(ModuleSymbol module) { VerifyRequiresUnsafeAttribute( module.ReferencedAssemblySymbols.Single(a => a.Name == "lib").Modules.Single(), expectedUnsafeSymbols: [], expectedSafeSymbols: ["C", "C.M", "I"], expectedDefinition: AttributeDefinition.None); } } [Theory, CombinatorialData] public void CompatMode_Method_ExtensionMethod_ReceiverType(bool useCompilationReference) { var lib = CreateCompilation(""" public static class E { public static unsafe void M1(this int x) { } public static unsafe void M2(this int*[] y) { } } """, options: TestOptions.UnsafeReleaseDll, assemblyName: "lib") .VerifyDiagnostics(); var libRef = AsReference(lib, useCompilationReference); var source = """ 123.M1(); new int*[0].M2(); E.M1(123); E.M2(null); unsafe { new int*[0].M2(); } unsafe { E.M2(null); } """; var expectedDiagnostics = new[] { // (2,1): error CS9363: 'E.M2(int*[])' must be used in an unsafe context because it has pointers in its signature // new int*[0].M2(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperationCompat, "new int*[0].M2()").WithArguments("E.M2(int*[])").WithLocation(2, 1), // (4,1): error CS9363: 'E.M2(int*[])' must be used in an unsafe context because it has pointers in its signature // E.M2(null); Diagnostic(ErrorCode.ERR_UnsafeMemberOperationCompat, "E.M2(null)").WithArguments("E.M2(int*[])").WithLocation(4, 1), }; CreateCompilation(source, [libRef], options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(expectedDiagnostics); CompileAndVerify(""" 123.M1(); E.M1(123); unsafe { new int*[0].M2(); } unsafe { E.M2(null); } """, [libRef], options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules(), verify: Verification.Skipped, symbolValidator: m => VerifyRequiresUnsafeAttribute( m.ReferencedAssemblySymbols.Single(a => a.Name == "lib").Modules.Single(), expectedUnsafeSymbols: ["E.M2"], expectedSafeSymbols: ["E", "E.M1"], expectedUnsafeMode: CallerUnsafeMode.Implicit, expectedDefinition: AttributeDefinition.None)) .VerifyDiagnostics(); CreateCompilation(source, [libRef], parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseExe) .VerifyDiagnostics( // (2,5): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // new int*[0].M2(); Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(2, 5), // (2,1): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // new int*[0].M2(); Diagnostic(ErrorCode.ERR_UnsafeNeeded, "new int*[0]").WithLocation(2, 1), // (2,1): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // new int*[0].M2(); Diagnostic(ErrorCode.ERR_UnsafeNeeded, "new int*[0].M2()").WithLocation(2, 1), // (4,6): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // E.M2(null); Diagnostic(ErrorCode.ERR_UnsafeNeeded, "null").WithLocation(4, 6), // (4,1): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // E.M2(null); Diagnostic(ErrorCode.ERR_UnsafeNeeded, "E.M2(null)").WithLocation(4, 1)); CreateCompilation(source, [libRef], options: TestOptions.UnsafeReleaseExe) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, [libRef], parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe) .VerifyDiagnostics(expectedDiagnostics); } [Theory, CombinatorialData] public void CompatMode_Method_ExtensionMember_ReceiverType(bool useCompilationReference) { var lib = CreateCompilation(""" public unsafe static class E { extension(int x) { public void M1() { } } extension(int*[] y) { public void M2() { } } } """, options: TestOptions.UnsafeReleaseDll, assemblyName: "lib") .VerifyDiagnostics(); var libRef = AsReference(lib, useCompilationReference); var source = """ 123.M1(); new int*[0].M2(); E.M1(123); E.M2(null); unsafe { new int*[0].M2(); } unsafe { E.M2(null); } """; var expectedDiagnostics = new[] { // (2,1): error CS9363: 'E.extension(int*[]).M2()' must be used in an unsafe context because it has pointers in its signature // new int*[0].M2(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperationCompat, "new int*[0].M2()").WithArguments("E.extension(int*[]).M2()").WithLocation(2, 1), // (4,1): error CS9363: 'E.M2(int*[])' must be used in an unsafe context because it has pointers in its signature // E.M2(null); Diagnostic(ErrorCode.ERR_UnsafeMemberOperationCompat, "E.M2(null)").WithArguments("E.M2(int*[])").WithLocation(4, 1), }; CreateCompilation(source, [libRef], options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(expectedDiagnostics); CompileAndVerify(""" 123.M1(); E.M1(123); unsafe { new int*[0].M2(); } unsafe { E.M2(null); } """, [libRef], options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules(), verify: Verification.Skipped, symbolValidator: m => VerifyRequiresUnsafeAttribute( m.ReferencedAssemblySymbols.Single(a => a.Name == "lib").Modules.Single(), expectedUnsafeSymbols: ["E.M2", ExtensionMember("E", "M2")], expectedSafeSymbols: ["E", "E.M1", ExtensionMember("E", "M1")], expectedUnsafeMode: CallerUnsafeMode.Implicit, expectedDefinition: AttributeDefinition.None)) .VerifyDiagnostics(); CreateCompilation(source, [libRef], parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseExe) .VerifyDiagnostics( // (2,5): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // new int*[0].M2(); Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(2, 5), // (2,1): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // new int*[0].M2(); Diagnostic(ErrorCode.ERR_UnsafeNeeded, "new int*[0]").WithLocation(2, 1), // (2,1): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // new int*[0].M2(); Diagnostic(ErrorCode.ERR_UnsafeNeeded, "new int*[0].M2()").WithLocation(2, 1), // (4,6): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // E.M2(null); Diagnostic(ErrorCode.ERR_UnsafeNeeded, "null").WithLocation(4, 6), // (4,1): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // E.M2(null); Diagnostic(ErrorCode.ERR_UnsafeNeeded, "E.M2(null)").WithLocation(4, 1)); CreateCompilation(source, [libRef], options: TestOptions.UnsafeReleaseExe) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, [libRef], parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe) .VerifyDiagnostics(expectedDiagnostics); } [Theory, CombinatorialData] public void CompatMode_Method_Override(bool useCompilationReference) { var lib = CreateCompilation(""" public class B { public unsafe virtual int* M1() => null; public unsafe virtual int* M2() => null; public unsafe virtual int* M3() => null; public unsafe virtual void M4() { } public unsafe virtual void M5() { } public unsafe virtual void M6() { } } public class C : B { public unsafe override int* M1() => null; public unsafe new virtual int* M2() => null; public unsafe override void M4() { } public unsafe new virtual void M5() { } } """, options: TestOptions.UnsafeReleaseDll) .VerifyDiagnostics(); var libRef = AsReference(lib, useCompilationReference); CreateCompilation([""" var d1 = new D1(); d1.M1(); d1.M2(); d1.M3(); d1.M4(); d1.M5(); d1.M6(); var d2 = new D2(); d2.M1(); d2.M2(); d2.M3(); d2.M4(); d2.M5(); d2.M6(); var d3 = new D3(); d3.M1(); d3.M2(); d3.M3(); d3.M4(); d3.M5(); d3.M6(); C c = d1; c.M1(); c.M2(); c.M3(); c.M4(); c.M5(); c.M6(); class D1 : C { public override int* M1() => null; public override int* M2() => null; public override int* M3() => null; public override void M4() { } public override void M5() { } public override void M6() { } public void BaseCalls() { base.M1(); base.M2(); base.M3(); base.M4(); base.M5(); base.M6(); } } class D2 : C { public unsafe override int* M1() => null; public unsafe override int* M2() => null; public unsafe override int* M3() => null; public unsafe override void M4() { } public unsafe override void M5() { } public unsafe override void M6() { } } class D3 : C; """], [libRef], options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // (2,20): error CS9362: 'D2.M1()' must be used in an unsafe context because it is marked as 'unsafe' // var d2 = new D2(); d2.M1(); d2.M2(); d2.M3(); d2.M4(); d2.M5(); d2.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "d2.M1()").WithArguments("D2.M1()").WithLocation(2, 20), // (2,29): error CS9362: 'D2.M2()' must be used in an unsafe context because it is marked as 'unsafe' // var d2 = new D2(); d2.M1(); d2.M2(); d2.M3(); d2.M4(); d2.M5(); d2.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "d2.M2()").WithArguments("D2.M2()").WithLocation(2, 29), // (2,38): error CS9362: 'D2.M3()' must be used in an unsafe context because it is marked as 'unsafe' // var d2 = new D2(); d2.M1(); d2.M2(); d2.M3(); d2.M4(); d2.M5(); d2.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "d2.M3()").WithArguments("D2.M3()").WithLocation(2, 38), // (2,47): error CS9362: 'D2.M4()' must be used in an unsafe context because it is marked as 'unsafe' // var d2 = new D2(); d2.M1(); d2.M2(); d2.M3(); d2.M4(); d2.M5(); d2.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "d2.M4()").WithArguments("D2.M4()").WithLocation(2, 47), // (2,56): error CS9362: 'D2.M5()' must be used in an unsafe context because it is marked as 'unsafe' // var d2 = new D2(); d2.M1(); d2.M2(); d2.M3(); d2.M4(); d2.M5(); d2.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "d2.M5()").WithArguments("D2.M5()").WithLocation(2, 56), // (2,65): error CS9362: 'D2.M6()' must be used in an unsafe context because it is marked as 'unsafe' // var d2 = new D2(); d2.M1(); d2.M2(); d2.M3(); d2.M4(); d2.M5(); d2.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "d2.M6()").WithArguments("D2.M6()").WithLocation(2, 65), // (3,20): error CS9363: 'C.M1()' must be used in an unsafe context because it has pointers in its signature // var d3 = new D3(); d3.M1(); d3.M2(); d3.M3(); d3.M4(); d3.M5(); d3.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperationCompat, "d3.M1()").WithArguments("C.M1()").WithLocation(3, 20), // (3,29): error CS9363: 'C.M2()' must be used in an unsafe context because it has pointers in its signature // var d3 = new D3(); d3.M1(); d3.M2(); d3.M3(); d3.M4(); d3.M5(); d3.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperationCompat, "d3.M2()").WithArguments("C.M2()").WithLocation(3, 29), // (3,38): error CS9363: 'B.M3()' must be used in an unsafe context because it has pointers in its signature // var d3 = new D3(); d3.M1(); d3.M2(); d3.M3(); d3.M4(); d3.M5(); d3.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperationCompat, "d3.M3()").WithArguments("B.M3()").WithLocation(3, 38), // (4,11): error CS9363: 'C.M1()' must be used in an unsafe context because it has pointers in its signature // C c = d1; c.M1(); c.M2(); c.M3(); c.M4(); c.M5(); c.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperationCompat, "c.M1()").WithArguments("C.M1()").WithLocation(4, 11), // (4,19): error CS9363: 'C.M2()' must be used in an unsafe context because it has pointers in its signature // C c = d1; c.M1(); c.M2(); c.M3(); c.M4(); c.M5(); c.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperationCompat, "c.M2()").WithArguments("C.M2()").WithLocation(4, 19), // (4,27): error CS9363: 'B.M3()' must be used in an unsafe context because it has pointers in its signature // C c = d1; c.M1(); c.M2(); c.M3(); c.M4(); c.M5(); c.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperationCompat, "c.M3()").WithArguments("B.M3()").WithLocation(4, 27), // (14,31): error CS9363: 'C.M1()' must be used in an unsafe context because it has pointers in its signature // public void BaseCalls() { base.M1(); base.M2(); base.M3(); base.M4(); base.M5(); base.M6(); } Diagnostic(ErrorCode.ERR_UnsafeMemberOperationCompat, "base.M1()").WithArguments("C.M1()").WithLocation(14, 31), // (14,42): error CS9363: 'C.M2()' must be used in an unsafe context because it has pointers in its signature // public void BaseCalls() { base.M1(); base.M2(); base.M3(); base.M4(); base.M5(); base.M6(); } Diagnostic(ErrorCode.ERR_UnsafeMemberOperationCompat, "base.M2()").WithArguments("C.M2()").WithLocation(14, 42), // (14,53): error CS9363: 'B.M3()' must be used in an unsafe context because it has pointers in its signature // public void BaseCalls() { base.M1(); base.M2(); base.M3(); base.M4(); base.M5(); base.M6(); } Diagnostic(ErrorCode.ERR_UnsafeMemberOperationCompat, "base.M3()").WithArguments("B.M3()").WithLocation(14, 53), // (22,33): error CS9364: Unsafe member 'D2.M4()' cannot override safe member 'B.M4()' // public unsafe override void M4() { } Diagnostic(ErrorCode.ERR_CallerUnsafeOverridingSafe, "M4").WithArguments("D2.M4()", "B.M4()").WithLocation(22, 33), // (23,33): error CS9364: Unsafe member 'D2.M5()' cannot override safe member 'C.M5()' // public unsafe override void M5() { } Diagnostic(ErrorCode.ERR_CallerUnsafeOverridingSafe, "M5").WithArguments("D2.M5()", "C.M5()").WithLocation(23, 33), // (24,33): error CS9364: Unsafe member 'D2.M6()' cannot override safe member 'B.M6()' // public unsafe override void M6() { } Diagnostic(ErrorCode.ERR_CallerUnsafeOverridingSafe, "M6").WithArguments("D2.M6()", "B.M6()").WithLocation(24, 33)); } [Theory, CombinatorialData] public void CompatMode_Method_Implementation(bool useCompilationReference) { var lib = CreateCompilation(""" public interface I { unsafe int* M1(); unsafe void M2(); } """, options: TestOptions.UnsafeReleaseDll) .VerifyDiagnostics(); var libRef = AsReference(lib, useCompilationReference); var source = """ I i = new C1(); i.M1(); i.M2(); public class C1 : I { public int* M1() => null; public void M2() { } } public class C2 : I { int* I.M1() => null; void I.M2() { } } public class C3 : I { public unsafe int* M1() => null; public unsafe void M2() { } } public class C4 : I { unsafe int* I.M1() => null; unsafe void I.M2() { } } """; CreateCompilation([source], [libRef], options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // (2,1): error CS9363: 'I.M1()' must be used in an unsafe context because it has pointers in its signature // i.M1(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperationCompat, "i.M1()").WithArguments("I.M1()").WithLocation(2, 1), // (20,24): error CS9365: Unsafe member 'C3.M2()' cannot implicitly implement safe member 'I.M2()' // public unsafe void M2() { } Diagnostic(ErrorCode.ERR_CallerUnsafeImplicitlyImplementingSafe, "M2").WithArguments("C3.M2()", "I.M2()").WithLocation(20, 24), // (26,19): error CS9366: Unsafe member 'C4.I.M2()' cannot implement safe member 'I.M2()' // unsafe void I.M2() { } Diagnostic(ErrorCode.ERR_CallerUnsafeExplicitlyImplementingSafe, "M2").WithArguments("C4.I.M2()", "I.M2()").WithLocation(26, 19)); CreateCompilation(source, [libRef], parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseExe) .VerifyDiagnostics( // (2,1): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // i.M1(); Diagnostic(ErrorCode.ERR_UnsafeNeeded, "i.M1()").WithLocation(2, 1), // (7,12): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // public int* M1() => null; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(7, 12), // (13,5): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // int* I.M1() => null; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(13, 5)); CreateCompilation(source, [libRef], options: TestOptions.UnsafeReleaseExe) .VerifyDiagnostics( // (2,1): error CS9363: 'I.M1()' must be used in an unsafe context because it has pointers in its signature // i.M1(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperationCompat, "i.M1()").WithArguments("I.M1()").WithLocation(2, 1)); CreateCompilation(source, [libRef], parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe) .VerifyDiagnostics( // (2,1): error CS9363: 'I.M1()' must be used in an unsafe context because it has pointers in its signature // i.M1(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperationCompat, "i.M1()").WithArguments("I.M1()").WithLocation(2, 1)); } [Theory, CombinatorialData] public void CompatMode_Method_Implementation_Generic(bool useCompilationReference) { var lib = CreateCompilation(""" public interface I<T> { T M1(); void M2(); } """, options: TestOptions.UnsafeReleaseDll) .VerifyDiagnostics(); var libRef = AsReference(lib, useCompilationReference); var source = """ I<int*[]> i = new C1(); i.M1(); i.M2(); public class C1 : I<int*[]> { public int*[] M1() => null; public void M2() { } } public class C2 : I<int*[]> { int*[] I<int*[]>.M1() => null; void I<int*[]>.M2() { } } public class C3 : I<int*[]> { public unsafe int*[] M1() => null; public unsafe void M2() { } } public class C4 : I<int*[]> { unsafe int*[] I<int*[]>.M1() => null; unsafe void I<int*[]>.M2() { } } """; CreateCompilation([source], [libRef], options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // (19,26): error CS9365: Unsafe member 'C3.M1()' cannot implicitly implement safe member 'I<int*[]>.M1()' // public unsafe int*[] M1() => null; Diagnostic(ErrorCode.ERR_CallerUnsafeImplicitlyImplementingSafe, "M1").WithArguments("C3.M1()", "I<int*[]>.M1()").WithLocation(19, 26), // (20,24): error CS9365: Unsafe member 'C3.M2()' cannot implicitly implement safe member 'I<int*[]>.M2()' // public unsafe void M2() { } Diagnostic(ErrorCode.ERR_CallerUnsafeImplicitlyImplementingSafe, "M2").WithArguments("C3.M2()", "I<int*[]>.M2()").WithLocation(20, 24), // (25,29): error CS9366: Unsafe member 'C4.I<int*[]>.M1()' cannot implement safe member 'I<int*[]>.M1()' // unsafe int*[] I<int*[]>.M1() => null; Diagnostic(ErrorCode.ERR_CallerUnsafeExplicitlyImplementingSafe, "M1").WithArguments("C4.I<int*[]>.M1()", "I<int*[]>.M1()").WithLocation(25, 29), // (26,27): error CS9366: Unsafe member 'C4.I<int*[]>.M2()' cannot implement safe member 'I<int*[]>.M2()' // unsafe void I<int*[]>.M2() { } Diagnostic(ErrorCode.ERR_CallerUnsafeExplicitlyImplementingSafe, "M2").WithArguments("C4.I<int*[]>.M2()", "I<int*[]>.M2()").WithLocation(26, 27)); CreateCompilation(source, [libRef], parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseExe) .VerifyDiagnostics( // (5,21): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // public class C1 : I<int*[]> Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(5, 21), // (11,21): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // public class C2 : I<int*[]> Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(11, 21), // (23,21): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // public class C4 : I<int*[]> Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(23, 21), // (17,21): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // public class C3 : I<int*[]> Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(17, 21), // (7,12): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // public int*[] M1() => null; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(7, 12), // (13,14): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // int*[] I<int*[]>.M1() => null; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(13, 14), // (14,12): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // void I<int*[]>.M2() { } Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(14, 12), // (13,5): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // int*[] I<int*[]>.M1() => null; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(13, 5), // (1,3): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // I<int*[]> i = new C1(); Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(1, 3), // (2,1): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // i.M1(); Diagnostic(ErrorCode.ERR_UnsafeNeeded, "i.M1()").WithLocation(2, 1)); CreateCompilation(source, [libRef], options: TestOptions.UnsafeReleaseExe) .VerifyDiagnostics(); CreateCompilation(source, [libRef], parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe) .VerifyDiagnostics(); } [Theory, CombinatorialData] public void CompatMode_Property( [CombinatorialValues("int*", "int*[]", "delegate*<void>")] string type, bool useCompilationReference) { var lib = CreateCompilation($$""" public class C { public unsafe int P1 { get; set; } public unsafe {{type}} P2 { get; set; } } """, options: TestOptions.UnsafeReleaseDll, assemblyName: "lib") .VerifyDiagnostics(); var libRef = AsReference(lib, useCompilationReference); var source = """ var c = new C(); c.P1 = c.P1; c.P2 = c.P2; unsafe { c.P2 = c.P2; } """; var expectedDiagnostics = new[] { // (3,1): error CS9363: 'C.P2.set' must be used in an unsafe context because it has pointers in its signature // c.P2 = c.P2; Diagnostic(ErrorCode.ERR_UnsafeMemberOperationCompat, "c.P2").WithArguments("C.P2.set").WithLocation(3, 1), // (3,8): error CS9363: 'C.P2.get' must be used in an unsafe context because it has pointers in its signature // c.P2 = c.P2; Diagnostic(ErrorCode.ERR_UnsafeMemberOperationCompat, "c.P2").WithArguments("C.P2.get").WithLocation(3, 8), }; CreateCompilation(source, [libRef], options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(expectedDiagnostics); CompileAndVerify(""" var c = new C(); c.P1 = c.P1; unsafe { c.P2 = c.P2; } """, [libRef], options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules(), verify: Verification.Skipped, symbolValidator: m => VerifyRequiresUnsafeAttribute( m.ReferencedAssemblySymbols.Single(a => a.Name == "lib").Modules.Single(), expectedUnsafeSymbols: ["C.P2", "C.get_P2", "C.set_P2"], expectedSafeSymbols: ["C", "C.P1", "C.get_P1", "C.set_P1"], expectedUnsafeMode: CallerUnsafeMode.Implicit, expectedDefinition: AttributeDefinition.None)) .VerifyDiagnostics(); CreateCompilation(source, [libRef], parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseExe) .VerifyDiagnostics( // (3,1): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // c.P2 = c.P2; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "c.P2").WithLocation(3, 1), // (3,8): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // c.P2 = c.P2; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "c.P2").WithLocation(3, 8), // (3,1): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // c.P2 = c.P2; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "c.P2 = c.P2").WithLocation(3, 1)); CreateCompilation(source, [libRef], options: TestOptions.UnsafeReleaseExe) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, [libRef], parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe) .VerifyDiagnostics(expectedDiagnostics); } [Theory, CombinatorialData] public void CompatMode_Property_Extension_ReceiverType(bool useCompilationReference) { var lib = CreateCompilation(""" public unsafe static class E { extension(int x) { public int P1 { get => 0; set { } } } extension(int*[] y) { public int P2 { get => 0; set { } } } } """, options: TestOptions.UnsafeReleaseDll, assemblyName: "lib") .VerifyDiagnostics(); var libRef = AsReference(lib, useCompilationReference); var source = """ var x = 123; x.P1 = x.P1; new int*[0].P2 = new int*[0].P2; E.get_P1(x); E.set_P1(x, 0); E.get_P2(null); E.set_P2(null, 0); unsafe { new int*[0].P2 = new int*[0].P2; } unsafe { E.get_P2(null); E.set_P2(null, 0); } """; var expectedDiagnostics = new[] { // (3,1): error CS9363: 'E.extension(int*[]).P2.set' must be used in an unsafe context because it has pointers in its signature // new int*[0].P2 = new int*[0].P2; Diagnostic(ErrorCode.ERR_UnsafeMemberOperationCompat, "new int*[0].P2").WithArguments("E.extension(int*[]).P2.set").WithLocation(3, 1), // (3,18): error CS9363: 'E.extension(int*[]).P2.get' must be used in an unsafe context because it has pointers in its signature // new int*[0].P2 = new int*[0].P2; Diagnostic(ErrorCode.ERR_UnsafeMemberOperationCompat, "new int*[0].P2").WithArguments("E.extension(int*[]).P2.get").WithLocation(3, 18), // (5,1): error CS9363: 'E.get_P2(int*[])' must be used in an unsafe context because it has pointers in its signature // E.get_P2(null); E.set_P2(null, 0); Diagnostic(ErrorCode.ERR_UnsafeMemberOperationCompat, "E.get_P2(null)").WithArguments("E.get_P2(int*[])").WithLocation(5, 1), // (5,17): error CS9363: 'E.set_P2(int*[], int)' must be used in an unsafe context because it has pointers in its signature // E.get_P2(null); E.set_P2(null, 0); Diagnostic(ErrorCode.ERR_UnsafeMemberOperationCompat, "E.set_P2(null, 0)").WithArguments("E.set_P2(int*[], int)").WithLocation(5, 17), }; CreateCompilation(source, [libRef], options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(expectedDiagnostics); CompileAndVerify(""" var x = 123; x.P1 = x.P1; E.get_P1(x); E.set_P1(x, 0); unsafe { new int*[0].P2 = new int*[0].P2; } unsafe { E.get_P2(null); E.set_P2(null, 0); } """, [libRef], options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules(), verify: Verification.Skipped, symbolValidator: m => VerifyRequiresUnsafeAttribute( m.ReferencedAssemblySymbols.Single(a => a.Name == "lib").Modules.Single(), expectedUnsafeSymbols: [ExtensionMember("E", "P2"), "E.get_P2", ExtensionMember("E", "get_P2"), "E.set_P2", ExtensionMember("E", "set_P2")], expectedSafeSymbols: ["E", ExtensionMember("E", "P1"), "E.get_P1", ExtensionMember("E", "get_P1"), "E.set_P1", ExtensionMember("E", "set_P1")], expectedUnsafeMode: CallerUnsafeMode.Implicit, expectedDefinition: AttributeDefinition.None)) .VerifyDiagnostics(); CreateCompilation(source, [libRef], parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseExe) .VerifyDiagnostics( // (3,5): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // new int*[0].P2 = new int*[0].P2; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(3, 5), // (3,1): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // new int*[0].P2 = new int*[0].P2; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "new int*[0]").WithLocation(3, 1), // (3,22): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // new int*[0].P2 = new int*[0].P2; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(3, 22), // (3,18): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // new int*[0].P2 = new int*[0].P2; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "new int*[0]").WithLocation(3, 18), // (5,10): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // E.get_P2(null); E.set_P2(null, 0); Diagnostic(ErrorCode.ERR_UnsafeNeeded, "null").WithLocation(5, 10), // (5,1): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // E.get_P2(null); E.set_P2(null, 0); Diagnostic(ErrorCode.ERR_UnsafeNeeded, "E.get_P2(null)").WithLocation(5, 1), // (5,26): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // E.get_P2(null); E.set_P2(null, 0); Diagnostic(ErrorCode.ERR_UnsafeNeeded, "null").WithLocation(5, 26), // (5,17): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // E.get_P2(null); E.set_P2(null, 0); Diagnostic(ErrorCode.ERR_UnsafeNeeded, "E.set_P2(null, 0)").WithLocation(5, 17)); CreateCompilation(source, [libRef], options: TestOptions.UnsafeReleaseExe) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, [libRef], parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe) .VerifyDiagnostics(expectedDiagnostics); } [Theory, CombinatorialData] public void CompatMode_Indexer( [CombinatorialValues("int*", "int*[]", "delegate*<void>")] string type, bool useCompilationReference) { var lib = CreateCompilation($$""" public class C1 { public unsafe int this[int i] { get => i; set { } } } public class C2 { public unsafe {{type}} this[int i] { get => null; set { } } } """, options: TestOptions.UnsafeReleaseDll, assemblyName: "lib") .VerifyDiagnostics(); var libRef = AsReference(lib, useCompilationReference); var source = """ var c1 = new C1(); c1[0] = c1[0]; var c2 = new C2(); c2[0] = c2[0]; unsafe { c2[0] = c2[0]; } """; var expectedDiagnostics = new[] { // (4,1): error CS9363: 'C2.this[int].set' must be used in an unsafe context because it has pointers in its signature // c2[0] = c2[0]; Diagnostic(ErrorCode.ERR_UnsafeMemberOperationCompat, "c2[0]").WithArguments("C2.this[int].set").WithLocation(4, 1), // (4,9): error CS9363: 'C2.this[int].get' must be used in an unsafe context because it has pointers in its signature // c2[0] = c2[0]; Diagnostic(ErrorCode.ERR_UnsafeMemberOperationCompat, "c2[0]").WithArguments("C2.this[int].get").WithLocation(4, 9), }; CreateCompilation(source, [libRef], options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(expectedDiagnostics); CompileAndVerify(""" var c1 = new C1(); c1[0] = c1[0]; var c2 = new C2(); unsafe { c2[0] = c2[0]; } """, [libRef], options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules(), verify: Verification.Skipped, symbolValidator: m => VerifyRequiresUnsafeAttribute( m.ReferencedAssemblySymbols.Single(a => a.Name == "lib").Modules.Single(), expectedUnsafeSymbols: ["C2.this[]", "C2.get_Item", "C2.set_Item"], expectedSafeSymbols: ["C1", "C2", "C1.this[]", "C1.get_Item", "C1.set_Item"], expectedUnsafeMode: CallerUnsafeMode.Implicit, expectedDefinition: AttributeDefinition.None)) .VerifyDiagnostics(); CreateCompilation(source, [libRef], parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseExe) .VerifyDiagnostics( // (4,1): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // c2[0] = c2[0]; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "c2[0]").WithLocation(4, 1), // (4,9): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // c2[0] = c2[0]; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "c2[0]").WithLocation(4, 9), // (4,1): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // c2[0] = c2[0]; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "c2[0] = c2[0]").WithLocation(4, 1)); CreateCompilation(source, [libRef], options: TestOptions.UnsafeReleaseExe) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, [libRef], parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe) .VerifyDiagnostics(expectedDiagnostics); } [Theory, CombinatorialData] public void CompatMode_Event(bool useCompilationReference) { var lib = CreateCompilation(""" #pragma warning disable CS0067 // unused event public class C { public unsafe event System.Action E1; public unsafe event System.Action<int*[]> E2; } """, options: TestOptions.UnsafeReleaseDll, assemblyName: "lib") .VerifyDiagnostics(); var libRef = AsReference(lib, useCompilationReference); var source = """ var c = new C(); c.E1 += null; c.E2 += null; unsafe { c.E2 += null; } """; var expectedDiagnostics = new[] { // (3,6): error CS9363: 'C.E2.add' must be used in an unsafe context because it has pointers in its signature // c.E2 += null; Diagnostic(ErrorCode.ERR_UnsafeMemberOperationCompat, "+=").WithArguments("C.E2.add").WithLocation(3, 6), }; CreateCompilation(source, [libRef], options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(expectedDiagnostics); CompileAndVerify(""" var c = new C(); c.E1 += null; unsafe { c.E2 += null; } """, [libRef], options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules(), verify: Verification.Skipped, symbolValidator: m => VerifyRequiresUnsafeAttribute( m.ReferencedAssemblySymbols.Single(a => a.Name == "lib").Modules.Single(), expectedUnsafeSymbols: ["C.E2", "C.add_E2", "C.remove_E2"], expectedSafeSymbols: ["C", "C.E1", "C.add_E1", "C.remove_E1"], expectedUnsafeMode: CallerUnsafeMode.Implicit, expectedDefinition: AttributeDefinition.None)) .VerifyDiagnostics(); CreateCompilation(source, [libRef], parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseExe) .VerifyDiagnostics( // (3,1): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // c.E2 += null; Diagnostic(ErrorCode.ERR_UnsafeNeeded, "c.E2").WithLocation(3, 1)); CreateCompilation(source, [libRef], options: TestOptions.UnsafeReleaseExe) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, [libRef], parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe) .VerifyDiagnostics(expectedDiagnostics); } [Theory, CombinatorialData] public void CompatMode_Constructor(bool useCompilationReference) { var lib = CreateCompilation(""" public class C { public unsafe C() { } public unsafe C(int* p) { } } """, options: TestOptions.UnsafeReleaseDll, assemblyName: "lib") .VerifyDiagnostics(); var libRef = AsReference(lib, useCompilationReference); var source = """ _ = new C(); _ = new C(null); """; var expectedDiagnostics = new[] { // (2,5): error CS9363: 'C.C(int*)' must be used in an unsafe context because it has pointers in its signature // _ = new C(null); Diagnostic(ErrorCode.ERR_UnsafeMemberOperationCompat, "new C(null)").WithArguments("C.C(int*)").WithLocation(2, 5), }; CreateCompilation(source, [libRef], options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(expectedDiagnostics); CompileAndVerify(""" _ = new C(); unsafe { _ = new C(null); } """, [libRef], options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules(), verify: Verification.Skipped, symbolValidator: m => VerifyRequiresUnsafeAttribute( m.ReferencedAssemblySymbols.Single(a => a.Name == "lib").Modules.Single(), expectedUnsafeSymbols: [Overload("C..ctor", parameterCount: 1)], expectedSafeSymbols: ["C", Overload("C..ctor", parameterCount: 0)], expectedUnsafeMode: CallerUnsafeMode.Implicit, expectedDefinition: AttributeDefinition.None)) .VerifyDiagnostics(); CreateCompilation(source, [libRef], parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseExe) .VerifyDiagnostics( // (2,5): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // _ = new C(null); Diagnostic(ErrorCode.ERR_UnsafeNeeded, "new C(null)").WithLocation(2, 5), // (2,11): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // _ = new C(null); Diagnostic(ErrorCode.ERR_UnsafeNeeded, "null").WithLocation(2, 11)); CreateCompilation(source, [libRef], options: TestOptions.UnsafeReleaseExe) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, [libRef], parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe) .VerifyDiagnostics(expectedDiagnostics); } [Theory, CombinatorialData] public void CompatMode_Operator(bool useCompilationReference) { var lib = CreateCompilation( [ """ public class C { public unsafe void operator +=(int i) { } public unsafe void operator -=(int* p) { } } """, CompilerFeatureRequiredAttribute, ], options: TestOptions.UnsafeReleaseDll, assemblyName: "lib") .VerifyDiagnostics(); var libRef = AsReference(lib, useCompilationReference); var source = """ var c = new C(); c += 0; c -= null; """; var expectedDiagnostics = new[] { // (3,1): error CS9363: 'C.operator -=(int*)' must be used in an unsafe context because it has pointers in its signature // c -= null; Diagnostic(ErrorCode.ERR_UnsafeMemberOperationCompat, "c -= null").WithArguments("C.operator -=(int*)").WithLocation(3, 1), }; CreateCompilation(source, [libRef], options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(expectedDiagnostics); CompileAndVerify(""" var c = new C(); c += 0; unsafe { c -= null; } """, [libRef], options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules(), verify: Verification.Skipped, symbolValidator: m => VerifyRequiresUnsafeAttribute( m.ReferencedAssemblySymbols.Single(a => a.Name == "lib").Modules.Single(), expectedUnsafeSymbols: ["C.op_SubtractionAssignment"], expectedSafeSymbols: ["C", "C.op_AdditionAssignment"], expectedUnsafeMode: CallerUnsafeMode.Implicit, expectedDefinition: AttributeDefinition.None)) .VerifyDiagnostics(); CreateCompilation(source, [libRef], options: TestOptions.UnsafeReleaseExe) .VerifyEmitDiagnostics(expectedDiagnostics); } [Fact] public void Extern_Method() { var libSource = """ #pragma warning disable CS0626 // extern without attributes using System.Runtime.CompilerServices; using System.Runtime.InteropServices; public class C { public void M1() { } unsafe public extern void M2(); [DllImport("test")] unsafe public static extern void M3(); [MethodImpl(MethodImplOptions.InternalCall)] unsafe public extern void M4(); } """; var callerSource = """ var c = new C(); c.M1(); c.M2(); C.M3(); c.M4(); """; object[] unsafeSymbols = ["C.M2", "C.M3", "C.M4"]; var commonDiagnostics = new[] { // (3,1): error CS9362: 'C.M2()' must be used in an unsafe context because it is marked as 'unsafe' // c.M2(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c.M2()").WithArguments("C.M2()").WithLocation(3, 1), // (4,1): error CS9362: 'C.M3()' must be used in an unsafe context because it is marked as 'unsafe' // C.M3(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "C.M3()").WithArguments("C.M3()").WithLocation(4, 1), // (5,1): error CS9362: 'C.M4()' must be used in an unsafe context because it is marked as 'unsafe' // c.M4(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c.M4()").WithArguments("C.M4()").WithLocation(5, 1), }; CompileAndVerifyUnsafe( libSource, callerSource, verify: Verification.Skipped, expectedUnsafeSymbols: unsafeSymbols, expectedSafeSymbols: ["C", "C.M1"], expectedDiagnostics: commonDiagnostics); CreateCompilation([libSource, callerSource], options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(commonDiagnostics); CreateCompilation([libSource], parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyEmitDiagnostics(); CreateCompilation([libSource], parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // error CS8630: Invalid 'MemorySafetyRules' value: '2' for C# 14.0. Please use language version 'preview' or greater. Diagnostic(ErrorCode.ERR_CompilationOptionNotAvailable).WithArguments("MemorySafetyRules", "2", "14.0", "preview").WithLocation(1, 1)); CreateCompilation(libSource, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll) .VerifyEmitDiagnostics(); CreateCompilation(libSource, parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseDll) .VerifyEmitDiagnostics(); } [Fact] public void Extern_Method_SafeModifier() { var libSource = """ #pragma warning disable CS0626 // extern without attributes public class C { safe public extern void M2(); } """; var callerSource = """ var c = new C(); c.M2(); """; CompileAndVerifyUnsafe( libSource, callerSource, verify: Verification.Skipped, expectedUnsafeSymbols: [], expectedSafeSymbols: ["C", "C.M2"], expectedDiagnostics: []); } [Fact] public void Extern_Method_Partial_Differences() { var source = """ #pragma warning disable CS0067, CS0626, CS0824 // unused event, extern without attributes, extern constructor using System; public partial class C { public unsafe static partial void M1(); public safe static extern partial void M1(); public static partial void M2(); public unsafe static extern partial void M2(); public unsafe partial event Action E1; public safe extern partial event Action E1; public partial event Action E2; public unsafe extern partial event Action E2; public unsafe partial C(); public safe extern partial C(); } """; CreateCompilation(source, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()).VerifyDiagnostics( // (7,44): error CS0764: Both partial member declarations must be unsafe or neither may be unsafe // public safe static extern partial void M1(); Diagnostic(ErrorCode.ERR_PartialMemberUnsafeDifference, "M1").WithLocation(7, 44), // (7,44): error CS9390: Both partial member declarations must be marked 'safe' or neither may be marked 'safe' // public safe static extern partial void M1(); Diagnostic(ErrorCode.ERR_PartialMemberSafeDifference, "M1").WithLocation(7, 44), // (9,32): error CS9389: 'extern' member must be marked 'unsafe' or 'safe'. // public static partial void M2(); Diagnostic(ErrorCode.ERR_ExternMemberRequiresUnsafeOrSafe, "M2").WithLocation(9, 32), // (10,46): error CS0764: Both partial member declarations must be unsafe or neither may be unsafe // public unsafe static extern partial void M2(); Diagnostic(ErrorCode.ERR_PartialMemberUnsafeDifference, "M2").WithLocation(10, 46), // (13,45): error CS0764: Both partial member declarations must be unsafe or neither may be unsafe // public safe extern partial event Action E1; Diagnostic(ErrorCode.ERR_PartialMemberUnsafeDifference, "E1").WithLocation(13, 45), // (13,45): error CS9390: Both partial member declarations must be marked 'safe' or neither may be marked 'safe' // public safe extern partial event Action E1; Diagnostic(ErrorCode.ERR_PartialMemberSafeDifference, "E1").WithLocation(13, 45), // (15,33): error CS9389: 'extern' member must be marked 'unsafe' or 'safe'. // public partial event Action E2; Diagnostic(ErrorCode.ERR_ExternMemberRequiresUnsafeOrSafe, "E2").WithLocation(15, 33), // (16,47): error CS0764: Both partial member declarations must be unsafe or neither may be unsafe // public unsafe extern partial event Action E2; Diagnostic(ErrorCode.ERR_PartialMemberUnsafeDifference, "E2").WithLocation(16, 47), // (19,32): error CS0764: Both partial member declarations must be unsafe or neither may be unsafe // public safe extern partial C(); Diagnostic(ErrorCode.ERR_PartialMemberUnsafeDifference, "C").WithLocation(19, 32), // (19,32): error CS9390: Both partial member declarations must be marked 'safe' or neither may be marked 'safe' // public safe extern partial C(); Diagnostic(ErrorCode.ERR_PartialMemberSafeDifference, "C").WithLocation(19, 32)); } [Fact] public void Extern_Method_WithPointers() { static string getLibSource(string modifiers) => $$""" #pragma warning disable CS0626 // extern without attributes public class C { public {{modifiers}} int* M(); } """; var callerSource = """ var c = new C(); c.M(); """; var libUpdated = CreateCompilation( [getLibSource("unsafe extern")], options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(); foreach (var useCompilationReference in new[] { false, true }) { var libUpdatedRef = AsReference(libUpdated, useCompilationReference); var libAssemblySymbol = CreateCompilation(callerSource, [libUpdatedRef], options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // (2,1): error CS9362: 'C.M()' must be used in an unsafe context because it is marked as 'unsafe' // c.M(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c.M()").WithArguments("C.M()").WithLocation(2, 1)) .GetReferencedAssemblySymbol(libUpdatedRef); VerifyRequiresUnsafeAttribute( libAssemblySymbol.Modules.Single(), expectedUnsafeSymbols: ["C.M"], expectedSafeSymbols: ["C"], expectedDefinition: !useCompilationReference ? AttributeDefinition.Synthesized : AttributeDefinition.None); } CreateCompilation(getLibSource("extern")).VerifyDiagnostics(); CreateCompilation(getLibSource("extern"), parseOptions: TestOptions.Regular14) .VerifyDiagnostics( // (4,19): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // public extern int* M(); Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(4, 19)); CreateCompilation(getLibSource("extern"), parseOptions: TestOptions.RegularNext) .VerifyDiagnostics(); // When compiling the lib under legacy rules, extern members are not unsafe, but members with pointers are. var libLegacy = CreateCompilation( getLibSource("unsafe extern"), options: TestOptions.UnsafeReleaseDll) .VerifyDiagnostics(); foreach (var useCompilationReference in new[] { false, true }) { var libLegacyRef = AsReference(libLegacy, useCompilationReference); var libAssemblySymbol = CreateCompilation(callerSource, [libLegacyRef], options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // (2,1): error CS9363: 'C.M()' must be used in an unsafe context because it has pointers in its signature // c.M(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperationCompat, "c.M()").WithArguments("C.M()").WithLocation(2, 1)) .GetReferencedAssemblySymbol(libLegacyRef); VerifyRequiresUnsafeAttribute( libAssemblySymbol.Modules.Single(), expectedUnsafeSymbols: ["C.M"], expectedSafeSymbols: ["C"], expectedUnsafeMode: CallerUnsafeMode.Implicit, expectedDefinition: AttributeDefinition.None); } } [Fact] public void Extern_Method_Explicit() { CompileAndVerifyUnsafe( lib: """ #pragma warning disable CS0626 // extern without attributes public class C { unsafe public extern void M(); } """, caller: """ var c = new C(); c.M(); """, verify: Verification.Skipped, expectedUnsafeSymbols: ["C.M"], expectedSafeSymbols: ["C"], expectedDiagnostics: [ // (2,1): error CS9362: 'C.M()' must be used in an unsafe context because it is marked as 'unsafe' // c.M(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c.M()").WithArguments("C.M()").WithLocation(2, 1), ]); } [Fact] public void Extern_Method_Override() { object[] unsafeSymbols = ["B.M1", "B.M2", "B.M3"]; CompileAndVerifyUnsafe( lib: """ #pragma warning disable CS0626 // extern without attributes public class B { unsafe public extern virtual void M1(); unsafe public extern virtual void M2(); unsafe public extern virtual void M3(); public virtual void M4() { } public virtual void M5() { } public virtual void M6() { } } """, caller: """ #pragma warning disable CS0626 // extern without attributes var d1 = new D1(); d1.M1(); d1.M2(); d1.M3(); d1.M4(); d1.M5(); d1.M6(); var d2 = new D2(); d2.M1(); d2.M2(); d2.M3(); d2.M4(); d2.M5(); d2.M6(); var d3 = new D3(); d3.M1(); d3.M2(); d3.M3(); d3.M4(); d3.M5(); d3.M6(); var d4 = new D4(); d4.M1(); d4.M2(); d4.M3(); d4.M4(); d4.M5(); d4.M6(); C c = d1; c.M1(); c.M2(); c.M3(); c.M4(); c.M5(); c.M6(); class C : B { unsafe public extern override void M1(); unsafe public extern new virtual void M2(); unsafe public extern override void M4(); unsafe public extern new virtual void M5(); } class D1 : C { public override void M1() { } public override void M2() { } public override void M3() { } public override void M4() { } public override void M5() { } public override void M6() { } public void BaseCalls() { base.M1(); base.M2(); base.M3(); base.M4(); base.M5(); base.M6(); } } class D2 : C { public unsafe override void M1() { } public unsafe override void M2() { } public unsafe override void M3() { } public unsafe override void M4() { } public unsafe override void M5() { } public unsafe override void M6() { } } class D3 : C { unsafe public extern override void M1(); unsafe public extern override void M2(); unsafe public extern override void M3(); unsafe public extern override void M4(); unsafe public extern override void M5(); unsafe public extern override void M6(); } class D4 : C; """, verify: Verification.Skipped, expectedUnsafeSymbols: unsafeSymbols, expectedSafeSymbols: ["B", "B.M4", "B.M5", "B.M6"], expectedDiagnostics: [ // (3,20): error CS9362: 'D2.M1()' must be used in an unsafe context because it is marked as 'unsafe' // var d2 = new D2(); d2.M1(); d2.M2(); d2.M3(); d2.M4(); d2.M5(); d2.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "d2.M1()").WithArguments("D2.M1()").WithLocation(3, 20), // (3,29): error CS9362: 'D2.M2()' must be used in an unsafe context because it is marked as 'unsafe' // var d2 = new D2(); d2.M1(); d2.M2(); d2.M3(); d2.M4(); d2.M5(); d2.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "d2.M2()").WithArguments("D2.M2()").WithLocation(3, 29), // (3,38): error CS9362: 'D2.M3()' must be used in an unsafe context because it is marked as 'unsafe' // var d2 = new D2(); d2.M1(); d2.M2(); d2.M3(); d2.M4(); d2.M5(); d2.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "d2.M3()").WithArguments("D2.M3()").WithLocation(3, 38), // (3,47): error CS9362: 'D2.M4()' must be used in an unsafe context because it is marked as 'unsafe' // var d2 = new D2(); d2.M1(); d2.M2(); d2.M3(); d2.M4(); d2.M5(); d2.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "d2.M4()").WithArguments("D2.M4()").WithLocation(3, 47), // (3,56): error CS9362: 'D2.M5()' must be used in an unsafe context because it is marked as 'unsafe' // var d2 = new D2(); d2.M1(); d2.M2(); d2.M3(); d2.M4(); d2.M5(); d2.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "d2.M5()").WithArguments("D2.M5()").WithLocation(3, 56), // (3,65): error CS9362: 'D2.M6()' must be used in an unsafe context because it is marked as 'unsafe' // var d2 = new D2(); d2.M1(); d2.M2(); d2.M3(); d2.M4(); d2.M5(); d2.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "d2.M6()").WithArguments("D2.M6()").WithLocation(3, 65), // (4,20): error CS9362: 'D3.M1()' must be used in an unsafe context because it is marked as 'unsafe' // var d3 = new D3(); d3.M1(); d3.M2(); d3.M3(); d3.M4(); d3.M5(); d3.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "d3.M1()").WithArguments("D3.M1()").WithLocation(4, 20), // (4,29): error CS9362: 'D3.M2()' must be used in an unsafe context because it is marked as 'unsafe' // var d3 = new D3(); d3.M1(); d3.M2(); d3.M3(); d3.M4(); d3.M5(); d3.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "d3.M2()").WithArguments("D3.M2()").WithLocation(4, 29), // (4,38): error CS9362: 'D3.M3()' must be used in an unsafe context because it is marked as 'unsafe' // var d3 = new D3(); d3.M1(); d3.M2(); d3.M3(); d3.M4(); d3.M5(); d3.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "d3.M3()").WithArguments("D3.M3()").WithLocation(4, 38), // (4,47): error CS9362: 'D3.M4()' must be used in an unsafe context because it is marked as 'unsafe' // var d3 = new D3(); d3.M1(); d3.M2(); d3.M3(); d3.M4(); d3.M5(); d3.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "d3.M4()").WithArguments("D3.M4()").WithLocation(4, 47), // (4,56): error CS9362: 'D3.M5()' must be used in an unsafe context because it is marked as 'unsafe' // var d3 = new D3(); d3.M1(); d3.M2(); d3.M3(); d3.M4(); d3.M5(); d3.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "d3.M5()").WithArguments("D3.M5()").WithLocation(4, 56), // (4,65): error CS9362: 'D3.M6()' must be used in an unsafe context because it is marked as 'unsafe' // var d3 = new D3(); d3.M1(); d3.M2(); d3.M3(); d3.M4(); d3.M5(); d3.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "d3.M6()").WithArguments("D3.M6()").WithLocation(4, 65), // (5,20): error CS9362: 'C.M1()' must be used in an unsafe context because it is marked as 'unsafe' // var d4 = new D4(); d4.M1(); d4.M2(); d4.M3(); d4.M4(); d4.M5(); d4.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "d4.M1()").WithArguments("C.M1()").WithLocation(5, 20), // (5,29): error CS9362: 'C.M2()' must be used in an unsafe context because it is marked as 'unsafe' // var d4 = new D4(); d4.M1(); d4.M2(); d4.M3(); d4.M4(); d4.M5(); d4.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "d4.M2()").WithArguments("C.M2()").WithLocation(5, 29), // (5,38): error CS9362: 'B.M3()' must be used in an unsafe context because it is marked as 'unsafe' // var d4 = new D4(); d4.M1(); d4.M2(); d4.M3(); d4.M4(); d4.M5(); d4.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "d4.M3()").WithArguments("B.M3()").WithLocation(5, 38), // (5,47): error CS9362: 'C.M4()' must be used in an unsafe context because it is marked as 'unsafe' // var d4 = new D4(); d4.M1(); d4.M2(); d4.M3(); d4.M4(); d4.M5(); d4.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "d4.M4()").WithArguments("C.M4()").WithLocation(5, 47), // (5,56): error CS9362: 'C.M5()' must be used in an unsafe context because it is marked as 'unsafe' // var d4 = new D4(); d4.M1(); d4.M2(); d4.M3(); d4.M4(); d4.M5(); d4.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "d4.M5()").WithArguments("C.M5()").WithLocation(5, 56), // (6,11): error CS9362: 'C.M1()' must be used in an unsafe context because it is marked as 'unsafe' // C c = d1; c.M1(); c.M2(); c.M3(); c.M4(); c.M5(); c.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c.M1()").WithArguments("C.M1()").WithLocation(6, 11), // (6,19): error CS9362: 'C.M2()' must be used in an unsafe context because it is marked as 'unsafe' // C c = d1; c.M1(); c.M2(); c.M3(); c.M4(); c.M5(); c.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c.M2()").WithArguments("C.M2()").WithLocation(6, 19), // (6,27): error CS9362: 'B.M3()' must be used in an unsafe context because it is marked as 'unsafe' // C c = d1; c.M1(); c.M2(); c.M3(); c.M4(); c.M5(); c.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c.M3()").WithArguments("B.M3()").WithLocation(6, 27), // (6,35): error CS9362: 'C.M4()' must be used in an unsafe context because it is marked as 'unsafe' // C c = d1; c.M1(); c.M2(); c.M3(); c.M4(); c.M5(); c.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c.M4()").WithArguments("C.M4()").WithLocation(6, 35), // (6,43): error CS9362: 'C.M5()' must be used in an unsafe context because it is marked as 'unsafe' // C c = d1; c.M1(); c.M2(); c.M3(); c.M4(); c.M5(); c.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c.M5()").WithArguments("C.M5()").WithLocation(6, 43), // (12,40): error CS9364: Unsafe member 'C.M4()' cannot override safe member 'B.M4()' // unsafe public extern override void M4(); Diagnostic(ErrorCode.ERR_CallerUnsafeOverridingSafe, "M4").WithArguments("C.M4()", "B.M4()").WithLocation(12, 40), // (24,31): error CS9362: 'C.M1()' must be used in an unsafe context because it is marked as 'unsafe' // public void BaseCalls() { base.M1(); base.M2(); base.M3(); base.M4(); base.M5(); base.M6(); } Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "base.M1()").WithArguments("C.M1()").WithLocation(24, 31), // (24,42): error CS9362: 'C.M2()' must be used in an unsafe context because it is marked as 'unsafe' // public void BaseCalls() { base.M1(); base.M2(); base.M3(); base.M4(); base.M5(); base.M6(); } Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "base.M2()").WithArguments("C.M2()").WithLocation(24, 42), // (24,53): error CS9362: 'B.M3()' must be used in an unsafe context because it is marked as 'unsafe' // public void BaseCalls() { base.M1(); base.M2(); base.M3(); base.M4(); base.M5(); base.M6(); } Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "base.M3()").WithArguments("B.M3()").WithLocation(24, 53), // (24,64): error CS9362: 'C.M4()' must be used in an unsafe context because it is marked as 'unsafe' // public void BaseCalls() { base.M1(); base.M2(); base.M3(); base.M4(); base.M5(); base.M6(); } Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "base.M4()").WithArguments("C.M4()").WithLocation(24, 64), // (24,75): error CS9362: 'C.M5()' must be used in an unsafe context because it is marked as 'unsafe' // public void BaseCalls() { base.M1(); base.M2(); base.M3(); base.M4(); base.M5(); base.M6(); } Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "base.M5()").WithArguments("C.M5()").WithLocation(24, 75), // (32,33): error CS9364: Unsafe member 'D2.M4()' cannot override safe member 'B.M4()' // public unsafe override void M4() { } Diagnostic(ErrorCode.ERR_CallerUnsafeOverridingSafe, "M4").WithArguments("D2.M4()", "B.M4()").WithLocation(32, 33), // (34,33): error CS9364: Unsafe member 'D2.M6()' cannot override safe member 'B.M6()' // public unsafe override void M6() { } Diagnostic(ErrorCode.ERR_CallerUnsafeOverridingSafe, "M6").WithArguments("D2.M6()", "B.M6()").WithLocation(34, 33), // (42,40): error CS9364: Unsafe member 'D3.M4()' cannot override safe member 'B.M4()' // unsafe public extern override void M4(); Diagnostic(ErrorCode.ERR_CallerUnsafeOverridingSafe, "M4").WithArguments("D3.M4()", "B.M4()").WithLocation(42, 40), // (44,40): error CS9364: Unsafe member 'D3.M6()' cannot override safe member 'B.M6()' // unsafe public extern override void M6(); Diagnostic(ErrorCode.ERR_CallerUnsafeOverridingSafe, "M6").WithArguments("D3.M6()", "B.M6()").WithLocation(44, 40), ], expectedDiagnosticsWhenReferencingLegacyLib: [ // (3,20): error CS9362: 'D2.M1()' must be used in an unsafe context because it is marked as 'unsafe' // var d2 = new D2(); d2.M1(); d2.M2(); d2.M3(); d2.M4(); d2.M5(); d2.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "d2.M1()").WithArguments("D2.M1()").WithLocation(3, 20), // (3,29): error CS9362: 'D2.M2()' must be used in an unsafe context because it is marked as 'unsafe' // var d2 = new D2(); d2.M1(); d2.M2(); d2.M3(); d2.M4(); d2.M5(); d2.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "d2.M2()").WithArguments("D2.M2()").WithLocation(3, 29), // (3,38): error CS9362: 'D2.M3()' must be used in an unsafe context because it is marked as 'unsafe' // var d2 = new D2(); d2.M1(); d2.M2(); d2.M3(); d2.M4(); d2.M5(); d2.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "d2.M3()").WithArguments("D2.M3()").WithLocation(3, 38), // (3,47): error CS9362: 'D2.M4()' must be used in an unsafe context because it is marked as 'unsafe' // var d2 = new D2(); d2.M1(); d2.M2(); d2.M3(); d2.M4(); d2.M5(); d2.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "d2.M4()").WithArguments("D2.M4()").WithLocation(3, 47), // (3,56): error CS9362: 'D2.M5()' must be used in an unsafe context because it is marked as 'unsafe' // var d2 = new D2(); d2.M1(); d2.M2(); d2.M3(); d2.M4(); d2.M5(); d2.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "d2.M5()").WithArguments("D2.M5()").WithLocation(3, 56), // (3,65): error CS9362: 'D2.M6()' must be used in an unsafe context because it is marked as 'unsafe' // var d2 = new D2(); d2.M1(); d2.M2(); d2.M3(); d2.M4(); d2.M5(); d2.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "d2.M6()").WithArguments("D2.M6()").WithLocation(3, 65), // (4,20): error CS9362: 'D3.M1()' must be used in an unsafe context because it is marked as 'unsafe' // var d3 = new D3(); d3.M1(); d3.M2(); d3.M3(); d3.M4(); d3.M5(); d3.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "d3.M1()").WithArguments("D3.M1()").WithLocation(4, 20), // (4,29): error CS9362: 'D3.M2()' must be used in an unsafe context because it is marked as 'unsafe' // var d3 = new D3(); d3.M1(); d3.M2(); d3.M3(); d3.M4(); d3.M5(); d3.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "d3.M2()").WithArguments("D3.M2()").WithLocation(4, 29), // (4,38): error CS9362: 'D3.M3()' must be used in an unsafe context because it is marked as 'unsafe' // var d3 = new D3(); d3.M1(); d3.M2(); d3.M3(); d3.M4(); d3.M5(); d3.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "d3.M3()").WithArguments("D3.M3()").WithLocation(4, 38), // (4,47): error CS9362: 'D3.M4()' must be used in an unsafe context because it is marked as 'unsafe' // var d3 = new D3(); d3.M1(); d3.M2(); d3.M3(); d3.M4(); d3.M5(); d3.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "d3.M4()").WithArguments("D3.M4()").WithLocation(4, 47), // (4,56): error CS9362: 'D3.M5()' must be used in an unsafe context because it is marked as 'unsafe' // var d3 = new D3(); d3.M1(); d3.M2(); d3.M3(); d3.M4(); d3.M5(); d3.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "d3.M5()").WithArguments("D3.M5()").WithLocation(4, 56), // (4,65): error CS9362: 'D3.M6()' must be used in an unsafe context because it is marked as 'unsafe' // var d3 = new D3(); d3.M1(); d3.M2(); d3.M3(); d3.M4(); d3.M5(); d3.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "d3.M6()").WithArguments("D3.M6()").WithLocation(4, 65), // (5,20): error CS9362: 'C.M1()' must be used in an unsafe context because it is marked as 'unsafe' // var d4 = new D4(); d4.M1(); d4.M2(); d4.M3(); d4.M4(); d4.M5(); d4.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "d4.M1()").WithArguments("C.M1()").WithLocation(5, 20), // (5,29): error CS9362: 'C.M2()' must be used in an unsafe context because it is marked as 'unsafe' // var d4 = new D4(); d4.M1(); d4.M2(); d4.M3(); d4.M4(); d4.M5(); d4.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "d4.M2()").WithArguments("C.M2()").WithLocation(5, 29), // (5,47): error CS9362: 'C.M4()' must be used in an unsafe context because it is marked as 'unsafe' // var d4 = new D4(); d4.M1(); d4.M2(); d4.M3(); d4.M4(); d4.M5(); d4.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "d4.M4()").WithArguments("C.M4()").WithLocation(5, 47), // (5,56): error CS9362: 'C.M5()' must be used in an unsafe context because it is marked as 'unsafe' // var d4 = new D4(); d4.M1(); d4.M2(); d4.M3(); d4.M4(); d4.M5(); d4.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "d4.M5()").WithArguments("C.M5()").WithLocation(5, 56), // (6,11): error CS9362: 'C.M1()' must be used in an unsafe context because it is marked as 'unsafe' // C c = d1; c.M1(); c.M2(); c.M3(); c.M4(); c.M5(); c.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c.M1()").WithArguments("C.M1()").WithLocation(6, 11), // (6,19): error CS9362: 'C.M2()' must be used in an unsafe context because it is marked as 'unsafe' // C c = d1; c.M1(); c.M2(); c.M3(); c.M4(); c.M5(); c.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c.M2()").WithArguments("C.M2()").WithLocation(6, 19), // (6,35): error CS9362: 'C.M4()' must be used in an unsafe context because it is marked as 'unsafe' // C c = d1; c.M1(); c.M2(); c.M3(); c.M4(); c.M5(); c.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c.M4()").WithArguments("C.M4()").WithLocation(6, 35), // (6,43): error CS9362: 'C.M5()' must be used in an unsafe context because it is marked as 'unsafe' // C c = d1; c.M1(); c.M2(); c.M3(); c.M4(); c.M5(); c.M6(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c.M5()").WithArguments("C.M5()").WithLocation(6, 43), // (10,40): error CS9364: Unsafe member 'C.M1()' cannot override safe member 'B.M1()' // unsafe public extern override void M1(); Diagnostic(ErrorCode.ERR_CallerUnsafeOverridingSafe, "M1").WithArguments("C.M1()", "B.M1()").WithLocation(10, 40), // (12,40): error CS9364: Unsafe member 'C.M4()' cannot override safe member 'B.M4()' // unsafe public extern override void M4(); Diagnostic(ErrorCode.ERR_CallerUnsafeOverridingSafe, "M4").WithArguments("C.M4()", "B.M4()").WithLocation(12, 40), // (24,31): error CS9362: 'C.M1()' must be used in an unsafe context because it is marked as 'unsafe' // public void BaseCalls() { base.M1(); base.M2(); base.M3(); base.M4(); base.M5(); base.M6(); } Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "base.M1()").WithArguments("C.M1()").WithLocation(24, 31), // (24,42): error CS9362: 'C.M2()' must be used in an unsafe context because it is marked as 'unsafe' // public void BaseCalls() { base.M1(); base.M2(); base.M3(); base.M4(); base.M5(); base.M6(); } Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "base.M2()").WithArguments("C.M2()").WithLocation(24, 42), // (24,64): error CS9362: 'C.M4()' must be used in an unsafe context because it is marked as 'unsafe' // public void BaseCalls() { base.M1(); base.M2(); base.M3(); base.M4(); base.M5(); base.M6(); } Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "base.M4()").WithArguments("C.M4()").WithLocation(24, 64), // (24,75): error CS9362: 'C.M5()' must be used in an unsafe context because it is marked as 'unsafe' // public void BaseCalls() { base.M1(); base.M2(); base.M3(); base.M4(); base.M5(); base.M6(); } Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "base.M5()").WithArguments("C.M5()").WithLocation(24, 75), // (29,33): error CS9364: Unsafe member 'D2.M1()' cannot override safe member 'B.M1()' // public unsafe override void M1() { } Diagnostic(ErrorCode.ERR_CallerUnsafeOverridingSafe, "M1").WithArguments("D2.M1()", "B.M1()").WithLocation(29, 33), // (31,33): error CS9364: Unsafe member 'D2.M3()' cannot override safe member 'B.M3()' // public unsafe override void M3() { } Diagnostic(ErrorCode.ERR_CallerUnsafeOverridingSafe, "M3").WithArguments("D2.M3()", "B.M3()").WithLocation(31, 33), // (32,33): error CS9364: Unsafe member 'D2.M4()' cannot override safe member 'B.M4()' // public unsafe override void M4() { } Diagnostic(ErrorCode.ERR_CallerUnsafeOverridingSafe, "M4").WithArguments("D2.M4()", "B.M4()").WithLocation(32, 33), // (34,33): error CS9364: Unsafe member 'D2.M6()' cannot override safe member 'B.M6()' // public unsafe override void M6() { } Diagnostic(ErrorCode.ERR_CallerUnsafeOverridingSafe, "M6").WithArguments("D2.M6()", "B.M6()").WithLocation(34, 33), // (39,40): error CS9364: Unsafe member 'D3.M1()' cannot override safe member 'B.M1()' // unsafe public extern override void M1(); Diagnostic(ErrorCode.ERR_CallerUnsafeOverridingSafe, "M1").WithArguments("D3.M1()", "B.M1()").WithLocation(39, 40), // (41,40): error CS9364: Unsafe member 'D3.M3()' cannot override safe member 'B.M3()' // unsafe public extern override void M3(); Diagnostic(ErrorCode.ERR_CallerUnsafeOverridingSafe, "M3").WithArguments("D3.M3()", "B.M3()").WithLocation(41, 40), // (42,40): error CS9364: Unsafe member 'D3.M4()' cannot override safe member 'B.M4()' // unsafe public extern override void M4(); Diagnostic(ErrorCode.ERR_CallerUnsafeOverridingSafe, "M4").WithArguments("D3.M4()", "B.M4()").WithLocation(42, 40), // (44,40): error CS9364: Unsafe member 'D3.M6()' cannot override safe member 'B.M6()' // unsafe public extern override void M6(); Diagnostic(ErrorCode.ERR_CallerUnsafeOverridingSafe, "M6").WithArguments("D3.M6()", "B.M6()").WithLocation(44, 40), ]); } [Fact] public void Extern_Method_Implementation() { CompileAndVerifyUnsafe( lib: """ #pragma warning disable CS0626 // extern without attributes public interface I { unsafe extern void M1(); void M2(); } """, caller: """ #pragma warning disable CS0626 // extern without attributes I i = new C1(); i.M1(); i.M2(); public class C1 : I { public void M1() { } public void M2() { } } public class C2 : I { void I.M1() { } void I.M2() { } } public class C3 : I { public unsafe void M1() { } public unsafe void M2() { } } public class C4 : I { unsafe void I.M1() { } unsafe void I.M2() { } } public class C5 : I { unsafe public extern void M1(); unsafe public extern void M2(); } public class C6 : I { unsafe extern void I.M1(); unsafe extern void I.M2(); } """, targetFramework: TargetFramework.Net100, verify: Verification.Skipped, expectedUnsafeSymbols: ["I.M1"], expectedSafeSymbols: ["I", "I.M2"], expectedDiagnostics: [ // (3,1): error CS9362: 'I.M1()' must be used in an unsafe context because it is marked as 'unsafe' // i.M1(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "i.M1()").WithArguments("I.M1()").WithLocation(3, 1), // (21,24): error CS9365: Unsafe member 'C3.M2()' cannot implicitly implement safe member 'I.M2()' // public unsafe void M2() { } Diagnostic(ErrorCode.ERR_CallerUnsafeImplicitlyImplementingSafe, "M2").WithArguments("C3.M2()", "I.M2()").WithLocation(21, 24), // (27,19): error CS9366: Unsafe member 'C4.I.M2()' cannot implement safe member 'I.M2()' // unsafe void I.M2() { } Diagnostic(ErrorCode.ERR_CallerUnsafeExplicitlyImplementingSafe, "M2").WithArguments("C4.I.M2()", "I.M2()").WithLocation(27, 19), // (33,31): error CS9365: Unsafe member 'C5.M2()' cannot implicitly implement safe member 'I.M2()' // unsafe public extern void M2(); Diagnostic(ErrorCode.ERR_CallerUnsafeImplicitlyImplementingSafe, "M2").WithArguments("C5.M2()", "I.M2()").WithLocation(33, 31), // (39,26): error CS9366: Unsafe member 'C6.I.M2()' cannot implement safe member 'I.M2()' // unsafe extern void I.M2(); Diagnostic(ErrorCode.ERR_CallerUnsafeExplicitlyImplementingSafe, "M2").WithArguments("C6.I.M2()", "I.M2()").WithLocation(39, 26), ], expectedDiagnosticsWhenReferencingLegacyLib: [ // (20,24): error CS9365: Unsafe member 'C3.M1()' cannot implicitly implement safe member 'I.M1()' // public unsafe void M1() { } Diagnostic(ErrorCode.ERR_CallerUnsafeImplicitlyImplementingSafe, "M1").WithArguments("C3.M1()", "I.M1()").WithLocation(20, 24), // (21,24): error CS9365: Unsafe member 'C3.M2()' cannot implicitly implement safe member 'I.M2()' // public unsafe void M2() { } Diagnostic(ErrorCode.ERR_CallerUnsafeImplicitlyImplementingSafe, "M2").WithArguments("C3.M2()", "I.M2()").WithLocation(21, 24), // (26,19): error CS9366: Unsafe member 'C4.I.M1()' cannot implement safe member 'I.M1()' // unsafe void I.M1() { } Diagnostic(ErrorCode.ERR_CallerUnsafeExplicitlyImplementingSafe, "M1").WithArguments("C4.I.M1()", "I.M1()").WithLocation(26, 19), // (27,19): error CS9366: Unsafe member 'C4.I.M2()' cannot implement safe member 'I.M2()' // unsafe void I.M2() { } Diagnostic(ErrorCode.ERR_CallerUnsafeExplicitlyImplementingSafe, "M2").WithArguments("C4.I.M2()", "I.M2()").WithLocation(27, 19), // (32,31): error CS9365: Unsafe member 'C5.M1()' cannot implicitly implement safe member 'I.M1()' // unsafe public extern void M1(); Diagnostic(ErrorCode.ERR_CallerUnsafeImplicitlyImplementingSafe, "M1").WithArguments("C5.M1()", "I.M1()").WithLocation(32, 31), // (33,31): error CS9365: Unsafe member 'C5.M2()' cannot implicitly implement safe member 'I.M2()' // unsafe public extern void M2(); Diagnostic(ErrorCode.ERR_CallerUnsafeImplicitlyImplementingSafe, "M2").WithArguments("C5.M2()", "I.M2()").WithLocation(33, 31), // (38,26): error CS9366: Unsafe member 'C6.I.M1()' cannot implement safe member 'I.M1()' // unsafe extern void I.M1(); Diagnostic(ErrorCode.ERR_CallerUnsafeExplicitlyImplementingSafe, "M1").WithArguments("C6.I.M1()", "I.M1()").WithLocation(38, 26), // (39,26): error CS9366: Unsafe member 'C6.I.M2()' cannot implement safe member 'I.M2()' // unsafe extern void I.M2(); Diagnostic(ErrorCode.ERR_CallerUnsafeExplicitlyImplementingSafe, "M2").WithArguments("C6.I.M2()", "I.M2()").WithLocation(39, 26), ]); } [Fact] public void Extern_LocalFunction() { var libSource = """ #pragma warning disable CS0626 // extern without attributes #pragma warning disable CS8321 // unused local function public class C { public void M() { unsafe static extern void F(); } } """; var callerSource = """ new C().M(); """; object[] unsafeSymbols = ["C.<M>g__F|0_0"]; CompileAndVerifyUnsafe( libSource, callerSource, optionsDll: TestOptions.UnsafeReleaseDll.WithMetadataImportOptions(MetadataImportOptions.All), verify: Verification.Skipped, expectedUnsafeSymbols: unsafeSymbols, expectedSafeSymbols: ["C"], skipSymbolsInSource: unsafeSymbols, expectedDiagnostics: []); CreateCompilation([libSource, callerSource], options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(); CreateCompilation([libSource], parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyEmitDiagnostics(); CreateCompilation([libSource], parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // error CS8630: Invalid 'MemorySafetyRules' value: '2' for C# 14.0. Please use language version 'preview' or greater. Diagnostic(ErrorCode.ERR_CompilationOptionNotAvailable).WithArguments("MemorySafetyRules", "2", "14.0", "preview").WithLocation(1, 1)); CreateCompilation(libSource, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll) .VerifyEmitDiagnostics(); CreateCompilation(libSource, parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseDll) .VerifyEmitDiagnostics(); } [Fact] public void Extern_Property() { var libSource = """ #pragma warning disable CS0626 // extern without attributes using System.Runtime.CompilerServices; using System.Runtime.InteropServices; public class C { public int P1 { set { } } unsafe public extern int P2 { set; } unsafe public static extern int P3 { [DllImport("test")] set; } unsafe public extern int P4 { [MethodImpl(MethodImplOptions.InternalCall)] set; } } """; var callerSource = """ var c = new C(); c.P1 = 0; c.P2 = 0; C.P3 = 0; c.P4 = 0; """; object[] unsafeSymbols = ["C.P2", "C.set_P2", "C.P3", "C.set_P3", "C.P4", "C.set_P4"]; var commonDiagnostics = new[] { // (3,1): error CS9362: 'C.P2.set' must be used in an unsafe context because it is marked as 'unsafe' // c.P2 = 0; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c.P2").WithArguments("C.P2.set").WithLocation(3, 1), // (4,1): error CS9362: 'C.P3.set' must be used in an unsafe context because it is marked as 'unsafe' // C.P3 = 0; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "C.P3").WithArguments("C.P3.set").WithLocation(4, 1), // (5,1): error CS9362: 'C.P4.set' must be used in an unsafe context because it is marked as 'unsafe' // c.P4 = 0; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c.P4").WithArguments("C.P4.set").WithLocation(5, 1), }; CompileAndVerifyUnsafe( libSource, callerSource, verify: Verification.Skipped, expectedUnsafeSymbols: unsafeSymbols, expectedSafeSymbols: ["C", "C.P1", "C.set_P1"], expectedDiagnostics: commonDiagnostics); CreateCompilation([libSource, callerSource], options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(commonDiagnostics); CreateCompilation([libSource], parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyEmitDiagnostics(); CreateCompilation([libSource], parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // error CS8630: Invalid 'MemorySafetyRules' value: '2' for C# 14.0. Please use language version 'preview' or greater. Diagnostic(ErrorCode.ERR_CompilationOptionNotAvailable).WithArguments("MemorySafetyRules", "2", "14.0", "preview").WithLocation(1, 1)); CreateCompilation(libSource, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll) .VerifyEmitDiagnostics(); CreateCompilation(libSource, parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseDll) .VerifyEmitDiagnostics(); } [Fact] public void Extern_Property_SafeModifier() { var libSource = """ #pragma warning disable CS0626 // extern without attributes public class C { safe public extern int P2 { set; } } """; var callerSource = """ var c = new C(); c.P2 = 0; """; CompileAndVerifyUnsafe( libSource, callerSource, verify: Verification.Skipped, expectedUnsafeSymbols: [], expectedSafeSymbols: ["C", "C.P2", "C.set_P2"], expectedDiagnostics: []); } [Fact] public void Extern_Property_SafeModifier_Accessor() { CreateCompilation(""" #pragma warning disable CS0626 // extern without attributes var c = new C(); c.P1 = 0; _ = c.P1; _ = c.P2; c.P3 = 0; _ = c.P3; c.P4 = 0; _ = c.P4; public class C { public unsafe extern int P1 { get; private safe set; } public unsafe extern int P2 { safe get; } public unsafe extern int P3 { safe get; set; } public safe extern int P4 { get; unsafe set; } } """, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // (4,1): error CS0272: The property or indexer 'C.P1' cannot be used in this context because the set accessor is inaccessible // c.P1 = 0; Diagnostic(ErrorCode.ERR_InaccessibleSetter, "c.P1").WithArguments("C.P1").WithLocation(4, 1), // (5,5): error CS9362: 'C.P1.get' must be used in an unsafe context because it is marked as 'unsafe' // _ = c.P1; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c.P1").WithArguments("C.P1.get").WithLocation(5, 5), // (7,1): error CS9362: 'C.P3.set' must be used in an unsafe context because it is marked as 'unsafe' // c.P3 = 0; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c.P3").WithArguments("C.P3.set").WithLocation(7, 1), // (9,1): error CS9362: 'C.P4.set' must be used in an unsafe context because it is marked as 'unsafe' // c.P4 = 0; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c.P4").WithArguments("C.P4.set").WithLocation(9, 1), // (14,30): error CS9396: Cannot specify 'unsafe' or 'safe' modifiers on both property or indexer 'C.P1' and its accessor. Remove one of them. // public unsafe extern int P1 { get; private safe set; } Diagnostic(ErrorCode.ERR_InvalidPropertyUnsafeMods, "P1").WithArguments("C.P1").WithLocation(14, 30), // (15,30): error CS9396: Cannot specify 'unsafe' or 'safe' modifiers on both property or indexer 'C.P2' and its accessor. Remove one of them. // public unsafe extern int P2 { safe get; } Diagnostic(ErrorCode.ERR_InvalidPropertyUnsafeMods, "P2").WithArguments("C.P2").WithLocation(15, 30), // (16,30): error CS9396: Cannot specify 'unsafe' or 'safe' modifiers on both property or indexer 'C.P3' and its accessor. Remove one of them. // public unsafe extern int P3 { safe get; set; } Diagnostic(ErrorCode.ERR_InvalidPropertyUnsafeMods, "P3").WithArguments("C.P3").WithLocation(16, 30), // (17,28): error CS9396: Cannot specify 'unsafe' or 'safe' modifiers on both property or indexer 'C.P4' and its accessor. Remove one of them. // public safe extern int P4 { get; unsafe set; } Diagnostic(ErrorCode.ERR_InvalidPropertyUnsafeMods, "P4").WithArguments("C.P4").WithLocation(17, 28)); } [Fact] public void Extern_Property_WithPointers() { static string getLibSource(string modifiers) => $$""" #pragma warning disable CS0626 // extern without attributes public class C { public {{modifiers}} int* P { set; } } """; var callerSource = """ var c = new C(); c.P = null; """; var libUpdated = CreateCompilation( [getLibSource("unsafe extern")], options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(); foreach (var useCompilationReference in new[] { false, true }) { var libUpdatedRef = AsReference(libUpdated, useCompilationReference); var libAssemblySymbol = CreateCompilation(callerSource, [libUpdatedRef], options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // (2,1): error CS9362: 'C.P.set' must be used in an unsafe context because it is marked as 'unsafe' // c.P = null; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c.P").WithArguments("C.P.set").WithLocation(2, 1)) .GetReferencedAssemblySymbol(libUpdatedRef); VerifyRequiresUnsafeAttribute( libAssemblySymbol.Modules.Single(), expectedUnsafeSymbols: ["C.P", "C.set_P"], expectedSafeSymbols: ["C"], expectedDefinition: !useCompilationReference ? AttributeDefinition.Synthesized : AttributeDefinition.None); } CreateCompilation(getLibSource("extern")).VerifyDiagnostics(); CreateCompilation(getLibSource("extern"), parseOptions: TestOptions.Regular14) .VerifyDiagnostics( // (4,19): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // public extern int* P { set; } Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(4, 19)); CreateCompilation(getLibSource("extern"), parseOptions: TestOptions.RegularNext) .VerifyDiagnostics(); // When compiling the lib under legacy rules, extern members are not unsafe, but members with pointers are. var libLegacy = CreateCompilation( getLibSource("unsafe extern"), options: TestOptions.UnsafeReleaseDll) .VerifyDiagnostics(); foreach (var useCompilationReference in new[] { false, true }) { var libLegacyRef = AsReference(libLegacy, useCompilationReference); var libAssemblySymbol = CreateCompilation(callerSource, [libLegacyRef], options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // (2,1): error CS9363: 'C.P.set' must be used in an unsafe context because it has pointers in its signature // c.P = null; Diagnostic(ErrorCode.ERR_UnsafeMemberOperationCompat, "c.P").WithArguments("C.P.set").WithLocation(2, 1)) .GetReferencedAssemblySymbol(libLegacyRef); VerifyRequiresUnsafeAttribute( libAssemblySymbol.Modules.Single(), expectedUnsafeSymbols: ["C.P", "C.set_P"], expectedSafeSymbols: ["C"], expectedUnsafeMode: CallerUnsafeMode.Implicit, expectedDefinition: AttributeDefinition.None); } } [Fact] public void Extern_Property_Explicit() { CompileAndVerifyUnsafe( lib: """ #pragma warning disable CS0626 // extern without attributes public class C { unsafe public extern int P1 { set; } } """, caller: """ var c = new C(); c.P1 = 0; """, verify: Verification.Skipped, expectedUnsafeSymbols: ["C.P1", "C.set_P1"], expectedSafeSymbols: ["C"], expectedDiagnostics: [ // (2,1): error CS9362: 'C.P1.set' must be used in an unsafe context because it is marked as 'unsafe' // c.P1 = 0; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c.P1").WithArguments("C.P1.set").WithLocation(2, 1), ]); } [Fact] public void Extern_Indexer() { var libSource = """ #pragma warning disable CS0626 // extern without attributes public class C { unsafe public extern int this[int i] { get; set; } } """; var callerSource = """ var c = new C(); c[0] = c[0] + 123; """; object[] unsafeSymbols = ["C.this[]", "C.get_Item", "C.set_Item"]; var commonDiagnostics = new[] { // (2,1): error CS9362: 'C.this[int].set' must be used in an unsafe context because it is marked as 'unsafe' // c[0] = c[0] + 123; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c[0]").WithArguments("C.this[int].set").WithLocation(2, 1), // (2,8): error CS9362: 'C.this[int].get' must be used in an unsafe context because it is marked as 'unsafe' // c[0] = c[0] + 123; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c[0]").WithArguments("C.this[int].get").WithLocation(2, 8), }; CompileAndVerifyUnsafe( libSource, callerSource, verify: Verification.Skipped, expectedUnsafeSymbols: unsafeSymbols, expectedSafeSymbols: ["C"], expectedDiagnostics: commonDiagnostics); CreateCompilation([libSource, callerSource], options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(commonDiagnostics); CreateCompilation([libSource], parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyEmitDiagnostics(); CreateCompilation([libSource], parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // error CS8630: Invalid 'MemorySafetyRules' value: '2' for C# 14.0. Please use language version 'preview' or greater. Diagnostic(ErrorCode.ERR_CompilationOptionNotAvailable).WithArguments("MemorySafetyRules", "2", "14.0", "preview").WithLocation(1, 1)); CreateCompilation(libSource, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll) .VerifyEmitDiagnostics(); CreateCompilation(libSource, parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseDll) .VerifyEmitDiagnostics(); } [Fact] public void Extern_Indexer_SafeModifier() { var libSource = """ #pragma warning disable CS0626 // extern without attributes public class C { public safe extern int this[int i] { get; set; } } """; var callerSource = """ var c = new C(); c[0] = c[0] + 123; """; CompileAndVerifyUnsafe( libSource, callerSource, verify: Verification.Skipped, expectedUnsafeSymbols: [], expectedSafeSymbols: ["C", "C.this[]", "C.get_Item", "C.set_Item"], expectedDiagnostics: []); } [Fact] public void Extern_Indexer_WithPointers() { static string getLibSource(string modifiers) => $$""" #pragma warning disable CS0626 // extern without attributes public class C { public {{modifiers}} int* this[int i] { get; set; } } """; var callerSource = """ var c = new C(); c[0] = c[0] + 123; """; var libUpdated = CreateCompilation( [getLibSource("unsafe extern")], options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(); foreach (var useCompilationReference in new[] { false, true }) { var libUpdatedRef = AsReference(libUpdated, useCompilationReference); var libAssemblySymbol = CreateCompilation(callerSource, [libUpdatedRef], options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // (2,1): error CS9362: 'C.this[int].set' must be used in an unsafe context because it is marked as 'unsafe' // c[0] = c[0] + 123; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c[0]").WithArguments("C.this[int].set").WithLocation(2, 1), // (2,8): error CS9362: 'C.this[int].get' must be used in an unsafe context because it is marked as 'unsafe' // c[0] = c[0] + 123; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c[0]").WithArguments("C.this[int].get").WithLocation(2, 8)) .GetReferencedAssemblySymbol(libUpdatedRef); object[] unsafeSymbols = ["C.this[]", "C.get_Item", "C.set_Item"]; VerifyRequiresUnsafeAttribute( libAssemblySymbol.Modules.Single(), expectedUnsafeSymbols: unsafeSymbols, expectedSafeSymbols: ["C"], expectedDefinition: !useCompilationReference ? AttributeDefinition.Synthesized : AttributeDefinition.None); } CreateCompilation(getLibSource("extern")).VerifyDiagnostics(); CreateCompilation(getLibSource("extern"), parseOptions: TestOptions.Regular14) .VerifyDiagnostics( // (4,19): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // public extern int* this[int i] { get; set; } Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(4, 19)); CreateCompilation(getLibSource("extern"), parseOptions: TestOptions.RegularNext) .VerifyDiagnostics(); // When compiling the lib under legacy rules, extern members are not unsafe, but members with pointers are. var libLegacy = CreateCompilation( getLibSource("unsafe extern"), options: TestOptions.UnsafeReleaseDll) .VerifyDiagnostics(); foreach (var useCompilationReference in new[] { false, true }) { var libLegacyRef = AsReference(libLegacy, useCompilationReference); var libAssemblySymbol = CreateCompilation(callerSource, [libLegacyRef], options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // (2,1): error CS9363: 'C.this[int].set' must be used in an unsafe context because it has pointers in its signature // c[0] = c[0] + 123; Diagnostic(ErrorCode.ERR_UnsafeMemberOperationCompat, "c[0]").WithArguments("C.this[int].set").WithLocation(2, 1), // (2,8): error CS9363: 'C.this[int].get' must be used in an unsafe context because it has pointers in its signature // c[0] = c[0] + 123; Diagnostic(ErrorCode.ERR_UnsafeMemberOperationCompat, "c[0]").WithArguments("C.this[int].get").WithLocation(2, 8)) .GetReferencedAssemblySymbol(libLegacyRef); VerifyRequiresUnsafeAttribute( libAssemblySymbol.Modules.Single(), expectedUnsafeSymbols: ["C.this[]", "C.get_Item", "C.set_Item"], expectedSafeSymbols: ["C"], expectedUnsafeMode: CallerUnsafeMode.Implicit, expectedDefinition: AttributeDefinition.None); } } [Fact] public void Extern_Indexer_Explicit() { CompileAndVerifyUnsafe( lib: """ #pragma warning disable CS0626 // extern without attributes public class C1 { unsafe public extern int this[int i] { set; } } """, caller: """ new C1()[0] = 0; """, verify: Verification.Skipped, expectedUnsafeSymbols: ["C1.this[]", "C1.set_Item"], expectedSafeSymbols: ["C1"], expectedDiagnostics: [ // (1,1): error CS9362: 'C1.this[int].set' must be used in an unsafe context because it is marked as 'unsafe' // new C1()[0] = 0; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "new C1()[0]").WithArguments("C1.this[int].set").WithLocation(1, 1), ]); } [Fact] public void Extern_Event() { var libSource = """ #pragma warning disable CS0067 // unused event public class C { [method: System.Runtime.InteropServices.DllImport("test")] unsafe public static extern event System.Action E; } """; var callerSource = """ C.E += null; """; object[] unsafeSymbols = ["C.E", "C.add_E", "C.remove_E"]; var commonDiagnostics = new[] { // (1,5): error CS9362: 'C.E.add' must be used in an unsafe context because it is marked as 'unsafe' // C.E += null; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "+=").WithArguments("C.E.add").WithLocation(1, 5), }; CompileAndVerifyUnsafe( libSource, callerSource, verify: Verification.Skipped, expectedUnsafeSymbols: unsafeSymbols, expectedSafeSymbols: ["C"], expectedDiagnostics: commonDiagnostics); CreateCompilation([libSource, callerSource], options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(commonDiagnostics); CreateCompilation([libSource], parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyEmitDiagnostics(); CreateCompilation([libSource], parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // error CS8630: Invalid 'MemorySafetyRules' value: '2' for C# 14.0. Please use language version 'preview' or greater. Diagnostic(ErrorCode.ERR_CompilationOptionNotAvailable).WithArguments("MemorySafetyRules", "2", "14.0", "preview").WithLocation(1, 1)); CreateCompilation(libSource, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll) .VerifyEmitDiagnostics(); CreateCompilation(libSource, parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseDll) .VerifyEmitDiagnostics(); } [Fact] public void Extern_Event_SafeModifier() { var libSource = """ #pragma warning disable CS0626 // extern without attributes public class C { safe public static extern event System.Action E1; } """; var callerSource = """ C.E1 += null; """; CompileAndVerifyUnsafe( libSource, callerSource, verify: Verification.Skipped, expectedUnsafeSymbols: [], expectedSafeSymbols: ["C", "C.E1", "C.add_E1", "C.remove_E1"], expectedDiagnostics: []); } [Fact] public void Extern_Event_WithPointers() { var libSource = """ #pragma warning disable CS0067 // unused event public class C { [method: System.Runtime.InteropServices.DllImport("test")] unsafe public static extern event System.Action<int*[]> E; } """; var callerSource = """ C.E += null; """; var libUpdated = CreateCompilation( [libSource], options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(); foreach (var useCompilationReference in new[] { false, true }) { var libUpdatedRef = AsReference(libUpdated, useCompilationReference); var libAssemblySymbol = CreateCompilation(callerSource, [libUpdatedRef], options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // (1,5): error CS9362: 'C.E.add' must be used in an unsafe context because it is marked as 'unsafe' // C.E += null; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "+=").WithArguments("C.E.add").WithLocation(1, 5)) .GetReferencedAssemblySymbol(libUpdatedRef); object[] unsafeSymbols = ["C.E", "C.add_E", "C.remove_E"]; VerifyRequiresUnsafeAttribute( libAssemblySymbol.Modules.Single(), expectedUnsafeSymbols: unsafeSymbols, expectedSafeSymbols: ["C"], expectedDefinition: !useCompilationReference ? AttributeDefinition.Synthesized : AttributeDefinition.None); } // When compiling the lib under legacy rules, extern members are not unsafe, but members with pointers are. var libLegacy = CreateCompilation( libSource, options: TestOptions.UnsafeReleaseDll) .VerifyDiagnostics(); foreach (var useCompilationReference in new[] { false, true }) { var libLegacyRef = AsReference(libLegacy, useCompilationReference); var libAssemblySymbol = CreateCompilation(callerSource, [libLegacyRef], options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // (1,5): error CS9363: 'C.E.add' must be used in an unsafe context because it has pointers in its signature // C.E += null; Diagnostic(ErrorCode.ERR_UnsafeMemberOperationCompat, "+=").WithArguments("C.E.add").WithLocation(1, 5)) .GetReferencedAssemblySymbol(libLegacyRef); VerifyRequiresUnsafeAttribute( libAssemblySymbol.Modules.Single(), expectedUnsafeSymbols: ["C.E", "C.add_E", "C.remove_E"], expectedSafeSymbols: ["C"], expectedUnsafeMode: CallerUnsafeMode.Implicit, expectedDefinition: AttributeDefinition.None); } } [Fact] public void Extern_Event_Explicit() { CompileAndVerifyUnsafe( lib: """ #pragma warning disable CS0626 // extern without attributes public class C { unsafe public static extern event System.Action E; } """, caller: """ C.E += null; C.E -= null; """, verify: Verification.Skipped, expectedUnsafeSymbols: ["C.E", "C.add_E", "C.remove_E"], expectedSafeSymbols: ["C"], expectedDiagnostics: [ // (1,5): error CS9362: 'C.E.add' must be used in an unsafe context because it is marked as 'unsafe' // C.E += null; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "+=").WithArguments("C.E.add").WithLocation(1, 5), // (2,5): error CS9362: 'C.E.remove' must be used in an unsafe context because it is marked as 'unsafe' // C.E -= null; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "-=").WithArguments("C.E.remove").WithLocation(2, 5), ]); } [Fact] public void Extern_Constructor() { var libSource = """ #pragma warning disable CS0824 // extern constructor public class C { unsafe public extern C(); } """; var callerSource = """ _ = new C(); """; object[] unsafeSymbols = ["C..ctor"]; var commonDiagnostics = new[] { // (1,5): error CS9362: 'C.C()' must be used in an unsafe context because it is marked as 'unsafe' // _ = new C(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "new C()").WithArguments("C.C()").WithLocation(1, 5), }; CompileAndVerifyUnsafe( libSource, callerSource, verify: Verification.Skipped, expectedUnsafeSymbols: unsafeSymbols, expectedSafeSymbols: ["C"], expectedDiagnostics: commonDiagnostics); CreateCompilation([libSource, callerSource], options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(commonDiagnostics); CreateCompilation([libSource], parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyEmitDiagnostics(); CreateCompilation([libSource], parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // error CS8630: Invalid 'MemorySafetyRules' value: '2' for C# 14.0. Please use language version 'preview' or greater. Diagnostic(ErrorCode.ERR_CompilationOptionNotAvailable).WithArguments("MemorySafetyRules", "2", "14.0", "preview").WithLocation(1, 1)); CreateCompilation(libSource, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll) .VerifyEmitDiagnostics(); CreateCompilation(libSource, parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseDll) .VerifyEmitDiagnostics(); } [Fact] public void Extern_Constructor_WithPointers() { static string getLibSource(string modifiers) => $$""" #pragma warning disable CS0824 // extern constructor public class C { public {{modifiers}} C(int* p); } """; var callerSource = """ _ = new C(null); """; var libUpdated = CreateCompilation( [getLibSource("unsafe extern")], options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(); foreach (var useCompilationReference in new[] { false, true }) { var libUpdatedRef = AsReference(libUpdated, useCompilationReference); var libAssemblySymbol = CreateCompilation(callerSource, [libUpdatedRef], options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // (1,5): error CS9362: 'C.C(int*)' must be used in an unsafe context because it is marked as 'unsafe' // _ = new C(null); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "new C(null)").WithArguments("C.C(int*)").WithLocation(1, 5)) .GetReferencedAssemblySymbol(libUpdatedRef); VerifyRequiresUnsafeAttribute( libAssemblySymbol.Modules.Single(), expectedUnsafeSymbols: ["C..ctor"], expectedSafeSymbols: ["C"], expectedDefinition: !useCompilationReference ? AttributeDefinition.Synthesized : AttributeDefinition.None); } CreateCompilation(getLibSource("extern")).VerifyDiagnostics(); CreateCompilation(getLibSource("extern"), parseOptions: TestOptions.Regular14) .VerifyDiagnostics( // (4,21): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // public extern C(int* p); Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(4, 21)); CreateCompilation(getLibSource("extern"), parseOptions: TestOptions.RegularNext) .VerifyDiagnostics(); // When compiling the lib under legacy rules, extern members are not unsafe, but members with pointers are. var libLegacy = CreateCompilation( getLibSource("unsafe extern"), options: TestOptions.UnsafeReleaseDll) .VerifyDiagnostics(); foreach (var useCompilationReference in new[] { false, true }) { var libLegacyRef = AsReference(libLegacy, useCompilationReference); var libAssemblySymbol = CreateCompilation(callerSource, [libLegacyRef], options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // (1,5): error CS9363: 'C.C(int*)' must be used in an unsafe context because it has pointers in its signature // _ = new C(null); Diagnostic(ErrorCode.ERR_UnsafeMemberOperationCompat, "new C(null)").WithArguments("C.C(int*)").WithLocation(1, 5)) .GetReferencedAssemblySymbol(libLegacyRef); VerifyRequiresUnsafeAttribute( libAssemblySymbol.Modules.Single(), expectedUnsafeSymbols: ["C..ctor"], expectedSafeSymbols: ["C"], expectedUnsafeMode: CallerUnsafeMode.Implicit, expectedDefinition: AttributeDefinition.None); } } [Fact] public void Extern_Constructor_Explicit() { CompileAndVerifyUnsafe( lib: """ #pragma warning disable CS0824 // extern constructor public class C { unsafe public extern C(); } """, caller: """ _ = new C(); """, verify: Verification.Skipped, expectedUnsafeSymbols: ["C..ctor"], expectedSafeSymbols: ["C"], expectedDiagnostics: [ // (1,5): error CS9362: 'C.C()' must be used in an unsafe context because it is marked as 'unsafe' // _ = new C(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "new C()").WithArguments("C.C()").WithLocation(1, 5), ]); } [Fact] public void Extern_Operator() { var libSource = """ #pragma warning disable CS0626 // extern without attributes public class C { unsafe public extern void operator +=(C c); } """; var callerSource = """ var c = new C(); c += c; """; object[] unsafeSymbols = ["C.op_AdditionAssignment"]; var commonDiagnostics = new[] { // (2,1): error CS9362: 'C.operator +=(C)' must be used in an unsafe context because it is marked as 'unsafe' // c += c; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c += c").WithArguments("C.operator +=(C)").WithLocation(2, 1), }; CompileAndVerifyUnsafe( libSource, callerSource, additionalSources: [CompilerFeatureRequiredAttribute], verify: Verification.Skipped, expectedUnsafeSymbols: unsafeSymbols, expectedSafeSymbols: ["C"], expectedDiagnostics: commonDiagnostics); CreateCompilation([libSource, callerSource, CompilerFeatureRequiredAttribute], options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(commonDiagnostics); CreateCompilation([libSource, CompilerFeatureRequiredAttribute], parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyEmitDiagnostics(); CreateCompilation([libSource, CompilerFeatureRequiredAttribute], parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // error CS8630: Invalid 'MemorySafetyRules' value: '2' for C# 14.0. Please use language version 'preview' or greater. Diagnostic(ErrorCode.ERR_CompilationOptionNotAvailable).WithArguments("MemorySafetyRules", "2", "14.0", "preview").WithLocation(1, 1)); CreateCompilation([libSource, CompilerFeatureRequiredAttribute], parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll) .VerifyEmitDiagnostics(); CreateCompilation([libSource, CompilerFeatureRequiredAttribute], parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseDll) .VerifyEmitDiagnostics(); } [Fact] public void Extern_Operator_SafeModifier() { var libSource = """ #pragma warning disable CS0626 // extern without attributes public class C { public safe static extern C operator +(C x, C y); } """; var callerSource = """ var c = new C(); _ = c + c; """; CompileAndVerifyUnsafe( libSource, callerSource, verify: Verification.Skipped, expectedUnsafeSymbols: [], expectedSafeSymbols: ["C", "C.op_Addition"], expectedDiagnostics: []); } [Fact] public void Extern_Operator_WithPointers() { static string getLibSource(string modifiers) => $$""" #pragma warning disable CS0626 // extern without attributes public class C { public {{modifiers}} void operator +=(int* p); } """; var callerSource = """ var c = new C(); c += null; """; var libUpdated = CreateCompilation( [getLibSource("unsafe extern"), CompilerFeatureRequiredAttribute], options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(); foreach (var useCompilationReference in new[] { false, true }) { var libUpdatedRef = AsReference(libUpdated, useCompilationReference); var libAssemblySymbol = CreateCompilation(callerSource, [libUpdatedRef], options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // (2,1): error CS9362: 'C.operator +=(int*)' must be used in an unsafe context because it is marked as 'unsafe' // c += null; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c += null").WithArguments("C.operator +=(int*)").WithLocation(2, 1)) .GetReferencedAssemblySymbol(libUpdatedRef); VerifyRequiresUnsafeAttribute( libAssemblySymbol.Modules.Single(), expectedUnsafeSymbols: ["C.op_AdditionAssignment"], expectedSafeSymbols: ["C"], expectedDefinition: !useCompilationReference ? AttributeDefinition.Synthesized : AttributeDefinition.None); } CreateCompilation([getLibSource("extern"), CompilerFeatureRequiredAttribute]).VerifyDiagnostics(); CreateCompilation([getLibSource("extern"), CompilerFeatureRequiredAttribute], parseOptions: TestOptions.Regular14) .VerifyDiagnostics( // (4,36): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // public extern void operator +=(int* p); Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(4, 36)); CreateCompilation([getLibSource("extern"), CompilerFeatureRequiredAttribute], parseOptions: TestOptions.RegularNext) .VerifyDiagnostics(); // When compiling the lib under legacy rules, extern members are not unsafe, but members with pointers are. var libLegacy = CreateCompilation( [getLibSource("unsafe extern"), CompilerFeatureRequiredAttribute], options: TestOptions.UnsafeReleaseDll) .VerifyDiagnostics(); foreach (var useCompilationReference in new[] { false, true }) { var libLegacyRef = AsReference(libLegacy, useCompilationReference); var libAssemblySymbol = CreateCompilation(callerSource, [libLegacyRef], options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // (2,1): error CS9363: 'C.operator +=(int*)' must be used in an unsafe context because it has pointers in its signature // c += null; Diagnostic(ErrorCode.ERR_UnsafeMemberOperationCompat, "c += null").WithArguments("C.operator +=(int*)").WithLocation(2, 1)) .GetReferencedAssemblySymbol(libLegacyRef); VerifyRequiresUnsafeAttribute( libAssemblySymbol.Modules.Single(), expectedUnsafeSymbols: ["C.op_AdditionAssignment"], expectedSafeSymbols: ["C"], expectedUnsafeMode: CallerUnsafeMode.Implicit, expectedDefinition: AttributeDefinition.None); } } [Fact] public void Extern_Operator_Explicit() { CompileAndVerifyUnsafe( lib: """ #pragma warning disable CS0626 // extern without attributes public class C { unsafe public extern void operator +=(C c); } """, caller: """ var c = new C(); c += null; """, additionalSources: [CompilerFeatureRequiredAttribute], verify: Verification.Skipped, expectedUnsafeSymbols: ["C.op_AdditionAssignment"], expectedSafeSymbols: ["C"], expectedDiagnostics: [ // (2,1): error CS9362: 'C.operator +=(C)' must be used in an unsafe context because it is marked as 'unsafe' // c += null; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c += null").WithArguments("C.operator +=(C)").WithLocation(2, 1), ]); } [Fact] public void SafeModifier_ReturnType() { var source = """ class safe { } class C { safe M1() => new safe(); public safe M2() => new safe(); @safe M3() => new safe(); } """; var comp = CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()); comp.VerifyDiagnostics( // error CS8630: Invalid 'MemorySafetyRules' value: '2' for C# 14.0. Please use language version 'preview' or greater. Diagnostic(ErrorCode.ERR_CompilationOptionNotAvailable).WithArguments("MemorySafetyRules", "2", "14.0", "preview").WithLocation(1, 1), // (1,7): warning CS8981: The type name 'safe' only contains lower-cased ascii characters. Such names may become reserved for the language. // class safe { } Diagnostic(ErrorCode.WRN_LowerCaseTypeName, "safe").WithArguments("safe").WithLocation(1, 7)); var type = comp.GlobalNamespace.GetMember<NamedTypeSymbol>("safe"); var containingType = comp.GlobalNamespace.GetMember<NamedTypeSymbol>("C"); Assert.Equal(type, containingType.GetMember<MethodSymbol>("M1").ReturnType); Assert.Equal(type, containingType.GetMember<MethodSymbol>("M2").ReturnType); Assert.Equal(type, containingType.GetMember<MethodSymbol>("M3").ReturnType); var expectedDiagnostics = new[] { // (1,7): warning CS8981: The type name 'safe' only contains lower-cased ascii characters. Such names may become reserved for the language. // class safe { } Diagnostic(ErrorCode.WRN_LowerCaseTypeName, "safe").WithArguments("safe").WithLocation(1, 7), // (5,10): error CS1520: Method must have a return type // safe M1() => new safe(); Diagnostic(ErrorCode.ERR_MemberNeedsType, "M1").WithLocation(5, 10), // (6,17): error CS1520: Method must have a return type // public safe M2() => new safe(); Diagnostic(ErrorCode.ERR_MemberNeedsType, "M2").WithLocation(6, 17) }; CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.RegularPreview, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(expectedDiagnostics); } [Theory, CombinatorialData] public void SafeModifier_Declarations_ExternSafe(bool allowUnsafe) { var source = """ #pragma warning disable CS0067, CS0626, CS0824, CS8321 // unused event, extern without attributes, extern constructor, unused local function public class C { safe public extern void M(); safe public extern int P { get; set; } safe public static extern event System.Action E; safe public extern C(int x); public void M2() { safe static extern void Local(); } safe public extern int A { get; } safe extern ~C(); } """; CreateCompilation(source, options: TestOptions.ReleaseDll.WithAllowUnsafe(allowUnsafe).WithUpdatedMemorySafetyRules()) .VerifyEmitDiagnostics(); CreateCompilation(source, options: TestOptions.ReleaseDll.WithAllowUnsafe(allowUnsafe)) .VerifyEmitDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseDll.WithAllowUnsafe(allowUnsafe)) .VerifyEmitDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.ReleaseDll.WithAllowUnsafe(allowUnsafe)) .VerifyDiagnostics( // (4,5): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // safe public extern void M(); Diagnostic(ErrorCode.ERR_FeatureInPreview, "safe").WithArguments("updated memory safety rules").WithLocation(4, 5), // (5,5): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // safe public extern int P { get; set; } Diagnostic(ErrorCode.ERR_FeatureInPreview, "safe").WithArguments("updated memory safety rules").WithLocation(5, 5), // (6,5): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // safe public static extern event System.Action E; Diagnostic(ErrorCode.ERR_FeatureInPreview, "safe").WithArguments("updated memory safety rules").WithLocation(6, 5), // (7,5): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // safe public extern C(int x); Diagnostic(ErrorCode.ERR_FeatureInPreview, "safe").WithArguments("updated memory safety rules").WithLocation(7, 5), // (10,9): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // safe static extern void Local(); Diagnostic(ErrorCode.ERR_FeatureInPreview, "safe").WithArguments("updated memory safety rules").WithLocation(10, 9), // (12,5): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // safe public extern int A { get; } Diagnostic(ErrorCode.ERR_FeatureInPreview, "safe").WithArguments("updated memory safety rules").WithLocation(12, 5), // (13,5): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // safe extern ~C(); Diagnostic(ErrorCode.ERR_FeatureInPreview, "safe").WithArguments("updated memory safety rules").WithLocation(13, 5)); } [Theory, CombinatorialData] public void SafeModifier_Declarations_Interface(bool allowUnsafe) { var source = """ interface I1 { safe void M(); safe int P { get; set; } safe event System.Action E; int A { safe get; set; } } interface I2 : I1 { safe void I1.M() { } safe int I1.P { get => 0; set { } } safe event System.Action I1.E { add { } remove { } } int I1.A { safe get => 0; set { } } } #pragma warning disable CS0626 // extern without attributes interface I3 : I1 { safe extern void I1.M(); safe extern int I1.P { get; set; } safe extern event System.Action I1.E; int I1.A { safe extern get; safe extern set; } } interface I4 : I1 { safe extern int I1.A { safe get; safe set; } } """; CreateCompilation(source, options: TestOptions.ReleaseDll.WithAllowUnsafe(allowUnsafe).WithUpdatedMemorySafetyRules(), targetFramework: TargetFramework.Net100) .VerifyDiagnostics( // (20,40): error CS0106: The modifier 'extern' is not valid for this item // safe extern event System.Action I1.E; Diagnostic(ErrorCode.ERR_BadMemberFlag, "E").WithArguments("extern").WithLocation(20, 40), // (20,40): error CS0071: An explicit interface implementation of an event must use event accessor syntax // safe extern event System.Action I1.E; Diagnostic(ErrorCode.ERR_ExplicitEventFieldImpl, "E").WithLocation(20, 40), // (21,12): error CS9397: Cannot specify the same 'unsafe' or 'safe' modifier on all accessors of property or indexer 'I3.I1.A'. Instead, put that modifier on the property itself. // int I1.A { safe extern get; safe extern set; } Diagnostic(ErrorCode.ERR_SamePropertyUnsafeAccessorMods, "A").WithArguments("I3.I1.A").WithLocation(21, 12), // (21,28): error CS0106: The modifier 'extern' is not valid for this item // int I1.A { safe extern get; safe extern set; } Diagnostic(ErrorCode.ERR_BadMemberFlag, "get").WithArguments("extern").WithLocation(21, 28), // (21,45): error CS0106: The modifier 'extern' is not valid for this item // int I1.A { safe extern get; safe extern set; } Diagnostic(ErrorCode.ERR_BadMemberFlag, "set").WithArguments("extern").WithLocation(21, 45), // (25,24): error CS9396: Cannot specify 'unsafe' or 'safe' modifiers on both property or indexer 'I4.I1.A' and its accessor. Remove one of them. // safe extern int I1.A { safe get; safe set; } Diagnostic(ErrorCode.ERR_InvalidPropertyUnsafeMods, "A").WithArguments("I4.I1.A").WithLocation(25, 24)); } [Theory, CombinatorialData] public void SafeModifier_Declarations_SafeOnly(bool allowUnsafe, bool updatedRules) { var source = """ #pragma warning disable CS0067, CS8321 // unused event, unused local function public class C { safe public void M1() { } safe public int P1 { get; set; } safe public int this[int i] { get => i; set { } } safe public event System.Action E1; safe public C() { } safe public static C operator +(C x, C y) => x; safe public static explicit operator int(C c) => 0; safe public int F; safe public class NestedClass { } safe public struct NestedStruct { } safe public interface INested { } safe public enum ENested { } safe public delegate void D(); public void M2() { safe void Local() { } } public int P2 { safe get; set; } public string this[string s] { safe get => s; set { } } safe ~C() { } safe public const int CONST = 0; } """; CreateCompilation(source, options: TestOptions.ReleaseDll.WithAllowUnsafe(allowUnsafe).WithUpdatedMemorySafetyRules(updatedRules)).VerifyDiagnostics( // (15,22): error CS0106: The modifier 'safe' is not valid for this item // safe public enum ENested { } Diagnostic(ErrorCode.ERR_BadMemberFlag, "ENested").WithArguments("safe").WithLocation(15, 22), // (24,27): error CS0106: The modifier 'safe' is not valid for this item // safe public const int CONST = 0; Diagnostic(ErrorCode.ERR_BadMemberFlag, "CONST").WithArguments("safe").WithLocation(24, 27)); } [Theory, CombinatorialData] public void SafeModifier_Declarations_Types(bool allowUnsafe, bool updatedRules) { var source = """ safe class C; safe struct S; safe interface I; safe delegate void D(); safe record R; safe record struct RS; """; CreateCompilation(source, options: TestOptions.ReleaseDll.WithAllowUnsafe(allowUnsafe).WithUpdatedMemorySafetyRules(updatedRules)) .VerifyEmitDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.ReleaseDll.WithAllowUnsafe(allowUnsafe)) .VerifyDiagnostics( // (1,12): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // safe class C; Diagnostic(ErrorCode.ERR_FeatureInPreview, "C").WithArguments("updated memory safety rules").WithLocation(1, 12), // (2,13): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // safe struct S; Diagnostic(ErrorCode.ERR_FeatureInPreview, "S").WithArguments("updated memory safety rules").WithLocation(2, 13), // (3,16): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // safe interface I; Diagnostic(ErrorCode.ERR_FeatureInPreview, "I").WithArguments("updated memory safety rules").WithLocation(3, 16), // (4,20): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // safe delegate void D(); Diagnostic(ErrorCode.ERR_FeatureInPreview, "D").WithArguments("updated memory safety rules").WithLocation(4, 20), // (5,13): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // safe record R; Diagnostic(ErrorCode.ERR_FeatureInPreview, "R").WithArguments("updated memory safety rules").WithLocation(5, 13), // (6,20): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // safe record struct RS; Diagnostic(ErrorCode.ERR_FeatureInPreview, "RS").WithArguments("updated memory safety rules").WithLocation(6, 20)); CreateCompilation("safe unsafe class C;", options: TestOptions.UnsafeReleaseDll) .VerifyDiagnostics( // (1,19): error CS9388: The 'safe' and 'unsafe' modifiers cannot be used together. // safe unsafe class C; Diagnostic(ErrorCode.ERR_SafeModifierCannotBeUsedWithUnsafe, "C").WithLocation(1, 19)); } [Fact] public void SafeModifier_Declarations_Accessors() { CompileAndVerifyUnsafe( lib: """ #pragma warning disable CS0626 // extern without attributes public class C { public safe int P1 { get; set; } public int P2 { safe get; unsafe set; } public unsafe int P3 { get; set; } public int P4 { unsafe get; safe set; } public extern unsafe int P5 { get; set; } public extern safe int P6 { get; set; } } """, caller: """ var c = new C(); c.P1 += 1; c.P2 += 1; c.P3 += 1; c.P4 += 1; c.P5 += 1; c.P6 += 1; """, verify: Verification.FailsPEVerify, expectedUnsafeSymbols: ["C.set_P2", "C.P3", "C.get_P3", "C.set_P3", "C.get_P4", "C.P5", "C.get_P5", "C.set_P5"], expectedSafeSymbols: ["C.P1", "C.get_P1", "C.set_P1", "C.P2", "C.get_P2", "C.P4", "C.set_P4", "C.P6", "C.get_P6", "C.set_P6"], expectedDiagnostics: [ // (3,1): error CS9362: 'C.P2.set' must be used in an unsafe context because it is marked as 'unsafe' // c.P2 += 1; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c.P2").WithArguments("C.P2.set").WithLocation(3, 1), // (4,1): error CS9362: 'C.P3.set' must be used in an unsafe context because it is marked as 'unsafe' // c.P3 += 1; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c.P3").WithArguments("C.P3.set").WithLocation(4, 1), // (4,1): error CS9362: 'C.P3.get' must be used in an unsafe context because it is marked as 'unsafe' // c.P3 += 1; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c.P3").WithArguments("C.P3.get").WithLocation(4, 1), // (5,1): error CS9362: 'C.P4.get' must be used in an unsafe context because it is marked as 'unsafe' // c.P4 += 1; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c.P4").WithArguments("C.P4.get").WithLocation(5, 1), // (6,1): error CS9362: 'C.P5.set' must be used in an unsafe context because it is marked as 'unsafe' // c.P5 += 1; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c.P5").WithArguments("C.P5.set").WithLocation(6, 1), // (6,1): error CS9362: 'C.P5.get' must be used in an unsafe context because it is marked as 'unsafe' // c.P5 += 1; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c.P5").WithArguments("C.P5.get").WithLocation(6, 1), ]); var source = """ class C { unsafe int M() => 0; public safe int P1 { get => M(); set => M(); } public unsafe int P2 { get => M(); set => M(); } } """; CreateCompilation(source, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()).VerifyDiagnostics( // (7,16): error CS9362: 'C.M()' must be used in an unsafe context because it is marked as 'unsafe' // get => M(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "M()").WithArguments("C.M()").WithLocation(7, 16), // (8,16): error CS9362: 'C.M()' must be used in an unsafe context because it is marked as 'unsafe' // set => M(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "M()").WithArguments("C.M()").WithLocation(8, 16), // (13,16): error CS9362: 'C.M()' must be used in an unsafe context because it is marked as 'unsafe' // get => M(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "M()").WithArguments("C.M()").WithLocation(13, 16), // (14,16): error CS9362: 'C.M()' must be used in an unsafe context because it is marked as 'unsafe' // set => M(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "M()").WithArguments("C.M()").WithLocation(14, 16)); CreateCompilation(source, options: TestOptions.UnsafeReleaseDll).VerifyEmitDiagnostics(); } [Fact] public void SafeModifier_Declarations_Partial() { var source = """ partial class C { public partial void M1(); public safe partial void M1() { } public safe partial void M2(); public partial void M2() { } public partial void M3(); public safe extern partial void M3(); public partial int P1 { get; } public safe partial int P1 => 0; public partial C(); safe public partial C() { } public partial event System.Action E1; public safe partial event System.Action E1 { add { } remove { } } public partial int P2 { get; } public partial int P2 { safe get => 0; } } """; CreateCompilation(source, options: TestOptions.ReleaseDll.WithUpdatedMemorySafetyRules()).VerifyDiagnostics( // (4,30): error CS9390: Both partial member declarations must be marked 'safe' or neither may be marked 'safe' // public safe partial void M1() { } Diagnostic(ErrorCode.ERR_PartialMemberSafeDifference, "M1").WithLocation(4, 30), // (7,25): error CS9390: Both partial member declarations must be marked 'safe' or neither may be marked 'safe' // public partial void M2() { } Diagnostic(ErrorCode.ERR_PartialMemberSafeDifference, "M2").WithLocation(7, 25), // (9,25): error CS9389: 'extern' member must be marked 'unsafe' or 'safe'. // public partial void M3(); Diagnostic(ErrorCode.ERR_ExternMemberRequiresUnsafeOrSafe, "M3").WithLocation(9, 25), // (10,37): error CS9390: Both partial member declarations must be marked 'safe' or neither may be marked 'safe' // public safe extern partial void M3(); Diagnostic(ErrorCode.ERR_PartialMemberSafeDifference, "M3").WithLocation(10, 37), // (13,29): error CS9390: Both partial member declarations must be marked 'safe' or neither may be marked 'safe' // public safe partial int P1 => 0; Diagnostic(ErrorCode.ERR_PartialMemberSafeDifference, "P1").WithLocation(13, 29), // (16,25): error CS9390: Both partial member declarations must be marked 'safe' or neither may be marked 'safe' // safe public partial C() { } Diagnostic(ErrorCode.ERR_PartialMemberSafeDifference, "C").WithLocation(16, 25), // (19,45): error CS9390: Both partial member declarations must be marked 'safe' or neither may be marked 'safe' // public safe partial event System.Action E1 { add { } remove { } } Diagnostic(ErrorCode.ERR_PartialMemberSafeDifference, "E1").WithLocation(19, 45), // (22,34): error CS9390: Both partial member declarations must be marked 'safe' or neither may be marked 'safe' // public partial int P2 { safe get => 0; } Diagnostic(ErrorCode.ERR_PartialMemberSafeDifference, "get").WithLocation(22, 34)); } [Theory, CombinatorialData] public void SafeModifier_Declarations_ExternOnly(bool allowUnsafe) { var source = """ #pragma warning disable CS0067, CS0626, CS0824 // unused event, extern without attributes, extern constructor public class C { public extern void M(); public extern int P { get; set; } public extern int this[int i] { get; set; } public static extern event System.Action E; public extern C(int x); public static extern C operator +(C x, C y); public static extern explicit operator int(C c); extern ~C(); } public class Program { public static void Test(C c) { c.M(); c.P = c.P + 1; c[0] = c[0] + 1; C.E += null; _ = new C(0); _ = c + c; _ = (int)c; static extern void Local(); Local(); } } """; CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.ReleaseDll.WithAllowUnsafe(allowUnsafe)).VerifyEmitDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseDll.WithAllowUnsafe(allowUnsafe)).VerifyEmitDiagnostics(); CreateCompilation(source, options: TestOptions.ReleaseDll.WithAllowUnsafe(allowUnsafe)).VerifyEmitDiagnostics(); CreateCompilation(source, options: TestOptions.ReleaseDll.WithAllowUnsafe(allowUnsafe).WithUpdatedMemorySafetyRules()).VerifyDiagnostics( // (4,12): error CS9389: 'extern' member must be marked 'unsafe' or 'safe'. // public extern void M(); Diagnostic(ErrorCode.ERR_ExternMemberRequiresUnsafeOrSafe, "extern").WithLocation(4, 12), // (5,12): error CS9389: 'extern' member must be marked 'unsafe' or 'safe'. // public extern int P { get; set; } Diagnostic(ErrorCode.ERR_ExternMemberRequiresUnsafeOrSafe, "extern").WithLocation(5, 12), // (6,12): error CS9389: 'extern' member must be marked 'unsafe' or 'safe'. // public extern int this[int i] { get; set; } Diagnostic(ErrorCode.ERR_ExternMemberRequiresUnsafeOrSafe, "extern").WithLocation(6, 12), // (7,19): error CS9389: 'extern' member must be marked 'unsafe' or 'safe'. // public static extern event System.Action E; Diagnostic(ErrorCode.ERR_ExternMemberRequiresUnsafeOrSafe, "extern").WithLocation(7, 19), // (8,12): error CS9389: 'extern' member must be marked 'unsafe' or 'safe'. // public extern C(int x); Diagnostic(ErrorCode.ERR_ExternMemberRequiresUnsafeOrSafe, "extern").WithLocation(8, 12), // (9,19): error CS9389: 'extern' member must be marked 'unsafe' or 'safe'. // public static extern C operator +(C x, C y); Diagnostic(ErrorCode.ERR_ExternMemberRequiresUnsafeOrSafe, "extern").WithLocation(9, 19), // (10,19): error CS9389: 'extern' member must be marked 'unsafe' or 'safe'. // public static extern explicit operator int(C c); Diagnostic(ErrorCode.ERR_ExternMemberRequiresUnsafeOrSafe, "extern").WithLocation(10, 19), // (11,5): error CS9389: 'extern' member must be marked 'unsafe' or 'safe'. // extern ~C(); Diagnostic(ErrorCode.ERR_ExternMemberRequiresUnsafeOrSafe, "extern").WithLocation(11, 5), // (25,16): error CS9389: 'extern' member must be marked 'unsafe' or 'safe'. // static extern void Local(); Diagnostic(ErrorCode.ERR_ExternMemberRequiresUnsafeOrSafe, "extern").WithLocation(25, 16)); } [Fact] public void SafeModifier_Extern_SafeUnsafe() { var source = """ #pragma warning disable CS0067, CS0626, CS0824, CS8321 // unused event, extern without attributes, extern constructor, unused local function public class C { safe unsafe public extern void M1(); safe unsafe public extern int P1 { get; set; } public safe extern int P2 { safe unsafe get; set; } safe unsafe public static extern event System.Action E1; safe unsafe public extern C(int x); safe unsafe extern ~C(); public void Locals() { safe unsafe static extern void Local1(); } } """; var expectedDiagnostics = new[] { // (4,5): error CS9388: The 'safe' and 'unsafe' modifiers cannot be used together. // safe unsafe public extern void M1(); Diagnostic(ErrorCode.ERR_SafeModifierCannotBeUsedWithUnsafe, "safe").WithLocation(4, 5), // (5,5): error CS9388: The 'safe' and 'unsafe' modifiers cannot be used together. // safe unsafe public extern int P1 { get; set; } Diagnostic(ErrorCode.ERR_SafeModifierCannotBeUsedWithUnsafe, "safe").WithLocation(5, 5), // (6,28): error CS9396: Cannot specify 'unsafe' or 'safe' modifiers on both property or indexer 'C.P2' and its accessor. Remove one of them. // public safe extern int P2 { safe unsafe get; set; } Diagnostic(ErrorCode.ERR_InvalidPropertyUnsafeMods, "P2").WithArguments("C.P2").WithLocation(6, 28), // (6,33): error CS9388: The 'safe' and 'unsafe' modifiers cannot be used together. // public safe extern int P2 { safe unsafe get; set; } Diagnostic(ErrorCode.ERR_SafeModifierCannotBeUsedWithUnsafe, "safe").WithLocation(6, 33), // (7,5): error CS9388: The 'safe' and 'unsafe' modifiers cannot be used together. // safe unsafe public static extern event System.Action E1; Diagnostic(ErrorCode.ERR_SafeModifierCannotBeUsedWithUnsafe, "safe").WithLocation(7, 5), // (8,5): error CS9388: The 'safe' and 'unsafe' modifiers cannot be used together. // safe unsafe public extern C(int x); Diagnostic(ErrorCode.ERR_SafeModifierCannotBeUsedWithUnsafe, "safe").WithLocation(8, 5), // (9,5): error CS9388: The 'safe' and 'unsafe' modifiers cannot be used together. // safe unsafe extern ~C(); Diagnostic(ErrorCode.ERR_SafeModifierCannotBeUsedWithUnsafe, "safe").WithLocation(9, 5), // (12,9): error CS9388: The 'safe' and 'unsafe' modifiers cannot be used together. // safe unsafe static extern void Local1(); Diagnostic(ErrorCode.ERR_SafeModifierCannotBeUsedWithUnsafe, "safe").WithLocation(12, 9), }; CreateCompilation(source, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()).VerifyDiagnostics( [ .. expectedDiagnostics, // (9,10): error CS9377: The 'unsafe' modifier does not have any effect here under the current memory safety rules. // safe unsafe extern ~C(); Diagnostic(ErrorCode.ERR_UnsafeMeaningless, "unsafe").WithLocation(9, 10), ]); } [Theory, CombinatorialData] public void SafeModifier_CompatMode(bool compilationReference) { var source1 = """ public static class C { public static safe void M(int* p) { } } """; var ref1 = CreateCompilation(source1).VerifyEmitDiagnostics(); var source2 = """ C.M(null); """; var expectedDiagnostics = new[] { // (1,1): error CS9363: 'C.M(int*)' must be used in an unsafe context because it has pointers in its signature // C.M(null); Diagnostic(ErrorCode.ERR_UnsafeMemberOperationCompat, "C.M(null)").WithArguments("C.M(int*)").WithLocation(1, 1), }; CreateCompilation(source2, [AsReference(ref1, compilationReference)], options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()).VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source2, [AsReference(ref1, compilationReference)]).VerifyDiagnostics(expectedDiagnostics); } [Fact] public void SafeModifier_CompatMode_SameAssembly() { var source = """ class C { safe void M1(int* p) { } void M2() { M1(null); } } """; CreateCompilation(source, options: TestOptions.ReleaseDll.WithUpdatedMemorySafetyRules()).VerifyEmitDiagnostics(); CreateCompilation(source).VerifyDiagnostics( // (4,17): error CS9363: 'C.M1(int*)' must be used in an unsafe context because it has pointers in its signature // void M2() { M1(null); } Diagnostic(ErrorCode.ERR_UnsafeMemberOperationCompat, "M1(null)").WithArguments("C.M1(int*)").WithLocation(4, 17)); } [Fact] public void RequiresUnsafeAttribute_Synthesized() { var source = """ public class C { public unsafe void M() { } } """; foreach (var parseOptions in new[] { TestOptions.RegularPreview, TestOptions.RegularNext, TestOptions.Regular14 }) { CompileAndVerify(source, parseOptions: parseOptions, options: TestOptions.UnsafeReleaseDll, symbolValidator: m => VerifyRequiresUnsafeAttribute( m, expectedUnsafeSymbols: [], expectedSafeSymbols: ["C", "C.M"], expectedDefinition: AttributeDefinition.None)) .VerifyDiagnostics(); } foreach (var parseOptions in new[] { TestOptions.RegularPreview, TestOptions.RegularNext }) { CompileAndVerify(source, parseOptions: parseOptions, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules(), symbolValidator: m => VerifyRequiresUnsafeAttribute( m, expectedUnsafeSymbols: ["C.M"], expectedSafeSymbols: ["C"], expectedDefinition: AttributeDefinition.Synthesized)) .VerifyDiagnostics(); } CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // (1,1): error CS9274: 'MemorySafetyRules' version '2' is not available in C# 14.0. Please use language version 'preview' or greater. Diagnostic(ErrorCode.ERR_CompilationOptionNotAvailable).WithArguments("MemorySafetyRules", "2", "14.0", "preview").WithLocation(1, 1)); } [Fact] public void RequiresUnsafeAttribute_Reflection() { var sourceA = """ using System; using System.Linq; using System.Reflection; public class A { unsafe public void M1() { } public void M2() { } public static void RequiresUnsafe(MethodInfo method) { var count = method.GetCustomAttributes(inherit: false).Count(a => a.GetType().Name == "RequiresUnsafeAttribute"); Console.Write(count); } } """; var refA = CreateCompilation([sourceA], options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics() .EmitToImageReference(); var sourceB = """ class B : A { unsafe public void M3() { } public void M4() { } static void Main() { RequiresUnsafe(typeof(A).GetMethod("M1")); RequiresUnsafe(typeof(A).GetMethod("M2")); RequiresUnsafe(typeof(B).GetMethod("M3")); RequiresUnsafe(typeof(B).GetMethod("M4")); } } """; CompileAndVerify(sourceB, [refA], options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules(), expectedOutput: "1010") .VerifyDiagnostics(); } [Fact] public void RequiresUnsafeAttribute_FromSource() { var source = """ public class C { unsafe public void M() { } } """; CompileAndVerify(source, options: TestOptions.UnsafeReleaseDll, symbolValidator: m => VerifyRequiresUnsafeAttribute( m, expectedUnsafeSymbols: [], expectedSafeSymbols: ["C", "C.M"], expectedDefinition: AttributeDefinition.None)) .VerifyDiagnostics(); CompileAndVerify([source, RequiresUnsafeAttributeDefinition], options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules(), symbolValidator: m => VerifyRequiresUnsafeAttribute( m, expectedUnsafeSymbols: ["C.M"], expectedSafeSymbols: ["C"], expectedDefinition: AttributeDefinition.FromSource)) .VerifyDiagnostics(); } [Theory, CombinatorialData] public void RequiresUnsafeAttribute_FromMetadata(bool useCompilationReference) { var comp = CreateCompilation(RequiresUnsafeAttributeDefinition); CompileAndVerify(comp, symbolValidator: m => VerifyRequiresUnsafeAttribute( m, expectedUnsafeSymbols: [], expectedSafeSymbols: [AttributeDescription.RequiresUnsafeAttribute.FullName], expectedDefinition: AttributeDefinition.FromSource)) .VerifyDiagnostics(); var ref1 = AsReference(comp, useCompilationReference); var source = """ public class C { unsafe public void M() { } } """; CompileAndVerify(source, [ref1], options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules(), symbolValidator: m => VerifyRequiresUnsafeAttribute( m, expectedUnsafeSymbols: ["C.M"], expectedSafeSymbols: ["C"], expectedDefinition: AttributeDefinition.None)) .VerifyDiagnostics(); } [Theory, CombinatorialData] public void RequiresUnsafeAttribute_FromMetadata_Multiple(bool useCompilationReference) { var comp1 = CreateCompilation(RequiresUnsafeAttributeDefinition, assemblyName: "lib1").VerifyDiagnostics(); var ref1 = AsReference(comp1, useCompilationReference); var comp2 = CreateCompilation(RequiresUnsafeAttributeDefinition, assemblyName: "lib2").VerifyDiagnostics(); var ref2 = AsReference(comp2, useCompilationReference); var source = """ public class C { unsafe public void M() { } } """; // Ambiguous attribute definitions from references, but the compiler synthesizes its own. CompileAndVerify(source, [ref1, ref2], options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules(), symbolValidator: m => VerifyRequiresUnsafeAttribute( m, expectedUnsafeSymbols: ["C.M"], expectedSafeSymbols: ["C"], expectedDefinition: AttributeDefinition.Synthesized)) .VerifyDiagnostics(); // Also defined in source. var lib = CreateCompilation([source, RequiresUnsafeAttributeDefinition], [ref1, ref2], options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(); CreateCompilation(""" new C().M(); """, [AsReference(lib, useCompilationReference)], options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // (1,1): error CS9362: 'C.M()' must be used in an unsafe context because it is marked as 'unsafe' // new C().M(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "new C().M()").WithArguments("C.M()").WithLocation(1, 1)); } [Theory, CombinatorialData] public void RequiresUnsafeAttribute_FromMetadata_Multiple_AndCorLib(bool useCompilationReference) { var corlibSource = """ namespace System { public class Object; public class ValueType; public class String; public class Attribute; public struct Void; public struct Int32; public struct Boolean; public class AttributeUsageAttribute { public AttributeUsageAttribute(AttributeTargets t) { } public bool AllowMultiple { get; set; } public bool Inherited { get; set; } } public class Enum; public enum AttributeTargets; } namespace System.Diagnostics.CodeAnalysis { public sealed class RequiresUnsafeAttribute : Attribute; } """; var corlib = CreateEmptyCompilation(corlibSource, assemblyName: "corlib").VerifyDiagnostics(); var corlibRef = AsReference(corlib, useCompilationReference); var comp1 = CreateEmptyCompilation("", [corlibRef], assemblyName: "lib1").VerifyDiagnostics(); var ref1 = AsReference(comp1, useCompilationReference); var comp2 = CreateEmptyCompilation("", [corlibRef], assemblyName: "lib2").VerifyDiagnostics(); var ref2 = AsReference(comp2, useCompilationReference); var source = """ #pragma warning disable CS0626 // extern without attributes public class C { unsafe public extern void M(); } """; // Using the attribute from corlib even if there are ambiguous definitions in other references. var lib = CreateEmptyCompilation(source, [ref1, ref2, corlibRef], options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules() .WithSpecificDiagnosticOptions([KeyValuePair.Create("CS8021", ReportDiagnostic.Suppress)])) .VerifyDiagnostics(); CreateEmptyCompilation(""" new C().M(); """, [AsReference(lib, useCompilationReference), ref1, ref2, corlibRef], options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // (1,1): error CS9362: 'C.M()' must be used in an unsafe context because it is marked as 'unsafe' // new C().M(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "new C().M()").WithArguments("C.M()").WithLocation(1, 1)); } [Fact] public void RequiresUnsafeAttribute_FromMetadata_UnrecognizedConstructor() { // [module: MemorySafetyRules(2)] // public class A // { // [RequiresUnsafe(1), RequiresUnsafe(0)] // public static void M() => throw null; // } var sourceA = $$""" .assembly extern mscorlib { .ver 4:0:0:0 .publickeytoken = (B7 7A 5C 56 19 34 E0 89) } .assembly '<<GeneratedFileName>>' { } .module '<<GeneratedFileName>>.dll' .custom instance void System.Runtime.CompilerServices.MemorySafetyRulesAttribute::.ctor(int32) = { int32({{CSharpCompilationOptions.UpdatedMemorySafetyRulesVersion}}) } .class private System.Runtime.CompilerServices.MemorySafetyRulesAttribute extends [mscorlib]System.Attribute { .method public hidebysig specialname rtspecialname instance void .ctor(int32 version) cil managed { ret } } .class private System.Diagnostics.CodeAnalysis.RequiresUnsafeAttribute extends [mscorlib]System.Attribute { .method public hidebysig specialname rtspecialname instance void .ctor(int32 version) cil managed { ret } } .class public A { .method public static void M() { .custom instance void System.Diagnostics.CodeAnalysis.RequiresUnsafeAttribute::.ctor(int32) = { int32(1) } .custom instance void System.Diagnostics.CodeAnalysis.RequiresUnsafeAttribute::.ctor(int32) = { int32(0) } ldnull throw } } """; var refA = CompileIL(sourceA, prependDefaultHeader: false); var a = CreateCompilation("", [refA]).VerifyDiagnostics().GetReferencedAssemblySymbol(refA); Assert.Equal(CallerUnsafeMode.None, a.GlobalNamespace.GetMember("A.M").GetCallerUnsafeMode(ConsList<FieldSymbol>.Empty)); var sourceB = """ A.M(); """; CreateCompilation(sourceB, [refA], options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyEmitDiagnostics(); } [Fact] public void RequiresUnsafeAttribute_FromMetadata_UnrecognizedAndRecognizedConstructor() { // [module: MemorySafetyRules(2)] // public class A // { // [RequiresUnsafe(1), RequiresUnsafe()] // public static void M() => throw null; // } var sourceA = $$""" .assembly extern mscorlib { .ver 4:0:0:0 .publickeytoken = (B7 7A 5C 56 19 34 E0 89) } .assembly '<<GeneratedFileName>>' { } .module '<<GeneratedFileName>>.dll' .custom instance void System.Runtime.CompilerServices.MemorySafetyRulesAttribute::.ctor(int32) = { int32({{CSharpCompilationOptions.UpdatedMemorySafetyRulesVersion}}) } .class private System.Runtime.CompilerServices.MemorySafetyRulesAttribute extends [mscorlib]System.Attribute { .method public hidebysig specialname rtspecialname instance void .ctor(int32 version) cil managed { ret } .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ret } } .class private System.Diagnostics.CodeAnalysis.RequiresUnsafeAttribute extends [mscorlib]System.Attribute { .method public hidebysig specialname rtspecialname instance void .ctor(int32 version) cil managed { ret } } .class public A { .method public static void M() { .custom instance void System.Diagnostics.CodeAnalysis.RequiresUnsafeAttribute::.ctor(int32) = { int32(1) } .custom instance void System.Diagnostics.CodeAnalysis.RequiresUnsafeAttribute::.ctor() ldnull throw } } """; var refA = CompileIL(sourceA, prependDefaultHeader: false); var a = CreateCompilation("", [refA]).VerifyDiagnostics().GetReferencedAssemblySymbol(refA); Assert.Equal(CallerUnsafeMode.Explicit, a.GlobalNamespace.GetMember("A.M").GetCallerUnsafeMode(ConsList<FieldSymbol>.Empty)); var sourceB = """ A.M(); """; CreateCompilation(sourceB, [refA], options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // (1,1): error CS9362: 'A.M()' must be used in an unsafe context because it is marked as 'unsafe' // A.M(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "A.M()").WithArguments("A.M()").WithLocation(1, 1)); } [Fact] public void RequiresUnsafeAttribute_FromMetadata_AppliedMultipleTimes() { // [module: MemorySafetyRules(2)] // public class A // { // [RequiresUnsafe, RequiresUnsafe] // public static void M() => throw null; // } var sourceA = $$""" .assembly extern mscorlib { .ver 4:0:0:0 .publickeytoken = (B7 7A 5C 56 19 34 E0 89) } .assembly '<<GeneratedFileName>>' { } .module '<<GeneratedFileName>>.dll' .custom instance void System.Runtime.CompilerServices.MemorySafetyRulesAttribute::.ctor(int32) = { int32({{CSharpCompilationOptions.UpdatedMemorySafetyRulesVersion}}) } .class private System.Runtime.CompilerServices.MemorySafetyRulesAttribute extends [mscorlib]System.Attribute { .method public hidebysig specialname rtspecialname instance void .ctor(int32 version) cil managed { ret } } .class private System.Diagnostics.CodeAnalysis.RequiresUnsafeAttribute extends [mscorlib]System.Attribute { .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ret } } .class public A { .method public static void M() { .custom instance void System.Diagnostics.CodeAnalysis.RequiresUnsafeAttribute::.ctor() .custom instance void System.Diagnostics.CodeAnalysis.RequiresUnsafeAttribute::.ctor() ldnull throw } } """; var refA = CompileIL(sourceA, prependDefaultHeader: false); var a = CreateCompilation("", [refA]).VerifyDiagnostics().GetReferencedAssemblySymbol(refA); Assert.Equal(CallerUnsafeMode.Explicit, a.GlobalNamespace.GetMember("A.M").GetCallerUnsafeMode(ConsList<FieldSymbol>.Empty)); var sourceB = """ A.M(); """; CreateCompilation(sourceB, [refA], options: TestOptions.ReleaseExe.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics( // (1,1): error CS9362: 'A.M()' must be used in an unsafe context because it is marked as 'unsafe' // A.M(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "A.M()").WithArguments("A.M()").WithLocation(1, 1)); } [Theory, CombinatorialData] public void RequiresUnsafeAttribute_ReferencedInSource( bool updatedRules, bool useCompilationReference) { var comp1 = CreateCompilation(""" namespace System.Diagnostics.CodeAnalysis { public sealed class RequiresUnsafeAttribute : Attribute; } """).VerifyDiagnostics(); var ref1 = AsReference(comp1, useCompilationReference); CSharpTestSource source = [ """ using System.Diagnostics.CodeAnalysis; [RequiresUnsafeAttribute] class C { [RequiresUnsafeAttribute] void M() { } [RequiresUnsafeAttribute] int P1 { get; set; } [field: RequiresUnsafeAttribute] int P2 { get; set; } int P3 { [RequiresUnsafeAttribute] get; [RequiresUnsafeAttribute] set; } #pragma warning disable CS0067 // unused event [RequiresUnsafeAttribute] event System.Action E1; [field: RequiresUnsafeAttribute] event System.Action E2; event System.Action E3 { [RequiresUnsafeAttribute] add { } [RequiresUnsafeAttribute] remove { } } [RequiresUnsafeAttribute] int this[int i] { get => i; set { } } [RequiresUnsafeAttribute] C() { } [RequiresUnsafeAttribute] ~C() { } [RequiresUnsafeAttribute] static C() { } [RequiresUnsafeAttribute] public static C operator +(C c1, C c2) => c1; [RequiresUnsafeAttribute] public void operator +=(C c) { } public void M([RequiresUnsafeAttribute] int x) { } [return: RequiresUnsafeAttribute] public int Func() => 0; public void M<[RequiresUnsafeAttribute] T>() { } #pragma warning disable CS0169 // unused field [RequiresUnsafeAttribute] int F; void L() { var lam = [RequiresUnsafeAttribute] () => { }; } } [RequiresUnsafeAttribute] delegate void D(); [RequiresUnsafeAttribute] enum E { X } enum F { [RequiresUnsafeAttribute] X } """, """ using System.Diagnostics.CodeAnalysis; [module: RequiresUnsafeAttribute] [assembly: RequiresUnsafeAttribute] """, CompilerFeatureRequiredAttribute, ]; var sourceErrors = new[] { // (4,6): error CS9379: Do not use 'RequiresUnsafeAttribute' in source; use the 'unsafe' modifier instead. // [RequiresUnsafeAttribute] void M() { } Diagnostic(ErrorCode.ERR_RequiresUnsafeAttributeInSource, "RequiresUnsafeAttribute").WithLocation(4, 6), // (5,6): error CS9379: Do not use 'RequiresUnsafeAttribute' in source; use the 'unsafe' modifier instead. // [RequiresUnsafeAttribute] int P1 { get; set; } Diagnostic(ErrorCode.ERR_RequiresUnsafeAttributeInSource, "RequiresUnsafeAttribute").WithLocation(5, 6), // (6,13): error CS9379: Do not use 'RequiresUnsafeAttribute' in source; use the 'unsafe' modifier instead. // [field: RequiresUnsafeAttribute] int P2 { get; set; } Diagnostic(ErrorCode.ERR_RequiresUnsafeAttributeInSource, "RequiresUnsafeAttribute").WithLocation(6, 13), // (7,15): error CS9379: Do not use 'RequiresUnsafeAttribute' in source; use the 'unsafe' modifier instead. // int P3 { [RequiresUnsafeAttribute] get; [RequiresUnsafeAttribute] set; } Diagnostic(ErrorCode.ERR_RequiresUnsafeAttributeInSource, "RequiresUnsafeAttribute").WithLocation(7, 15), // (7,46): error CS9379: Do not use 'RequiresUnsafeAttribute' in source; use the 'unsafe' modifier instead. // int P3 { [RequiresUnsafeAttribute] get; [RequiresUnsafeAttribute] set; } Diagnostic(ErrorCode.ERR_RequiresUnsafeAttributeInSource, "RequiresUnsafeAttribute").WithLocation(7, 46), // (9,6): error CS9379: Do not use 'RequiresUnsafeAttribute' in source; use the 'unsafe' modifier instead. // [RequiresUnsafeAttribute] event System.Action E1; Diagnostic(ErrorCode.ERR_RequiresUnsafeAttributeInSource, "RequiresUnsafeAttribute").WithLocation(9, 6), // (10,13): error CS9379: Do not use 'RequiresUnsafeAttribute' in source; use the 'unsafe' modifier instead. // [field: RequiresUnsafeAttribute] event System.Action E2; Diagnostic(ErrorCode.ERR_RequiresUnsafeAttributeInSource, "RequiresUnsafeAttribute").WithLocation(10, 13), // (11,31): error CS9379: Do not use 'RequiresUnsafeAttribute' in source; use the 'unsafe' modifier instead. // event System.Action E3 { [RequiresUnsafeAttribute] add { } [RequiresUnsafeAttribute] remove { } } Diagnostic(ErrorCode.ERR_RequiresUnsafeAttributeInSource, "RequiresUnsafeAttribute").WithLocation(11, 31), // (11,65): error CS9379: Do not use 'RequiresUnsafeAttribute' in source; use the 'unsafe' modifier instead. // event System.Action E3 { [RequiresUnsafeAttribute] add { } [RequiresUnsafeAttribute] remove { } } Diagnostic(ErrorCode.ERR_RequiresUnsafeAttributeInSource, "RequiresUnsafeAttribute").WithLocation(11, 65), // (12,6): error CS9379: Do not use 'RequiresUnsafeAttribute' in source; use the 'unsafe' modifier instead. // [RequiresUnsafeAttribute] int this[int i] { get => i; set { } } Diagnostic(ErrorCode.ERR_RequiresUnsafeAttributeInSource, "RequiresUnsafeAttribute").WithLocation(12, 6), // (13,6): error CS9379: Do not use 'RequiresUnsafeAttribute' in source; use the 'unsafe' modifier instead. // [RequiresUnsafeAttribute] C() { } Diagnostic(ErrorCode.ERR_RequiresUnsafeAttributeInSource, "RequiresUnsafeAttribute").WithLocation(13, 6), // (16,6): error CS9379: Do not use 'RequiresUnsafeAttribute' in source; use the 'unsafe' modifier instead. // [RequiresUnsafeAttribute] public static C operator +(C c1, C c2) => c1; Diagnostic(ErrorCode.ERR_RequiresUnsafeAttributeInSource, "RequiresUnsafeAttribute").WithLocation(16, 6), // (17,6): error CS9379: Do not use 'RequiresUnsafeAttribute' in source; use the 'unsafe' modifier instead. // [RequiresUnsafeAttribute] public void operator +=(C c) { } Diagnostic(ErrorCode.ERR_RequiresUnsafeAttributeInSource, "RequiresUnsafeAttribute").WithLocation(17, 6), // (14,6): error CS9379: Do not use 'RequiresUnsafeAttribute' in source; use the 'unsafe' modifier instead. // [RequiresUnsafeAttribute] ~C() { } Diagnostic(ErrorCode.ERR_RequiresUnsafeAttributeInSource, "RequiresUnsafeAttribute").WithLocation(14, 6), // (15,6): error CS9379: Do not use 'RequiresUnsafeAttribute' in source; use the 'unsafe' modifier instead. // [RequiresUnsafeAttribute] static C() { } Diagnostic(ErrorCode.ERR_RequiresUnsafeAttributeInSource, "RequiresUnsafeAttribute").WithLocation(15, 6), // (23,27): error CS9379: Do not use 'RequiresUnsafeAttribute' in source; use the 'unsafe' modifier instead. // void L() { var lam = [RequiresUnsafeAttribute] () => { }; } Diagnostic(ErrorCode.ERR_RequiresUnsafeAttributeInSource, "RequiresUnsafeAttribute").WithLocation(23, 27), // (22,6): error CS9379: Do not use 'RequiresUnsafeAttribute' in source; use the 'unsafe' modifier instead. // [RequiresUnsafeAttribute] int F; Diagnostic(ErrorCode.ERR_RequiresUnsafeAttributeInSource, "RequiresUnsafeAttribute").WithLocation(22, 6), // (27,11): error CS9379: Do not use 'RequiresUnsafeAttribute' in source; use the 'unsafe' modifier instead. // enum F { [RequiresUnsafeAttribute] X } Diagnostic(ErrorCode.ERR_RequiresUnsafeAttributeInSource, "RequiresUnsafeAttribute").WithLocation(27, 11), }; CreateCompilation(source, [ref1], options: TestOptions.ReleaseDll.WithUpdatedMemorySafetyRules(updatedRules)) .VerifyDiagnostics( [ .. sourceErrors, // (2,2): error CS8335: Do not use 'System.Diagnostics.CodeAnalysis.RequiresUnsafeAttribute'. This is reserved for compiler usage. // [RequiresUnsafeAttribute] class C Diagnostic(ErrorCode.ERR_ExplicitReservedAttr, "RequiresUnsafeAttribute").WithArguments("System.Diagnostics.CodeAnalysis.RequiresUnsafeAttribute").WithLocation(2, 2), // (18,20): error CS8335: Do not use 'System.Diagnostics.CodeAnalysis.RequiresUnsafeAttribute'. This is reserved for compiler usage. // public void M([RequiresUnsafeAttribute] int x) { } Diagnostic(ErrorCode.ERR_ExplicitReservedAttr, "RequiresUnsafeAttribute").WithArguments("System.Diagnostics.CodeAnalysis.RequiresUnsafeAttribute").WithLocation(18, 20), // (19,14): error CS8335: Do not use 'System.Diagnostics.CodeAnalysis.RequiresUnsafeAttribute'. This is reserved for compiler usage. // [return: RequiresUnsafeAttribute] public int Func() => 0; Diagnostic(ErrorCode.ERR_ExplicitReservedAttr, "RequiresUnsafeAttribute").WithArguments("System.Diagnostics.CodeAnalysis.RequiresUnsafeAttribute").WithLocation(19, 14), // (20,20): error CS8335: Do not use 'System.Diagnostics.CodeAnalysis.RequiresUnsafeAttribute'. This is reserved for compiler usage. // public void M<[RequiresUnsafeAttribute] T>() { } Diagnostic(ErrorCode.ERR_ExplicitReservedAttr, "RequiresUnsafeAttribute").WithArguments("System.Diagnostics.CodeAnalysis.RequiresUnsafeAttribute").WithLocation(20, 20), // (25,2): error CS8335: Do not use 'System.Diagnostics.CodeAnalysis.RequiresUnsafeAttribute'. This is reserved for compiler usage. // [RequiresUnsafeAttribute] delegate void D(); Diagnostic(ErrorCode.ERR_ExplicitReservedAttr, "RequiresUnsafeAttribute").WithArguments("System.Diagnostics.CodeAnalysis.RequiresUnsafeAttribute").WithLocation(25, 2), // (26,2): error CS8335: Do not use 'System.Diagnostics.CodeAnalysis.RequiresUnsafeAttribute'. This is reserved for compiler usage. // [RequiresUnsafeAttribute] enum E { X } Diagnostic(ErrorCode.ERR_ExplicitReservedAttr, "RequiresUnsafeAttribute").WithArguments("System.Diagnostics.CodeAnalysis.RequiresUnsafeAttribute").WithLocation(26, 2), // (2,10): error CS8335: Do not use 'System.Diagnostics.CodeAnalysis.RequiresUnsafeAttribute'. This is reserved for compiler usage. // [module: RequiresUnsafeAttribute] Diagnostic(ErrorCode.ERR_ExplicitReservedAttr, "RequiresUnsafeAttribute").WithArguments("System.Diagnostics.CodeAnalysis.RequiresUnsafeAttribute").WithLocation(2, 10), // (3,12): error CS8335: Do not use 'System.Diagnostics.CodeAnalysis.RequiresUnsafeAttribute'. This is reserved for compiler usage. // [assembly: RequiresUnsafeAttribute] Diagnostic(ErrorCode.ERR_ExplicitReservedAttr, "RequiresUnsafeAttribute").WithArguments("System.Diagnostics.CodeAnalysis.RequiresUnsafeAttribute").WithLocation(3, 12), ]); var comp2 = CreateCompilation(RequiresUnsafeAttributeDefinition).VerifyDiagnostics(); var ref2 = AsReference(comp2, useCompilationReference); CreateCompilation(source, [ref2], options: TestOptions.ReleaseDll.WithUpdatedMemorySafetyRules(updatedRules)) .VerifyDiagnostics( [ .. sourceErrors, // (3,12): error CS0592: Attribute 'RequiresUnsafeAttribute' is not valid on this declaration type. It is only valid on 'constructor, method, property, indexer, field, event' declarations. // [assembly: RequiresUnsafeAttribute] Diagnostic(ErrorCode.ERR_AttributeOnBadSymbolType, "RequiresUnsafeAttribute").WithArguments("RequiresUnsafeAttribute", "constructor, method, property, indexer, field, event").WithLocation(3, 12), // (2,10): error CS0592: Attribute 'RequiresUnsafeAttribute' is not valid on this declaration type. It is only valid on 'constructor, method, property, indexer, field, event' declarations. // [module: RequiresUnsafeAttribute] Diagnostic(ErrorCode.ERR_AttributeOnBadSymbolType, "RequiresUnsafeAttribute").WithArguments("RequiresUnsafeAttribute", "constructor, method, property, indexer, field, event").WithLocation(2, 10), // (2,2): error CS0592: Attribute 'RequiresUnsafeAttribute' is not valid on this declaration type. It is only valid on 'constructor, method, property, indexer, field, event' declarations. // [RequiresUnsafeAttribute] class C Diagnostic(ErrorCode.ERR_AttributeOnBadSymbolType, "RequiresUnsafeAttribute").WithArguments("RequiresUnsafeAttribute", "constructor, method, property, indexer, field, event").WithLocation(2, 2), // (25,2): error CS0592: Attribute 'RequiresUnsafeAttribute' is not valid on this declaration type. It is only valid on 'constructor, method, property, indexer, field, event' declarations. // [RequiresUnsafeAttribute] delegate void D(); Diagnostic(ErrorCode.ERR_AttributeOnBadSymbolType, "RequiresUnsafeAttribute").WithArguments("RequiresUnsafeAttribute", "constructor, method, property, indexer, field, event").WithLocation(25, 2), // (26,2): error CS0592: Attribute 'RequiresUnsafeAttribute' is not valid on this declaration type. It is only valid on 'constructor, method, property, indexer, field, event' declarations. // [RequiresUnsafeAttribute] enum E { X } Diagnostic(ErrorCode.ERR_AttributeOnBadSymbolType, "RequiresUnsafeAttribute").WithArguments("RequiresUnsafeAttribute", "constructor, method, property, indexer, field, event").WithLocation(26, 2), // (18,20): error CS0592: Attribute 'RequiresUnsafeAttribute' is not valid on this declaration type. It is only valid on 'constructor, method, property, indexer, field, event' declarations. // public void M([RequiresUnsafeAttribute] int x) { } Diagnostic(ErrorCode.ERR_AttributeOnBadSymbolType, "RequiresUnsafeAttribute").WithArguments("RequiresUnsafeAttribute", "constructor, method, property, indexer, field, event").WithLocation(18, 20), // (20,20): error CS0592: Attribute 'RequiresUnsafeAttribute' is not valid on this declaration type. It is only valid on 'constructor, method, property, indexer, field, event' declarations. // public void M<[RequiresUnsafeAttribute] T>() { } Diagnostic(ErrorCode.ERR_AttributeOnBadSymbolType, "RequiresUnsafeAttribute").WithArguments("RequiresUnsafeAttribute", "constructor, method, property, indexer, field, event").WithLocation(20, 20), // (19,14): error CS0592: Attribute 'RequiresUnsafeAttribute' is not valid on this declaration type. It is only valid on 'constructor, method, property, indexer, field, event' declarations. // [return: RequiresUnsafeAttribute] public int Func() => 0; Diagnostic(ErrorCode.ERR_AttributeOnBadSymbolType, "RequiresUnsafeAttribute").WithArguments("RequiresUnsafeAttribute", "constructor, method, property, indexer, field, event").WithLocation(19, 14), ]); } [Fact] public void RequiresUnsafeAttribute_Partial() { CompileAndVerifyUnsafe( lib: """ public partial class C { unsafe public partial int M1(); unsafe public partial int M1() => 0; unsafe public partial int M2(); unsafe public partial int M2() => 0; } """, caller: """ var c = new C(); c.M1(); c.M2(); """, expectedUnsafeSymbols: ["C.M1", "C.M2"], expectedSafeSymbols: ["C"], expectedDiagnostics: [ // (2,1): error CS9362: 'C.M1()' must be used in an unsafe context because it is marked as 'unsafe' // c.M1(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c.M1()").WithArguments("C.M1()").WithLocation(2, 1), // (3,1): error CS9362: 'C.M2()' must be used in an unsafe context because it is marked as 'unsafe' // c.M2(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "c.M2()").WithArguments("C.M2()").WithLocation(3, 1), ]); // Both partial declarations agree on `unsafe`. CreateCompilation( [ """ partial class C { unsafe public partial int M(); unsafe public partial int M() => 0; } """, ], options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()) .VerifyDiagnostics(); } [Fact] public void PublicApi_RequiresUnsafeContext() { var source = """ public unsafe class C { unsafe public void M1() { } public void M2() { } unsafe safe public void M3() { } public void M4(int* p) { } } """; var comp = CreateCompilation(source, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()); comp.VerifyDiagnostics( // (1,21): error CS9377: The 'unsafe' modifier does not have any effect here under the current memory safety rules. // public unsafe class C Diagnostic(ErrorCode.ERR_UnsafeMeaningless, "C").WithLocation(1, 21), // (5,12): error CS9388: The 'safe' and 'unsafe' modifiers cannot be used together. // unsafe safe public void M3() { } Diagnostic(ErrorCode.ERR_SafeModifierCannotBeUsedWithUnsafe, "safe").WithLocation(5, 12)); var m1 = comp.GetMember<MethodSymbol>("C.M1").GetPublicSymbol(); Assert.True(m1.RequiresUnsafeContext); var m2 = comp.GetMember<MethodSymbol>("C.M2").GetPublicSymbol(); Assert.False(m2.RequiresUnsafeContext); var m3 = comp.GetMember<MethodSymbol>("C.M3").GetPublicSymbol(); Assert.True(m3.RequiresUnsafeContext); var m4 = comp.GetMember<MethodSymbol>("C.M4").GetPublicSymbol(); Assert.False(m4.RequiresUnsafeContext); Assert.False(m1.ContainingAssembly.RequiresUnsafeContext); Assert.False(m1.ContainingModule.RequiresUnsafeContext); Assert.False(m1.ContainingType.RequiresUnsafeContext); // Module has no attributes since it is the source symbol. Assert.Empty(m1.ContainingModule.GetAttributes()); // https://github.com/dotnet/roslyn/issues/82546: we should add and test an API to obtain memory safety rules version of the module } [Fact] public void PublicApi_RequiresUnsafeContext_CompatMode() { var source = """ public unsafe class C { public void M1(int* p) { } public void M2() { } unsafe public void M3() { } safe public void M4(int* p) { } } """; var comp = CreateCompilation(source, options: TestOptions.UnsafeReleaseDll); comp.VerifyEmitDiagnostics(); var m1 = comp.GetMember<MethodSymbol>("C.M1").GetPublicSymbol(); Assert.True(m1.RequiresUnsafeContext); var m2 = comp.GetMember<MethodSymbol>("C.M2").GetPublicSymbol(); Assert.False(m2.RequiresUnsafeContext); var m3 = comp.GetMember<MethodSymbol>("C.M3").GetPublicSymbol(); Assert.False(m3.RequiresUnsafeContext); var m4 = comp.GetMember<MethodSymbol>("C.M4").GetPublicSymbol(); Assert.True(m4.RequiresUnsafeContext); Assert.False(m1.ContainingAssembly.RequiresUnsafeContext); Assert.False(m1.ContainingModule.RequiresUnsafeContext); Assert.False(m1.ContainingType.RequiresUnsafeContext); Assert.Empty(m1.ContainingModule.GetAttributes()); // https://github.com/dotnet/roslyn/issues/82546: we should add and test an API to obtain memory safety rules version of the module } [Theory, CombinatorialData] public void PublicApi_RequiresUnsafeContext_VisualBasic(bool useMetadata) { var source = """ Public Class C Public Sub M() End Sub End Class """; var sourceComp = CreateVisualBasicCompilation(source).VerifyDiagnostics(); var comp = useMetadata ? CreateVisualBasicCompilation("", referencedAssemblies: [sourceComp.EmitToImageReference()]) : sourceComp; comp.VerifyDiagnostics(); var method = comp.GetTypeByMetadataName("C")!.GetMember("M"); Assert.False(method.RequiresUnsafeContext); } [Fact] public void Await_InUnsafeBlock() { var source = """ using System; using System.Threading.Tasks; class C { static async Task Main() { unsafe { await Task.Yield(); } Console.Write(123); } } """; CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics( // (9,13): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // await Task.Yield(); Diagnostic(ErrorCode.ERR_FeatureInPreview, "await").WithArguments("updated memory safety rules").WithLocation(9, 13)); var expectedOutput = "123"; CompileAndVerify(source, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe, expectedOutput: expectedOutput).VerifyDiagnostics(); CompileAndVerify(source, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules(), expectedOutput: expectedOutput).VerifyDiagnostics(); CompileAndVerify(source, parseOptions: TestOptions.RegularPreview, options: TestOptions.UnsafeReleaseExe, expectedOutput: expectedOutput).VerifyDiagnostics(); CompileAndVerify(source, parseOptions: TestOptions.RegularPreview, options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules(), expectedOutput: expectedOutput).VerifyDiagnostics(); } [Fact] public void Await_InFixedStatement_Body() { var source = """ using System.Threading.Tasks; class C { static async Task M(int[] a) { unsafe { fixed (int* p = a) { await Task.Yield(); } } } } """; var expectedDiagnostics = new[] { // (10,17): error CS9398: Cannot await in context of a 'fixed' statement // await Task.Yield(); Diagnostic(ErrorCode.ERR_BadAwaitInFixed, "await").WithLocation(10, 17), }; CreateCompilation(source, parseOptions: TestOptions.Regular14, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()).VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.RegularPreview, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.RegularPreview, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()).VerifyDiagnostics(expectedDiagnostics); } [Fact] public void Await_InFixedStatement_Body_SafeContext() { var source = """ using System.Threading.Tasks; class C { static async Task M(int[] a) { fixed (int* p = a) { await Task.Yield(); } } } """; CreateCompilation(source, parseOptions: TestOptions.Regular14).VerifyDiagnostics( // (6,9): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // fixed (int* p = a) Diagnostic(ErrorCode.ERR_UnsafeNeeded, @"fixed (int* p = a) { await Task.Yield(); }").WithLocation(6, 9), // (6,16): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // fixed (int* p = a) Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(6, 16), // (8,13): error CS9398: Cannot await in context of a 'fixed' statement // await Task.Yield(); Diagnostic(ErrorCode.ERR_BadAwaitInFixed, "await").WithLocation(8, 13)); var expectedDiagnostics = new[] { // (8,13): error CS9398: Cannot await in context of a 'fixed' statement // await Task.Yield(); Diagnostic(ErrorCode.ERR_BadAwaitInFixed, "await").WithLocation(8, 13), }; CreateCompilation(source, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseDll.WithUpdatedMemorySafetyRules()).VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.RegularPreview, options: TestOptions.ReleaseDll.WithUpdatedMemorySafetyRules()).VerifyDiagnostics(expectedDiagnostics); } [Fact] public void Await_InFixedStatement_Initializer() { var source = """ using System.Threading.Tasks; class C { static async Task M() { fixed (byte* p = await GetAsync()) { } } static Task<byte[]> GetAsync() => null; } """; CreateCompilation(source, parseOptions: TestOptions.Regular14).VerifyDiagnostics( // (6,9): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // fixed (byte* p = await GetAsync()) Diagnostic(ErrorCode.ERR_UnsafeNeeded, @"fixed (byte* p = await GetAsync()) { }").WithLocation(6, 9), // (6,16): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // fixed (byte* p = await GetAsync()) Diagnostic(ErrorCode.ERR_UnsafeNeeded, "byte*").WithLocation(6, 16), // (6,26): error CS9398: Cannot await in context of a 'fixed' statement // fixed (byte* p = await GetAsync()) Diagnostic(ErrorCode.ERR_BadAwaitInFixed, "await").WithLocation(6, 26)); var expectedDiagnostics = new[] { // (6,26): error CS9398: Cannot await in context of a 'fixed' statement // fixed (byte* p = await GetAsync()) Diagnostic(ErrorCode.ERR_BadAwaitInFixed, "await").WithLocation(6, 26), }; CreateCompilation(source, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseDll.WithUpdatedMemorySafetyRules()).VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.RegularPreview, options: TestOptions.ReleaseDll.WithUpdatedMemorySafetyRules()).VerifyDiagnostics(expectedDiagnostics); } [Fact] public void Await_InFixedStatement_Initializer_Multiple() { var source = """ using System.Threading.Tasks; class C { static async Task M() { fixed (byte* p1 = await GetAsync(), p2 = await GetAsync()) { } } static Task<byte[]> GetAsync() => null; } """; CreateCompilation(source, parseOptions: TestOptions.Regular14).VerifyDiagnostics( // (6,9): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // fixed (byte* p1 = await GetAsync(), p2 = await GetAsync()) Diagnostic(ErrorCode.ERR_UnsafeNeeded, @"fixed (byte* p1 = await GetAsync(), p2 = await GetAsync()) { }").WithLocation(6, 9), // (6,16): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // fixed (byte* p1 = await GetAsync(), p2 = await GetAsync()) Diagnostic(ErrorCode.ERR_UnsafeNeeded, "byte*").WithLocation(6, 16), // (6,27): error CS9398: Cannot await in context of a 'fixed' statement // fixed (byte* p1 = await GetAsync(), p2 = await GetAsync()) Diagnostic(ErrorCode.ERR_BadAwaitInFixed, "await").WithLocation(6, 27), // (6,50): error CS9398: Cannot await in context of a 'fixed' statement // fixed (byte* p1 = await GetAsync(), p2 = await GetAsync()) Diagnostic(ErrorCode.ERR_BadAwaitInFixed, "await").WithLocation(6, 50)); var expectedDiagnostics = new[] { // (6,27): error CS9398: Cannot await in context of a 'fixed' statement // fixed (byte* p1 = await GetAsync(), p2 = await GetAsync()) Diagnostic(ErrorCode.ERR_BadAwaitInFixed, "await").WithLocation(6, 27), // (6,50): error CS9398: Cannot await in context of a 'fixed' statement // fixed (byte* p1 = await GetAsync(), p2 = await GetAsync()) Diagnostic(ErrorCode.ERR_BadAwaitInFixed, "await").WithLocation(6, 50), }; CreateCompilation(source, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseDll.WithUpdatedMemorySafetyRules()).VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.RegularPreview, options: TestOptions.ReleaseDll.WithUpdatedMemorySafetyRules()).VerifyDiagnostics(expectedDiagnostics); } [Fact] public void Await_InFixedStatement_NestedLambda() { var source = """ using System; using System.Threading.Tasks; class C { static async Task M(int[] a) { fixed (int* p = a) { Func<Task> f = async () => { await Task.Yield(); }; await f(); } } } """; CreateCompilation(source, parseOptions: TestOptions.Regular14).VerifyDiagnostics( // (7,9): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // fixed (int* p = a) Diagnostic(ErrorCode.ERR_UnsafeNeeded, @"fixed (int* p = a) { Func<Task> f = async () => { await Task.Yield(); }; await f(); }").WithLocation(7, 9), // (7,16): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // fixed (int* p = a) Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(7, 16), // (10,13): error CS9398: Cannot await in context of a 'fixed' statement // await f(); Diagnostic(ErrorCode.ERR_BadAwaitInFixed, "await").WithLocation(10, 13)); var expectedDiagnostics = new[] { // (10,13): error CS9398: Cannot await in context of a 'fixed' statement // await f(); Diagnostic(ErrorCode.ERR_BadAwaitInFixed, "await").WithLocation(10, 13), }; CreateCompilation(source, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseDll.WithUpdatedMemorySafetyRules()).VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.RegularPreview, options: TestOptions.ReleaseDll.WithUpdatedMemorySafetyRules()).VerifyDiagnostics(expectedDiagnostics); } }