Skip to content
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

Continued Extract method cleanup #76577

Merged
merged 23 commits into from
Jan 2, 2025

Conversation

CyrusNajmabadi
Copy link
Member

Followup to #76550

@dotnet-issue-labeler dotnet-issue-labeler bot added Area-IDE untriaged Issues and PRs which have not yet been triaged by a lead labels Dec 29, 2024
@CyrusNajmabadi CyrusNajmabadi marked this pull request as ready for review January 2, 2025 16:11
@CyrusNajmabadi CyrusNajmabadi requested a review from a team as a code owner January 2, 2025 16:11
@@ -1520,7 +1520,7 @@ public async Task AnonymousTypeMember1()
{
var code = """
using System;
class C { void M() { {|r:var x = new { {|b:String|} = true }; |}} }
class C { void M() { {|r:var x = new { {|b:String|} = true };|} } }
Copy link
Member Author

Choose a reason for hiding this comment

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

note: the test previously were non-functional. they were not actually validating that the spans unless a special bit was set on a reseult avlue (which wasn't computed properly). I also remove this bit in a follow up pr because the whole "feature adds special out of band data, for tests to validate, but which doesn't impactthe feature itself" is an awful paradigm.

@@ -16,16 +16,13 @@ namespace Microsoft.CodeAnalysis.CSharp.ExtractMethod;
[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 partial class CSharpExtractMethodService() : AbstractExtractMethodService<
CSharpExtractMethodService.CSharpSelectionValidator,
CSharpExtractMethodService.CSharpMethodExtractor,
CSharpExtractMethodService.CSharpSelectionResult,
Copy link
Member Author

Choose a reason for hiding this comment

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

got rid of theese generics. they are not needed, and just add type system complexity.

{
var analyzer = new CSharpAnalyzer(selectionResult, localFunction, cancellationToken);
return analyzer.Analyze();
}
Copy link
Member Author

Choose a reason for hiding this comment

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

inlined these static construction methods at callsite.

@@ -202,9 +190,25 @@ protected override SyntaxNode GetCallSiteContainerFromOutermostMoveInVariable(Ca
return declStatement.Parent;
}

private bool ShouldPutUnsafeModifier()
Copy link
Member Author

Choose a reason for hiding this comment

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

this used to be on the strongly typed CSharpXXX types, but were only ever called from a single location (here). so instead of needing all the generic type system wonkyness to pass in one of these subtypes, just to call special methods (not on the base type), i just moved the mtehods to the places that use them. simple, easy.

var root = selectionResult.SemanticDocument.Root;
var result = preservationService.SaveTriviaAroundSelection(root, selectionResult.FinalSpan);
return new CSharpTriviaResult(
await selectionResult.SemanticDocument.WithSyntaxRootAsync(result.Root, cancellationToken).ConfigureAwait(false),
Copy link
Member Author

Choose a reason for hiding this comment

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

static factories inlined to callsite.

}

private CSharpTriviaResult(SemanticDocument document, ITriviaSavedResult result)
: base(document, result, (int)SyntaxKind.EndOfLineTrivia, (int)SyntaxKind.WhitespaceTrivia)
Copy link
Member Author

Choose a reason for hiding this comment

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

primary constructorified.


protected override SyntaxNode GetInsertionPointNode(
AnalyzerResult analyzerResult, CancellationToken cancellationToken)
{
var originalSpanStart = OriginalSelectionResult.OriginalSpan.Start;
var originalSpanStart = OriginalSelectionResult.FinalSpan.Start;
Copy link
Member Author

Choose a reason for hiding this comment

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

unified a lot of the behavior around which pieces of data we use. the user's original selection, or what we updated the selection to.

Note: the followup PR on this cleans this up much further, but separating out this data into two types so that the feature itself is very clear about if it's using the pre/post selection and why.

@@ -24,10 +24,9 @@ internal abstract partial class CSharpSelectionResult
private sealed class ExpressionResult(
SemanticDocument document,
SelectionType selectionType,
TextSpan originalSpan,
Copy link
Member Author

Choose a reason for hiding this comment

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

remove this. this should only be the starting info from the user. we don't want to pass this along as it's actively confusing as to when you'd use this over 'final span'.

public override bool ContainsNonReturnExitPointsStatements(ImmutableArray<SyntaxNode> jumpsOutOfRegion)
=> jumpsOutOfRegion.Any(n => n is not ReturnStatementSyntax);

public override ImmutableArray<StatementSyntax> GetOuterReturnStatements(SyntaxNode commonRoot, ImmutableArray<SyntaxNode> jumpsOutOfRegion)
Copy link
Member Author

Choose a reason for hiding this comment

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

just a move.

}

return false;
}
Copy link
Member Author

Choose a reason for hiding this comment

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

moved to common location. uses IBLockFacts to unify logic for C#/vb.

selectionInfo = AdjustFinalTokensBasedOnContext(selectionInfo, model, cancellationToken);
selectionInfo = AssignFinalSpan(selectionInfo, text);
selectionInfo = ApplySpecialCases(selectionInfo, text, SemanticDocument.SyntaxTree.Options, _localFunction);
Copy link
Member Author

Choose a reason for hiding this comment

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

we used to passa a lot of instance-state data to helper instance methods. i cleaned that up to access off of 'this' instead.


var (firstStatement, lastStatement) = this.SelectionResult.GetFlowAnalysisNodeRange();
return this.SemanticModel.AnalyzeDataFlow(firstStatement, lastStatement);
}
Copy link
Member Author

Choose a reason for hiding this comment

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

a high level goal was to stop exposing the 'start/end nodes' and having callers then do things like AnalyzeDataFlow/AnalyzeControlFlow. By the end of hte cleanup, the nodes are not exposed, and that data is computed once and cached for all callers to use.

@@ -226,5 +265,49 @@ protected static SyntaxNode AddAnnotations(SyntaxNode root, IEnumerable<(SyntaxN
var tokenMap = pairs.GroupBy(p => p.Item1, p => p.Item2).ToDictionary(g => g.Key, g => g.ToArray());
return root.ReplaceNodes(tokenMap.Keys, (o, n) => o.WithAdditionalAnnotations(tokenMap[o]));
}

public OperationStatus ValidateSelectionResult(CancellationToken cancellationToken)
Copy link
Member Author

Choose a reason for hiding this comment

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

trying to move common logic when i can between c# and vb to a common location.

return OperationStatus.SucceededStatus;
}

protected bool IsFinalSpanSemanticallyValidSpan(CancellationToken cancellationToken)
Copy link
Member Author

Choose a reason for hiding this comment

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

this is a move.

if (selectionInfo.Status.Failed)
return (null, selectionInfo.Status);

TStatementSyntax? firstStatement = null;
Copy link
Member Author

Choose a reason for hiding this comment

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

this will get more cleaned up in followup. but the idea here is again just pulling logic up from C#/VB to common location.

@@ -158,12 +149,28 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExtractMethod
Return declStatement.Parent
End Function

Private Function IsUnderModuleBlock() As Boolean
Copy link
Member Author

Choose a reason for hiding this comment

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

vb specific helpers that were on VBSelectionResult, like with C#, these were called in one lcation. so i just moved the helpers right to that location.

@CyrusNajmabadi
Copy link
Member Author

@JoeRobich @ToddGrun ptal. thanks :)

CyrusNajmabadi and others added 2 commits January 2, 2025 10:24
Co-authored-by: Joey Robichaud <joseph.robichaud@microsoft.com>
@@ -316,18 +313,19 @@ protected ImmutableArray<TStatementSyntax> AppendReturnStatementIfNeeded(Immutab
protected static HashSet<SyntaxAnnotation> CreateVariableDeclarationToRemoveMap(
IEnumerable<VariableInfo> variables, CancellationToken cancellationToken)
{
var annotations = new List<(SyntaxToken, SyntaxAnnotation)>();
var annotations = new MultiDictionary<SyntaxToken, SyntaxAnnotation>();
Copy link
Contributor

Choose a reason for hiding this comment

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

MultiDictionary

Need more coffee, what's the advantage of MultiDictionary in this case?

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh, I see, MethodExtractor.GetAnnotatedDocumentAndInsertionPointAsync is simplified by changing AddIdentifierTokenAnnotationPair to use a MultiDictionary

Copy link
Member Author

Choose a reason for hiding this comment

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

It just more accurately reflects what the code was doing. It was building a list of K/V pairs, then later flattening to an IGrouping (or ILookup) of K to many Vs.

Copy link
Contributor

@ToddGrun ToddGrun left a comment

Choose a reason for hiding this comment

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

:shipit:

@CyrusNajmabadi CyrusNajmabadi merged commit ccbc092 into dotnet:main Jan 2, 2025
25 checks passed
CyrusNajmabadi added a commit that referenced this pull request Jan 2, 2025
@dotnet-policy-service dotnet-policy-service bot added this to the Next milestone Jan 2, 2025
@CyrusNajmabadi CyrusNajmabadi deleted the extractMethodCleanup2 branch January 2, 2025 20:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-IDE untriaged Issues and PRs which have not yet been triaged by a lead VSCode
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants