public static ImmutableArray <Diagnostic> GetDiagnostics( SyntaxNode node, SemanticModel model, CancellationToken token) { var nullableParametersVisitor = new NullableParametersVisitor(model, token); nullableParametersVisitor.Visit(node); var nullableParameters = nullableParametersVisitor.NullableParameters; var existingNullChecksVisitor = new ExistingNullChecksVisitor(model, token); existingNullChecksVisitor.Visit(node); var existingNullChecks = existingNullChecksVisitor.ExistingNullChecks; var builder = ImmutableArray.CreateBuilder <Diagnostic>(); foreach (var nullableParameter in nullableParameters) { if (!existingNullChecks.Any(nullCheck => nullCheck.ParameterIndex == nullableParameter.Index)) { var diagnostic = Diagnostic.Create( Descriptor, nullableParameter.Syntax.Identifier.GetLocation(), properties: ImmutableDictionary <string, string?> .Empty.Add( nameof(NullableParameter.Index), nullableParameter.Index.ToString())); builder.Add(diagnostic); } } return(builder.ToImmutable()); }
private Document FixDiagnostics( Document document, ImmutableArray <Diagnostic> diagnostics, SyntaxNode root, SemanticModel model, SyntaxNode node, CancellationToken token) { var parameterIndexes = new List <int>(); foreach (var diagnostic in diagnostics) { if (diagnostic.Properties.TryGetValue(nameof(NullableParameter.Index), out string?index) && int.TryParse(index, out int parameterIndex)) { parameterIndexes.Add(parameterIndex); } } var nullableParametersVisitor = new NullableParametersVisitor(model, token); nullableParametersVisitor.Visit(node); var nullableParameters = nullableParametersVisitor.NullableParameters .Where(x => parameterIndexes.Contains(x.Index)) .ToImmutableArray(); var addNullChecksRewriter = GetRewriter(document, model, nullableParameters, token); var newNode = addNullChecksRewriter.Visit(node); var newDocument = document.WithSyntaxRoot(root.ReplaceNode(node, newNode)); return(newDocument); }
public override async Task RegisterCodeFixesAsync(CodeFixContext context) { var document = context.Document; var token = context.CancellationToken; var root = await document.GetSyntaxRootAsync(token); if (root is null) { return; } var model = await document.GetSemanticModelAsync(token); if (model is null) { return; } foreach (var diagnostic in context.Diagnostics) { if (diagnostic.Id != NullCheckAnalyzer.Id) { continue; } var node = root.FindNode(diagnostic.Location.SourceSpan) .Ancestors() .Where(node => node is BaseMethodDeclarationSyntax or LocalFunctionStatementSyntax) .FirstOrDefault(); if (node is null) { continue; } if (!diagnostic.Properties.TryGetValue(nameof(NullableParameter.Index), out string?index) || !int.TryParse(index, out int parameterIndex)) { continue; } var nullableParametersVisitor = new NullableParametersVisitor(model, token); nullableParametersVisitor.Visit(node); var nullableParameters = nullableParametersVisitor.NullableParameters; var nullableParameter = nullableParameters.SingleOrDefault(x => x.Index == parameterIndex); if (nullableParameter is null) { continue; } if (!ShouldRegisterCodeFix(nullableParameter)) { continue; } // Add null check. { var codeAction = CodeAction.Create( AddNullCheckTitle, token => { var newDocument = FixDiagnostic(document, nullableParameter, root, model, node, token); return(Task.FromResult(newDocument)); }, equivalenceKey: AddNullCheckTitle); context.RegisterCodeFix(codeAction, diagnostic); } // Add null checks. var methodNullCheckDiagnostics = NullCheckAnalyzer.GetDiagnostics(node, model, token); if (methodNullCheckDiagnostics.Length > 1) { var codeAction = CodeAction.Create( AddNullChecksTitle, token => { var newDocument = FixDiagnostics( document, methodNullCheckDiagnostics, root, model, node, token); return(Task.FromResult(newDocument)); }, equivalenceKey: AddNullChecksTitle); context.RegisterCodeFix(codeAction, diagnostic); } } }