/// <inheritdoc/> public override async Task RegisterCodeFixesAsync(CodeFixContext context) { var syntaxRoot = await context.Document.GetSyntaxRootAsync(context.CancellationToken) .ConfigureAwait(false); var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken) .ConfigureAwait(false); foreach (var diagnostic in context.Diagnostics) { var token = syntaxRoot.FindToken(diagnostic.Location.SourceSpan.Start); if (string.IsNullOrEmpty(token.ValueText) || token.IsMissing) { continue; } var node = syntaxRoot.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true); if (node is ObjectCreationExpressionSyntax objectCreation) { var type = (ITypeSymbol)semanticModel.GetSymbolSafe(objectCreation, context.CancellationToken)?.ContainingSymbol; if (type == null) { continue; } var parameterSyntax = SyntaxFactory.Parameter(SyntaxFactory.Identifier(ParameterName(type))) .WithType(objectCreation.Type); switch (GU0007PreferInjecting.CanInject(objectCreation, semanticModel, context.CancellationToken)) { case GU0007PreferInjecting.Injectable.No: continue; case GU0007PreferInjecting.Injectable.Safe: context.RegisterCodeFix( CodeAction.Create( "Inject", cancellationToken => ApplyFixAsync(context, semanticModel, cancellationToken, objectCreation, parameterSyntax), nameof(InjectFix)), diagnostic); break; case GU0007PreferInjecting.Injectable.Unsafe: context.RegisterCodeFix( CodeAction.Create( "Inject UNSAFE", cancellationToken => ApplyFixAsync(context, semanticModel, cancellationToken, objectCreation, parameterSyntax), nameof(InjectFix)), diagnostic); break; default: throw new ArgumentOutOfRangeException(); } } if (node is IdentifierNameSyntax identifierName && identifierName.Parent is MemberAccessExpressionSyntax memberAccess) { var type = GU0007PreferInjecting.MemberType(memberAccess, semanticModel, context.CancellationToken); var parameterSyntax = SyntaxFactory.Parameter(SyntaxFactory.Identifier(ParameterName(type))) .WithType(SyntaxFactory.ParseTypeName(type.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat))); switch (GU0007PreferInjecting.IsInjectable(identifierName, semanticModel, context.CancellationToken)) { case GU0007PreferInjecting.Injectable.No: continue; case GU0007PreferInjecting.Injectable.Safe: case GU0007PreferInjecting.Injectable.Unsafe: context.RegisterCodeFix( CodeAction.Create( "Inject UNSAFE", cancellationToken => ApplyFixAsync(context, semanticModel, cancellationToken, identifierName, parameterSyntax), nameof(InjectFix)), diagnostic); break; default: throw new ArgumentOutOfRangeException(); } } } }
private static async Task <Document> ApplyFixAsync(CodeFixContext context, SemanticModel semanticModel, CancellationToken cancellationToken, ExpressionSyntax expression, ParameterSyntax parameterSyntax) { if (GU0007PreferInjecting.TrySingleConstructor(expression, out var ctor)) { var editor = await DocumentEditor.CreateAsync(context.Document, cancellationToken) .ConfigureAwait(false); parameterSyntax = UniqueName(ctor.ParameterList, parameterSyntax); if (expression is ObjectCreationExpressionSyntax) { editor.ReplaceNode(expression, SyntaxFactory.IdentifierName(parameterSyntax.Identifier)); } else if (expression is IdentifierNameSyntax identifierName && expression.Parent is MemberAccessExpressionSyntax) { var replaceNodes = new ReplaceNodes(identifierName, semanticModel, cancellationToken).Nodes; if (replaceNodes.Count == 0) { return(context.Document); } ExpressionSyntax fieldAccess = null; foreach (var replaceNode in replaceNodes) { if (replaceNode.FirstAncestor <ConstructorDeclarationSyntax>() == null) { if (fieldAccess == null) { fieldAccess = WithField(editor, ctor, parameterSyntax); } editor.ReplaceNode(replaceNode, fieldAccess.WithLeadingTrivia(replaceNode.GetLeadingTrivia())); } else { editor.ReplaceNode(replaceNode, SyntaxFactory.IdentifierName(parameterSyntax.Identifier).WithLeadingTrivia(replaceNode.GetLeadingTrivia())); } } } else { return(context.Document); } if (ctor.ParameterList == null) { editor.ReplaceNode(ctor, ctor.WithParameterList(SyntaxFactory.ParameterList(SyntaxFactory.SingletonSeparatedList(parameterSyntax)))); } else { if (ctor.ParameterList.Parameters.TryFirst(p => p.Default != null || p.Modifiers.Any(SyntaxKind.ParamsKeyword), out var existing)) { editor.InsertBefore(existing, parameterSyntax); } else { editor.ReplaceNode(ctor.ParameterList, ctor.ParameterList.AddParameters(parameterSyntax)); } } return(editor.GetChangedDocument()); }