Skip to content
Draft
Original file line number Diff line number Diff line change
Expand Up @@ -382,7 +382,13 @@ public static bool ReportDelegateOrFunctionPointerMethodGroupDiagnostics(Binder
typeContainingConstructor: null, delegateTypeBeingInvoked: null,
isMethodGroupConversion: true, returnRefKind: invokeMethodOpt?.RefKind, delegateOrFunctionPointerType: targetType);

hasErrors = overloadDiagnostics.HasAnyErrors();
// Even if no diagnostics were reported, check if any candidate method has error types
// in its return type or parameters. This can happen when ERR_BadRetType or similar errors
// are suppressed for methods with omitted type arguments.
hasErrors = overloadDiagnostics.HasAnyErrors()
|| result.Results.Any(static candidate => candidate.Member.ReturnType.ContainsErrorType()
|| candidate.Member.Parameters.Any(static p => p.Type.ContainsErrorType()));

diagnostics.AddRangeAndFree(overloadDiagnostics);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -726,7 +726,13 @@ private bool HadReturnMismatch(Location location, BindingDiagnosticBag diagnosti
if (!mismatch.IsNull)
{
var method = (MethodSymbol)(Symbol)mismatch.Member;
diagnostics.Add(ErrorCode.ERR_BadRetType, location, method, method.ReturnType);
// Only report ERR_BadRetType if the return type is well-defined.
// If the return type is an error type (from omitted type arguments like Method<>),
// there's already a more specific error reported elsewhere (e.g., ERR_OmittedTypeArgument).
if (!method.ReturnType.ContainsErrorType())
{
diagnostics.Add(ErrorCode.ERR_BadRetType, location, method, method.ReturnType);
}
return true;
}

Expand Down
29 changes: 26 additions & 3 deletions src/Compilers/CSharp/Test/Semantic/Semantics/BindingTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3860,9 +3860,6 @@ public void Test()
// (33,51): error CS8389: Omitting the type argument is not allowed in the current context
// System.Func<object> delegateConversion1 = "string literal".ExtensionMethod1<>;
Diagnostic(ErrorCode.ERR_OmittedTypeArgument, @"""string literal"".ExtensionMethod1<>").WithLocation(33, 51),
// (33,51): error CS0407: '? FooExtensions.ExtensionMethod1<?>(object)' has the wrong return type
// System.Func<object> delegateConversion1 = "string literal".ExtensionMethod1<>;
Diagnostic(ErrorCode.ERR_BadRetType, @"""string literal"".ExtensionMethod1<>").WithArguments("FooExtensions.ExtensionMethod1<?>(object)", "?").WithLocation(33, 51),
Copy link
Member

Choose a reason for hiding this comment

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

@copilot you made two changes to the code. Are there tests demonstrating the effect of each change?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The existing test at line 3785 (System.Func<object> delegateConversion1 = "string literal".ExtensionMethod1<>;) exercises both changes. During method group conversion to a delegate type:

  1. First, overload resolution is performed, which may call HadReturnMismatch in OverloadResolutionResult.cs if the return type doesn't match
  2. Then, if a method is selected, MethodIsCompatibleWithDelegateOrFunctionPointer in Binder_Conversions.cs verifies the delegate compatibility

Both code paths suppress ERR_BadRetType when the return type contains error types (from omitted type arguments). The test verifies that the confusing ERR_BadRetType error with ? symbols is no longer reported for this scenario, confirming both changes work correctly together.

Copy link
Member

Choose a reason for hiding this comment

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

@copilot this is a good update that validates the case where the return type contains an error type.

Can you add a test after this that is a trimmed down version of this, except that the extension method has this form:

public static void ExtensionMethod1<T>(this object obj, T t) { } and assigns to Action<object>.

Let's see if we have a similar problem where we report ? for the parameter type. If not, then we don't need to do anything more. If we see a similar problem, then we should update our checks accordingly to check parameters as well as return type.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added test in commit 878ac15. The same issue exists for parameter types - ERR_MethDelegateMismatch was being reported. Extended the fix to also check for error types in method parameters, not just return types. Both tests now pass.

// (34,51): error CS8389: Omitting the type argument is not allowed in the current context
// System.Func<object> delegateConversion2 = "string literal".ExtensionMethod2<>;
Diagnostic(ErrorCode.ERR_OmittedTypeArgument, @"""string literal"".ExtensionMethod2<>").WithLocation(34, 51),
Expand All @@ -3871,6 +3868,32 @@ public void Test()
Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "ExtensionMethod2<>").WithArguments("string", "ExtensionMethod2").WithLocation(34, 68));
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/16041")]
public void MissingTypeArgumentInGenericExtensionMethod_ParameterType()
{
var source =
"""
public static class GooExtensions
{
public static void ExtensionMethod1<T>(this object obj, T t) { }
}

public class Class1
{
public void Test()
{
System.Action<object> delegateConversion1 = "literal".ExtensionMethod1<>;
}
}
""";
var compilation = CreateCompilationWithMscorlib40AndSystemCore(source);

compilation.VerifyDiagnostics(
// (10,53): error CS8389: Omitting the type argument is not allowed in the current context
// System.Action<object> delegateConversion1 = "literal".ExtensionMethod1<>;
Diagnostic(ErrorCode.ERR_OmittedTypeArgument, @"""literal"".ExtensionMethod1<>").WithLocation(10, 53));
}

[WorkItem(22757, "https://github.com/dotnet/roslyn/issues/22757")]
[Fact]
public void MethodGroupConversionNoReceiver()
Expand Down
Loading