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;