public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { if (!Settings.IsAnyCodeFixEnabled( CodeFixIdentifiers.AddOutModifierToArgument, CodeFixIdentifiers.RemoveRefModifier, CodeFixIdentifiers.CreateSingletonArray, CodeFixIdentifiers.AddArgumentList, CodeFixIdentifiers.ReplaceNullLiteralExpressionWithDefaultValue)) { return; } SyntaxNode root = await context.GetSyntaxRootAsync().ConfigureAwait(false); if (!TryFindFirstAncestorOrSelf(root, context.Span, out ArgumentSyntax argument)) { return; } foreach (Diagnostic diagnostic in context.Diagnostics) { switch (diagnostic.Id) { case CompilerDiagnosticIdentifiers.ArgumentMustBePassedWithRefOrOutKeyword: { if (!Settings.IsCodeFixEnabled(CodeFixIdentifiers.AddOutModifierToArgument)) { return; } CodeAction codeAction = CodeAction.Create( "Add 'out' modifier", cancellationToken => { ArgumentSyntax newArgument = argument .WithRefOrOutKeyword(CSharpFactory.OutKeyword()) .WithFormatterAnnotation(); return(context.Document.ReplaceNodeAsync(argument, newArgument, cancellationToken)); }, GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } case CompilerDiagnosticIdentifiers.ArgumentShouldNotBePassedWithRefOrOutKeyword: { if (!Settings.IsCodeFixEnabled(CodeFixIdentifiers.RemoveRefModifier)) { return; } CodeAction codeAction = CodeAction.Create( "Remove 'ref' modifier", cancellationToken => { ArgumentSyntax newArgument = argument .WithRefOrOutKeyword(default(SyntaxToken)) .PrependToLeadingTrivia(argument.RefOrOutKeyword.GetAllTrivia()) .WithFormatterAnnotation(); return(context.Document.ReplaceNodeAsync(argument, newArgument, cancellationToken)); }, GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } case CompilerDiagnosticIdentifiers.CannotConvertArgumentType: { if (Settings.IsCodeFixEnabled(CodeFixIdentifiers.ReplaceNullLiteralExpressionWithDefaultValue)) { ExpressionSyntax expression = argument.Expression; if (expression.Kind() == SyntaxKind.NullLiteralExpression && argument.Parent is ArgumentListSyntax argumentList) { SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); ImmutableArray <IParameterSymbol> parameterSymbols = FindParameters(argumentList, semanticModel, context.CancellationToken); if (!parameterSymbols.IsDefault) { int index = argumentList.Arguments.IndexOf(argument); IParameterSymbol parameterSymbol = parameterSymbols[index]; ITypeSymbol typeSymbol = parameterSymbol.Type; if (typeSymbol.IsValueType) { CodeFixRegistrator.ReplaceNullWithDefaultValue( context, diagnostic, expression, typeSymbol, semanticModel, CodeFixIdentifiers.ReplaceNullLiteralExpressionWithDefaultValue); } } } } if (Settings.IsCodeFixEnabled(CodeFixIdentifiers.AddArgumentList)) { ExpressionSyntax expression = argument.Expression; if (expression.IsKind( SyntaxKind.IdentifierName, SyntaxKind.GenericName, SyntaxKind.SimpleMemberAccessExpression)) { InvocationExpressionSyntax invocationExpression = InvocationExpression( expression.WithoutTrailingTrivia(), ArgumentList().WithTrailingTrivia(expression.GetTrailingTrivia())); SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); if (semanticModel.GetSpeculativeMethodSymbol(expression.SpanStart, invocationExpression) != null) { CodeAction codeAction = CodeAction.Create( "Add argument list", cancellationToken => { ArgumentSyntax newNode = argument.WithExpression(invocationExpression); return(context.Document.ReplaceNodeAsync(argument, newNode, cancellationToken)); }, GetEquivalenceKey(diagnostic, CodeFixIdentifiers.AddArgumentList)); context.RegisterCodeFix(codeAction, diagnostic); break; } } } if (Settings.IsCodeFixEnabled(CodeFixIdentifiers.CreateSingletonArray)) { ExpressionSyntax expression = argument.Expression; if (expression?.IsMissing == false) { SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); ITypeSymbol typeSymbol = semanticModel.GetTypeSymbol(expression); if (typeSymbol?.IsErrorType() == false) { foreach (ITypeSymbol typeSymbol2 in DetermineParameterTypeHelper.DetermineParameterTypes(argument, semanticModel, context.CancellationToken)) { if (!typeSymbol.Equals(typeSymbol2) && typeSymbol2 is IArrayTypeSymbol arrayType && semanticModel.IsImplicitConversion(expression, arrayType.ElementType)) { CodeAction codeAction = CodeAction.Create( "Create singleton array", cancellationToken => CreateSingletonArrayRefactoring.RefactorAsync(context.Document, expression, arrayType.ElementType, semanticModel, cancellationToken), GetEquivalenceKey(diagnostic, CodeFixIdentifiers.CreateSingletonArray)); context.RegisterCodeFix(codeAction, diagnostic); break; } } } } } break; } } } }
public override async Task RegisterCodeFixesAsync(CodeFixContext context) { SyntaxNode root = await context.GetSyntaxRootAsync().ConfigureAwait(false); if (!TryFindNode(root, context.Span, out ExpressionSyntax expression)) { return; } foreach (Diagnostic diagnostic in context.Diagnostics) { switch (diagnostic.Id) { case CompilerDiagnosticIdentifiers.CannotImplicitlyConvertTypeExplicitConversionExists: { SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); TypeInfo typeInfo = semanticModel.GetTypeInfo(expression, context.CancellationToken); ITypeSymbol type = typeInfo.Type; ITypeSymbol convertedType = typeInfo.ConvertedType; if ((type is INamedTypeSymbol namedType) && namedType.IsNullableType()) { if (convertedType?.SpecialType == SpecialType.System_Boolean || AddComparisonWithBooleanLiteralRefactoring.IsCondition(expression)) { if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.AddComparisonWithBooleanLiteral)) { CodeAction codeAction = CodeAction.Create( AddComparisonWithBooleanLiteralRefactoring.GetTitle(expression), cancellationToken => AddComparisonWithBooleanLiteralRefactoring.RefactorAsync(context.Document, expression, cancellationToken), GetEquivalenceKey(diagnostic, CodeFixIdentifiers.AddComparisonWithBooleanLiteral)); context.RegisterCodeFix(codeAction, diagnostic); } } else if (SymbolEqualityComparer.Default.Equals(namedType.TypeArguments[0], convertedType)) { if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.UseCoalesceExpression)) { CodeAction codeAction = CodeAction.Create( "Use coalesce expression", cancellationToken => { ExpressionSyntax defaultValue = convertedType.GetDefaultValueSyntax(context.Document.GetDefaultSyntaxOptions()); ExpressionSyntax newNode = CoalesceExpression(expression.WithoutTrivia(), defaultValue) .WithTriviaFrom(expression) .Parenthesize() .WithFormatterAnnotation(); return(context.Document.ReplaceNodeAsync(expression, newNode, cancellationToken)); }, GetEquivalenceKey(diagnostic, CodeFixIdentifiers.UseCoalesceExpression)); context.RegisterCodeFix(codeAction, diagnostic); } } } if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.ChangeMemberTypeAccordingToReturnExpression) && expression.IsParentKind(SyntaxKind.ReturnStatement, SyntaxKind.YieldReturnStatement)) { ChangeMemberTypeRefactoring.ComputeCodeFix(context, diagnostic, expression, semanticModel); } if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.AddCastExpression)) { CodeFixRegistrator.AddCastExpression(context, diagnostic, expression, convertedType, semanticModel); } if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.ChangeTypeAccordingToInitializer)) { ChangeTypeAccordingToInitializerRefactoring.ComputeCodeFix(context, diagnostic, expression, semanticModel); } if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.CreateSingletonArray) && type?.IsErrorType() == false && !SymbolEqualityComparer.Default.Equals(type, convertedType) && (convertedType is IArrayTypeSymbol arrayType) && semanticModel.IsImplicitConversion(expression, arrayType.ElementType)) { CodeAction codeAction = CodeAction.Create( "Create singleton array", cancellationToken => CreateSingletonArrayRefactoring.RefactorAsync(context.Document, expression, arrayType.ElementType, semanticModel, cancellationToken), GetEquivalenceKey(diagnostic, CodeFixIdentifiers.CreateSingletonArray)); context.RegisterCodeFix(codeAction, diagnostic); } break; } case CompilerDiagnosticIdentifiers.ConstantValueCannotBeConverted: { if (!Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.UseUncheckedExpression)) { break; } CodeAction codeAction = CodeAction.Create( "Use 'unchecked'", cancellationToken => { CheckedExpressionSyntax newNode = CSharpFactory.UncheckedExpression(expression.WithoutTrivia()); newNode = newNode.WithTriviaFrom(expression); return(context.Document.ReplaceNodeAsync(expression, newNode, cancellationToken)); }, GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } case CompilerDiagnosticIdentifiers.ExpressionBeingAssignedMustBeConstant: { SyntaxNode parent = expression.Parent; if (parent?.IsKind(SyntaxKind.EqualsValueClause) != true) { break; } parent = parent.Parent; if (parent?.IsKind(SyntaxKind.VariableDeclarator) != true) { break; } parent = parent.Parent; if (!(parent is VariableDeclarationSyntax variableDeclaration)) { break; } if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.RemoveConstModifier) && variableDeclaration.Parent is LocalDeclarationStatementSyntax localDeclarationStatement) { SyntaxTokenList modifiers = localDeclarationStatement.Modifiers; if (!modifiers.Contains(SyntaxKind.ConstKeyword)) { break; } ModifiersCodeFixRegistrator.RemoveModifier(context, diagnostic, localDeclarationStatement, SyntaxKind.ConstKeyword); } else if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.ReplaceConstantWithField) && variableDeclaration.Variables.Count == 1 && (variableDeclaration.Parent is FieldDeclarationSyntax fieldDeclaration) && fieldDeclaration.Modifiers.Contains(SyntaxKind.ConstKeyword)) { CodeAction codeAction = CodeAction.Create( ReplaceConstantWithFieldRefactoring.Title, cancellationToken => ReplaceConstantWithFieldRefactoring.RefactorAsync(context.Document, fieldDeclaration, cancellationToken), GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } break; } case CompilerDiagnosticIdentifiers.CannotConvertNullToTypeBecauseItIsNonNullableValueType: case CompilerDiagnosticIdentifiers.CannotConvertNullToTypeParameterBecauseItCouldBeNonNullableValueType: { if (!Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.ReplaceNullLiteralExpressionWithDefaultValue)) { break; } SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); CodeFixRegistrator.ReplaceNullWithDefaultValue(context, diagnostic, expression, semanticModel); break; } case CompilerDiagnosticIdentifiers.ResultOfExpressionIsAlwaysConstantSinceValueIsNeverEqualToNull: { if (!Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.RemoveConditionThatIsAlwaysEqualToTrueOrFalse)) { break; } NullCheckExpressionInfo nullCheck = SyntaxInfo.NullCheckExpressionInfo(expression, allowedStyles: NullCheckStyles.ComparisonToNull); if (!nullCheck.Success) { break; } CodeAction codeAction = CodeAction.Create( "Remove condition", cancellationToken => { cancellationToken.ThrowIfCancellationRequested(); SyntaxNode newRoot = RemoveCondition(root, expression, nullCheck.Style == NullCheckStyles.NotEqualsToNull); cancellationToken.ThrowIfCancellationRequested(); return(Task.FromResult(context.Document.WithSyntaxRoot(newRoot))); }, GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } case CompilerDiagnosticIdentifiers.OnlyAssignmentCallIncrementDecrementAndNewObjectExpressionsCanBeUsedAsStatement: { if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.RemoveParentheses) && expression is ParenthesizedExpressionSyntax parenthesizedExpression && parenthesizedExpression?.IsMissing == false) { CodeAction codeAction = CodeActionFactory.RemoveParentheses(context.Document, parenthesizedExpression, equivalenceKey: GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); if (expression.Parent is ArrowExpressionClauseSyntax arrowExpresssionClause) { if (!Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.ChangeMemberTypeAccordingToReturnExpression)) { break; } ChangeMemberTypeRefactoring.ComputeCodeFix(context, diagnostic, expression, semanticModel); } else if (expression.Parent is ExpressionStatementSyntax expressionStatement) { if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.AddArgumentList) && expression.IsKind( SyntaxKind.IdentifierName, SyntaxKind.SimpleMemberAccessExpression)) { SyntaxNode invocationExpression = InvocationExpression(expression); if (semanticModel.GetSpeculativeMethodSymbol(expression.SpanStart, invocationExpression) != null) { CodeAction codeAction = CodeAction.Create( "Add argument list", cancellationToken => context.Document.ReplaceNodeAsync(expression, invocationExpression, cancellationToken), GetEquivalenceKey(diagnostic, CodeFixIdentifiers.AddArgumentList)); context.RegisterCodeFix(codeAction, diagnostic); } } if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.ReplaceComparisonWithAssignment) && expression.IsKind(SyntaxKind.EqualsExpression)) { BinaryExpressionInfo info = SyntaxInfo.BinaryExpressionInfo(expression); if (!info.Success) { break; } ITypeSymbol leftTypeSymbol = semanticModel.GetTypeSymbol(info.Left, context.CancellationToken); if (leftTypeSymbol?.IsErrorType() != false) { break; } if (!semanticModel.IsImplicitConversion(info.Right, leftTypeSymbol)) { break; } CodeAction codeAction = CodeAction.Create( "Replace comparison with assignment", cancellationToken => { AssignmentExpressionSyntax simpleAssignment = SimpleAssignmentExpression(info.Left, info.Right).WithTriviaFrom(expression); return(context.Document.ReplaceNodeAsync(expression, simpleAssignment, cancellationToken)); }, GetEquivalenceKey(diagnostic, CodeFixIdentifiers.ReplaceComparisonWithAssignment)); context.RegisterCodeFix(codeAction, diagnostic); } if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.ReplaceConditionalExpressionWithIfElse) && (expression is ConditionalExpressionSyntax conditionalExpression) && conditionalExpression.Condition != null) { ExpressionSyntax whenTrue = conditionalExpression.WhenTrue; ExpressionSyntax whenFalse = conditionalExpression.WhenFalse; if (whenTrue != null && whenFalse != null && semanticModel.GetTypeSymbol(whenTrue, context.CancellationToken)?.SpecialType == SpecialType.System_Void && semanticModel.GetTypeSymbol(whenFalse, context.CancellationToken)?.SpecialType == SpecialType.System_Void) { CodeAction codeAction = CodeAction.Create( "Replace ?: with if-else", cancellationToken => { IfStatementSyntax newNode = IfStatement( conditionalExpression.Condition.WalkDownParentheses(), Block(ExpressionStatement(whenTrue)), ElseClause(Block(ExpressionStatement(whenFalse)))); newNode = newNode .WithTriviaFrom(expressionStatement) .WithFormatterAnnotation(); return(context.Document.ReplaceNodeAsync(expressionStatement, newNode, cancellationToken)); }, GetEquivalenceKey(diagnostic, CodeFixIdentifiers.ReplaceConditionalExpressionWithIfElse)); context.RegisterCodeFix(codeAction, diagnostic); } } if (semanticModel.GetSymbol(expression, context.CancellationToken)?.IsErrorType() != false) { break; } ITypeSymbol typeSymbol = semanticModel.GetTypeSymbol(expression, context.CancellationToken); if (typeSymbol?.IsErrorType() != false) { break; } if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.IntroduceLocalVariable) && !expressionStatement.IsEmbedded()) { bool addAwait = typeSymbol.OriginalDefinition.EqualsOrInheritsFromTaskOfT() && semanticModel.GetEnclosingSymbol(expressionStatement.SpanStart, context.CancellationToken).IsAsyncMethod(); CodeAction codeAction = CodeAction.Create( IntroduceLocalVariableRefactoring.GetTitle(expression), cancellationToken => IntroduceLocalVariableRefactoring.RefactorAsync(context.Document, expressionStatement, typeSymbol, addAwait, semanticModel, cancellationToken), GetEquivalenceKey(diagnostic, CodeFixIdentifiers.IntroduceLocalVariable)); context.RegisterCodeFix(codeAction, diagnostic); } if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.IntroduceField)) { CodeAction codeAction = CodeAction.Create( $"Introduce field for '{expression}'", cancellationToken => IntroduceFieldRefactoring.RefactorAsync(context.Document, expressionStatement, typeSymbol, semanticModel, cancellationToken), GetEquivalenceKey(diagnostic, CodeFixIdentifiers.IntroduceField)); context.RegisterCodeFix(codeAction, diagnostic); } } break; } case CompilerDiagnosticIdentifiers.CannotImplicitlyConvertType: { if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.ReplaceYieldReturnWithForEach) && expression.IsParentKind(SyntaxKind.YieldReturnStatement)) { SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); ReplaceYieldReturnWithForEachRefactoring.ComputeCodeFix(context, diagnostic, expression, semanticModel); break; } if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.ChangeMemberTypeAccordingToReturnExpression) && expression.IsParentKind(SyntaxKind.ReturnStatement, SyntaxKind.YieldReturnStatement, SyntaxKind.ArrowExpressionClause)) { SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); ChangeMemberTypeRefactoring.ComputeCodeFix(context, diagnostic, expression, semanticModel); break; } if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.ChangeTypeAccordingToInitializer)) { SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); CodeFixRegistrationResult result = ChangeTypeAccordingToInitializerRefactoring.ComputeCodeFix(context, diagnostic, expression, semanticModel); if (!result.Success) { RemoveAssignmentOfVoidExpression(context, diagnostic, expression, semanticModel); } break; } if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.ReplaceStringLiteralWithCharacterLiteral) && expression?.Kind() == SyntaxKind.StringLiteralExpression) { var literalExpression = (LiteralExpressionSyntax)expression; if (literalExpression.Token.ValueText.Length == 1) { SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); if (semanticModel.GetTypeInfo(expression, context.CancellationToken).ConvertedType?.SpecialType == SpecialType.System_Char) { CodeAction codeAction = CodeAction.Create( "Replace string literal with character literal", cancellationToken => ReplaceStringLiteralWithCharacterLiteralRefactoring.RefactorAsync(context.Document, literalExpression, cancellationToken), GetEquivalenceKey(diagnostic, CodeFixIdentifiers.ReplaceStringLiteralWithCharacterLiteral)); context.RegisterCodeFix(codeAction, diagnostic); } } } if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.UseYieldReturnInsteadOfReturn) && expression.IsParentKind(SyntaxKind.ReturnStatement)) { var returnStatement = (ReturnStatementSyntax)expression.Parent; SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); ISymbol containingSymbol = semanticModel.GetEnclosingSymbol(returnStatement.SpanStart, context.CancellationToken); if (containingSymbol?.Kind == SymbolKind.Method && ((IMethodSymbol)containingSymbol).ReturnType.OriginalDefinition.IsIEnumerableOrIEnumerableOfT()) { CodeAction codeAction = CodeAction.Create( "Use yield return instead of return", cancellationToken => UseYieldReturnInsteadOfReturnRefactoring.RefactorAsync(context.Document, returnStatement, SyntaxKind.YieldReturnStatement, semanticModel, cancellationToken), GetEquivalenceKey(diagnostic, CodeFixIdentifiers.UseYieldReturnInsteadOfReturn)); context.RegisterCodeFix(codeAction, diagnostic); } } break; } case CompilerDiagnosticIdentifiers.LeftHandSideOfAssignmentMustBeVariablePropertyOrIndexer: { if (!Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.RemoveConstModifier)) { return; } if (!expression.IsKind(SyntaxKind.IdentifierName)) { return; } if (!expression.IsParentKind(SyntaxKind.SimpleAssignmentExpression)) { return; } SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); SymbolInfo symbolInfo = semanticModel.GetSymbolInfo(expression, context.CancellationToken); if (symbolInfo.CandidateReason != CandidateReason.NotAVariable) { return; } if (!(symbolInfo.CandidateSymbols.SingleOrDefault(shouldThrow: false) is ILocalSymbol localSymbol)) { return; } if (!localSymbol.IsConst) { return; } SyntaxNode node = localSymbol.GetSyntaxOrDefault(context.CancellationToken); if (!node.IsKind(SyntaxKind.VariableDeclarator)) { return; } node = node.Parent; if (!node.IsKind(SyntaxKind.VariableDeclaration)) { return; } node = node.Parent; if (!(node is LocalDeclarationStatementSyntax localDeclaration)) { return; } SyntaxToken constModifier = localDeclaration.Modifiers.Find(SyntaxKind.ConstKeyword); if (!constModifier.IsKind(SyntaxKind.ConstKeyword)) { return; } ModifiersCodeFixRegistrator.RemoveModifier(context, diagnostic, localDeclaration, constModifier); break; } case CompilerDiagnosticIdentifiers.ReadOnlyFieldCannotBeAssignedTo: { if (!Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.MakeFieldWritable)) { break; } SimpleAssignmentExpressionInfo simpleAssignment = SyntaxInfo.SimpleAssignmentExpressionInfo(expression.Parent); if (!simpleAssignment.Success) { return; } if (simpleAssignment.Left != expression) { return; } SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); SymbolInfo symbolInfo = semanticModel.GetSymbolInfo(expression, context.CancellationToken); if (symbolInfo.CandidateReason != CandidateReason.NotAVariable) { return; } if (!(symbolInfo.CandidateSymbols.SingleOrDefault(shouldThrow: false) is IFieldSymbol fieldSymbol)) { return; } if (fieldSymbol.DeclaredAccessibility != Accessibility.Private) { return; } if (!(fieldSymbol.GetSyntax().Parent.Parent is FieldDeclarationSyntax fieldDeclaration)) { return; } TypeDeclarationSyntax containingTypeDeclaration = fieldDeclaration.FirstAncestor <TypeDeclarationSyntax>(); if (!expression.Ancestors().Any(f => f == containingTypeDeclaration)) { return; } ModifiersCodeFixRegistrator.RemoveModifier( context, diagnostic, fieldDeclaration, SyntaxKind.ReadOnlyKeyword, title: $"Make '{fieldSymbol.Name}' writable"); break; } } } }
public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { if (!Settings.IsCodeFixEnabled(CodeFixIdentifiers.AddArgumentList) && !Settings.IsCodeFixEnabled(CodeFixIdentifiers.ReorderModifiers) && !Settings.IsCodeFixEnabled(CodeFixIdentifiers.ReplaceNullLiteralExpressionWithDefaultValue) && !Settings.IsCodeFixEnabled(CodeFixIdentifiers.ReturnDefaultValue) && !Settings.IsCodeFixEnabled(CodeFixIdentifiers.AddMissingType) && !Settings.IsCodeFixEnabled(CodeFixIdentifiers.RemoveSemicolon) && !Settings.IsCodeFixEnabled(CodeFixIdentifiers.RemoveConditionalAccess) && !Settings.IsCodeFixEnabled(CodeFixIdentifiers.ChangeForEachType)) { return; } SyntaxNode root = await context.GetSyntaxRootAsync().ConfigureAwait(false); if (!TryFindToken(root, context.Span.Start, out SyntaxToken token)) { return; } SyntaxKind kind = token.Kind(); foreach (Diagnostic diagnostic in context.Diagnostics) { switch (diagnostic.Id) { case CompilerDiagnosticIdentifiers.OperatorCannotBeAppliedToOperandOfType: { if (kind == SyntaxKind.QuestionToken && token.Parent is ConditionalAccessExpressionSyntax conditionalAccess) { SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); ITypeSymbol typeSymbol = semanticModel.GetTypeSymbol(conditionalAccess.Expression, context.CancellationToken); if (typeSymbol?.IsErrorType() == false && !typeSymbol.IsConstructedFrom(SpecialType.System_Nullable_T)) { if (typeSymbol.IsValueType) { if (Settings.IsCodeFixEnabled(CodeFixIdentifiers.RemoveConditionalAccess)) { CodeAction codeAction = CodeAction.Create( "Remove '?' operator", cancellationToken => { var textChange = new TextChange(token.Span, ""); return(context.Document.WithTextChangeAsync(textChange, cancellationToken)); }, GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); } } else if (typeSymbol.IsReferenceType) { if (Settings.IsCodeFixEnabled(CodeFixIdentifiers.AddArgumentList) && conditionalAccess.WhenNotNull is MemberBindingExpressionSyntax memberBindingExpression) { ConditionalAccessExpressionSyntax newNode = conditionalAccess.WithWhenNotNull( InvocationExpression( memberBindingExpression.WithoutTrailingTrivia(), ArgumentList().WithTrailingTrivia(memberBindingExpression.GetTrailingTrivia()))); CodeAction codeAction = CodeAction.Create( "Add argument list", cancellationToken => context.Document.ReplaceNodeAsync(conditionalAccess, newNode, cancellationToken), GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); } } } if (Settings.IsCodeFixEnabled(CodeFixIdentifiers.AddArgumentList)) { break; } if (Settings.IsCodeFixEnabled(CodeFixIdentifiers.RemoveConditionalAccess)) { } } break; } case CompilerDiagnosticIdentifiers.PartialModifierCanOnlyAppearImmediatelyBeforeClassStructInterfaceOrVoid: { if (!Settings.IsCodeFixEnabled(CodeFixIdentifiers.ReorderModifiers)) { break; } ModifiersCodeFixRegistrator.MoveModifier(context, diagnostic, token.Parent, token); break; } case CompilerDiagnosticIdentifiers.ValueCannotBeUsedAsDefaultParameter: { if (!Settings.IsCodeFixEnabled(CodeFixIdentifiers.ReplaceNullLiteralExpressionWithDefaultValue)) { break; } if (!(token.Parent is ParameterSyntax parameter)) { break; } ExpressionSyntax value = parameter.Default?.Value; if (value == null) { break; } if (value.Kind() != SyntaxKind.NullLiteralExpression) { break; } SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); CodeFixRegistrator.ReplaceNullWithDefaultValue(context, diagnostic, value, semanticModel); break; } case CompilerDiagnosticIdentifiers.ObjectOfTypeConvertibleToTypeIsRequired: { if (!Settings.IsCodeFixEnabled(CodeFixIdentifiers.ReturnDefaultValue)) { break; } if (token.Kind() != SyntaxKind.ReturnKeyword) { break; } if (!token.IsParentKind(SyntaxKind.ReturnStatement)) { break; } SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); ISymbol symbol = semanticModel.GetEnclosingSymbol(token.SpanStart, context.CancellationToken); if (symbol == null) { break; } SymbolKind symbolKind = symbol.Kind; ITypeSymbol typeSymbol = null; if (symbolKind == SymbolKind.Method) { var methodSymbol = (IMethodSymbol)symbol; typeSymbol = methodSymbol.ReturnType; if (methodSymbol.IsAsync && (typeSymbol is INamedTypeSymbol namedTypeSymbol)) { ImmutableArray <ITypeSymbol> typeArguments = namedTypeSymbol.TypeArguments; if (typeArguments.Any()) { typeSymbol = typeArguments[0]; } } } else if (symbolKind == SymbolKind.Property) { typeSymbol = ((IPropertySymbol)symbol).Type; } else { Debug.Fail(symbolKind.ToString()); } if (typeSymbol == null) { break; } if (typeSymbol.Kind == SymbolKind.ErrorType) { break; } if (!typeSymbol.SupportsExplicitDeclaration()) { break; } var returnStatement = (ReturnStatementSyntax)token.Parent; CodeAction codeAction = CodeAction.Create( "Return default value", cancellationToken => { ExpressionSyntax expression = typeSymbol.ToDefaultValueSyntax(semanticModel, returnStatement.SpanStart); ReturnStatementSyntax newNode = returnStatement.WithExpression(expression); return(context.Document.ReplaceNodeAsync(returnStatement, newNode, cancellationToken)); }, GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } case CompilerDiagnosticIdentifiers.TypeExpected: { if (!Settings.IsCodeFixEnabled(CodeFixIdentifiers.AddMissingType)) { break; } if (token.Kind() != SyntaxKind.CloseParenToken) { break; } if (!(token.Parent is DefaultExpressionSyntax defaultExpression)) { break; } if (!(defaultExpression.Type is IdentifierNameSyntax identifierName)) { break; } if (!identifierName.IsMissing) { break; } SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); TypeInfo typeInfo = semanticModel.GetTypeInfo(defaultExpression, context.CancellationToken); ITypeSymbol convertedType = typeInfo.ConvertedType; if (convertedType?.SupportsExplicitDeclaration() != true) { break; } CodeAction codeAction = CodeAction.Create( $"Add type '{SymbolDisplay.GetMinimalString(convertedType, semanticModel, defaultExpression.SpanStart)}'", cancellationToken => { TypeSyntax newNode = convertedType.ToMinimalTypeSyntax(semanticModel, defaultExpression.SpanStart); newNode = newNode .WithTriviaFrom(identifierName) .WithFormatterAnnotation(); return(context.Document.ReplaceNodeAsync(identifierName, newNode, cancellationToken)); }, GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } case CompilerDiagnosticIdentifiers.SemicolonAfterMethodOrAccessorBlockIsNotValid: { if (!Settings.IsCodeFixEnabled(CodeFixIdentifiers.RemoveSemicolon)) { break; } if (token.Kind() != SyntaxKind.SemicolonToken) { break; } switch (token.Parent) { case MethodDeclarationSyntax methodDeclaration: { BlockSyntax body = methodDeclaration.Body; if (body == null) { break; } CodeAction codeAction = CodeAction.Create( "Remove semicolon", cancellationToken => { SyntaxTriviaList trivia = body .GetTrailingTrivia() .EmptyIfWhitespace() .AddRange(token.LeadingTrivia.EmptyIfWhitespace()) .AddRange(token.TrailingTrivia); MethodDeclarationSyntax newNode = methodDeclaration .WithBody(body.WithTrailingTrivia(trivia)) .WithSemicolonToken(default(SyntaxToken)); return(context.Document.ReplaceNodeAsync(methodDeclaration, newNode, cancellationToken)); }, GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } case PropertyDeclarationSyntax propertyDeclaration: { AccessorListSyntax accessorList = propertyDeclaration.AccessorList; if (accessorList == null) { break; } CodeAction codeAction = CodeAction.Create( "Remove semicolon", cancellationToken => { SyntaxTriviaList trivia = accessorList .GetTrailingTrivia() .EmptyIfWhitespace() .AddRange(token.LeadingTrivia.EmptyIfWhitespace()) .AddRange(token.TrailingTrivia); PropertyDeclarationSyntax newNode = propertyDeclaration .WithAccessorList(accessorList.WithTrailingTrivia(trivia)) .WithSemicolonToken(default(SyntaxToken)); return(context.Document.ReplaceNodeAsync(propertyDeclaration, newNode, cancellationToken)); }, GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } case AccessorDeclarationSyntax accessorDeclaration: { BlockSyntax body = accessorDeclaration.Body; if (body == null) { break; } CodeAction codeAction = CodeAction.Create( "Remove semicolon", cancellationToken => { SyntaxTriviaList trivia = body .GetTrailingTrivia() .EmptyIfWhitespace() .AddRange(token.LeadingTrivia.EmptyIfWhitespace()) .AddRange(token.TrailingTrivia); AccessorDeclarationSyntax newNode = accessorDeclaration .WithBody(body.WithTrailingTrivia(trivia)) .WithSemicolonToken(default(SyntaxToken)); return(context.Document.ReplaceNodeAsync(accessorDeclaration, newNode, cancellationToken)); }, GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } } break; } case CompilerDiagnosticIdentifiers.CannotConvertType: { if (!Settings.IsCodeFixEnabled(CodeFixIdentifiers.ChangeForEachType)) { break; } if (token.Kind() != SyntaxKind.ForEachKeyword) { break; } if (!(token.Parent is ForEachStatementSyntax forEachStatement)) { break; } SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); ForEachStatementInfo info = semanticModel.GetForEachStatementInfo(forEachStatement); ITypeSymbol typeSymbol = info.ElementType; if (typeSymbol.SupportsExplicitDeclaration()) { CodeFixRegistrator.ChangeType(context, diagnostic, forEachStatement.Type, typeSymbol, semanticModel, CodeFixIdentifiers.ChangeForEachType); } CodeFixRegistrator.ChangeTypeToVar(context, diagnostic, forEachStatement.Type, CodeFixIdentifiers.ChangeTypeToVar); break; } } } }
public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { SyntaxNode root = await context.GetSyntaxRootAsync().ConfigureAwait(false); if (!TryFindFirstAncestorOrSelf(root, context.Span, out ArgumentSyntax argument)) { return; } foreach (Diagnostic diagnostic in context.Diagnostics) { switch (diagnostic.Id) { case CompilerDiagnosticIdentifiers.ArgumentMustBePassedWithRefOrOutKeyword: { if (!Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.AddOutModifierToArgument)) { return; } SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); IParameterSymbol parameter = semanticModel.DetermineParameter(argument, allowCandidate: true, cancellationToken: context.CancellationToken); if (parameter == null) { return; } SyntaxToken refOrOutKeyword = default; if (parameter.RefKind == RefKind.Out) { refOrOutKeyword = Token(SyntaxKind.OutKeyword); } else if (parameter.RefKind == RefKind.Ref) { refOrOutKeyword = Token(SyntaxKind.RefKeyword); } else { return; } CodeAction codeAction = CodeAction.Create( $"Add '{SyntaxFacts.GetText(refOrOutKeyword.Kind())}' modifier", cancellationToken => { ArgumentSyntax newArgument = argument .WithRefOrOutKeyword(refOrOutKeyword) .WithFormatterAnnotation(); return(context.Document.ReplaceNodeAsync(argument, newArgument, cancellationToken)); }, GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } case CompilerDiagnosticIdentifiers.ArgumentShouldNotBePassedWithRefOrOutKeyword: { if (!Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.RemoveRefModifier)) { return; } CodeAction codeAction = CodeAction.Create( "Remove 'ref' modifier", cancellationToken => { ArgumentSyntax newArgument = argument .WithRefOrOutKeyword(default(SyntaxToken)) .PrependToLeadingTrivia(argument.RefOrOutKeyword.GetAllTrivia()) .WithFormatterAnnotation(); return(context.Document.ReplaceNodeAsync(argument, newArgument, cancellationToken)); }, GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } case CompilerDiagnosticIdentifiers.CannotConvertArgumentType: { if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.ReplaceNullLiteralExpressionWithDefaultValue)) { ExpressionSyntax expression = argument.Expression; if (expression.Kind() == SyntaxKind.NullLiteralExpression && argument.Parent is ArgumentListSyntax argumentList) { SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); ImmutableArray <IParameterSymbol> parameterSymbols = FindParameters(argumentList, semanticModel, context.CancellationToken); if (!parameterSymbols.IsDefault) { int index = argumentList.Arguments.IndexOf(argument); IParameterSymbol parameterSymbol = parameterSymbols[index]; ITypeSymbol typeSymbol = parameterSymbol.Type; if (typeSymbol.IsValueType) { CodeFixRegistrator.ReplaceNullWithDefaultValue( context, diagnostic, expression, typeSymbol, CodeFixIdentifiers.ReplaceNullLiteralExpressionWithDefaultValue); } } } } if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.AddArgumentList)) { ExpressionSyntax expression = argument.Expression; if (expression.IsKind( SyntaxKind.IdentifierName, SyntaxKind.GenericName, SyntaxKind.SimpleMemberAccessExpression)) { InvocationExpressionSyntax invocationExpression = InvocationExpression( expression.WithoutTrailingTrivia(), ArgumentList().WithTrailingTrivia(expression.GetTrailingTrivia())); SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); if (semanticModel.GetSpeculativeMethodSymbol(expression.SpanStart, invocationExpression) != null) { CodeAction codeAction = CodeAction.Create( "Add argument list", cancellationToken => { ArgumentSyntax newNode = argument.WithExpression(invocationExpression); return(context.Document.ReplaceNodeAsync(argument, newNode, cancellationToken)); }, GetEquivalenceKey(diagnostic, CodeFixIdentifiers.AddArgumentList)); context.RegisterCodeFix(codeAction, diagnostic); break; } } } if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.CreateSingletonArray)) { ExpressionSyntax expression = argument.Expression; if (expression?.IsMissing == false) { SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); ITypeSymbol typeSymbol = semanticModel.GetTypeSymbol(expression); if (typeSymbol?.IsErrorType() == false) { foreach (ITypeSymbol typeSymbol2 in DetermineParameterTypeHelper.DetermineParameterTypes(argument, semanticModel, context.CancellationToken)) { if (!typeSymbol.Equals(typeSymbol2) && typeSymbol2 is IArrayTypeSymbol arrayType && semanticModel.IsImplicitConversion(expression, arrayType.ElementType)) { CodeAction codeAction = CodeAction.Create( "Create singleton array", cancellationToken => CreateSingletonArrayRefactoring.RefactorAsync(context.Document, expression, arrayType.ElementType, semanticModel, cancellationToken), GetEquivalenceKey(diagnostic, CodeFixIdentifiers.CreateSingletonArray)); context.RegisterCodeFix(codeAction, diagnostic); break; } } } } } break; } case CompilerDiagnosticIdentifiers.ReadOnlyFieldCannotBePassedAsRefOrOutValue: { if (!Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.MakeFieldWritable)) { return; } SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); SymbolInfo symbolInfo = semanticModel.GetSymbolInfo(argument.Expression, context.CancellationToken); if (symbolInfo.CandidateReason != CandidateReason.NotAVariable) { return; } if (!(symbolInfo.CandidateSymbols.SingleOrDefault(shouldThrow: false) is IFieldSymbol fieldSymbol)) { return; } if (fieldSymbol.DeclaredAccessibility != Accessibility.Private) { return; } if (!(fieldSymbol.GetSyntax().Parent.Parent is FieldDeclarationSyntax fieldDeclaration)) { return; } TypeDeclarationSyntax containingTypeDeclaration = fieldDeclaration.FirstAncestor <TypeDeclarationSyntax>(); if (!argument.Ancestors().Any(f => f == containingTypeDeclaration)) { return; } ModifiersCodeFixRegistrator.RemoveModifier( context, diagnostic, fieldDeclaration, SyntaxKind.ReadOnlyKeyword, title: $"Make '{fieldSymbol.Name}' writable", additionalKey: CodeFixIdentifiers.MakeFieldWritable); break; } } } }
public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { if (!Settings.IsAnyCodeFixEnabled( CodeFixIdentifiers.AddArgumentList, CodeFixIdentifiers.ReorderModifiers, CodeFixIdentifiers.ReplaceNullLiteralExpressionWithDefaultValue, CodeFixIdentifiers.ReturnDefaultValue, CodeFixIdentifiers.AddMissingType)) { return; } SyntaxNode root = await context.GetSyntaxRootAsync().ConfigureAwait(false); if (!TryFindToken(root, context.Span.Start, out SyntaxToken token)) { return; } SyntaxKind kind = token.Kind(); foreach (Diagnostic diagnostic in context.Diagnostics) { switch (diagnostic.Id) { case CompilerDiagnosticIdentifiers.OperatorCannotBeAppliedToOperandOfType: { if (!Settings.IsCodeFixEnabled(CodeFixIdentifiers.AddArgumentList)) { break; } if (kind != SyntaxKind.QuestionToken) { break; } if (!token.IsParentKind(SyntaxKind.ConditionalAccessExpression)) { break; } var conditionalAccess = (ConditionalAccessExpressionSyntax)token.Parent; SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); ITypeSymbol typeSymbol = semanticModel.GetTypeSymbol(conditionalAccess.Expression, context.CancellationToken); if (typeSymbol == null || typeSymbol.IsErrorType() || !typeSymbol.IsValueType || typeSymbol.IsConstructedFrom(SpecialType.System_Nullable_T)) { break; } CodeAction codeAction = CodeAction.Create( "Remove '?' operator", cancellationToken => { var textChange = new TextChange(token.Span, ""); return(context.Document.WithTextChangeAsync(textChange, cancellationToken)); }, GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } case CompilerDiagnosticIdentifiers.PartialModifierCanOnlyAppearImmediatelyBeforeClassStructInterfaceOrVoid: { if (!Settings.IsCodeFixEnabled(CodeFixIdentifiers.ReorderModifiers)) { break; } ModifiersCodeFixRegistrator.MoveModifier(context, diagnostic, token.Parent, token); break; } case CompilerDiagnosticIdentifiers.ValueCannotBeUsedAsDefaultParameter: { if (!Settings.IsCodeFixEnabled(CodeFixIdentifiers.ReplaceNullLiteralExpressionWithDefaultValue)) { break; } if (!(token.Parent is ParameterSyntax parameter)) { break; } ExpressionSyntax value = parameter.Default?.Value; if (value == null) { break; } if (value.Kind() != SyntaxKind.NullLiteralExpression) { break; } SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); CodeFixRegistrator.ReplaceNullWithDefaultValue(context, diagnostic, value, semanticModel); break; } case CompilerDiagnosticIdentifiers.ObjectOfTypeConvertibleToTypeIsRequired: { if (!Settings.IsCodeFixEnabled(CodeFixIdentifiers.ReturnDefaultValue)) { break; } if (token.Kind() != SyntaxKind.ReturnKeyword) { break; } if (!token.IsParentKind(SyntaxKind.ReturnStatement)) { break; } SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); ISymbol symbol = semanticModel.GetEnclosingSymbol(token.SpanStart, context.CancellationToken); if (symbol == null) { break; } SymbolKind symbolKind = symbol.Kind; ITypeSymbol typeSymbol = null; if (symbolKind == SymbolKind.Method) { var methodSymbol = (IMethodSymbol)symbol; typeSymbol = methodSymbol.ReturnType; if (methodSymbol.IsAsync && (typeSymbol is INamedTypeSymbol namedTypeSymbol)) { ImmutableArray <ITypeSymbol> typeArguments = namedTypeSymbol.TypeArguments; if (typeArguments.Any()) { typeSymbol = typeArguments[0]; } } } else if (symbolKind == SymbolKind.Property) { typeSymbol = ((IPropertySymbol)symbol).Type; } else { Debug.Fail(symbolKind.ToString()); } if (typeSymbol == null) { break; } if (typeSymbol.Kind == SymbolKind.ErrorType) { break; } if (!typeSymbol.SupportsExplicitDeclaration()) { break; } var returnStatement = (ReturnStatementSyntax)token.Parent; CodeAction codeAction = CodeAction.Create( "Return default value", cancellationToken => { ExpressionSyntax expression = typeSymbol.ToDefaultValueSyntax(semanticModel, returnStatement.SpanStart); ReturnStatementSyntax newNode = returnStatement.WithExpression(expression); return(context.Document.ReplaceNodeAsync(returnStatement, newNode, cancellationToken)); }, GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } case CompilerDiagnosticIdentifiers.TypeExpected: { if (!Settings.IsCodeFixEnabled(CodeFixIdentifiers.AddMissingType)) { break; } if (token.Kind() != SyntaxKind.CloseParenToken) { break; } if (!(token.Parent is DefaultExpressionSyntax defaultExpression)) { break; } if (!(defaultExpression.Type is IdentifierNameSyntax identifierName)) { break; } if (!identifierName.IsMissing) { break; } SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); TypeInfo typeInfo = semanticModel.GetTypeInfo(defaultExpression, context.CancellationToken); ITypeSymbol convertedType = typeInfo.ConvertedType; if (convertedType?.SupportsExplicitDeclaration() != true) { break; } CodeAction codeAction = CodeAction.Create( $"Add type '{SymbolDisplay.GetMinimalString(convertedType, semanticModel, defaultExpression.SpanStart)}'", cancellationToken => { TypeSyntax newNode = convertedType.ToMinimalTypeSyntax(semanticModel, defaultExpression.SpanStart); newNode = newNode .WithTriviaFrom(identifierName) .WithFormatterAnnotation(); return(context.Document.ReplaceNodeAsync(identifierName, newNode, cancellationToken)); }, GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } } } }
public override async Task RegisterCodeFixesAsync(CodeFixContext context) { SyntaxNode root = await context.GetSyntaxRootAsync().ConfigureAwait(false); if (!TryFindFirstAncestorOrSelf(root, context.Span, out ExpressionSyntax expression, getInnermostNodeForTie: false)) { return; } if (expression.Parent is not ArgumentSyntax argument || argument.Expression != expression) { return; } Document document = context.Document; Diagnostic diagnostic = context.Diagnostics[0]; switch (diagnostic.Id) { case CompilerDiagnosticIdentifiers.CS1503_CannotConvertArgumentType: { if (IsEnabled(diagnostic.Id, CodeFixIdentifiers.ReplaceNullLiteralExpressionWithDefaultValue, document, root.SyntaxTree) && expression.Kind() == SyntaxKind.NullLiteralExpression && argument.Parent is ArgumentListSyntax argumentList) { SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); ImmutableArray <IParameterSymbol> parameterSymbols = FindParameters(argumentList, semanticModel, context.CancellationToken); if (!parameterSymbols.IsDefault) { int index = argumentList.Arguments.IndexOf(argument); IParameterSymbol parameterSymbol = parameterSymbols[index]; ITypeSymbol typeSymbol = parameterSymbol.Type; if (typeSymbol.IsValueType) { CodeFixRegistrator.ReplaceNullWithDefaultValue( context, diagnostic, expression, typeSymbol, CodeFixIdentifiers.ReplaceNullLiteralExpressionWithDefaultValue); } } } if (IsEnabled(diagnostic.Id, CodeFixIdentifiers.AddArgumentList, document, root.SyntaxTree) && expression.IsKind( SyntaxKind.IdentifierName, SyntaxKind.GenericName, SyntaxKind.SimpleMemberAccessExpression)) { InvocationExpressionSyntax invocationExpression = InvocationExpression( expression.WithoutTrailingTrivia(), ArgumentList().WithTrailingTrivia(expression.GetTrailingTrivia())); SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); if (semanticModel.GetSpeculativeMethodSymbol(expression.SpanStart, invocationExpression) != null) { CodeAction codeAction = CodeAction.Create( "Add argument list", ct => { ArgumentSyntax newNode = argument.WithExpression(invocationExpression); return(document.ReplaceNodeAsync(argument, newNode, ct)); }, GetEquivalenceKey(diagnostic, CodeFixIdentifiers.AddArgumentList)); context.RegisterCodeFix(codeAction, diagnostic); break; } } if (IsEnabled(diagnostic.Id, CodeFixIdentifiers.CreateSingletonArray, document, root.SyntaxTree) && expression?.IsMissing == false) { SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); ITypeSymbol typeSymbol = semanticModel.GetTypeSymbol(expression); if (typeSymbol?.IsErrorType() == false) { foreach (ITypeSymbol typeSymbol2 in DetermineParameterTypeHelper.DetermineParameterTypes(argument, semanticModel, context.CancellationToken)) { if (!SymbolEqualityComparer.Default.Equals(typeSymbol, typeSymbol2) && typeSymbol2 is IArrayTypeSymbol arrayType && semanticModel.IsImplicitConversion(expression, arrayType.ElementType)) { CodeAction codeAction = CodeAction.Create( "Create singleton array", ct => CreateSingletonArrayRefactoring.RefactorAsync(document, expression, arrayType.ElementType, semanticModel, ct), GetEquivalenceKey(diagnostic, CodeFixIdentifiers.CreateSingletonArray)); context.RegisterCodeFix(codeAction, diagnostic); break; } } } } break; } } }
public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { if (!Settings.IsCodeFixEnabled(CodeFixIdentifiers.AddComparisonWithBooleanLiteral) && !Settings.IsCodeFixEnabled(CodeFixIdentifiers.CreateSingletonArray) && !Settings.IsCodeFixEnabled(CodeFixIdentifiers.UseUncheckedExpression) && !Settings.IsCodeFixEnabled(CodeFixIdentifiers.RemoveConstModifier) && !Settings.IsCodeFixEnabled(CodeFixIdentifiers.ReplaceNullLiteralExpressionWithDefaultValue) && !Settings.IsCodeFixEnabled(CodeFixIdentifiers.UseCoalesceExpression) && !Settings.IsCodeFixEnabled(CodeFixIdentifiers.RemoveConditionThatIsAlwaysEqualToTrueOrFalse) && !Settings.IsCodeFixEnabled(CodeFixIdentifiers.IntroduceLocalVariable) && !Settings.IsCodeFixEnabled(CodeFixIdentifiers.IntroduceField) && !Settings.IsCodeFixEnabled(CodeFixIdentifiers.ReplaceStringLiteralWithCharacterLiteral) && !Settings.IsCodeFixEnabled(CodeFixIdentifiers.UseYieldReturnInsteadOfReturn) && !Settings.IsCodeFixEnabled(CodeFixIdentifiers.ChangeMemberTypeAccordingToReturnExpression) && !Settings.IsCodeFixEnabled(CodeFixIdentifiers.AddArgumentList)) { return; } SyntaxNode root = await context.GetSyntaxRootAsync().ConfigureAwait(false); if (!TryFindNode(root, context.Span, out ExpressionSyntax expression)) { return; } foreach (Diagnostic diagnostic in context.Diagnostics) { switch (diagnostic.Id) { case CompilerDiagnosticIdentifiers.CannotImplicitlyConvertTypeExplicitConversionExists: { SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); TypeInfo typeInfo = semanticModel.GetTypeInfo(expression, context.CancellationToken); ITypeSymbol type = typeInfo.Type; ITypeSymbol convertedType = typeInfo.ConvertedType; if ((type is INamedTypeSymbol namedType) && namedType.ConstructedFrom.SpecialType == SpecialType.System_Nullable_T) { if (convertedType?.IsBoolean() == true || AddComparisonWithBooleanLiteralRefactoring.IsCondition(expression)) { if (Settings.IsCodeFixEnabled(CodeFixIdentifiers.AddComparisonWithBooleanLiteral)) { CodeAction codeAction = CodeAction.Create( AddComparisonWithBooleanLiteralRefactoring.GetTitle(expression), cancellationToken => AddComparisonWithBooleanLiteralRefactoring.RefactorAsync(context.Document, expression, cancellationToken), GetEquivalenceKey(diagnostic, CodeFixIdentifiers.AddComparisonWithBooleanLiteral)); context.RegisterCodeFix(codeAction, diagnostic); } } else if (namedType.TypeArguments[0].Equals(convertedType)) { if (Settings.IsCodeFixEnabled(CodeFixIdentifiers.UseCoalesceExpression)) { CodeAction codeAction = CodeAction.Create( "Use coalesce expression", cancellationToken => { ExpressionSyntax defaultValue = convertedType.ToDefaultValueSyntax(semanticModel, expression.SpanStart); ExpressionSyntax newNode = CoalesceExpression(expression.WithoutTrivia(), defaultValue) .WithTriviaFrom(expression) .Parenthesize() .WithFormatterAnnotation(); return(context.Document.ReplaceNodeAsync(expression, newNode, cancellationToken)); }, GetEquivalenceKey(diagnostic, CodeFixIdentifiers.UseCoalesceExpression)); context.RegisterCodeFix(codeAction, diagnostic); } } } if (Settings.IsCodeFixEnabled(CodeFixIdentifiers.ChangeMemberTypeAccordingToReturnExpression) && expression.IsParentKind(SyntaxKind.ReturnStatement, SyntaxKind.YieldReturnStatement)) { ChangeMemberTypeRefactoring.ComputeCodeFix(context, diagnostic, expression, semanticModel); } if (Settings.IsCodeFixEnabled(CodeFixIdentifiers.AddCastExpression)) { CodeFixRegistrator.AddCastExpression(context, diagnostic, expression, convertedType, semanticModel); } if (Settings.IsCodeFixEnabled(CodeFixIdentifiers.CreateSingletonArray) && type?.IsErrorType() == false && !type.Equals(convertedType) && (convertedType is IArrayTypeSymbol arrayType) && semanticModel.IsImplicitConversion(expression, arrayType.ElementType)) { CodeAction codeAction = CodeAction.Create( "Create singleton array", cancellationToken => CreateSingletonArrayRefactoring.RefactorAsync(context.Document, expression, arrayType.ElementType, semanticModel, cancellationToken), GetEquivalenceKey(diagnostic, CodeFixIdentifiers.CreateSingletonArray)); context.RegisterCodeFix(codeAction, diagnostic); } break; } case CompilerDiagnosticIdentifiers.ConstantValueCannotBeConverted: { if (!Settings.IsCodeFixEnabled(CodeFixIdentifiers.UseUncheckedExpression)) { break; } CodeAction codeAction = CodeAction.Create( "Use 'unchecked'", cancellationToken => { CheckedExpressionSyntax newNode = CSharpFactory.UncheckedExpression(expression.WithoutTrivia()); newNode = newNode.WithTriviaFrom(expression); return(context.Document.ReplaceNodeAsync(expression, newNode, cancellationToken)); }, GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } case CompilerDiagnosticIdentifiers.ExpressionBeingAssignedMustBeConstant: { if (!Settings.IsCodeFixEnabled(CodeFixIdentifiers.RemoveConstModifier)) { break; } LocalDeclarationStatementSyntax localDeclarationStatement = GetLocalDeclarationStatement(expression); if (localDeclarationStatement == null) { break; } SyntaxTokenList modifiers = localDeclarationStatement.Modifiers; if (!modifiers.Contains(SyntaxKind.ConstKeyword)) { break; } ModifiersCodeFixRegistrator.RemoveModifier(context, diagnostic, localDeclarationStatement, SyntaxKind.ConstKeyword); break; } case CompilerDiagnosticIdentifiers.CannotConvertNullToTypeBecauseItIsNonNullableValueType: case CompilerDiagnosticIdentifiers.CannotConvertNullToTypeParameterBecauseItCouldBeNonNullableValueType: { if (!Settings.IsCodeFixEnabled(CodeFixIdentifiers.ReplaceNullLiteralExpressionWithDefaultValue)) { break; } SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); CodeFixRegistrator.ReplaceNullWithDefaultValue(context, diagnostic, expression, semanticModel); break; } case CompilerDiagnosticIdentifiers.ResultOfExpressionIsAlwaysConstantSinceValueIsNeverEqualToNull: { if (!Settings.IsCodeFixEnabled(CodeFixIdentifiers.RemoveConditionThatIsAlwaysEqualToTrueOrFalse)) { break; } NullCheckExpressionInfo nullCheck = SyntaxInfo.NullCheckExpressionInfo(expression, allowedKinds: NullCheckKind.ComparisonToNull); if (!nullCheck.Success) { break; } CodeAction codeAction = CodeAction.Create( "Remove condition", cancellationToken => { cancellationToken.ThrowIfCancellationRequested(); SyntaxNode newRoot = RemoveHelper.RemoveCondition(root, expression, nullCheck.Kind == NullCheckKind.NotEqualsToNull); return(Task.FromResult(context.Document.WithSyntaxRoot(newRoot))); }, GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } case CompilerDiagnosticIdentifiers.OnlyAssignmentCallIncrementDecrementAndNewObjectExpressionsCanBeUsedAsStatement: { SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); if (expression.Parent is ArrowExpressionClauseSyntax arrowExpresssionClause) { if (!Settings.IsCodeFixEnabled(CodeFixIdentifiers.ChangeMemberTypeAccordingToReturnExpression)) { break; } ChangeMemberTypeRefactoring.ComputeCodeFix(context, diagnostic, expression, semanticModel); } else if (expression.Parent is ExpressionStatementSyntax expressionStatement) { if (Settings.IsCodeFixEnabled(CodeFixIdentifiers.AddArgumentList) && expression.IsKind( SyntaxKind.IdentifierName, SyntaxKind.SimpleMemberAccessExpression)) { SyntaxNode invocationExpression = SyntaxFactory.InvocationExpression(expression); if (semanticModel.GetSpeculativeMethodSymbol(expression.SpanStart, invocationExpression) != null) { CodeAction codeAction = CodeAction.Create( "Add argument list", cancellationToken => context.Document.ReplaceNodeAsync(expression, invocationExpression, cancellationToken), GetEquivalenceKey(diagnostic, CodeFixIdentifiers.AddArgumentList)); context.RegisterCodeFix(codeAction, diagnostic); } } if (Settings.IsAnyCodeFixEnabled( CodeFixIdentifiers.IntroduceLocalVariable, CodeFixIdentifiers.IntroduceField)) { if (semanticModel.GetSymbol(expression, context.CancellationToken)?.IsErrorType() != false) { break; } ITypeSymbol typeSymbol = semanticModel.GetTypeSymbol(expression, context.CancellationToken); if (typeSymbol?.IsErrorType() != false) { break; } if (Settings.IsCodeFixEnabled(CodeFixIdentifiers.IntroduceLocalVariable) && !expressionStatement.IsEmbedded()) { bool addAwait = typeSymbol.IsConstructedFromTaskOfT(semanticModel) && semanticModel.GetEnclosingSymbol(expressionStatement.SpanStart, context.CancellationToken).IsAsyncMethod(); CodeAction codeAction = CodeAction.Create( IntroduceLocalVariableRefactoring.GetTitle(expression), cancellationToken => IntroduceLocalVariableRefactoring.RefactorAsync(context.Document, expressionStatement, typeSymbol, addAwait, semanticModel, cancellationToken), GetEquivalenceKey(diagnostic, CodeFixIdentifiers.IntroduceLocalVariable)); context.RegisterCodeFix(codeAction, diagnostic); } if (Settings.IsCodeFixEnabled(CodeFixIdentifiers.IntroduceField)) { CodeAction codeAction = CodeAction.Create( $"Introduce field for '{expression}'", cancellationToken => IntroduceFieldRefactoring.RefactorAsync(context.Document, expressionStatement, typeSymbol, semanticModel, cancellationToken), GetEquivalenceKey(diagnostic, CodeFixIdentifiers.IntroduceField)); context.RegisterCodeFix(codeAction, diagnostic); } } } break; } case CompilerDiagnosticIdentifiers.CannotImplicitlyConvertType: { if (Settings.IsCodeFixEnabled(CodeFixIdentifiers.ChangeMemberTypeAccordingToReturnExpression) && expression.IsParentKind(SyntaxKind.ReturnStatement, SyntaxKind.YieldReturnStatement)) { SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); ChangeMemberTypeRefactoring.ComputeCodeFix(context, diagnostic, expression, semanticModel); break; } if (Settings.IsCodeFixEnabled(CodeFixIdentifiers.ReplaceStringLiteralWithCharacterLiteral) && expression?.IsKind(SyntaxKind.StringLiteralExpression) == true) { var literalExpression = (LiteralExpressionSyntax)expression; if (literalExpression.Token.ValueText.Length == 1) { SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); if (semanticModel.GetTypeInfo(expression, context.CancellationToken).ConvertedType?.IsChar() == true) { CodeAction codeAction = CodeAction.Create( "Replace string literal with character literal", cancellationToken => ReplaceStringLiteralWithCharacterLiteralRefactoring.RefactorAsync(context.Document, literalExpression, cancellationToken), GetEquivalenceKey(diagnostic, CodeFixIdentifiers.ReplaceStringLiteralWithCharacterLiteral)); context.RegisterCodeFix(codeAction, diagnostic); } } } if (Settings.IsCodeFixEnabled(CodeFixIdentifiers.UseYieldReturnInsteadOfReturn) && expression.IsParentKind(SyntaxKind.ReturnStatement)) { var returnStatement = (ReturnStatementSyntax)expression.Parent; SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); ISymbol containingSymbol = semanticModel.GetEnclosingSymbol(returnStatement.SpanStart, context.CancellationToken); if (containingSymbol?.IsKind(SymbolKind.Method) == true && ((IMethodSymbol)containingSymbol).ReturnType?.IsIEnumerableOrConstructedFromIEnumerableOfT() == true) { CodeAction codeAction = CodeAction.Create( "Use yield return instead of return", cancellationToken => UseYieldReturnInsteadOfReturnRefactoring.RefactorAsync(context.Document, returnStatement, SyntaxKind.YieldReturnStatement, semanticModel, cancellationToken), GetEquivalenceKey(diagnostic, CodeFixIdentifiers.UseYieldReturnInsteadOfReturn)); context.RegisterCodeFix(codeAction, diagnostic); } } break; } } } }
public override async Task RegisterCodeFixesAsync(CodeFixContext context) { SyntaxNode root = await context.GetSyntaxRootAsync().ConfigureAwait(false); if (!TryFindToken(root, context.Span.Start, out SyntaxToken token)) { return; } SyntaxKind kind = token.Kind(); Diagnostic diagnostic = context.Diagnostics[0]; Document document = context.Document; switch (diagnostic.Id) { case CompilerDiagnosticIdentifiers.CS0023_OperatorCannotBeAppliedToOperand: { if (kind == SyntaxKind.QuestionToken && token.Parent is ConditionalAccessExpressionSyntax conditionalAccess) { SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); ITypeSymbol typeSymbol = semanticModel.GetTypeSymbol(conditionalAccess.Expression, context.CancellationToken); if (typeSymbol?.IsErrorType() == false && !typeSymbol.IsNullableType()) { if (typeSymbol.IsValueType) { if (IsEnabled(diagnostic.Id, CodeFixIdentifiers.RemoveConditionalAccess, document, root.SyntaxTree)) { CodeAction codeAction = CodeAction.Create( "Remove '?' operator", ct => document.WithTextChangeAsync(token.Span, "", ct), GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); } } else if (typeSymbol.IsReferenceType) { if (IsEnabled(diagnostic.Id, CodeFixIdentifiers.AddArgumentList, document, root.SyntaxTree) && conditionalAccess.WhenNotNull is MemberBindingExpressionSyntax memberBindingExpression) { ConditionalAccessExpressionSyntax newNode = conditionalAccess.WithWhenNotNull( InvocationExpression( memberBindingExpression.WithoutTrailingTrivia(), ArgumentList().WithTrailingTrivia(memberBindingExpression.GetTrailingTrivia()))); CodeAction codeAction = CodeAction.Create( "Add argument list", ct => document.ReplaceNodeAsync(conditionalAccess, newNode, ct), GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); } } } } break; } case CompilerDiagnosticIdentifiers.CS0267_PartialModifierCanOnlyAppearImmediatelyBeforeClassStructInterfaceOrVoid: { if (!IsEnabled(diagnostic.Id, CodeFixIdentifiers.OrderModifiers, document, root.SyntaxTree)) { break; } ModifiersCodeFixRegistrator.MoveModifier(context, diagnostic, token.Parent, token); break; } case CompilerDiagnosticIdentifiers.CS1750_ValueCannotBeUsedAsDefaultParameter: { if (token.Parent is not ParameterSyntax parameter) { break; } ExpressionSyntax value = parameter.Default?.Value; if (value == null) { break; } if (value.IsKind(SyntaxKind.NullLiteralExpression)) { if (IsEnabled(diagnostic.Id, CodeFixIdentifiers.ReplaceNullLiteralExpressionWithDefaultValue, document, root.SyntaxTree)) { SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); CodeFixRegistrator.ReplaceNullWithDefaultValue(context, diagnostic, value, semanticModel); } } else if (!value.IsKind(SyntaxKind.DefaultExpression, SyntaxKind.DefaultLiteralExpression)) { if (IsEnabled(diagnostic.Id, CodeFixIdentifiers.ChangeParameterType, document, root.SyntaxTree)) { SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); ITypeSymbol typeSymbol = semanticModel.GetTypeSymbol(value, context.CancellationToken); if (!typeSymbol.IsKind(SymbolKind.ErrorType)) { CodeFixRegistrator.ChangeType(context, diagnostic, parameter.Type, typeSymbol, semanticModel); } } } break; } case CompilerDiagnosticIdentifiers.CS0126_ObjectOfTypeConvertibleToTypeIsRequired: { if (!IsEnabled(diagnostic.Id, CodeFixIdentifiers.ReturnDefaultValue, document, root.SyntaxTree)) { break; } if (!token.IsKind(SyntaxKind.ReturnKeyword)) { break; } if (!token.IsParentKind(SyntaxKind.ReturnStatement)) { break; } SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); ISymbol symbol = semanticModel.GetEnclosingSymbol(token.SpanStart, context.CancellationToken); if (symbol == null) { break; } SymbolKind symbolKind = symbol.Kind; ITypeSymbol typeSymbol = null; if (symbolKind == SymbolKind.Method) { var methodSymbol = (IMethodSymbol)symbol; typeSymbol = methodSymbol.ReturnType; if (methodSymbol.IsAsync && (typeSymbol is INamedTypeSymbol namedTypeSymbol)) { ImmutableArray <ITypeSymbol> typeArguments = namedTypeSymbol.TypeArguments; if (typeArguments.Any()) { typeSymbol = typeArguments[0]; } } } else if (symbolKind == SymbolKind.Property) { typeSymbol = ((IPropertySymbol)symbol).Type; } else { Debug.Fail(symbolKind.ToString()); } if (typeSymbol == null) { break; } if (typeSymbol.Kind == SymbolKind.ErrorType) { break; } if (!typeSymbol.SupportsExplicitDeclaration()) { break; } var returnStatement = (ReturnStatementSyntax)token.Parent; CodeAction codeAction = CodeAction.Create( "Return default value", ct => { ExpressionSyntax expression = typeSymbol.GetDefaultValueSyntax(document.GetDefaultSyntaxOptions()); if (expression.IsKind(SyntaxKind.DefaultExpression) && document.SupportsLanguageFeature(CSharpLanguageFeature.DefaultLiteral)) { expression = CSharpFactory.DefaultLiteralExpression().WithTriviaFrom(expression); } ReturnStatementSyntax newNode = returnStatement.WithExpression(expression); return(document.ReplaceNodeAsync(returnStatement, newNode, ct)); }, GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } case CompilerDiagnosticIdentifiers.CS1031_TypeExpected: { if (!IsEnabled(diagnostic.Id, CodeFixIdentifiers.AddMissingType, document, root.SyntaxTree)) { break; } if (!token.IsKind(SyntaxKind.CloseParenToken)) { break; } if (token.Parent is not DefaultExpressionSyntax defaultExpression) { break; } if (defaultExpression.Type is not IdentifierNameSyntax identifierName) { break; } if (!identifierName.IsMissing) { break; } SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); TypeInfo typeInfo = semanticModel.GetTypeInfo(defaultExpression, context.CancellationToken); ITypeSymbol convertedType = typeInfo.ConvertedType; if (convertedType?.SupportsExplicitDeclaration() != true) { break; } CodeAction codeAction = CodeAction.Create( $"Add type '{SymbolDisplay.ToMinimalDisplayString(convertedType, semanticModel, defaultExpression.SpanStart, SymbolDisplayFormats.DisplayName)}'", ct => { TypeSyntax newType = convertedType.ToTypeSyntax() .WithTriviaFrom(identifierName) .WithFormatterAndSimplifierAnnotation(); return(document.ReplaceNodeAsync(identifierName, newType, ct)); }, GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } case CompilerDiagnosticIdentifiers.CS1597_SemicolonAfterMethodOrAccessorBlockIsNotValid: { if (!IsEnabled(diagnostic.Id, CodeFixIdentifiers.RemoveSemicolon, document, root.SyntaxTree)) { break; } if (!token.IsKind(SyntaxKind.SemicolonToken)) { break; } switch (token.Parent) { case MethodDeclarationSyntax methodDeclaration: { BlockSyntax body = methodDeclaration.Body; if (body == null) { break; } CodeAction codeAction = CodeAction.Create( "Remove semicolon", ct => { SyntaxTriviaList trivia = body .GetTrailingTrivia() .EmptyIfWhitespace() .AddRange(token.LeadingTrivia.EmptyIfWhitespace()) .AddRange(token.TrailingTrivia); MethodDeclarationSyntax newNode = methodDeclaration .WithBody(body.WithTrailingTrivia(trivia)) .WithSemicolonToken(default(SyntaxToken)); return(document.ReplaceNodeAsync(methodDeclaration, newNode, ct)); }, GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } case PropertyDeclarationSyntax propertyDeclaration: { AccessorListSyntax accessorList = propertyDeclaration.AccessorList; if (accessorList == null) { break; } CodeAction codeAction = CodeAction.Create( "Remove semicolon", ct => { SyntaxTriviaList trivia = accessorList .GetTrailingTrivia() .EmptyIfWhitespace() .AddRange(token.LeadingTrivia.EmptyIfWhitespace()) .AddRange(token.TrailingTrivia); PropertyDeclarationSyntax newNode = propertyDeclaration .WithAccessorList(accessorList.WithTrailingTrivia(trivia)) .WithSemicolonToken(default(SyntaxToken)); return(document.ReplaceNodeAsync(propertyDeclaration, newNode, ct)); }, GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } case AccessorDeclarationSyntax accessorDeclaration: { BlockSyntax body = accessorDeclaration.Body; if (body == null) { break; } CodeAction codeAction = CodeAction.Create( "Remove semicolon", ct => { SyntaxTriviaList trivia = body .GetTrailingTrivia() .EmptyIfWhitespace() .AddRange(token.LeadingTrivia.EmptyIfWhitespace()) .AddRange(token.TrailingTrivia); AccessorDeclarationSyntax newNode = accessorDeclaration .WithBody(body.WithTrailingTrivia(trivia)) .WithSemicolonToken(default(SyntaxToken)); return(document.ReplaceNodeAsync(accessorDeclaration, newNode, ct)); }, GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } } break; } case CompilerDiagnosticIdentifiers.CS0030_CannotConvertType: { if (!IsEnabled(diagnostic.Id, CodeFixIdentifiers.ChangeForEachType, document, root.SyntaxTree)) { break; } if (!token.IsKind(SyntaxKind.ForEachKeyword)) { break; } if (token.Parent is not ForEachStatementSyntax forEachStatement) { break; } SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); ForEachStatementInfo info = semanticModel.GetForEachStatementInfo(forEachStatement); ITypeSymbol typeSymbol = info.ElementType; if (typeSymbol.SupportsExplicitDeclaration()) { CodeFixRegistrator.ChangeType(context, diagnostic, forEachStatement.Type, typeSymbol, semanticModel, CodeFixIdentifiers.ChangeForEachType); } CodeFixRegistrator.ChangeTypeToVar(context, diagnostic, forEachStatement.Type, CodeFixIdentifiers.ChangeTypeToVar); break; } case CompilerDiagnosticIdentifiers.CS1737_OptionalParametersMustAppearAfterAllRequiredParameters: { if (!IsEnabled(diagnostic.Id, CodeFixIdentifiers.AddDefaultValueToParameter, document, root.SyntaxTree)) { break; } if (token.Parent is not BaseParameterListSyntax parameterList) { break; } SeparatedSyntaxList <ParameterSyntax> parameters = parameterList.Parameters; ParameterSyntax parameter = null; for (int i = 0; i < parameters.Count; i++) { ParameterSyntax p = parameters[i]; if (p.FullSpan.End <= token.SpanStart) { parameter = p; } else { break; } } SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); IParameterSymbol parameterSymbol = semanticModel.GetDeclaredSymbol(parameter, context.CancellationToken); ITypeSymbol typeSymbol = parameterSymbol.Type; if (typeSymbol.Kind == SymbolKind.ErrorType) { break; } CodeAction codeAction = CodeAction.Create( "Add default value", ct => { ExpressionSyntax defaultValue = typeSymbol.GetDefaultValueSyntax(document.GetDefaultSyntaxOptions()); ParameterSyntax newParameter = parameter .WithDefault(EqualsValueClause(defaultValue).WithTrailingTrivia(parameter.GetTrailingTrivia())) .WithoutTrailingTrivia() .WithFormatterAnnotation(); return(document.ReplaceNodeAsync(parameter, newParameter, ct)); }, GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } case CompilerDiagnosticIdentifiers.CS8632_AnnotationForNullableReferenceTypesShouldOnlyBeUsedWithinNullableAnnotationsContext: { if (!IsEnabled(diagnostic.Id, CodeFixIdentifiers.RemoveAnnotationForNullableReferenceTypes, document, root.SyntaxTree)) { break; } if (!token.IsKind(SyntaxKind.QuestionToken)) { return; } CodeAction codeAction = CodeAction.Create( "Remove 'nullable' annotation", ct => { var textChange = new TextChange(token.Span, ""); return(document.WithTextChangeAsync(textChange, ct)); }, GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } case CompilerDiagnosticIdentifiers.CS8602_DereferenceOfPossiblyNullReference: { if (!IsEnabled(diagnostic.Id, CodeFixIdentifiers.UseNullForgivingOperator, document, root.SyntaxTree)) { break; } Debug.Assert(token.IsKind(SyntaxKind.IdentifierToken), token.Kind().ToString()); if (!token.IsKind(SyntaxKind.IdentifierToken)) { return; } if (!token.IsParentKind(SyntaxKind.IdentifierName)) { return; } SyntaxNode node = token.Parent .WalkUp(f => f.IsKind(SyntaxKind.SimpleMemberAccessExpression, SyntaxKind.InvocationExpression, SyntaxKind.ElementAccessExpression)) .Parent; if (node.IsKind(SyntaxKind.ExpressionStatement) && token.SpanStart == node.SpanStart) { CodeAction codeAction = CodeAction.Create( "Use null propagation operator", ct => { var textChange = new TextChange(new TextSpan(token.Span.End, 0), "?"); return(document.WithTextChangeAsync(textChange, ct)); }, GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); } return; } case CompilerDiagnosticIdentifiers.CS8604_PossibleNullReferenceArgumentForParameter: { if (!IsEnabled(diagnostic.Id, CodeFixIdentifiers.UseNullForgivingOperator, document, root.SyntaxTree)) { break; } Debug.Assert(token.IsKind(SyntaxKind.IdentifierToken), token.Kind().ToString()); if (!token.IsKind(SyntaxKind.IdentifierToken)) { return; } if (!token.IsParentKind(SyntaxKind.IdentifierName)) { return; } CodeAction codeAction = CodeAction.Create( "Use null-forgiving operator", ct => { var identifierName = (IdentifierNameSyntax)token.Parent; PostfixUnaryExpressionSyntax newExpression = SuppressNullableWarningExpression(identifierName.WithoutTrivia()) .WithTriviaFrom(identifierName); return(document.ReplaceNodeAsync(identifierName, newExpression, ct)); }, GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); return; } case CompilerDiagnosticIdentifiers.CS8618_NonNullableMemberIsUninitialized: { if (!IsEnabled(diagnostic.Id, CodeFixIdentifiers.UseNullForgivingOperator, document, root.SyntaxTree)) { break; } Debug.Assert(token.IsKind(SyntaxKind.IdentifierToken), token.Kind().ToString()); if (!token.IsKind(SyntaxKind.IdentifierToken)) { return; } if (token.IsParentKind(SyntaxKind.PropertyDeclaration)) { CodeAction codeAction = CodeAction.Create( "Use null-forgiving operator", ct => { var property = (PropertyDeclarationSyntax)token.Parent; PropertyDeclarationSyntax newProperty = property .WithoutTrailingTrivia() .WithInitializer(EqualsValueClause(SuppressNullableWarningExpression(NullLiteralExpression()))) .WithSemicolonToken(SemicolonToken().WithTrailingTrivia(property.GetTrailingTrivia())); return(document.ReplaceNodeAsync(property, newProperty, ct)); }, GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); } else { SyntaxDebug.Assert( (token.IsParentKind(SyntaxKind.VariableDeclarator) && token.Parent.IsParentKind(SyntaxKind.VariableDeclaration) && token.Parent.Parent.IsParentKind(SyntaxKind.FieldDeclaration, SyntaxKind.EventFieldDeclaration)) || token.IsParentKind(SyntaxKind.ConstructorDeclaration), token); if (token.IsParentKind(SyntaxKind.VariableDeclarator) && token.Parent.IsParentKind(SyntaxKind.VariableDeclaration) && token.Parent.Parent.IsParentKind(SyntaxKind.FieldDeclaration)) { CodeAction codeAction = CodeAction.Create( "Use null-forgiving operator", ct => { var declarator = (VariableDeclaratorSyntax)token.Parent; VariableDeclaratorSyntax newDeclarator = declarator .WithoutTrailingTrivia() .WithInitializer( EqualsValueClause(SuppressNullableWarningExpression(NullLiteralExpression())) .WithTrailingTrivia(declarator.GetTrailingTrivia())); return(document.ReplaceNodeAsync(declarator, newDeclarator, ct)); }, GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); } } break; } case CompilerDiagnosticIdentifiers.CS8403_MethodWithIteratorBlockMustBeAsyncToReturnIAsyncEnumerableOfT: { if (!IsEnabled(diagnostic.Id, CodeFixIdentifiers.AddAsyncModifier, document, root.SyntaxTree)) { break; } SyntaxDebug.Assert(token.IsKind(SyntaxKind.IdentifierToken), token); SyntaxDebug.Assert(token.IsParentKind(SyntaxKind.MethodDeclaration, SyntaxKind.LocalFunctionStatement), token.Parent); if (token.IsParentKind(SyntaxKind.MethodDeclaration, SyntaxKind.LocalFunctionStatement)) { ModifiersCodeFixRegistrator.AddModifier( context, diagnostic, token.Parent, SyntaxKind.AsyncKeyword, additionalKey: CodeFixIdentifiers.AddAsyncModifier); } break; } } }