-
Notifications
You must be signed in to change notification settings - Fork 4.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Extract method cleanup #76550
Extract method cleanup #76550
Changes from all commits
e420844
9fd5942
3d4972d
2ad448c
7498849
a268671
4279e02
66392a3
76a2126
c89ca66
bba902d
7812d84
96eb133
db593e9
c9a9089
8667d52
3008da4
d80f03d
c93e5ae
bf2878d
950c2fc
e92a3e0
7cc9a50
f17b48d
4b5bd3c
fc6bbec
e4a9ee1
9ebfa1f
9ab4590
4494693
dd3e177
168bd3b
5c62de5
8522cb1
a24134d
6df7847
c962711
3225e22
70a0fde
6830b2f
8bf7cd6
3e3fea0
73218cf
7987c9a
ef0568f
8d0afed
171b3d3
93a3846
6a1c3f3
d5fcdeb
39bfda0
8bdf297
07aeb41
acfbf81
9304403
2038e1a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -151,9 +151,6 @@ | |
<data name="Remove_this_qualification" xml:space="preserve"> | ||
<value>Remove 'this' qualification</value> | ||
</data> | ||
<data name="Cannot_determine_valid_range_of_statements_to_extract" xml:space="preserve"> | ||
<value>Cannot determine valid range of statements to extract</value> | ||
</data> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Moved to shared location so we can have one string for VB and C# |
||
<data name="Selection_does_not_contain_a_valid_node" xml:space="preserve"> | ||
<value>Selection does not contain a valid node</value> | ||
</data> | ||
|
@@ -181,9 +178,6 @@ | |
<data name="The_selected_code_is_inside_an_unsafe_context" xml:space="preserve"> | ||
<value>The selected code is inside an unsafe context.</value> | ||
</data> | ||
<data name="No_valid_statement_range_to_extract" xml:space="preserve"> | ||
<value>No valid statement range to extract</value> | ||
</data> | ||
<data name="deprecated" xml:space="preserve"> | ||
<value>deprecated</value> | ||
</data> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,10 +15,11 @@ namespace Microsoft.CodeAnalysis.CSharp.ExtractMethod; | |
[ExportLanguageService(typeof(IExtractMethodService), LanguageNames.CSharp)] | ||
[method: ImportingConstructor] | ||
[method: SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")] | ||
internal sealed class CSharpExtractMethodService() : AbstractExtractMethodService< | ||
CSharpSelectionValidator, | ||
CSharpMethodExtractor, | ||
CSharpSelectionResult, | ||
internal sealed partial class CSharpExtractMethodService() : AbstractExtractMethodService< | ||
CSharpExtractMethodService.CSharpSelectionValidator, | ||
CSharpExtractMethodService.CSharpMethodExtractor, | ||
CSharpExtractMethodService.CSharpSelectionResult, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. core helper types became nested types of the service. this way they do not have to repeat type arguments. |
||
StatementSyntax, | ||
StatementSyntax, | ||
ExpressionSyntax> | ||
{ | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,86 +3,88 @@ | |
// See the LICENSE file in the project root for more information. | ||
|
||
using System.Collections.Generic; | ||
using System.Collections.Immutable; | ||
using System.Linq; | ||
using System.Threading; | ||
using Microsoft.CodeAnalysis.CSharp.Syntax; | ||
using Microsoft.CodeAnalysis.ExtractMethod; | ||
|
||
namespace Microsoft.CodeAnalysis.CSharp.ExtractMethod; | ||
|
||
internal sealed partial class CSharpMethodExtractor | ||
internal sealed partial class CSharpExtractMethodService | ||
{ | ||
private sealed class CSharpAnalyzer(CSharpSelectionResult selectionResult, bool localFunction, CancellationToken cancellationToken) : Analyzer(selectionResult, localFunction, cancellationToken) | ||
internal sealed partial class CSharpMethodExtractor | ||
{ | ||
private static readonly HashSet<int> s_nonNoisySyntaxKindSet = [(int)SyntaxKind.WhitespaceTrivia, (int)SyntaxKind.EndOfLineTrivia]; | ||
|
||
public static AnalyzerResult Analyze(CSharpSelectionResult selectionResult, bool localFunction, CancellationToken cancellationToken) | ||
private sealed class CSharpAnalyzer(CSharpSelectionResult selectionResult, bool localFunction, CancellationToken cancellationToken) : Analyzer(selectionResult, localFunction, cancellationToken) | ||
{ | ||
var analyzer = new CSharpAnalyzer(selectionResult, localFunction, cancellationToken); | ||
return analyzer.Analyze(); | ||
} | ||
public static AnalyzerResult Analyze(CSharpSelectionResult selectionResult, bool localFunction, CancellationToken cancellationToken) | ||
{ | ||
var analyzer = new CSharpAnalyzer(selectionResult, localFunction, cancellationToken); | ||
return analyzer.Analyze(); | ||
} | ||
|
||
protected override bool TreatOutAsRef | ||
=> false; | ||
protected override bool TreatOutAsRef | ||
=> false; | ||
|
||
protected override bool IsInPrimaryConstructorBaseType() | ||
=> this.SelectionResult.GetContainingScopeOf<PrimaryConstructorBaseTypeSyntax>() != null; | ||
protected override bool IsInPrimaryConstructorBaseType() | ||
=> this.SelectionResult.GetContainingScopeOf<PrimaryConstructorBaseTypeSyntax>() != null; | ||
|
||
protected override VariableInfo CreateFromSymbol( | ||
ISymbol symbol, ITypeSymbol type, VariableStyle style, bool variableDeclared) | ||
{ | ||
return CreateFromSymbolCommon<LocalDeclarationStatementSyntax>(symbol, type, style, s_nonNoisySyntaxKindSet); | ||
} | ||
protected override VariableInfo CreateFromSymbol( | ||
ISymbol symbol, ITypeSymbol type, VariableStyle style, bool variableDeclared) | ||
{ | ||
return CreateFromSymbolCommon(symbol, type, style); | ||
} | ||
|
||
protected override ITypeSymbol? GetRangeVariableType(SemanticModel model, IRangeVariableSymbol symbol) | ||
{ | ||
var info = model.GetSpeculativeTypeInfo(SelectionResult.FinalSpan.Start, SyntaxFactory.ParseName(symbol.Name), SpeculativeBindingOption.BindAsExpression); | ||
if (info.Type is IErrorTypeSymbol) | ||
return null; | ||
protected override ITypeSymbol? GetRangeVariableType(IRangeVariableSymbol symbol) | ||
{ | ||
var info = this.SemanticModel.GetSpeculativeTypeInfo(SelectionResult.FinalSpan.Start, SyntaxFactory.ParseName(symbol.Name), SpeculativeBindingOption.BindAsExpression); | ||
if (info.Type is IErrorTypeSymbol) | ||
return null; | ||
|
||
return info.Type == null || info.Type.SpecialType == SpecialType.System_Object | ||
? info.Type | ||
: info.ConvertedType; | ||
} | ||
return info.Type == null || info.Type.SpecialType == SpecialType.System_Object | ||
? info.Type | ||
: info.ConvertedType; | ||
} | ||
|
||
protected override bool ContainsReturnStatementInSelectedCode(IEnumerable<SyntaxNode> jumpOutOfRegionStatements) | ||
=> jumpOutOfRegionStatements.Where(n => n is ReturnStatementSyntax).Any(); | ||
protected override bool ContainsReturnStatementInSelectedCode(ImmutableArray<SyntaxNode> exitPoints) | ||
=> exitPoints.Any(n => n is ReturnStatementSyntax); | ||
|
||
protected override bool ReadOnlyFieldAllowed() | ||
{ | ||
var scope = SelectionResult.GetContainingScopeOf<ConstructorDeclarationSyntax>(); | ||
return scope == null; | ||
} | ||
protected override bool ReadOnlyFieldAllowed() | ||
{ | ||
var scope = SelectionResult.GetContainingScopeOf<ConstructorDeclarationSyntax>(); | ||
return scope == null; | ||
} | ||
|
||
protected override bool IsReadOutside(ISymbol symbol, HashSet<ISymbol> readOutsideMap) | ||
{ | ||
if (!base.IsReadOutside(symbol, readOutsideMap)) | ||
return false; | ||
protected override bool IsReadOutside(ISymbol symbol, HashSet<ISymbol> readOutsideMap) | ||
{ | ||
if (!base.IsReadOutside(symbol, readOutsideMap)) | ||
return false; | ||
|
||
// Special case `using var v = ...` where the selection grabs the last statement that follows the local | ||
// declaration. The compiler here considers the local variable 'read outside' since it makes it to the | ||
// implicit 'dispose' call that comes after the last statement. However, as that implicit dispose would | ||
// move if we move the `using var v` entirely into the new method, then it's still safe to move as there's | ||
// no actual "explicit user read" that happens in the outer caller at all. | ||
if (!this.SelectionResult.SelectionInExpression && | ||
symbol is ILocalSymbol { IsUsing: true, DeclaringSyntaxReferences: [var reference] } && | ||
reference.GetSyntax(this.CancellationToken) is VariableDeclaratorSyntax | ||
{ | ||
Parent: VariableDeclarationSyntax | ||
// Special case `using var v = ...` where the selection grabs the last statement that follows the local | ||
// declaration. The compiler here considers the local variable 'read outside' since it makes it to the | ||
// implicit 'dispose' call that comes after the last statement. However, as that implicit dispose would | ||
// move if we move the `using var v` entirely into the new method, then it's still safe to move as there's | ||
// no actual "explicit user read" that happens in the outer caller at all. | ||
if (!this.SelectionResult.IsExtractMethodOnExpression && | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. renamed this property for consistency, and moved to an enum to fully specify it. |
||
symbol is ILocalSymbol { IsUsing: true, DeclaringSyntaxReferences: [var reference] } && | ||
reference.GetSyntax(this.CancellationToken) is VariableDeclaratorSyntax | ||
{ | ||
Parent: LocalDeclarationStatementSyntax | ||
Parent: VariableDeclarationSyntax | ||
{ | ||
Parent: BlockSyntax { Statements: [.., var lastBlockStatement] }, | ||
}, | ||
} | ||
}) | ||
{ | ||
var lastStatement = this.SelectionResult.GetLastStatement(); | ||
if (lastStatement == lastBlockStatement) | ||
return false; | ||
} | ||
Parent: LocalDeclarationStatementSyntax | ||
{ | ||
Parent: BlockSyntax { Statements: [.., var lastBlockStatement] }, | ||
}, | ||
} | ||
}) | ||
{ | ||
var lastStatement = this.SelectionResult.GetLastStatement(); | ||
if (lastStatement == lastBlockStatement) | ||
return false; | ||
} | ||
|
||
return true; | ||
return true; | ||
} | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
intentional change. EM now calls trhough the correct syntax generator code. but the tests don't actually run the simplification pass (since the tests unfortunately test parts of the api directly).