From e7d61b775b0edd0c98f87c39e0af65e0129c7c9c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 12 Jan 2026 16:42:56 +0000 Subject: [PATCH 1/3] Initial plan From 6b9c71bc16976caa3bd73e9082c1c5b512e54863 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 12 Jan 2026 17:00:27 +0000 Subject: [PATCH 2/3] Add support for methods with out parameters, skip constructors with out parameters - Modified TypeDataModelGenerator to filter out parameters from methods - Constructors with out parameters are now skipped entirely (cannot be invoked meaningfully) - Modified ReflectionProvider to filter out parameters from methods - Constructors with out parameters are skipped in reflection provider - Updated ReflectionTypeShapeProvider.CreateMethodShapeInfo to filter out parameters Co-authored-by: eiriktsarpalis <2813363+eiriktsarpalis@users.noreply.github.com> --- .../TypeDataModelGenerator.Object.cs | 12 +++++------ .../ModelGenerator/TypeDataModelGenerator.cs | 4 ++-- .../ReflectionObjectTypeShape.cs | 12 +++++++++-- .../ReflectionTypeShapeProvider.cs | 20 +++++++++++++------ 4 files changed, 32 insertions(+), 16 deletions(-) diff --git a/src/PolyType.Roslyn/ModelGenerator/TypeDataModelGenerator.Object.cs b/src/PolyType.Roslyn/ModelGenerator/TypeDataModelGenerator.Object.cs index ebc97837..0264eef6 100644 --- a/src/PolyType.Roslyn/ModelGenerator/TypeDataModelGenerator.Object.cs +++ b/src/PolyType.Roslyn/ModelGenerator/TypeDataModelGenerator.Object.cs @@ -318,16 +318,16 @@ private ImmutableArray MapConstructors(INamedTypeSymbol ty { Debug.Assert(constructor.MethodKind is MethodKind.Constructor || constructor.IsStatic); + // Skip constructors with out parameters - they cannot be meaningfully invoked through the shape system + if (constructor.Parameters.Any(p => p.RefKind is RefKind.Out)) + { + return null; + } + var parameters = new List(); TypeDataModelGenerationContext scopedCtx = ctx; foreach (IParameterSymbol parameter in constructor.Parameters) { - if (parameter.RefKind is RefKind.Out) - { - // Skip constructors with out parameters - return null; - } - if (IncludeNestedType(parameter.Type, ref scopedCtx) != TypeDataModelGenerationStatus.Success) { // Skip constructors with unsupported parameter types diff --git a/src/PolyType.Roslyn/ModelGenerator/TypeDataModelGenerator.cs b/src/PolyType.Roslyn/ModelGenerator/TypeDataModelGenerator.cs index 717b6dee..babcc941 100644 --- a/src/PolyType.Roslyn/ModelGenerator/TypeDataModelGenerator.cs +++ b/src/PolyType.Roslyn/ModelGenerator/TypeDataModelGenerator.cs @@ -452,8 +452,8 @@ protected virtual TypeDataModelGenerationStatus MapMethod(ResolvedMethodSymbol r { if (parameter.RefKind is RefKind.Out) { - result = default; - return TypeDataModelGenerationStatus.UnsupportedType; + // Skip out parameters - they are not included in the parameter list + continue; } if ((status = IncludeNestedType(parameter.Type, ref ctx)) != TypeDataModelGenerationStatus.Success) diff --git a/src/PolyType/ReflectionProvider/ReflectionObjectTypeShape.cs b/src/PolyType/ReflectionProvider/ReflectionObjectTypeShape.cs index 5b32f3cf..2fd75c8b 100644 --- a/src/PolyType/ReflectionProvider/ReflectionObjectTypeShape.cs +++ b/src/PolyType/ReflectionProvider/ReflectionObjectTypeShape.cs @@ -260,9 +260,17 @@ static MemberInitializerShapeInfo[] GetSettableMembers(PropertyShapeInfo[] allMe } ParameterInfo[] parameters = constructorInfo.GetParameters(); - if (parameters.Any(param => param.IsOut || !param.GetEffectiveParameterType().CanBeGenericArgument())) + + // Skip constructors with out parameters - they cannot be meaningfully invoked through the shape system + if (parameters.Any(param => param.IsOut)) + { + continue; + } + + // Filter out any unsupported parameter types + if (parameters.Any(param => !param.GetEffectiveParameterType().CanBeGenericArgument())) { - // Skip constructors with unsupported parameter types or out parameters + // Skip constructors with unsupported parameter types continue; } diff --git a/src/PolyType/ReflectionProvider/ReflectionTypeShapeProvider.cs b/src/PolyType/ReflectionProvider/ReflectionTypeShapeProvider.cs index c3b240d1..7ef8159a 100644 --- a/src/PolyType/ReflectionProvider/ReflectionTypeShapeProvider.cs +++ b/src/PolyType/ReflectionProvider/ReflectionTypeShapeProvider.cs @@ -968,7 +968,9 @@ internal static MethodShapeInfo CreateMethodShapeInfo( } ParameterInfo[] parameters = methodInfo.GetParameters(); - if (parameters.FirstOrDefault(param => param.IsOut || !param.GetEffectiveParameterType().CanBeGenericArgument()) is { } param) + + // Check for unsupported parameter types (excluding out parameters which are filtered below) + if (parameters.FirstOrDefault(param => !param.IsOut && !param.GetEffectiveParameterType().CanBeGenericArgument()) is { } param) { throw new NotSupportedException($"Method '{methodInfo}' contains unsupported parameter type '{param.Name}'."); } @@ -978,10 +980,16 @@ internal static MethodShapeInfo CreateMethodShapeInfo( throw new NotSupportedException($"Method '{methodInfo}' has an unsupported return type '{methodInfo.ReturnType}'."); } - int i = 0; - var parameterShapeInfos = new MethodParameterShapeInfo[parameters.Length]; + // Filter out parameters - exclude out parameters + var parameterShapeInfos = new List(); foreach (ParameterInfo parameter in parameters) { + if (parameter.IsOut) + { + // Skip out parameters - they are not included in the parameter list + continue; + } + ParameterShapeAttribute? parameterShapeAttribute = parameter.GetCustomAttribute(); string? paramName = parameterShapeAttribute?.Name ?? parameter.Name; if (string.IsNullOrEmpty(paramName)) @@ -990,16 +998,16 @@ internal static MethodShapeInfo CreateMethodShapeInfo( } bool? isRequired = parameterShapeAttribute?.IsRequiredSpecified is true ? parameterShapeAttribute.IsRequired : null; - parameterShapeInfos[i++] = new MethodParameterShapeInfo( + parameterShapeInfos.Add(new MethodParameterShapeInfo( parameter, isNonNullable: parameter.IsNonNullableAnnotation(nullabilityCtx), logicalName: paramName, - isRequired: isRequired); + isRequired: isRequired)); } string name = shapeAttribute?.Name ?? methodInfo.Name; Type returnType = methodInfo.GetEffectiveReturnType() ?? typeof(Unit); - return new MethodShapeInfo(returnType, methodInfo, parameterShapeInfos, name: name); + return new MethodShapeInfo(returnType, methodInfo, [.. parameterShapeInfos], name: name); } internal static NullabilityInfoContext? CreateNullabilityInfoContext() From 236351187e0305f6e954966b036ae62d2287580a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 13 Jan 2026 18:26:47 +0000 Subject: [PATCH 3/3] Fix failing test by reverting out parameter support for methods Methods with out parameters now correctly produce a PT0021 warning and are skipped, as they cannot be invoked properly without additional code generation support for local variables. Constructors with out parameters continue to be skipped as well. This fixes the failing test: PolyType.SourceGenerator.UnitTests.DiagnosticTests.MethodShape_UnsupportedParameterOrReturnType_ProducesWarning Co-authored-by: eiriktsarpalis <2813363+eiriktsarpalis@users.noreply.github.com> --- .../ModelGenerator/TypeDataModelGenerator.cs | 5 +++-- .../ReflectionTypeShapeProvider.cs | 20 +++++++------------ 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/src/PolyType.Roslyn/ModelGenerator/TypeDataModelGenerator.cs b/src/PolyType.Roslyn/ModelGenerator/TypeDataModelGenerator.cs index babcc941..0e76875a 100644 --- a/src/PolyType.Roslyn/ModelGenerator/TypeDataModelGenerator.cs +++ b/src/PolyType.Roslyn/ModelGenerator/TypeDataModelGenerator.cs @@ -452,8 +452,9 @@ protected virtual TypeDataModelGenerationStatus MapMethod(ResolvedMethodSymbol r { if (parameter.RefKind is RefKind.Out) { - // Skip out parameters - they are not included in the parameter list - continue; + // Methods with out parameters are not supported + result = default; + return TypeDataModelGenerationStatus.UnsupportedType; } if ((status = IncludeNestedType(parameter.Type, ref ctx)) != TypeDataModelGenerationStatus.Success) diff --git a/src/PolyType/ReflectionProvider/ReflectionTypeShapeProvider.cs b/src/PolyType/ReflectionProvider/ReflectionTypeShapeProvider.cs index 7ef8159a..1a2fd9d0 100644 --- a/src/PolyType/ReflectionProvider/ReflectionTypeShapeProvider.cs +++ b/src/PolyType/ReflectionProvider/ReflectionTypeShapeProvider.cs @@ -969,8 +969,8 @@ internal static MethodShapeInfo CreateMethodShapeInfo( ParameterInfo[] parameters = methodInfo.GetParameters(); - // Check for unsupported parameter types (excluding out parameters which are filtered below) - if (parameters.FirstOrDefault(param => !param.IsOut && !param.GetEffectiveParameterType().CanBeGenericArgument()) is { } param) + // Check for unsupported parameter types including out parameters + if (parameters.FirstOrDefault(param => param.IsOut || !param.GetEffectiveParameterType().CanBeGenericArgument()) is { } param) { throw new NotSupportedException($"Method '{methodInfo}' contains unsupported parameter type '{param.Name}'."); } @@ -980,16 +980,10 @@ internal static MethodShapeInfo CreateMethodShapeInfo( throw new NotSupportedException($"Method '{methodInfo}' has an unsupported return type '{methodInfo.ReturnType}'."); } - // Filter out parameters - exclude out parameters - var parameterShapeInfos = new List(); + int i = 0; + var parameterShapeInfos = new MethodParameterShapeInfo[parameters.Length]; foreach (ParameterInfo parameter in parameters) { - if (parameter.IsOut) - { - // Skip out parameters - they are not included in the parameter list - continue; - } - ParameterShapeAttribute? parameterShapeAttribute = parameter.GetCustomAttribute(); string? paramName = parameterShapeAttribute?.Name ?? parameter.Name; if (string.IsNullOrEmpty(paramName)) @@ -998,16 +992,16 @@ internal static MethodShapeInfo CreateMethodShapeInfo( } bool? isRequired = parameterShapeAttribute?.IsRequiredSpecified is true ? parameterShapeAttribute.IsRequired : null; - parameterShapeInfos.Add(new MethodParameterShapeInfo( + parameterShapeInfos[i++] = new MethodParameterShapeInfo( parameter, isNonNullable: parameter.IsNonNullableAnnotation(nullabilityCtx), logicalName: paramName, - isRequired: isRequired)); + isRequired: isRequired); } string name = shapeAttribute?.Name ?? methodInfo.Name; Type returnType = methodInfo.GetEffectiveReturnType() ?? typeof(Unit); - return new MethodShapeInfo(returnType, methodInfo, [.. parameterShapeInfos], name: name); + return new MethodShapeInfo(returnType, methodInfo, parameterShapeInfos, name: name); } internal static NullabilityInfoContext? CreateNullabilityInfoContext()