private static async Task <Solution> PullMembersIntoClassAsync( Document document, PullMembersUpOptions result, Solution solution, CancellationToken cancellationToken) { var solutionEditor = new SolutionEditor(solution); var codeGenerationService = document.Project.LanguageServices.GetRequiredService <ICodeGenerationService>(); var destinationSyntaxNode = await codeGenerationService.FindMostRelevantNameSpaceOrTypeDeclarationAsync( solution, result.Destination, options : null, cancellationToken).ConfigureAwait(false); var symbolToDeclarations = await InitializeSymbolToDeclarationsMapAsync(result, solution, solutionEditor, destinationSyntaxNode, cancellationToken).ConfigureAwait(false); // Add members to destination var pullUpMembersSymbols = result.MemberAnalysisResults.SelectAsArray( memberResult => { if (memberResult.MakeMemberDeclarationAbstract && !memberResult.Member.IsKind(SymbolKind.Field)) { // Change the member to abstract if user choose to make them abstract return(MakeAbstractVersion(memberResult.Member)); } else { return(memberResult.Member); } }); var options = new CodeGenerationOptions(reuseSyntax: true, generateMethodBodies: false); var newDestination = codeGenerationService.AddMembers(destinationSyntaxNode, pullUpMembersSymbols, options: options); // Remove some original members since we are pulling members into class. // Note: If the user chooses to make the member abstract, then the original member will be changed to an override, // and it will pull an abstract declaration up to the destination. // But if the member is abstract itself, it will still be removed. foreach (var analysisResult in result.MemberAnalysisResults) { foreach (var syntax in symbolToDeclarations[analysisResult.Member]) { var originalMemberEditor = await solutionEditor.GetDocumentEditorAsync( solution.GetDocumentId(syntax.SyntaxTree), cancellationToken).ConfigureAwait(false); if (!analysisResult.MakeMemberDeclarationAbstract || analysisResult.Member.IsAbstract) { originalMemberEditor.RemoveNode(originalMemberEditor.Generator.GetDeclaration(syntax)); } else { var declarationSyntax = originalMemberEditor.Generator.GetDeclaration(syntax); originalMemberEditor.ReplaceNode(declarationSyntax, (node, generator) => generator.WithModifiers(node, DeclarationModifiers.Override)); } } } // Change the destination to abstract class if needed. var destinationEditor = await solutionEditor.GetDocumentEditorAsync( solution.GetDocumentId(destinationSyntaxNode.SyntaxTree), cancellationToken).ConfigureAwait(false); if (!result.Destination.IsAbstract && result.MemberAnalysisResults.Any(analysis => analysis.Member.IsAbstract || analysis.MakeMemberDeclarationAbstract)) { var modifiers = DeclarationModifiers.From(result.Destination).WithIsAbstract(true); newDestination = destinationEditor.Generator.WithModifiers(newDestination, modifiers); } destinationEditor.ReplaceNode(destinationSyntaxNode, (syntaxNode, generator) => newDestination); return(solutionEditor.GetChangedSolution()); }
private async Task <Solution> InlineMethodAsync(Document document, TInvocationSyntax calleeInvocationNode, IMethodSymbol calleeMethodSymbol, TMethodDeclarationSyntax calleeMethodNode, ISymbol callerSymbol, SyntaxNode callerNode, TExpressionSyntax rawInlineExpression, IInvocationOperation invocationOperation, bool removeCalleeDeclarationNode, CancellationToken cancellationToken) { // Find the statement contains the invocation. This should happen when Callee is invoked in a block // example: // void Caller() // { // Action a = () => // { // var x = Callee(); // } // } (Local declaration x is the containing node) // Note: Stop the searching when it hits lambda or local function, because for this case below don't // treat the declaration of a is the containing node // void Caller() // { // Action a = () => Callee(); // } // it could be null if the caller is invoked as arrow function var statementContainsInvocation = calleeInvocationNode.GetAncestors() .TakeWhile(node => !_syntaxFacts.IsAnonymousFunction(node) && !_syntaxFacts.IsLocalFunction(node)) .FirstOrDefault(node => node is TStatementSyntax) as TStatementSyntax; var methodParametersInfo = await GetMethodParametersInfoAsync( document, calleeInvocationNode, calleeMethodNode, statementContainsInvocation, rawInlineExpression, invocationOperation, cancellationToken).ConfigureAwait(false); var inlineContext = await GetInlineMethodContextAsync( document, calleeMethodNode, calleeInvocationNode, calleeMethodSymbol, rawInlineExpression, methodParametersInfo, cancellationToken).ConfigureAwait(false); var solution = document.Project.Solution; var solutionEditor = new SolutionEditor(solution); if (removeCalleeDeclarationNode) { var calleeDocumentId = solution.GetDocumentId(calleeMethodNode.SyntaxTree); if (calleeDocumentId != null) { var calleeDocumentEditor = await solutionEditor.GetDocumentEditorAsync(calleeDocumentId, cancellationToken).ConfigureAwait(false); calleeDocumentEditor.RemoveNode(calleeMethodNode); } } var newCallerMethodNode = await GetChangedCallerAsync( document, calleeInvocationNode, calleeMethodSymbol, callerSymbol, callerNode, statementContainsInvocation, rawInlineExpression, methodParametersInfo, inlineContext, cancellationToken).ConfigureAwait(false); var callerDocumentEditor = await solutionEditor.GetDocumentEditorAsync(document.Id, cancellationToken).ConfigureAwait(false); callerDocumentEditor.ReplaceNode(callerNode, newCallerMethodNode); return(solutionEditor.GetChangedSolution()); }
protected abstract Task UpdateReferencesAsync(Project project, SolutionEditor solutionEditor, ISymbol implMember, INamedTypeSymbol containingType, CancellationToken cancellationToken);
// When converting to implicit, we don't need to update any references. protected override Task UpdateReferencesAsync(Project project, SolutionEditor solutionEditor, ISymbol implMember, INamedTypeSymbol containingType, CancellationToken cancellationToken) => Task.CompletedTask;
static async Task <Solution> ApplyInvocationsAsync( Solution solution, [NotNull] IEnumerable <KeyValuePair <DocumentId, Invocation[]> > invocationsByDocument, CancellationToken cancellationToken) { // apply changes at invocation sites var slnEditor = new SolutionEditor(solution); var invocationsByDocumentArray = invocationsByDocument as KeyValuePair <DocumentId, Invocation[]>[] ?? invocationsByDocument.ToArray(); foreach (var pair in invocationsByDocumentArray) { var docEditor = await slnEditor.GetDocumentEditorAsync(pair.Key, cancellationToken); foreach (var i in pair.Value) { docEditor.ReplaceNode( i.ExpressionToReplace, i.ConstantAccessSyntax.WithTriviaFrom(i.ExpressionToReplace)); if (i.NewMessageArgs.Any()) { docEditor.InsertAfter( i.ExpressionToReplace.FirstAncestorOrSelf <ArgumentSyntax>(), i.NewMessageArgs.Select(a => SyntaxFactory.Argument(a).WithAdditionalAnnotations(Formatter.Annotation))); } } AddUsingIfNeeded(docEditor); } solution = slnEditor.GetChangedSolution(); // refresh model and find message set types where we need to add constants var constantsByTypeName = from pair in invocationsByDocumentArray from inv in pair.Value group inv.GetConstantDeclarationSyntax by inv.MessagesTypeName; foreach (var group in constantsByTypeName) { // find type bool ok = false; foreach (var project in solution.Projects) { var compilation = await project.GetCompilationAsync(cancellationToken); var messagesTypeSymbol = GetAllTypes(compilation).FirstOrDefault(t => t.Name == group.Key); if (messagesTypeSymbol == null) { continue; } var messagesDefinition = messagesTypeSymbol.OriginalDefinition; var messagesDefSyntaxRef = messagesDefinition.DeclaringSyntaxReferences.First(); var messagesDefDocument = solution.GetDocument(messagesDefSyntaxRef.SyntaxTree); var messagesDefSyntaxRoot = await messagesDefDocument.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var messagesDefSyntax = (ClassDeclarationSyntax)await messagesDefSyntaxRef.GetSyntaxAsync(cancellationToken).ConfigureAwait(false); // find next unused message number var nextCode = (from child in messagesDefSyntax.DescendantNodes().OfType <FieldDeclarationSyntax>() where child.Modifiers.Any(SyntaxKind.ConstKeyword) from v in child.Declaration.Variables let initializer = v.Initializer.Value as LiteralExpressionSyntax where initializer != null && initializer.Kind() == SyntaxKind.NumericLiteralExpression select(int) initializer.Token.Value) .Concat(Enumerable.Repeat(1, 1)) .Max() + 1; var newConstants = group.Select((getConstant, i) => getConstant(nextCode + i)).Cast <MemberDeclarationSyntax>().ToArray(); var newMessagesDefSyntax = messagesDefSyntax.AddMembers(newConstants); var newSyntaxRoot = messagesDefSyntaxRoot.ReplaceNode(messagesDefSyntax, newMessagesDefSyntax); solution = solution.WithDocumentSyntaxRoot(messagesDefDocument.Id, newSyntaxRoot); ok = true; break; } if (!ok) { throw new InvalidOperationException("Can't find message set type " + group.Key); } } return(solution); }