@@ -6,6 +6,7 @@ namespace StyleChecker.CodeFixes.Refactoring.TypeClassParameter;
66using System . Threading . Tasks ;
77using Maroontress . Roastery ;
88using Microsoft . CodeAnalysis ;
9+ using Microsoft . CodeAnalysis . CSharp . Syntax ;
910using Microsoft . CodeAnalysis . FindSymbols ;
1011using Microsoft . CodeAnalysis . Formatting ;
1112using Microsoft . CodeAnalysis . Rename ;
@@ -68,8 +69,8 @@ public class SolutionKit(
6869 /// <param name="documentId">
6970 /// The document ID.
7071 /// </param>
71- /// <param name="realNode ">
72- /// The syntax node being tracked .
72+ /// <param name="node ">
73+ /// The parameter syntax node (of a method or a local function) .
7374 /// </param>
7475 /// <returns>
7576 /// The renamed solution.
@@ -79,40 +80,51 @@ public class SolutionKit(
7980 string name ,
8081 ISet < string > allSymbolNameSet ,
8182 DocumentId documentId ,
82- SyntaxNode realNode )
83+ ParameterSyntax node )
8384 {
84- if ( GetRenamingIndex ( allSymbolNameSet , 0 , name ) is not { } k )
85+ if ( await Document . GetSemanticModelAsync ( CancellationToken )
86+ . ConfigureAwait ( false ) is not { } model )
8587 {
8688 return null ;
8789 }
88- var options = default ( SymbolRenameOptions ) ;
89- var newSolution = await Renamer . RenameSymbolAsync (
90- Solution ,
91- symbol ,
92- options ,
93- $ "{ name } _{ k } ",
94- CancellationToken )
95- . ConfigureAwait ( false ) ;
96- var projectId = Document . Project . Id ;
97- if ( newSolution . GetProject ( projectId ) is not { } project )
90+ var symbolier = new Symbolizer ( model , CancellationToken ) ;
91+ if ( GetRenamingIndex ( allSymbolNameSet , 0 , name ) is not { } k
92+ || symbolier . ToSymbol ( node ) is not { } parameter )
9893 {
9994 return null ;
10095 }
101- var newDocument = project . Documents
102- . Where ( d => d . Id == documentId )
103- . First ( ) ;
104- if ( await newDocument . GetSyntaxRootAsync ( CancellationToken )
105- . ConfigureAwait ( false ) is not { } root
106- || root . GetCurrentNode ( realNode ) is not { } node
107- || await Documents . GetSymbols ( newDocument , node , CancellationToken )
108- . ConfigureAwait ( false ) is not { } symbols )
96+ var options = default ( SymbolRenameOptions ) ;
97+ var newName = $ "{ name } _{ k } ";
98+
99+ /*
100+ Note:
101+
102+ SyntaxNode.TrackNodes() and SyntaxNode.GetCurrentNode() do not
103+ always work before and after a call to Renamer.RenameSymbolAsync()
104+ (especially not in Visual Studio IDE). Instead, use
105+ SymbolFinder.FindSimilarSymbols() to track nodes by their
106+ corresponding symbols.
107+ */
108+ var newSolution = await RenameSymbolAsync (
109+ Solution , symbol , options , newName ) ;
110+ var projectId = Document . Project . Id ;
111+ if ( newSolution . GetProject ( projectId ) is not { } newProject
112+ || newProject . Documents
113+ . FirstOrDefault ( d => d . Id == documentId ) is not { } newDocument
114+ || await GetSyntaxRootAsync ( newDocument ) is not { } newRoot
115+ || await GetCompilationAsync ( newProject ) is not { } newCompilation
116+ || FindFirstSimilarSymbol ( parameter , newCompilation )
117+ is not { } newParameter
118+ || newParameter . DeclaringSyntaxReferences
119+ . Select ( async r => await GetSyntaxAsync ( r ) )
120+ . FirstOrDefault ( ) is not { } newNode
121+ || newParameter . ContainingSymbol is not IMethodSymbol newMethod )
109122 {
110- return null ;
123+ return newSolution ;
111124 }
112125 var kit = new SolutionKit (
113126 newSolution , newDocument , TypeName , CancellationToken ) ;
114- return await kit . GetNewSolution (
115- symbols . Parameter , symbols . Method , root ) ;
127+ return await kit . GetNewSolution ( newParameter , newMethod , newRoot ) ;
116128 }
117129
118130 /// <summary>
@@ -144,9 +156,7 @@ public class SolutionKit(
144156 }
145157 var index = parameter . Index ;
146158
147- var allReferences = await SymbolFinder . FindReferencesAsync (
148- methodSymbol , Solution , CancellationToken )
149- . ConfigureAwait ( false ) ;
159+ var allReferences = await FindReferencesAsync ( methodSymbol , Solution ) ;
150160 var documentGroups = allReferences . SelectMany ( r => r . Locations )
151161 . GroupBy ( w => w . Document ) ;
152162 if ( DocumentUpdater . UpdateMainDocument (
@@ -197,4 +207,49 @@ public class SolutionKit(
197207 set . Add ( id ) ;
198208 return index ;
199209 }
210+
211+ private async Task < Solution > RenameSymbolAsync (
212+ Solution solution ,
213+ ISymbol symbol ,
214+ SymbolRenameOptions options ,
215+ string newName )
216+ {
217+ return await Renamer . RenameSymbolAsync (
218+ solution , symbol , options , newName , CancellationToken )
219+ . ConfigureAwait ( false ) ;
220+ }
221+
222+ private async Task < SyntaxNode ? > GetSyntaxRootAsync ( Document document )
223+ {
224+ return await document . GetSyntaxRootAsync ( CancellationToken )
225+ . ConfigureAwait ( false ) ;
226+ }
227+
228+ private async Task < Compilation ? > GetCompilationAsync ( Project project )
229+ {
230+ return await project . GetCompilationAsync ( CancellationToken )
231+ . ConfigureAwait ( false ) ;
232+ }
233+
234+ private T FindFirstSimilarSymbol < T > ( T symbol , Compilation compilation )
235+ where T : ISymbol
236+ {
237+ return SymbolFinder . FindSimilarSymbols (
238+ symbol , compilation , CancellationToken )
239+ . FirstOrDefault ( ) ;
240+ }
241+
242+ private async Task < IEnumerable < ReferencedSymbol > > FindReferencesAsync (
243+ ISymbol symbol , Solution solution )
244+ {
245+ return await SymbolFinder . FindReferencesAsync (
246+ symbol , solution , CancellationToken )
247+ . ConfigureAwait ( false ) ;
248+ }
249+
250+ private async Task < SyntaxNode > GetSyntaxAsync ( SyntaxReference reference )
251+ {
252+ return await reference . GetSyntaxAsync ( CancellationToken )
253+ . ConfigureAwait ( false ) ;
254+ }
200255}
0 commit comments