Skip to content

Commit 3409430

Browse files
Add support for extract method in the PrimaryConstructor base list (#76371)
2 parents 31e345b + 2c9190a commit 3409430

12 files changed

+235
-163
lines changed

src/Features/CSharp/Portable/ExtractMethod/CSharpMethodExtractor.Analyzer.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ public static AnalyzerResult Analyze(CSharpSelectionResult selectionResult, bool
2525
protected override bool TreatOutAsRef
2626
=> false;
2727

28+
protected override bool IsInPrimaryConstructorBaseType()
29+
=> this.SelectionResult.GetContainingScopeOf<PrimaryConstructorBaseTypeSyntax>() != null;
30+
2831
protected override VariableInfo CreateFromSymbol(
2932
Compilation compilation,
3033
ISymbol symbol,

src/Features/CSharp/Portable/ExtractMethod/CSharpMethodExtractor.CSharpCodeGenerator.CallSiteContainerRewriter.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,15 @@ public override SyntaxNode VisitCompilationUnit(CompilationUnitSyntax node)
426426
return node.WithMembers(newMembers);
427427
}
428428

429+
public override SyntaxNode VisitBaseList(BaseListSyntax node)
430+
{
431+
if (node != ContainerOfStatementsOrFieldToReplace)
432+
return base.VisitBaseList(node);
433+
434+
var primaryConstructorBase = (PrimaryConstructorBaseTypeSyntax)_statementsOrMemberOrAccessorToInsert.Single();
435+
return node.WithTypes(node.Types.Replace(node.Types[0], primaryConstructorBase));
436+
}
437+
429438
private SyntaxNode GetUpdatedTypeDeclaration(TypeDeclarationSyntax node)
430439
{
431440
var newMembers = VisitList(ReplaceMembers(node.Members, global: false));

src/Features/CSharp/Portable/ExtractMethod/CSharpMethodExtractor.CSharpCodeGenerator.ExpressionCodeGenerator.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,9 @@ protected override SyntaxNode GetFirstStatementOrInitializerSelectedAtCallSite()
136136

137137
// This is similar to FieldDeclaration case but we only want to do this
138138
// if the member has an expression body.
139-
scope ??= this.SelectionResult.GetContainingScopeOf<ArrowExpressionClauseSyntax>().Parent;
139+
scope ??= this.SelectionResult.GetContainingScopeOf<ArrowExpressionClauseSyntax>()?.Parent;
140+
141+
scope ??= this.SelectionResult.GetContainingScopeOf<PrimaryConstructorBaseTypeSyntax>();
140142

141143
return scope;
142144
}

src/Features/CSharp/Portable/ExtractMethod/CSharpMethodExtractor.CSharpCodeGenerator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ private async Task<ImmutableArray<SyntaxNode>> CreateStatementsOrInitializerToIn
146146
var selectedNode = GetFirstStatementOrInitializerSelectedAtCallSite();
147147

148148
// field initializer, constructor initializer, expression bodied member case
149-
if (selectedNode is ConstructorInitializerSyntax or FieldDeclarationSyntax ||
149+
if (selectedNode is ConstructorInitializerSyntax or FieldDeclarationSyntax or PrimaryConstructorBaseTypeSyntax ||
150150
IsExpressionBodiedMember(selectedNode) ||
151151
IsExpressionBodiedAccessor(selectedNode))
152152
{

src/Features/CSharp/Portable/ExtractMethod/CSharpMethodExtractor.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,10 @@ protected override SyntaxNode GetInsertionPointNode(
113113
else
114114
{
115115
var baseToken = root.FindToken(originalSpanStart);
116+
var primaryConstructorBaseType = baseToken.GetAncestor<PrimaryConstructorBaseTypeSyntax>();
117+
if (primaryConstructorBaseType != null)
118+
return primaryConstructorBaseType;
119+
116120
var memberNode = baseToken.GetAncestor<MemberDeclarationSyntax>();
117121
Contract.ThrowIfNull(memberNode);
118122
Contract.ThrowIfTrue(memberNode.Kind() == SyntaxKind.NamespaceDeclaration);

src/Features/CSharp/Portable/ExtractMethod/CSharpSelectionResult.cs

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -109,10 +109,9 @@ public override SyntaxNode GetOutermostCallSiteContainerToProcess(CancellationTo
109109
var container = this.GetInnermostStatementContainer();
110110

111111
Contract.ThrowIfNull(container);
112-
Contract.ThrowIfFalse(container.IsStatementContainerNode() ||
113-
container is TypeDeclarationSyntax ||
114-
container is ConstructorDeclarationSyntax ||
115-
container is CompilationUnitSyntax);
112+
Contract.ThrowIfFalse(
113+
container.IsStatementContainerNode() ||
114+
container is BaseListSyntax or TypeDeclarationSyntax or ConstructorDeclarationSyntax or CompilationUnitSyntax);
116115

117116
return container;
118117
}
@@ -171,9 +170,7 @@ public SyntaxNode GetInnermostStatementContainer()
171170
foreach (var statement in statements)
172171
{
173172
if (statement.IsStatementContainerNode())
174-
{
175173
return statement;
176-
}
177174

178175
last = statement;
179176
}
@@ -190,16 +187,16 @@ public SyntaxNode GetInnermostStatementContainer()
190187
// constructor initializer case
191188
var constructorInitializer = GetContainingScopeOf<ConstructorInitializerSyntax>();
192189
if (constructorInitializer != null)
193-
{
194190
return constructorInitializer.Parent;
195-
}
196191

197192
// field initializer case
198193
var field = GetContainingScopeOf<FieldDeclarationSyntax>();
199194
if (field != null)
200-
{
201195
return field.Parent;
202-
}
196+
197+
var primaryConstructorBaseType = GetContainingScopeOf<PrimaryConstructorBaseTypeSyntax>();
198+
if (primaryConstructorBaseType != null)
199+
return primaryConstructorBaseType.Parent;
203200

204201
Contract.ThrowIfFalse(last.IsParentKind(SyntaxKind.GlobalStatement));
205202
Contract.ThrowIfFalse(last.Parent.IsParentKind(SyntaxKind.CompilationUnit));

src/Features/CSharp/Portable/ExtractMethod/CSharpSelectionValidator.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,8 +273,10 @@ private static bool CheckTopLevel(SyntaxNode node, TextSpan span)
273273
{
274274
case BlockSyntax block:
275275
return ContainsInBlockBody(block, span);
276+
276277
case ArrowExpressionClauseSyntax expressionBodiedMember:
277278
return ContainsInExpressionBodiedMemberBody(expressionBodiedMember, span);
279+
278280
case FieldDeclarationSyntax field:
279281
{
280282
foreach (var variable in field.Declaration.Variables)
@@ -290,8 +292,12 @@ private static bool CheckTopLevel(SyntaxNode node, TextSpan span)
290292

291293
case GlobalStatementSyntax:
292294
return true;
295+
293296
case ConstructorInitializerSyntax constructorInitializer:
294297
return constructorInitializer.ContainsInArgument(span);
298+
299+
case PrimaryConstructorBaseTypeSyntax primaryConstructorBaseType:
300+
return primaryConstructorBaseType.ArgumentList.Arguments.FullSpan.Contains(span);
295301
}
296302

297303
return false;

0 commit comments

Comments
 (0)