public override async Task RegisterCodeFixesAsync(CodeFixContext context) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); var diagnostic = context.Diagnostics.First(); var diagnosticSpan = diagnostic.Location.SourceSpan; var token = root.FindToken(diagnosticSpan.Start); // This is catch keyword. var catchBlock = token.Parent as CatchClauseSyntax; var newCatchClause = await CatchUtils.WitchExceptionDeclarationAsync(catchBlock, context.Document); var newRoot = root.ReplaceNode(catchBlock, newCatchClause); var codeAction = CodeAction.Create(FixText, ct => Task.FromResult(context.Document.WithSyntaxRoot(newRoot))); context.RegisterCodeFix(codeAction, diagnostic); }
public override async Task RegisterCodeFixesAsync(CodeFixContext context) { // Fix looks up for a constructor that accepts string argument // and exception object. var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); var diagnostic = context.Diagnostics.First(); var originalThrowStatement = GetThrowStatementFrom(diagnostic, root); var creationExpression = originalThrowStatement.Expression as ObjectCreationExpressionSyntax; Contract.Assert(creationExpression != null); var semanticModel = await context.Document.GetSemanticModelAsync(); // Looking for symbol for thrown excpetion (i.e. Exception type for throw new Exception()) var exceptionTypeSymbol = semanticModel.GetSymbolInfo(creationExpression.Type).Symbol as INamedTypeSymbol; if (exceptionTypeSymbol == null) { // Can't resolve excpetion type.. return; } // Naive implementation: just looking for string, exception constructor. bool hasAppropriateConstructor = exceptionTypeSymbol.Constructors .Any(c => c.Parameters.Length == 2 && c.Parameters[0].Type.Name.ToLower().Contains("string") && c.Parameters[1].Type.Name.ToLower().Contains("exception")); if (!hasAppropriateConstructor) { // Can't make the fix! throwing exception does not has appropriate constructor! return; } // Adding ex identifier if required var catchClause = originalThrowStatement.AncestorsAndSelf().OfType <CatchClauseSyntax>().First(); var originalCatchClause = catchClause; SyntaxToken identifier; if (catchClause.Declaration == null || catchClause.Declaration.Identifier.Kind() == SyntaxKind.None) { identifier = SyntaxFactory.Identifier("ex"); } else { identifier = catchClause.Declaration.Identifier; } ArgumentSyntax additionalArgument = SyntaxFactory.Argument(SyntaxFactory.IdentifierName(identifier)); ObjectCreationExpressionSyntax newCreationExpression; var arguments = creationExpression.ArgumentList; // Add fake "message" argument if needed if (creationExpression.ArgumentList.Arguments.Count == 0) { // Using just string.Empty arguments = arguments.AddArguments( SyntaxFactory.Argument( SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal("")))); } newCreationExpression = creationExpression.WithArgumentList( arguments.AddArguments(additionalArgument)); catchClause = catchClause.ReplaceNode(creationExpression, newCreationExpression); if (catchClause.Declaration == null) { // this is "catch {}" block catchClause = await CatchUtils.WitchExceptionDeclarationAsync(catchClause, context.Document); } if (catchClause.Declaration.Identifier.Kind() == SyntaxKind.None) { // This is catch(Exception) catchClause = catchClause.WithDeclaration(catchClause.Declaration.WithIdentifier(identifier)); } var newRoot = root.ReplaceNode(originalCatchClause, catchClause); var codeAction = CodeAction.Create(FixText, token => Task.FromResult(context.Document.WithSyntaxRoot(newRoot))); context.RegisterCodeFix(codeAction, diagnostic); }