Improve arity error message to prefer generic type when both generic and non-generic exist#80707
Conversation
…and non-generic exist Co-authored-by: CyrusNajmabadi <[email protected]>
Co-authored-by: CyrusNajmabadi <[email protected]>
| } | ||
| } | ||
|
|
||
| private static bool PreferGenericOverNonGeneric(Symbol currentSymbol, Symbol newSymbol) |
There was a problem hiding this comment.
@copilot make this a static local function inside MergeEqual #Closed
There was a problem hiding this comment.
Done in commit 7b5a8c6.
| [Fact] | ||
| public void TestGenericAndNonGenericType() | ||
| { | ||
| var text = @" |
There was a problem hiding this comment.
@copilot make all these test strings into raw string literals.
There was a problem hiding this comment.
Done in commit a2c4d9f.
| { | ||
| public class ImprovedArityErrorTests : CSharpTestBase | ||
| { | ||
| [Fact] |
There was a problem hiding this comment.
add an appropriate WorkItem attribute to all these tests.
There was a problem hiding this comment.
Done in commit a2c4d9f.
Co-authored-by: CyrusNajmabadi <[email protected]>
Co-authored-by: CyrusNajmabadi <[email protected]>
|
@dotnet/roslyn-ide ptal. |
Co-authored-by: jcouv <[email protected]>
jcouv
left a comment
There was a problem hiding this comment.
Done with review pass (commit 14)
Co-authored-by: jcouv <[email protected]>
|
@dotnet/roslyn-compiler For a second pair of eyes. |
| } | ||
|
|
||
| static bool preferGenericOverNonGeneric(Symbol firstSymbol, Symbol secondSymbol) | ||
| => firstSymbol is NamedTypeSymbol { Arity: 0 } && secondSymbol is NamedTypeSymbol { Arity: > 0 }; |
There was a problem hiding this comment.
I think we should just use Symbol.GetArity() instead of pattern matching, and also test the analogous method case:
class Program
{
static void Main()
{
// error CS0308: The non-generic method 'Program.M(object)' cannot be used with type arguments
// (It seems reasonable to prefer `M<T1, T2>` in this case for the same reason as a type.)
M<int>(default!);
}
void M(object obj) { }
T2 M<T1, T2>(T1 t) => throw null!;
}| { | ||
| // When both results are WrongArity, prefer reporting errors about generic types | ||
| // over non-generic types to provide more helpful error messages | ||
| if (preferGenericOverNonGeneric(this.SingleSymbolOrDefault, result.Symbol)) |
There was a problem hiding this comment.
Consider a name like isNonGenericVersusGeneric instead, so that the terms in the function name are in the same order as the tests we do on the parameters.
Currently the name reads as if we are trying to decide whether we prefer or not an incoming generic symbol over a non-generic symbol. But that's not really what it's doing.
| _symbolList.Add(result.Symbol); | ||
| } | ||
| } | ||
| // else: existing result is preferred (generic over non-generic), keep it |
There was a problem hiding this comment.
I think this method would be easier to understand if we just returned in the branches where we are "done" merging the results, and there wouldn't be a need to duplicate the logic to _symbolList.Add(result.Symbol);.
For example, in a branch where this result is better, just return. In a branch where this result is worse, call this.SetFrom(result) then return.
Summary
This PR fixes an inaccurate error message that occurs when a user supplies type arguments to a type name that has both generic and non-generic versions.
Problem
When attempting to use a type with the wrong number of type arguments, if both a generic and non-generic version exist with the same name, the compiler incorrectly reports an error about the non-generic version instead of the generic one.
For example, consider this code:
Before this fix, the compiler reported:
This is confusing because:
MyExpression<T>does existAfter this fix, the compiler reports:
This is much clearer because:
Implementation
The fix modifies the lookup result merging logic in
LookupResult.MergeEqual(SingleLookupResult)to add special handling forWrongArityresults. When both a generic type (arity > 0) and a non-generic type (arity == 0) are found with wrong arity, the logic now prefers reporting the error about the generic type.The implementation is minimal and surgical:
preferGenericOverNonGenericstatic local function to determine which symbol to preferMergeEqualmethod to use this logic when both results haveLookupResultKind.WrongArityTesting
Added 7 comprehensive test cases in
BindingTests.cscovering:Updated 1 existing test in
BindingTests.csthat was validating the old behaviorAll existing arity-related tests pass (1,293 tests)
No regressions found in semantic error tests
Fixes #24406
Original prompt
Fixes #24406
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.