Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,14 @@ internal void GetDeclarationDiagnostics(BindingDiagnosticBag addTo)
GetReturnTypeAttributes();

var compilation = DeclaringCompilation;
if (IsCallerUnsafe) compilation.EnsureRequiresUnsafeAttributeExists(addTo, GetFirstLocation(), modifyCompilation: false);

if (IsCallerUnsafe)
{
var location = Syntax.Identifier.GetLocation();
MessageID.IDS_FeatureUnsafeEvolution.CheckFeatureAvailability(addTo, compilation, location);
compilation.EnsureRequiresUnsafeAttributeExists(addTo, location, modifyCompilation: false);
}

ParameterHelpers.EnsureRefKindAttributesExist(compilation, Parameters, addTo, modifyCompilation: false);
ParameterHelpers.EnsureParamCollectionAttributeExists(compilation, Parameters, addTo, modifyCompilation: false);
ParameterHelpers.EnsureNativeIntegerAttributeExists(compilation, Parameters, addTo, modifyCompilation: false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -984,6 +984,7 @@ internal override void AfterAddingTypeMembersChecks(ConversionsBase conversions,

if (IsCallerUnsafe)
{
MessageID.IDS_FeatureUnsafeEvolution.CheckFeatureAvailability(diagnostics, compilation, _location);
compilation.EnsureRequiresUnsafeAttributeExists(diagnostics, _location, modifyCompilation: true);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1045,6 +1045,7 @@ internal override void AfterAddingTypeMembersChecks(ConversionsBase conversions,

if (IsCallerUnsafe)
{
MessageID.IDS_FeatureUnsafeEvolution.CheckFeatureAvailability(diagnostics, compilation, location);
compilation.EnsureRequiresUnsafeAttributeExists(diagnostics, location, modifyCompilation: true);
}

Expand Down
139 changes: 118 additions & 21 deletions src/Compilers/CSharp/Test/Emit3/Semantics/UnsafeEvolutionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ private void CompileAndVerify(
string caller,
object[] expectedUnsafeSymbols,
object[] expectedSafeSymbols,
params DiagnosticDescription[] expectedDiagnostics)
DiagnosticDescription[] expectedDiagnostics)
{
CreateCompilation([lib, caller],
options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules())
Expand All @@ -43,10 +43,19 @@ private void CompileAndVerify(
options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules())
.VerifyDiagnostics(expectedDiagnostics);

var libLegacy = CreateCompilation(lib,
options: TestOptions.UnsafeReleaseDll)
var libLegacy = CompileAndVerify(lib,
options: TestOptions.UnsafeReleaseDll,
symbolValidator: module =>
{
VerifyMemorySafetyRulesAttribute(module, includesAttributeDefinition: false, includesAttributeUse: false);
VerifyRequiresUnsafeAttribute(
module,
includesAttributeDefinition: false,
expectedUnsafeSymbols: [],
expectedSafeSymbols: [.. expectedUnsafeSymbols, .. expectedSafeSymbols]);
})
.VerifyDiagnostics()
.EmitToImageReference();
.GetImageReference();

CreateCompilation(caller, [libLegacy],
options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules())
Expand All @@ -64,6 +73,14 @@ private static Func<ModuleSymbol, Symbol> ExtensionMember(string containerName,
?? 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 void VerifyMemorySafetyRulesAttribute(
ModuleSymbol module,
bool includesAttributeDefinition,
Expand Down Expand Up @@ -2457,6 +2474,73 @@ public class SkipLocalsInitAttribute : Attribute;
Diagnostic(ErrorCode.ERR_UnsafeUninitializedStackAlloc, "stackalloc int[5]").WithLocation(3, 26));
}

// PROTOTYPE: Test all supported member kinds here.
[Fact]
public void Member_LangVersion()
{
var source = """
#pragma warning disable CS8321 // unused local function
unsafe void F() { }
class C
{
unsafe void M() { }
unsafe int P { get; set; }
}
""";

string[] safeSymbols = ["C"];
string[] unsafeSymbols = ["Program.<<Main>$>g__F|0_0", "C.M", "C.P", "C.get_P", "C.set_P"];

CompileAndVerify(source,
parseOptions: TestOptions.Regular14,
options: TestOptions.UnsafeReleaseExe.WithMetadataImportOptions(MetadataImportOptions.All),
symbolValidator: m =>
{
VerifyMemorySafetyRulesAttribute(m, includesAttributeDefinition: false, includesAttributeUse: false);
VerifyRequiresUnsafeAttribute(
m,
includesAttributeDefinition: false,
expectedUnsafeSymbols: [],
expectedSafeSymbols: [.. safeSymbols, .. unsafeSymbols]);
})
.VerifyDiagnostics();

CompileAndVerify(source,
Copy link
Member

@333fred 333fred Dec 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use an explicit langversion option here for clarity. #Closed

parseOptions: TestOptions.RegularPreview,
options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules().WithMetadataImportOptions(MetadataImportOptions.All),
symbolValidator: m =>
{
VerifyMemorySafetyRulesAttribute(m, includesAttributeDefinition: true, includesAttributeUse: true, isSynthesized: true);
VerifyRequiresUnsafeAttribute(
m,
includesAttributeDefinition: true,
isSynthesized: true,
expectedUnsafeSymbols: [.. unsafeSymbols],
expectedSafeSymbols: [.. safeSymbols]);
})
.VerifyDiagnostics();

CreateCompilation(source,
parseOptions: TestOptions.Regular14,
options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules())
.VerifyDiagnostics(
// (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.
// unsafe void F() { }
Diagnostic(ErrorCode.ERR_FeatureInPreview, "F").WithArguments("updated memory safety rules").WithLocation(2, 13),
// (5,17): 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 M() { }
Diagnostic(ErrorCode.ERR_FeatureInPreview, "M").WithArguments("updated memory safety rules").WithLocation(5, 17),
// (6,12): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// unsafe int P { get; set; }
Diagnostic(ErrorCode.ERR_FeatureInPreview, "int").WithArguments("updated memory safety rules").WithLocation(6, 12),
// (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.
// unsafe int P { get; set; }
Diagnostic(ErrorCode.ERR_FeatureInPreview, "get").WithArguments("updated memory safety rules").WithLocation(6, 20),
// (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.
// unsafe int P { get; set; }
Diagnostic(ErrorCode.ERR_FeatureInPreview, "set").WithArguments("updated memory safety rules").WithLocation(6, 25));
}

// PROTOTYPE: Test also implicit methods used in patterns like GetEnumerator in foreach.
// PROTOTYPE: Should some synthesized members be unsafe (like state machine methods that are declared unsafe)?
[Theory, CombinatorialData]
Expand Down Expand Up @@ -2514,6 +2598,14 @@ public class C
// public unsafe void M() => System.Console.Write(111);
Diagnostic(ErrorCode.ERR_IllegalUnsafe, "M").WithLocation(3, 24));
}

if (apiUnsafe && apiUpdatedRules && callerUpdatedRules && callerLangVersion < LanguageVersionFacts.CSharpNext)
{
expectedDiagnostics.Add(
// (3,24): error CS8652: The feature 'updated memory safety rules' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// public unsafe void M() => System.Console.Write(111);
Diagnostic(ErrorCode.ERR_FeatureInPreview, "M").WithArguments("updated memory safety rules").WithLocation(3, 24));
}
}

if (!callerAllowUnsafe && callerUnsafeBlock)
Expand Down Expand Up @@ -2553,23 +2645,28 @@ public class C
[Fact]
public void Member_Method_OverloadResolution()
{
var source = """
C.M(1);
C.M("s");
_ = nameof(C.M);

class C
{
public static void M(int x) { }
public static unsafe void M(string s) { }
}
""";
CreateCompilation(source,
options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules())
.VerifyDiagnostics(
// (2,1): error CS9502: 'C.M(string)' must be used in an unsafe context because it is marked as 'unsafe'
// C.M("s");
Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, @"C.M(""s"")").WithArguments("C.M(string)").WithLocation(2, 1));
CompileAndVerify(
lib: """
public class C
{
public static void M() { }
public static unsafe 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 CS9502: '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]
Expand Down
8 changes: 6 additions & 2 deletions src/Compilers/Test/Utilities/CSharp/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Threading;
Expand All @@ -16,7 +15,6 @@
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
using Xunit;

Expand Down Expand Up @@ -231,6 +229,12 @@ public static ImmutableArray<Symbol> GetMembers(this Compilation compilation, st
return members;
}

public static ImmutableArray<T> GetMembersByQualifiedName<T>(this NamespaceOrTypeSymbol container, string qualifiedName) where T : Symbol
=> GetMembersByQualifiedName(container, qualifiedName).SelectAsArray(s => (T)s);

public static ImmutableArray<Symbol> GetMembersByQualifiedName(this NamespaceOrTypeSymbol container, string qualifiedName)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: consider making this GetMembersByQualifiedName<T> like GetMembers<T> above. Then the caller doesn't need OfType<MethodSymbol> (unless there were multiple member kinds involved)

=> GetMembers(container, qualifiedName, lastContainer: out _);

private static ImmutableArray<Symbol> GetMembers(NamespaceOrTypeSymbol container, string qualifiedName, out NamespaceOrTypeSymbol lastContainer)
{
var parts = SplitMemberName(qualifiedName);
Expand Down