Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9092,4 +9092,35 @@ private static bool NewMethod(int v)
""",
options: NoBraces());
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/22597")]
public async Task TestFullyExtractedTypeParameter()
{
await TestInRegularAndScriptAsync(
"""
class C
{
private void Test()
{
[|void Goo<T>(T bar) => Console.WriteLine(bar);
Goo(3);|]
}
}
""",
"""
class C
{
private void Test()
{
{|Rename:NewMethod|}();
}

private static void NewMethod()
{
void Goo<T>(T bar) => Console.WriteLine(bar);
Goo(3);
}
}
""");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -483,7 +483,7 @@ private void GenerateVariableInfoMap(
if (symbol is IParameterSymbol && variableDeclared)
continue;

var type = GetSymbolType(symbol);
var type = GetSymbolTypeWithUpdatedNullability(symbol);
if (type == null)
continue;

Expand Down Expand Up @@ -527,16 +527,9 @@ PooledDisposer<PooledHashSet<ISymbol>> GetPooledSymbolSet(ImmutableArray<ISymbol
return disposer;
}

ITypeSymbol? GetSymbolType(ISymbol symbol)
ITypeSymbol? GetSymbolTypeWithUpdatedNullability(ISymbol symbol)
{
var type = symbol switch
{
ILocalSymbol local => local.Type,
IParameterSymbol parameter => parameter.Type,
IRangeVariableSymbol rangeVariable => GetRangeVariableType(rangeVariable),
_ => throw ExceptionUtilities.UnexpectedValue(symbol)
};

var type = GetUnderlyingSymbolType(symbol);
if (type is null)
return type;

Expand Down Expand Up @@ -578,6 +571,17 @@ static VariableInfo CreateFromSymbol(
}
}

private ITypeSymbol? GetUnderlyingSymbolType(ISymbol symbol)
{
return symbol switch
{
ILocalSymbol local => local.Type,
IParameterSymbol parameter => parameter.Type,
IRangeVariableSymbol rangeVariable => GetRangeVariableType(rangeVariable),
_ => throw ExceptionUtilities.UnexpectedValue(symbol)
};
}

private static void AddVariableToMap(IDictionary<ISymbol, VariableInfo> variableInfoMap, ISymbol localOrParameter, VariableInfo variableInfo)
=> variableInfoMap.Add(localOrParameter, variableInfo);

Expand Down Expand Up @@ -715,15 +719,13 @@ private static bool IsInteractiveSynthesizedParameter(ISymbol localOrParameter)
parameter.ContainingSymbol.ContainingType.IsScriptClass;
}

private static void AddTypeParametersToMap(IEnumerable<ITypeParameterSymbol> typeParameters, IDictionary<int, ITypeParameterSymbol> sortedMap)
private void AddTypeParametersToMap(IEnumerable<ITypeParameterSymbol> typeParameters, IDictionary<int, ITypeParameterSymbol> sortedMap)
{
foreach (var typeParameter in typeParameters)
{
AddTypeParameterToMap(typeParameter, sortedMap);
}
}

private static void AddTypeParameterToMap(ITypeParameterSymbol typeParameter, IDictionary<int, ITypeParameterSymbol> sortedMap)
private void AddTypeParameterToMap(ITypeParameterSymbol typeParameter, IDictionary<int, ITypeParameterSymbol> sortedMap)
{
if (typeParameter == null ||
typeParameter.DeclaringMethod == null ||
Expand All @@ -732,6 +734,13 @@ private static void AddTypeParameterToMap(ITypeParameterSymbol typeParameter, ID
return;
}

// Only care about type parameters declared outside of the span being selected. If the type parameter
// is within the selection, that means it comes from a generic local function and would not otherwise be
// usable by the calling method.
var selectionSpan = this.SelectionResult.FinalSpan;
if (typeParameter.Locations is not [var location] || selectionSpan.Contains(location.SourceSpan))
return;

sortedMap[typeParameter.Ordinal] = typeParameter;
}

Expand All @@ -740,29 +749,10 @@ private void AppendMethodTypeVariableFromDataFlowAnalysis(
IDictionary<int, ITypeParameterSymbol> sortedMap)
{
foreach (var symbol in variableInfoMap.Keys)
{
switch (symbol)
{
case IParameterSymbol parameter:
AddTypeParametersToMap(TypeParameterCollector.Collect(parameter.Type), sortedMap);
continue;

case ILocalSymbol local:
AddTypeParametersToMap(TypeParameterCollector.Collect(local.Type), sortedMap);
continue;

case IRangeVariableSymbol rangeVariable:
var type = GetRangeVariableType(rangeVariable);
AddTypeParametersToMap(TypeParameterCollector.Collect(type), sortedMap);
continue;

default:
throw ExceptionUtilities.UnexpectedValue(symbol);
}
}
AddTypeParametersToMap(TypeParameterCollector.Collect(GetUnderlyingSymbolType(symbol)), sortedMap);
}

private static void AppendMethodTypeParameterFromConstraint(SortedDictionary<int, ITypeParameterSymbol> sortedMap)
private void AppendMethodTypeParameterFromConstraint(SortedDictionary<int, ITypeParameterSymbol> sortedMap)
{
var typeParametersInConstraint = new List<ITypeParameterSymbol>();

Expand All @@ -771,9 +761,7 @@ private static void AppendMethodTypeParameterFromConstraint(SortedDictionary<int
{
var constraintTypes = typeParameter.ConstraintTypes;
if (constraintTypes.IsDefaultOrEmpty)
{
continue;
}

foreach (var type in constraintTypes)
{
Expand All @@ -784,21 +772,13 @@ private static void AppendMethodTypeParameterFromConstraint(SortedDictionary<int

// pick up only valid type parameter and add them to the map
foreach (var typeParameter in typeParametersInConstraint)
{
AddTypeParameterToMap(typeParameter, sortedMap);
}
}

private static void AppendMethodTypeParameterUsedDirectly(MultiDictionary<ISymbol, SyntaxToken> symbolMap, IDictionary<int, ITypeParameterSymbol> sortedMap)
private void AppendMethodTypeParameterUsedDirectly(MultiDictionary<ISymbol, SyntaxToken> symbolMap, IDictionary<int, ITypeParameterSymbol> sortedMap)
{
foreach (var typeParameter in symbolMap.Keys.OfType<ITypeParameterSymbol>())
{
if (typeParameter.DeclaringMethod != null &&
!sortedMap.ContainsKey(typeParameter.Ordinal))
{
sortedMap[typeParameter.Ordinal] = typeParameter;
}
}
AddTypeParameterToMap(typeParameter, sortedMap);
Copy link
Member Author

Choose a reason for hiding this comment

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

above checks are handled in this method.

}

private ImmutableArray<ITypeParameterSymbol> GetMethodTypeParametersInConstraintList(
Expand All @@ -816,7 +796,7 @@ private ImmutableArray<ITypeParameterSymbol> GetMethodTypeParametersInConstraint
return [.. sortedMap.Values];
}

private static void AppendTypeParametersInConstraintsUsedByConstructedTypeWithItsOwnConstraints(SortedDictionary<int, ITypeParameterSymbol> sortedMap)
private void AppendTypeParametersInConstraintsUsedByConstructedTypeWithItsOwnConstraints(SortedDictionary<int, ITypeParameterSymbol> sortedMap)
{
using var _1 = PooledHashSet<ITypeSymbol>.GetInstance(out var visited);
using var _2 = PooledHashSet<ITypeParameterSymbol>.GetInstance(out var candidates);
Expand Down Expand Up @@ -877,7 +857,7 @@ private static void AddTypeParametersInConstraintsUsedByConstructedTypeWithItsOw
}
}

private static ImmutableArray<ITypeParameterSymbol> GetMethodTypeParametersInDeclaration(ITypeSymbol returnType, SortedDictionary<int, ITypeParameterSymbol> sortedMap)
private ImmutableArray<ITypeParameterSymbol> GetMethodTypeParametersInDeclaration(ITypeSymbol returnType, SortedDictionary<int, ITypeParameterSymbol> sortedMap)
{
// add return type to the map
AddTypeParametersToMap(TypeParameterCollector.Collect(returnType), sortedMap);
Expand Down
Loading