From eaa14844e1c582e4e6c7d5df218c0f034658e628 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 4 Dec 2025 22:01:38 +0000
Subject: [PATCH 1/4] Initial plan
From 988044412776b9bae9565854e870cdd728422b11 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 4 Dec 2025 22:10:17 +0000
Subject: [PATCH 2/4] Apply analyzer and test changes for C# 14 extensions fix
Co-authored-by: sbomer <787361+sbomer@users.noreply.github.com>
---
.../TrimAnalysis/MethodParameterValue.cs | 5 --
.../TrimAnalysis/ParameterProxy.cs | 6 +-
.../TrimAnalysis/TrimAnalysisVisitor.cs | 12 +++-
src/tools/illink/test/Directory.Build.props | 7 +-
.../DataFlowTests.cs | 13 ++++
...pilerGeneratedCodeAccessedViaReflection.cs | 2 +
.../DataFlow/ExtensionMembersDataFlow.cs | 0
.../DataFlow/ExtensionsDataFlow.cs | 66 +++++++++++++++++++
.../Mono.Linker.Tests.Cases.csproj | 5 ++
.../TestCasesRunner/ResultChecker.cs | 15 +++++
10 files changed, 119 insertions(+), 12 deletions(-)
create mode 100644 src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/ExtensionMembersDataFlow.cs
create mode 100644 src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/ExtensionsDataFlow.cs
diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/MethodParameterValue.cs b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/MethodParameterValue.cs
index 67668004433ce9..589b0e8130efdf 100644
--- a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/MethodParameterValue.cs
+++ b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/MethodParameterValue.cs
@@ -9,11 +9,6 @@ namespace ILLink.Shared.TrimAnalysis
{
internal partial record MethodParameterValue
{
- public MethodParameterValue (IParameterSymbol parameterSymbol)
- : this (new ParameterProxy (parameterSymbol)) { }
- public MethodParameterValue (IMethodSymbol methodSymbol, ParameterIndex parameterIndex, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes)
- : this (new (new (methodSymbol), parameterIndex), dynamicallyAccessedMemberTypes) { }
-
public MethodParameterValue (ParameterProxy parameter)
: this (parameter, FlowAnnotations.GetMethodParameterAnnotation (parameter)) { }
diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/ParameterProxy.cs b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/ParameterProxy.cs
index f57de9138dbf88..baa21aca62c8a4 100644
--- a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/ParameterProxy.cs
+++ b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/ParameterProxy.cs
@@ -9,10 +9,10 @@ namespace ILLink.Shared.TypeSystemProxy
{
internal partial struct ParameterProxy
{
- public ParameterProxy (IParameterSymbol parameter)
+ public ParameterProxy (IParameterSymbol parameter, IMethodSymbol method)
{
- Method = new ((IMethodSymbol) parameter.ContainingSymbol);
- Index = (ParameterIndex) parameter.Ordinal + (Method.HasImplicitThis () ? 1 : 0);
+ Method = new (method);
+ Index = (ParameterIndex) parameter.Ordinal + (method.HasImplicitThis () ? 1 : 0);
}
public partial ReferenceKind GetReferenceKind ()
diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimAnalysisVisitor.cs b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimAnalysisVisitor.cs
index b960e2a461f97d..d75902f3f20da8 100644
--- a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimAnalysisVisitor.cs
+++ b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimAnalysisVisitor.cs
@@ -112,8 +112,10 @@ public override MultiValue VisitInstanceReference (IInstanceReferenceOperation i
// The instance reference operation represents a 'this' or 'base' reference to the containing type,
// so we get the annotation from the containing method.
- if (instanceRef.Type != null && instanceRef.Type.IsTypeInterestingForDataflow ())
- return new MethodParameterValue (Method, (ParameterIndex) 0, Method.GetDynamicallyAccessedMemberTypes ());
+ // It can also happen that we see this for a static method - for example a delegate creation
+ // over a local function does this, even thought the "this" makes no sense inside a static scope.
+ if (instanceRef.Type != null && instanceRef.Type.IsTypeInterestingForDataflow () && !Method.IsStatic)
+ return new MethodParameterValue (new ParameterProxy (new (Method), (ParameterIndex) 0));
return TopValue;
}
@@ -187,7 +189,11 @@ public override MultiValue GetFieldTargetValue (IFieldSymbol field)
public override MultiValue GetParameterTargetValue (IParameterSymbol parameter)
{
- return parameter.Type.IsTypeInterestingForDataflow () ? new MethodParameterValue (parameter) : TopValue;
+ // Skip analysis for extension members (we have no way to represent a parameter on an extension type).
+ if (parameter.ContainingSymbol is not IMethodSymbol method)
+ return TopValue;
+
+ return parameter.Type.IsTypeInterestingForDataflow () ? new MethodParameterValue (new ParameterProxy (parameter, method)) : TopValue;
}
public override void HandleAssignment (MultiValue source, MultiValue target, IOperation operation)
diff --git a/src/tools/illink/test/Directory.Build.props b/src/tools/illink/test/Directory.Build.props
index 0d526221a8804f..08758195a4202e 100644
--- a/src/tools/illink/test/Directory.Build.props
+++ b/src/tools/illink/test/Directory.Build.props
@@ -6,4 +6,9 @@
disable
$(MSBuildThisFileDirectory)
-
\ No newline at end of file
+
+
+
+
+
+
diff --git a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/DataFlowTests.cs b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/DataFlowTests.cs
index 29622dc32365c3..ca7ef1bdaa83bb 100644
--- a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/DataFlowTests.cs
+++ b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/DataFlowTests.cs
@@ -131,6 +131,19 @@ public Task EventDataFlow ()
return RunTest ();
}
+ // Disabled until we can use C# 14 in tests.
+ // [Fact]
+ // public Task ExtensionMembersDataFlow ()
+ // {
+ // return RunTest ();
+ // }
+
+ [Fact]
+ public Task ExtensionsDataFlow ()
+ {
+ return RunTest ();
+ }
+
[Fact]
public Task FieldDataFlow ()
{
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/CompilerGeneratedCodeAccessedViaReflection.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/CompilerGeneratedCodeAccessedViaReflection.cs
index 3dc23c8c5e1cd1..2603140c5baf69 100644
--- a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/CompilerGeneratedCodeAccessedViaReflection.cs
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/CompilerGeneratedCodeAccessedViaReflection.cs
@@ -79,6 +79,8 @@ public static IEnumerable IteratorWithCorrectDataflow ()
ProducedBy = Tool.Trimmer)]
[ExpectedWarning ("IL2119", "", CompilerGeneratedCode = true,
ProducedBy = Tool.Trimmer)]
+ // Using C# 14 compiler changes codegen in a way that introduces this warning
+ // [UnexpectedWarning("IL2062", Tool.Trimmer | Tool.NativeAot, "https://github.com/dotnet/runtime/issues/110553", CompilerGeneratedCode = true)]
public static IEnumerable IteratorWithIntegerDataflow ()
{
int integerLocal = 0;
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/ExtensionMembersDataFlow.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/ExtensionMembersDataFlow.cs
new file mode 100644
index 00000000000000..e69de29bb2d1d6
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/ExtensionsDataFlow.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/ExtensionsDataFlow.cs
new file mode 100644
index 00000000000000..1f7b8bcb63df4d
--- /dev/null
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/ExtensionsDataFlow.cs
@@ -0,0 +1,66 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Diagnostics.CodeAnalysis;
+using Mono.Linker.Tests.Cases.Expectations.Assertions;
+using Mono.Linker.Tests.Cases.Expectations.Helpers;
+
+namespace Mono.Linker.Tests.Cases.DataFlow
+{
+ [SkipKeptItemsValidation]
+ [ExpectedNoWarnings]
+ public class ExtensionsDataFlow
+ {
+ public static void Main ()
+ {
+ TestExtensionMethod ();
+ TestExtensionMethodMismatch ();
+ TestExtensionMethodRequires ();
+ }
+
+ [ExpectedWarning ("IL2072", "GetWithMethods", nameof (Extensions.ExtensionMethod))]
+ static void TestExtensionMethod ()
+ {
+ GetWithFields ().ExtensionMethod ();
+ GetWithMethods ().ExtensionMethod ();
+ }
+
+ static void TestExtensionMethodMismatch ()
+ {
+ GetWithFields ().ExtensionMethodMismatch ();
+ }
+
+ [ExpectedWarning ("IL2026", nameof (Extensions.ExtensionMethodRequires))]
+ static void TestExtensionMethodRequires ()
+ {
+ GetWithFields ().ExtensionMethodRequires ();
+ }
+
+ [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)]
+ static Type GetWithFields () => null;
+
+ [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)]
+ static Type GetWithMethods () => null;
+ }
+
+ [ExpectedNoWarnings]
+ public static class Extensions
+ {
+ public static void ExtensionMethod ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] this Type type)
+ {
+ type.RequiresPublicFields ();
+ }
+
+ [ExpectedWarning ("IL2067", "RequiresPublicMethods")]
+ public static void ExtensionMethodMismatch ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] this Type type)
+ {
+ type.RequiresPublicMethods ();
+ }
+
+ [RequiresUnreferencedCode (nameof (ExtensionMethodRequires))]
+ public static void ExtensionMethodRequires ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] this Type type)
+ {
+ }
+ }
+}
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Mono.Linker.Tests.Cases.csproj b/src/tools/illink/test/Mono.Linker.Tests.Cases/Mono.Linker.Tests.Cases.csproj
index b30d39a672b44d..dd20bc83bb46c2 100644
--- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Mono.Linker.Tests.Cases.csproj
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Mono.Linker.Tests.Cases.csproj
@@ -62,4 +62,9 @@
+
+
+
+
+
diff --git a/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs b/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs
index 692985443ae6b3..3402acbec1c865 100644
--- a/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs
+++ b/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs
@@ -786,6 +786,21 @@ void VerifyLoggedMessages (AssemblyDefinition original, LinkerTestLogger logger,
List loggedMessages = logger.GetLoggedMessages ();
List<(ICustomAttributeProvider, CustomAttribute)> expectedNoWarningsAttributes = new ();
foreach (var attrProvider in GetAttributeProviders (original)) {
+
+ if (attrProvider is IMemberDefinition attrMember &&
+ attrMember is not TypeDefinition &&
+ attrMember.DeclaringType is TypeDefinition declaringType &&
+ declaringType.Name.StartsWith("<>E__"))
+ {
+ // Workaround: C# 14 extension members result in a compiler-generated type
+ // that has a member for each extension member (this is in addition to the type
+ // which contains the actual extension member implementation).
+ // The generated members inherit attributes from the extension members, but
+ // have empty implementations. We don't want to check inherited ExpectedWarningAttributes
+ // for these members.
+ continue;
+ }
+
foreach (var attr in attrProvider.CustomAttributes) {
if (!IsProducedByLinker (attr))
continue;
From cd12d47ddd73016a2cd5cbda795a33567fae4f3c Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 4 Dec 2025 22:37:20 +0000
Subject: [PATCH 3/4] Remove unrelated IsStatic check and comments from
VisitInstanceReference
Co-authored-by: sbomer <787361+sbomer@users.noreply.github.com>
---
.../ILLink.RoslynAnalyzer/TrimAnalysis/TrimAnalysisVisitor.cs | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimAnalysisVisitor.cs b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimAnalysisVisitor.cs
index d75902f3f20da8..3eea09e78d4348 100644
--- a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimAnalysisVisitor.cs
+++ b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimAnalysisVisitor.cs
@@ -112,9 +112,7 @@ public override MultiValue VisitInstanceReference (IInstanceReferenceOperation i
// The instance reference operation represents a 'this' or 'base' reference to the containing type,
// so we get the annotation from the containing method.
- // It can also happen that we see this for a static method - for example a delegate creation
- // over a local function does this, even thought the "this" makes no sense inside a static scope.
- if (instanceRef.Type != null && instanceRef.Type.IsTypeInterestingForDataflow () && !Method.IsStatic)
+ if (instanceRef.Type != null && instanceRef.Type.IsTypeInterestingForDataflow ())
return new MethodParameterValue (new ParameterProxy (new (Method), (ParameterIndex) 0));
return TopValue;
From c67b9f77e15b46f8aa3cee4d92214db79a250dc5 Mon Sep 17 00:00:00 2001
From: Sven Boemer
Date: Thu, 4 Dec 2025 15:28:45 -0800
Subject: [PATCH 4/4] Exclude test changes
This branch will not be updated to support using C# 14 in tests.
---
src/tools/illink/test/Directory.Build.props | 7 +------
.../ILLink.RoslynAnalyzer.Tests/DataFlowTests.cs | 7 -------
.../CompilerGeneratedCodeAccessedViaReflection.cs | 2 --
.../DataFlow/ExtensionMembersDataFlow.cs | 0
.../Mono.Linker.Tests.Cases.csproj | 5 -----
.../TestCasesRunner/ResultChecker.cs | 15 ---------------
6 files changed, 1 insertion(+), 35 deletions(-)
delete mode 100644 src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/ExtensionMembersDataFlow.cs
diff --git a/src/tools/illink/test/Directory.Build.props b/src/tools/illink/test/Directory.Build.props
index 08758195a4202e..0d526221a8804f 100644
--- a/src/tools/illink/test/Directory.Build.props
+++ b/src/tools/illink/test/Directory.Build.props
@@ -6,9 +6,4 @@
disable
$(MSBuildThisFileDirectory)
-
-
-
-
-
-
+
\ No newline at end of file
diff --git a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/DataFlowTests.cs b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/DataFlowTests.cs
index ca7ef1bdaa83bb..9add1124769a19 100644
--- a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/DataFlowTests.cs
+++ b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/DataFlowTests.cs
@@ -131,13 +131,6 @@ public Task EventDataFlow ()
return RunTest ();
}
- // Disabled until we can use C# 14 in tests.
- // [Fact]
- // public Task ExtensionMembersDataFlow ()
- // {
- // return RunTest ();
- // }
-
[Fact]
public Task ExtensionsDataFlow ()
{
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/CompilerGeneratedCodeAccessedViaReflection.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/CompilerGeneratedCodeAccessedViaReflection.cs
index 2603140c5baf69..3dc23c8c5e1cd1 100644
--- a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/CompilerGeneratedCodeAccessedViaReflection.cs
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/CompilerGeneratedCodeAccessedViaReflection.cs
@@ -79,8 +79,6 @@ public static IEnumerable IteratorWithCorrectDataflow ()
ProducedBy = Tool.Trimmer)]
[ExpectedWarning ("IL2119", "", CompilerGeneratedCode = true,
ProducedBy = Tool.Trimmer)]
- // Using C# 14 compiler changes codegen in a way that introduces this warning
- // [UnexpectedWarning("IL2062", Tool.Trimmer | Tool.NativeAot, "https://github.com/dotnet/runtime/issues/110553", CompilerGeneratedCode = true)]
public static IEnumerable IteratorWithIntegerDataflow ()
{
int integerLocal = 0;
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/ExtensionMembersDataFlow.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/ExtensionMembersDataFlow.cs
deleted file mode 100644
index e69de29bb2d1d6..00000000000000
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Mono.Linker.Tests.Cases.csproj b/src/tools/illink/test/Mono.Linker.Tests.Cases/Mono.Linker.Tests.Cases.csproj
index dd20bc83bb46c2..b30d39a672b44d 100644
--- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Mono.Linker.Tests.Cases.csproj
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Mono.Linker.Tests.Cases.csproj
@@ -62,9 +62,4 @@
-
-
-
-
-
diff --git a/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs b/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs
index 3402acbec1c865..692985443ae6b3 100644
--- a/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs
+++ b/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs
@@ -786,21 +786,6 @@ void VerifyLoggedMessages (AssemblyDefinition original, LinkerTestLogger logger,
List loggedMessages = logger.GetLoggedMessages ();
List<(ICustomAttributeProvider, CustomAttribute)> expectedNoWarningsAttributes = new ();
foreach (var attrProvider in GetAttributeProviders (original)) {
-
- if (attrProvider is IMemberDefinition attrMember &&
- attrMember is not TypeDefinition &&
- attrMember.DeclaringType is TypeDefinition declaringType &&
- declaringType.Name.StartsWith("<>E__"))
- {
- // Workaround: C# 14 extension members result in a compiler-generated type
- // that has a member for each extension member (this is in addition to the type
- // which contains the actual extension member implementation).
- // The generated members inherit attributes from the extension members, but
- // have empty implementations. We don't want to check inherited ExpectedWarningAttributes
- // for these members.
- continue;
- }
-
foreach (var attr in attrProvider.CustomAttributes) {
if (!IsProducedByLinker (attr))
continue;