Skip to content
Open
21 changes: 21 additions & 0 deletions src/Compilers/CSharp/Portable/Binder/LookupResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -286,11 +286,32 @@ internal void MergeEqual(SingleLookupResult result)
{
this.SetFrom(result);
}
else if (Kind == LookupResultKind.WrongArity && result.Kind == LookupResultKind.WrongArity)
{
// When both results are WrongArity, prefer the generic type over the non-generic one
// if the user supplied type arguments
if (PreferGenericOverNonGeneric(this.SingleSymbolOrDefault, result.Symbol))
{
this.SetFrom(result);
}
else if (!PreferGenericOverNonGeneric(result.Symbol, this.SingleSymbolOrDefault))
{
// Neither is preferred, add both symbols
if ((object)result.Symbol != null)
{
_symbolList.Add(result.Symbol);
}
}
// else: existing result is preferred (generic over non-generic), keep it
}
else if ((object)result.Symbol != null)
{
// Same goodness. Include all symbols
_symbolList.Add(result.Symbol);
}

static bool PreferGenericOverNonGeneric(Symbol currentSymbol, Symbol newSymbol)
=> currentSymbol is NamedTypeSymbol { Arity: 0 } && newSymbol is NamedTypeSymbol { Arity: > 0 };
}

// global pool
Expand Down
125 changes: 123 additions & 2 deletions src/Compilers/CSharp/Test/Semantic/Semantics/BindingTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1833,9 +1833,9 @@ class C
// (20,27): error CS0305: Using the generic type 'N.A<T>' requires '1' type arguments
//
Diagnostic(ErrorCode.ERR_BadArity, "A").WithArguments("N.A<T>", "type", "1"),
// (21,34): error CS0308: The non-generic type 'N.B' cannot be used with type arguments
// (21,34): error CS0305: Using the generic type 'N.B<T1, T2>' requires '2' type arguments
//
Diagnostic(ErrorCode.ERR_HasNoTypeVars, "B<int>").WithArguments("N.B", "type")
Diagnostic(ErrorCode.ERR_BadArity, "B<int>").WithArguments("N.B<T1, T2>", "type", "2")
);
}

Expand Down Expand Up @@ -4080,5 +4080,126 @@ static void M(bool a, object b)

VerifyOperationTree(comp, model.GetOperation(ifStmt), operationString);
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/24406")]
public void TestGenericAndNonGenericType()
{
var text = """
class MyExpression { }
class MyExpression<T> { }

class Test
{
void M()
{
MyExpression<int, string> x;
}
}
""";

CreateCompilation(text).VerifyDiagnostics(
// (8,9): error CS0305: Using the generic type 'MyExpression<T>' requires 1 type arguments
// MyExpression<int, string> x;
Diagnostic(ErrorCode.ERR_BadArity, "MyExpression<int, string>").WithArguments("MyExpression<T>", "type", "1").WithLocation(8, 9),
// (8,35): warning CS0168: The variable 'x' is declared but never used
// MyExpression<int, string> x;
Diagnostic(ErrorCode.WRN_UnreferencedVar, "x").WithArguments("x").WithLocation(8, 35));
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/24406")]
public void TestGenericAndNonGenericType_SingleTypeArgument()
{
var text = """
class MyExpression { }
class MyExpression<T> { }

class Test
{
void M()
{
MyExpression<int> x = null;
}
}
""";

CreateCompilation(text).VerifyDiagnostics(
// (8,27): warning CS0219: The variable 'x' is assigned but its value is never used
// MyExpression<int> x = null;
Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "x").WithArguments("x").WithLocation(8, 27));
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/24406")]
public void TestNonGenericTypeOnly()
{
var text = """
class MyExpression { }

class Test
{
void M()
{
MyExpression<int> x;
}
}
""";

CreateCompilation(text).VerifyDiagnostics(
// (7,9): error CS0308: The non-generic type 'MyExpression' cannot be used with type arguments
// MyExpression<int> x;
Diagnostic(ErrorCode.ERR_HasNoTypeVars, "MyExpression<int>").WithArguments("MyExpression", "type").WithLocation(7, 9),
// (7,27): warning CS0168: The variable 'x' is declared but never used
// MyExpression<int> x;
Diagnostic(ErrorCode.WRN_UnreferencedVar, "x").WithArguments("x").WithLocation(7, 27));
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/24406")]
public void TestGenericTypeOnly()
{
var text = """
class MyExpression<T> { }

class Test
{
void M()
{
MyExpression<int, string> x;
}
}
""";

CreateCompilation(text).VerifyDiagnostics(
// (7,9): error CS0305: Using the generic type 'MyExpression<T>' requires 1 type arguments
// MyExpression<int, string> x;
Diagnostic(ErrorCode.ERR_BadArity, "MyExpression<int, string>").WithArguments("MyExpression<T>", "type", "1").WithLocation(7, 9),
// (7,35): warning CS0168: The variable 'x' is declared but never used
// MyExpression<int, string> x;
Diagnostic(ErrorCode.WRN_UnreferencedVar, "x").WithArguments("x").WithLocation(7, 35));
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/24406")]
public void TestMultipleGenericTypes()
{
var text = """
class MyExpression { }
class MyExpression<T> { }
class MyExpression<T1, T2> { }

class Test
{
void M()
{
MyExpression<int, string, bool> x;
}
}
""";

CreateCompilation(text).VerifyDiagnostics(
// (9,9): error CS0305: Using the generic type 'MyExpression<T>' requires 1 type arguments
// MyExpression<int, string, bool> x;
Diagnostic(ErrorCode.ERR_BadArity, "MyExpression<int, string, bool>").WithArguments("MyExpression<T>", "type", "1").WithLocation(9, 9),
// (9,41): warning CS0168: The variable 'x' is declared but never used
// MyExpression<int, string, bool> x;
Diagnostic(ErrorCode.WRN_UnreferencedVar, "x").WithArguments("x").WithLocation(9, 41));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -446,7 +446,7 @@ class A
private C<T> c2;
private C<int> c3;
private C<C<T>> c4;
private [|C|]<int,string> c5;
private C<int,string> c5;
Copy link
Member

Choose a reason for hiding this comment

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

IDE side is acceptable in this error case.

}
class C<T>
{
Expand Down