Created
September 26, 2025 00:59
-
-
Save KirillOsenkov/5aafbac9461e657484b2b320afaf6ac2 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System.Collections.Generic; | |
| using System.Threading.Tasks; | |
| using Microsoft.CodeAnalysis; | |
| using Microsoft.CodeAnalysis.CodeActions; | |
| using Microsoft.CodeAnalysis.CodeRefactorings; | |
| using Microsoft.CodeAnalysis.CSharp; | |
| using Microsoft.CodeAnalysis.CSharp.Syntax; | |
| using Microsoft.CodeAnalysis.FindSymbols; | |
| using Microsoft.CodeAnalysis.Operations; | |
| using Microsoft.CodeAnalysis.Text; | |
| [ExportCodeRefactoringProvider(LanguageNames.CSharp)] | |
| public class ConvertParamsCallSitesToUseArrays : CodeRefactoringProvider | |
| { | |
| public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) | |
| { | |
| var document = context.Document; | |
| var project = document.Project; | |
| var solution = project.Solution; | |
| var root = await document.GetSyntaxRootAsync(); | |
| var token = root.FindToken(context.Span.Start); | |
| if (!token.IsKind(SyntaxKind.ParamsKeyword)) | |
| { | |
| return; | |
| } | |
| var parameter = token.Parent as ParameterSyntax; | |
| if (parameter == null) | |
| { | |
| return; | |
| } | |
| var methodSyntax = parameter.FirstAncestorOrSelf<BaseMethodDeclarationSyntax>(); | |
| if (methodSyntax == null) | |
| { | |
| return; | |
| } | |
| var semanticModel = await document.GetSemanticModelAsync(); | |
| var methodSymbol = semanticModel.GetDeclaredSymbol(methodSyntax); | |
| if (methodSymbol == null) | |
| { | |
| return; | |
| } | |
| var action = CodeAction.Create("Convert params call sites to use arrays", async ct => | |
| { | |
| var callsites = await SymbolFinder.FindCallersAsync(methodSymbol, solution); | |
| var editsByDocumentId = new Dictionary<DocumentId, List<TextChange>>(); | |
| foreach (var callsite in callsites) | |
| { | |
| foreach (var location in callsite.Locations) | |
| { | |
| var tree = location.SourceTree; | |
| var root = await tree.GetRootAsync(); | |
| var document = solution.GetDocument(tree); | |
| var semanticModel = await document.GetSemanticModelAsync(); | |
| var methodNameNode = root.FindNode(location.SourceSpan); | |
| var invocationSyntax = methodNameNode.FirstAncestorOrSelf<InvocationExpressionSyntax>(); | |
| if (invocationSyntax == null) | |
| { | |
| continue; | |
| } | |
| var operation = semanticModel.GetOperation(invocationSyntax) as IInvocationOperation; | |
| if (operation == null) | |
| { | |
| continue; | |
| } | |
| int paramsIndex = -1; | |
| var operationArguments = operation.Arguments; | |
| for (var i = 0; i < operationArguments.Length; i++) | |
| { | |
| IArgumentOperation operationArgument = operationArguments[i]; | |
| if (operationArgument.ArgumentKind is ArgumentKind.ParamArray or ArgumentKind.ParamCollection) | |
| { | |
| if (operationArgument.Parameter.Name == parameter.Identifier.Text) | |
| { | |
| paramsIndex = i; | |
| break; | |
| } | |
| } | |
| } | |
| var args = invocationSyntax.ArgumentList.Arguments; | |
| if (paramsIndex >= 0 && paramsIndex < args.Count) | |
| { | |
| if (!editsByDocumentId.TryGetValue(document.Id, out var list)) | |
| { | |
| list = new(); | |
| editsByDocumentId[document.Id] = list; | |
| } | |
| var textChange = new TextChange(new TextSpan(args[paramsIndex].SpanStart, 0), "["); | |
| list.Add(textChange); | |
| textChange = new TextChange(new TextSpan(args[args.Count - 1].Span.End, 0), "]"); | |
| list.Add(textChange); | |
| } | |
| } | |
| } | |
| foreach (var kvp in editsByDocumentId) | |
| { | |
| var document = solution.GetDocument(kvp.Key); | |
| var text = await document.GetTextAsync(); | |
| text = text.WithChanges(kvp.Value); | |
| document = document.WithText(text); | |
| solution = document.Project.Solution; | |
| } | |
| return solution; | |
| }); | |
| context.RegisterRefactoring(action); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment