// TODO: extract this logic into common place public override async Task RegisterCodeFixesAsync(CodeFixContext context) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); var diagnostic = context.Diagnostics.First(); // Need to get method where diagnostic was reported var node = root.FindNode(diagnostic.Location.SourceSpan); var method = node.AncestorsAndSelf().OfType <MethodDeclarationSyntax>().First(); // Need to get precondition block of this method var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false); var preconditionBlock = PreconditionsBlock.GetPreconditions(method, semanticModel); Contract.Assert(preconditionBlock.Preconditions.Count != 0, "Method should have at least one precondition!"); // Extracting method that would not have preconditions, but would have all the other statements var preconditionStatements = preconditionBlock.Preconditions.Select(p => p.IfThrowStaement).ToImmutableHashSet(); var extractedMethodBody = method.Body.Statements.Where(s => !preconditionStatements.Contains(s)); // Need to change the name and make it private // TODO: check generated method name to avoid name conflict! var extractedMethod = method.WithStatements(extractedMethodBody) .WithIdentifier(Identifier($"Do{method.Identifier.Text}")) .WithVisibilityModifier(VisibilityModifier.Private); // Now we need to change original method: remove method body and replace it with // a method call to extracted method, and this method should no longer be async! var updatedMethodBody = method.Body.Statements.Where(s => preconditionStatements.Contains(s)).ToList(); // Creating call expression for extracted method with all parameters of this method var originalMethodCallExpression = CreateMethodCallExpression(extractedMethod, method.ParameterList.AsArguments()); updatedMethodBody.Add(SyntaxFactory.ReturnStatement(originalMethodCallExpression)); var updatedMethod = method.WithStatements(updatedMethodBody); var newRoot = root.ReplaceNode(method, new[] { updatedMethod, extractedMethod }); var codeAction = CodeAction.Create(FixText, ct => Task.FromResult(context.Document.WithSyntaxRoot(newRoot))); context.RegisterCodeFix(codeAction, diagnostic); }
public override async Task RegisterCodeFixesAsync(CodeFixContext context) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); var method = context.GetFirstNodeWithDiagnostic <MethodDeclarationSyntax>(root); // Extracting semantic model and precodition block var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false); var preconditionBlock = PreconditionsBlock.GetPreconditions(method, semanticModel); Contract.Assert(preconditionBlock.Preconditions.Count != 0, "Precondition block should have at least one statement!"); // Caching all precondition. This will help to remove or leave them in different methods. var preconditionStatements = preconditionBlock.Preconditions.Select(p => p.IfThrowStaement).ToImmutableHashSet(); // Extracting new method: it should contains all statements from the original method // but without preconditions. var extractedMethodBody = method.Body.Statements.Where(s => !preconditionStatements.Contains(s)); // Clonning original method by changing it's body and changing the visibility var extractedMethod = method.WithStatements(extractedMethodBody) .WithIdentifier(Identifier($"Do{method.Identifier.Text}")) .WithVisibilityModifier(VisibilityModifier.Private); // Updating original method: removing everything except preconditions var updatedMethodBody = method.Body.Statements.Where(s => preconditionStatements.Contains(s)).ToList(); // Creating an invocation for extracted method var originalMethodCallExpression = CreateMethodCallExpression(extractedMethod, method.ParameterList.AsArguments()); updatedMethodBody.Add(SyntaxFactory.ReturnStatement(originalMethodCallExpression)); // Removing 'async' var updatedMethod = method.WithStatements(updatedMethodBody) .WithoutModifiers(t => t.IsKind(SyntaxKind.AsyncKeyword)); // Заменяем метод парой узлов: новым методом и выделенным методом var newRoot = root.ReplaceNode(method, new[] { updatedMethod, extractedMethod }); var codeAction = CodeAction.Create(FixText, ct => Task.FromResult(context.Document.WithSyntaxRoot(newRoot))); context.RegisterCodeFix(codeAction, context.Diagnostics.First()); }
private static void AnalyzeMethod(SyntaxNodeAnalysisContext context) { var method = (MethodDeclarationSyntax)context.Node; var methodSymbol = context.SemanticModel.GetDeclaredSymbol(method); if (methodSymbol == null || !method.IsIteratorBlock()) { return; } var contractBlock = PreconditionsBlock.GetPreconditions(method, context.SemanticModel); foreach (var s in contractBlock.Preconditions) { context.ReportDiagnostic( Diagnostic.Create(Rule, s.ThrowStatement.GetLocation(), methodSymbol.Name)); } }