Skip to content

Commit

Permalink
Allow generic type parameters to be the same type in method generation
Browse files Browse the repository at this point in the history
Previouly we assumed that the type parameters in a method would not be of
the same `TypeSymbol` when generating a method and put them in a dictionary.
This causes exceptions when attempting to add to the dictionary and the
`TypeSymbol` is already added as a key. Now we use a list of instead of a
dictionary.

Fixes dotnet#10004
  • Loading branch information
jmarolf committed May 6, 2016
1 parent 326cdc0 commit 34fb562
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2879,6 +2879,46 @@ private static void OnChanged(object sender, EventArgs e)
}");
}

[WorkItem(10004, "https://github.com/dotnet/roslyn/issues/10004")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateMethod)]
public async Task TestGenerateMethodWithMultipleOfSameGenericType()
{
await TestAsync(
@"using System;
public class C
{
}
public static class Ex
{
public static T M1<T>(this T t) where T : C
{
return [|t.M<T, T>()|];
}
}
",
@"using System;
public class C
{
internal T1 M<T1, T2>()
where T1 : C
where T2 : C
{
}
}
public static class Ex
{
public static T M1<T>(this T t) where T : C
{
return t.M<T, T>();
}
}
");
}

public class GenerateConversionTest : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest
{
internal override Tuple<DiagnosticAnalyzer, CodeFixProvider> CreateDiagnosticProviderAndFixer(Workspace workspace)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2200,6 +2200,38 @@ NewLines("Imports System \n Imports System.Collections.Generic \n Module Program
NewLines("Imports System \n Imports System.Collections.Generic \n Module Program \n Sub M() \n Dim x = New Dictionary ( Of Integer , Boolean ) From { { 1, T() } } \n End Sub \n Private Function T() As Boolean \n Throw New NotImplementedException() \n End Function \n End Module"))
End Function

<WorkItem(10004, "https://github.com/dotnet/roslyn/issues/10004")>
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateMethod)>
Public Async Function TestGenerateMethodWithMultipleOfSameGenericType() As Task
Await TestAsync(
<text>
Namespace TestClasses
Public Class C
End Class

Module Ex
Public Function M(Of T As C)(a As T) As T
Return [|a.Test(Of T, T)()|]
End Function
End Module
End Namespace
</text>.Value.Replace(vbLf, vbCrLf),
<text>
Namespace TestClasses
Public Class C
Friend Function Test(Of T1 As C, T2 As C)() As T1
End Function
End Class

Module Ex
Public Function M(Of T As C)(a As T) As T
Return a.Test(Of T, T)()
End Function
End Module
End Namespace
</text>.Value.Replace(vbLf, vbCrLf))
End Function

Public Class GenerateConversionTests
Inherits AbstractVisualBasicDiagnosticProviderBasedUserDiagnosticTest

Expand Down Expand Up @@ -2484,7 +2516,6 @@ Class Digit
End Class
</text>.Value.Replace(vbLf, vbCrLf), compareTokens:=False)
End Function

End Class
End Class
End Namespace
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,10 @@ public static IMethodSymbol RenameTypeParameters(this IMethodSymbol method, ILis
var updatedTypeParameters = RenameTypeParameters(
method.TypeParameters, newNames, typeGenerator);

var mapping = new Dictionary<ITypeSymbol, ITypeSymbol>();
var mapping = new List<KeyValuePair<ITypeSymbol, ITypeSymbol>>();
for (int i = 0; i < method.TypeParameters.Length; i++)
{
mapping.Add(method.TypeParameters[i], updatedTypeParameters[i]);
mapping.Add(KeyValuePair.Create((ITypeSymbol)method.TypeParameters[i], (ITypeSymbol)updatedTypeParameters[i]));
}

return CodeGenerationSymbolFactory.CreateMethodSymbol(
Expand Down Expand Up @@ -114,7 +114,7 @@ public static IMethodSymbol RenameParameters(this IMethodSymbol method, IList<st
// We generate the type parameter in two passes. The first creates the new type
// parameter. The second updates the constraints to point at this new type parameter.
var newTypeParameters = new List<CodeGenerationTypeParameterSymbol>();
var mapping = new Dictionary<ITypeSymbol, ITypeSymbol>();
var mapping = new List<KeyValuePair<ITypeSymbol, ITypeSymbol>>();
for (int i = 0; i < typeParameters.Count; i++)
{
var typeParameter = typeParameters[i];
Expand All @@ -131,7 +131,8 @@ public static IMethodSymbol RenameParameters(this IMethodSymbol method, IList<st
typeParameter.Ordinal);

newTypeParameters.Add(newTypeParameter);
mapping.Add(typeParameter, newTypeParameter);
var map = KeyValuePair.Create<ITypeSymbol, ITypeSymbol>(typeParameter, newTypeParameter);
mapping.Add(map);
}

// Now we update the constraints.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ private class SubstituteTypesVisitor<TType1, TType2> : SymbolVisitor<ITypeSymbol
where TType2 : ITypeSymbol
{
// private readonly Compilation compilation;
private readonly IDictionary<TType1, TType2> _map;
private readonly IEnumerable<KeyValuePair<TType1, TType2>> _map;
private readonly ITypeGenerator _typeGenerator;

internal SubstituteTypesVisitor(
IDictionary<TType1, TType2> map,
IEnumerable<KeyValuePair<TType1, TType2>> map,
ITypeGenerator typeGenerator)
{
_map = map;
Expand All @@ -35,14 +35,27 @@ public override ITypeSymbol DefaultVisit(ISymbol node)
private ITypeSymbol VisitType(ITypeSymbol symbol)
{
TType2 converted;
if (symbol is TType1 && _map.TryGetValue((TType1)symbol, out converted))
if (symbol is TType1 && TryGetValue(_map, (TType1)symbol, out converted))
{
return converted;
}

return symbol;
}

public static bool TryGetValue<TKey, TValue>(IEnumerable<KeyValuePair<TKey, TValue>> map, TKey symbol, out TValue converted)
{
var types = map.Where(kvp => kvp.Key.GetHashCode() == symbol.GetHashCode());
if (!types.Any())
{
converted = default(TValue);
return false;
}

converted = types.SingleOrDefault().Value;
return converted != null;
}

public override ITypeSymbol VisitDynamicType(IDynamicTypeSymbol symbol)
{
return symbol;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -447,7 +447,7 @@ public static bool IsFormattableString(this ITypeSymbol symbol)

public static ITypeSymbol SubstituteTypes<TType1, TType2>(
this ITypeSymbol type,
IDictionary<TType1, TType2> mapping,
IEnumerable<KeyValuePair<TType1, TType2>> mapping,
Compilation compilation)
where TType1 : ITypeSymbol
where TType2 : ITypeSymbol
Expand All @@ -457,7 +457,7 @@ public static bool IsFormattableString(this ITypeSymbol symbol)

public static ITypeSymbol SubstituteTypes<TType1, TType2>(
this ITypeSymbol type,
IDictionary<TType1, TType2> mapping,
IEnumerable<KeyValuePair<TType1, TType2>> mapping,
ITypeGenerator typeGenerator)
where TType1 : ITypeSymbol
where TType2 : ITypeSymbol
Expand Down

0 comments on commit 34fb562

Please sign in to comment.