Skip to content

Commit 7b3d5fa

Browse files
Do not lift type parameters in extract method declared within the selected region (#76724)
Fixes #22597
2 parents b7e891b + 19cdd5b commit 7b3d5fa

File tree

2 files changed

+60
-49
lines changed

2 files changed

+60
-49
lines changed

src/Features/CSharpTest/ExtractMethod/ExtractMethodCodeRefactoringTests.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9092,4 +9092,35 @@ private static bool NewMethod(int v)
90929092
""",
90939093
options: NoBraces());
90949094
}
9095+
9096+
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/22597")]
9097+
public async Task TestFullyExtractedTypeParameter()
9098+
{
9099+
await TestInRegularAndScriptAsync(
9100+
"""
9101+
class C
9102+
{
9103+
private void Test()
9104+
{
9105+
[|void Goo<T>(T bar) => Console.WriteLine(bar);
9106+
Goo(3);|]
9107+
}
9108+
}
9109+
""",
9110+
"""
9111+
class C
9112+
{
9113+
private void Test()
9114+
{
9115+
{|Rename:NewMethod|}();
9116+
}
9117+
9118+
private static void NewMethod()
9119+
{
9120+
void Goo<T>(T bar) => Console.WriteLine(bar);
9121+
Goo(3);
9122+
}
9123+
}
9124+
""");
9125+
}
90959126
}

src/Features/Core/Portable/ExtractMethod/MethodExtractor.Analyzer.cs

Lines changed: 29 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -483,7 +483,7 @@ private void GenerateVariableInfoMap(
483483
if (symbol is IParameterSymbol && variableDeclared)
484484
continue;
485485

486-
var type = GetSymbolType(symbol);
486+
var type = GetSymbolTypeWithUpdatedNullability(symbol);
487487
if (type == null)
488488
continue;
489489

@@ -527,16 +527,9 @@ PooledDisposer<PooledHashSet<ISymbol>> GetPooledSymbolSet(ImmutableArray<ISymbol
527527
return disposer;
528528
}
529529

530-
ITypeSymbol? GetSymbolType(ISymbol symbol)
530+
ITypeSymbol? GetSymbolTypeWithUpdatedNullability(ISymbol symbol)
531531
{
532-
var type = symbol switch
533-
{
534-
ILocalSymbol local => local.Type,
535-
IParameterSymbol parameter => parameter.Type,
536-
IRangeVariableSymbol rangeVariable => GetRangeVariableType(rangeVariable),
537-
_ => throw ExceptionUtilities.UnexpectedValue(symbol)
538-
};
539-
532+
var type = GetUnderlyingSymbolType(symbol);
540533
if (type is null)
541534
return type;
542535

@@ -578,6 +571,17 @@ static VariableInfo CreateFromSymbol(
578571
}
579572
}
580573

574+
private ITypeSymbol? GetUnderlyingSymbolType(ISymbol symbol)
575+
{
576+
return symbol switch
577+
{
578+
ILocalSymbol local => local.Type,
579+
IParameterSymbol parameter => parameter.Type,
580+
IRangeVariableSymbol rangeVariable => GetRangeVariableType(rangeVariable),
581+
_ => throw ExceptionUtilities.UnexpectedValue(symbol)
582+
};
583+
}
584+
581585
private static void AddVariableToMap(IDictionary<ISymbol, VariableInfo> variableInfoMap, ISymbol localOrParameter, VariableInfo variableInfo)
582586
=> variableInfoMap.Add(localOrParameter, variableInfo);
583587

@@ -715,15 +719,13 @@ private static bool IsInteractiveSynthesizedParameter(ISymbol localOrParameter)
715719
parameter.ContainingSymbol.ContainingType.IsScriptClass;
716720
}
717721

718-
private static void AddTypeParametersToMap(IEnumerable<ITypeParameterSymbol> typeParameters, IDictionary<int, ITypeParameterSymbol> sortedMap)
722+
private void AddTypeParametersToMap(IEnumerable<ITypeParameterSymbol> typeParameters, IDictionary<int, ITypeParameterSymbol> sortedMap)
719723
{
720724
foreach (var typeParameter in typeParameters)
721-
{
722725
AddTypeParameterToMap(typeParameter, sortedMap);
723-
}
724726
}
725727

726-
private static void AddTypeParameterToMap(ITypeParameterSymbol typeParameter, IDictionary<int, ITypeParameterSymbol> sortedMap)
728+
private void AddTypeParameterToMap(ITypeParameterSymbol typeParameter, IDictionary<int, ITypeParameterSymbol> sortedMap)
727729
{
728730
if (typeParameter == null ||
729731
typeParameter.DeclaringMethod == null ||
@@ -732,6 +734,13 @@ private static void AddTypeParameterToMap(ITypeParameterSymbol typeParameter, ID
732734
return;
733735
}
734736

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

@@ -740,29 +749,10 @@ private void AppendMethodTypeVariableFromDataFlowAnalysis(
740749
IDictionary<int, ITypeParameterSymbol> sortedMap)
741750
{
742751
foreach (var symbol in variableInfoMap.Keys)
743-
{
744-
switch (symbol)
745-
{
746-
case IParameterSymbol parameter:
747-
AddTypeParametersToMap(TypeParameterCollector.Collect(parameter.Type), sortedMap);
748-
continue;
749-
750-
case ILocalSymbol local:
751-
AddTypeParametersToMap(TypeParameterCollector.Collect(local.Type), sortedMap);
752-
continue;
753-
754-
case IRangeVariableSymbol rangeVariable:
755-
var type = GetRangeVariableType(rangeVariable);
756-
AddTypeParametersToMap(TypeParameterCollector.Collect(type), sortedMap);
757-
continue;
758-
759-
default:
760-
throw ExceptionUtilities.UnexpectedValue(symbol);
761-
}
762-
}
752+
AddTypeParametersToMap(TypeParameterCollector.Collect(GetUnderlyingSymbolType(symbol)), sortedMap);
763753
}
764754

765-
private static void AppendMethodTypeParameterFromConstraint(SortedDictionary<int, ITypeParameterSymbol> sortedMap)
755+
private void AppendMethodTypeParameterFromConstraint(SortedDictionary<int, ITypeParameterSymbol> sortedMap)
766756
{
767757
var typeParametersInConstraint = new List<ITypeParameterSymbol>();
768758

@@ -771,9 +761,7 @@ private static void AppendMethodTypeParameterFromConstraint(SortedDictionary<int
771761
{
772762
var constraintTypes = typeParameter.ConstraintTypes;
773763
if (constraintTypes.IsDefaultOrEmpty)
774-
{
775764
continue;
776-
}
777765

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

785773
// pick up only valid type parameter and add them to the map
786774
foreach (var typeParameter in typeParametersInConstraint)
787-
{
788775
AddTypeParameterToMap(typeParameter, sortedMap);
789-
}
790776
}
791777

792-
private static void AppendMethodTypeParameterUsedDirectly(MultiDictionary<ISymbol, SyntaxToken> symbolMap, IDictionary<int, ITypeParameterSymbol> sortedMap)
778+
private void AppendMethodTypeParameterUsedDirectly(MultiDictionary<ISymbol, SyntaxToken> symbolMap, IDictionary<int, ITypeParameterSymbol> sortedMap)
793779
{
794780
foreach (var typeParameter in symbolMap.Keys.OfType<ITypeParameterSymbol>())
795-
{
796-
if (typeParameter.DeclaringMethod != null &&
797-
!sortedMap.ContainsKey(typeParameter.Ordinal))
798-
{
799-
sortedMap[typeParameter.Ordinal] = typeParameter;
800-
}
801-
}
781+
AddTypeParameterToMap(typeParameter, sortedMap);
802782
}
803783

804784
private ImmutableArray<ITypeParameterSymbol> GetMethodTypeParametersInConstraintList(
@@ -816,7 +796,7 @@ private ImmutableArray<ITypeParameterSymbol> GetMethodTypeParametersInConstraint
816796
return [.. sortedMap.Values];
817797
}
818798

819-
private static void AppendTypeParametersInConstraintsUsedByConstructedTypeWithItsOwnConstraints(SortedDictionary<int, ITypeParameterSymbol> sortedMap)
799+
private void AppendTypeParametersInConstraintsUsedByConstructedTypeWithItsOwnConstraints(SortedDictionary<int, ITypeParameterSymbol> sortedMap)
820800
{
821801
using var _1 = PooledHashSet<ITypeSymbol>.GetInstance(out var visited);
822802
using var _2 = PooledHashSet<ITypeParameterSymbol>.GetInstance(out var candidates);
@@ -877,7 +857,7 @@ private static void AddTypeParametersInConstraintsUsedByConstructedTypeWithItsOw
877857
}
878858
}
879859

880-
private static ImmutableArray<ITypeParameterSymbol> GetMethodTypeParametersInDeclaration(ITypeSymbol returnType, SortedDictionary<int, ITypeParameterSymbol> sortedMap)
860+
private ImmutableArray<ITypeParameterSymbol> GetMethodTypeParametersInDeclaration(ITypeSymbol returnType, SortedDictionary<int, ITypeParameterSymbol> sortedMap)
881861
{
882862
// add return type to the map
883863
AddTypeParametersToMap(TypeParameterCollector.Collect(returnType), sortedMap);

0 commit comments

Comments
 (0)