public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { if (!Settings.IsEnabled(CodeFixIdentifiers.ChangeMethodReturnType)) { return; } SyntaxNode root = await context.GetSyntaxRootAsync().ConfigureAwait(false); if (!TryFindFirstAncestorOrSelf(root, context.Span, out SyntaxNode node, predicate: f => f.IsKind(SyntaxKind.MethodDeclaration, SyntaxKind.LocalFunctionStatement))) { return; } Diagnostic diagnostic = context.Diagnostics[0]; SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); var methodSymbol = (IMethodSymbol)semanticModel.GetDeclaredSymbol(node, context.CancellationToken); Debug.Assert(methodSymbol != null, node.Kind().ToString()); ITypeSymbol typeSymbol = methodSymbol.ReturnType; if (typeSymbol.IsErrorType()) { return; } (bool containsReturnAwait, bool containsAwaitStatement) = AnalyzeAwaitExpressions(node); Debug.Assert(containsAwaitStatement || containsReturnAwait, node.ToString()); if (containsAwaitStatement) { INamedTypeSymbol taskSymbol = semanticModel.GetTypeByMetadataName("System.Threading.Tasks.Task"); CodeFixRegistrator.ChangeTypeOrReturnType(context, diagnostic, node, taskSymbol, semanticModel, "Task"); } if (containsReturnAwait) { typeSymbol = semanticModel.GetTypeByMetadataName("System.Threading.Tasks.Task`1").Construct(typeSymbol); CodeFixRegistrator.ChangeTypeOrReturnType(context, diagnostic, node, typeSymbol, semanticModel, "TaskOfT"); } }
public override async Task RegisterCodeFixesAsync(CodeFixContext context) { SyntaxNode root = await context.GetSyntaxRootAsync().ConfigureAwait(false); if (!TryFindFirstAncestorOrSelf(root, context.Span, out MemberDeclarationSyntax memberDeclaration)) { return; } foreach (Diagnostic diagnostic in context.Diagnostics) { switch (diagnostic.Id) { case CompilerDiagnosticIdentifiers.MissingXmlCommentForPubliclyVisibleTypeOrMember: { if (!Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.AddDocumentationComment)) { break; } CodeAction codeAction = CodeAction.Create( "Add documentation comment", cancellationToken => AddDocumentationCommentRefactoring.RefactorAsync(context.Document, memberDeclaration, false, cancellationToken), GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); CodeAction codeAction2 = CodeAction.Create( "Add documentation comment (copy from base if available)", cancellationToken => AddDocumentationCommentRefactoring.RefactorAsync(context.Document, memberDeclaration, true, cancellationToken), GetEquivalenceKey(diagnostic, "CopyFromBaseIfAvailable")); context.RegisterCodeFix(codeAction2, diagnostic); break; } case CompilerDiagnosticIdentifiers.MethodReturnTypeMustMatchOverriddenMethodReturnType: { if (!Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.ChangeMethodReturnType)) { break; } SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); var methodSymbol = (IMethodSymbol)semanticModel.GetDeclaredSymbol(memberDeclaration, context.CancellationToken); ITypeSymbol typeSymbol = methodSymbol.OverriddenMethod.ReturnType; CodeFixRegistrator.ChangeTypeOrReturnType(context, diagnostic, memberDeclaration, typeSymbol, semanticModel); break; } case CompilerDiagnosticIdentifiers.PartialMethodsMustHaveVoidReturnType: { if (!Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.ChangeMethodReturnType)) { break; } SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); var methodDeclaration = (MethodDeclarationSyntax)memberDeclaration; MethodDeclarationSyntax otherPart = semanticModel.GetOtherPart(methodDeclaration, context.CancellationToken); if (otherPart == null) { break; } CodeAction codeAction = CodeAction.Create( "Change return type to 'void'", cancellationToken => { return(context.Document.Solution().ReplaceNodesAsync( new MethodDeclarationSyntax[] { methodDeclaration, otherPart }, (node, _) => node.WithReturnType(CSharpFactory.VoidType().WithTriviaFrom(node.ReturnType)), cancellationToken)); }, GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } case CompilerDiagnosticIdentifiers.MemberTypeMustMatchOverriddenMemberType: { if (!Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.MemberTypeMustMatchOverriddenMemberType)) { break; } SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); ITypeSymbol typeSymbol = null; switch (memberDeclaration.Kind()) { case SyntaxKind.PropertyDeclaration: case SyntaxKind.IndexerDeclaration: { var propertySymbol = (IPropertySymbol)semanticModel.GetDeclaredSymbol(memberDeclaration, context.CancellationToken); typeSymbol = propertySymbol.OverriddenProperty.Type; break; } case SyntaxKind.EventDeclaration: { var eventSymbol = (IEventSymbol)semanticModel.GetDeclaredSymbol(memberDeclaration, context.CancellationToken); typeSymbol = eventSymbol.OverriddenEvent.Type; break; } case SyntaxKind.EventFieldDeclaration: { VariableDeclaratorSyntax declarator = ((EventFieldDeclarationSyntax)memberDeclaration).Declaration.Variables[0]; var eventSymbol = (IEventSymbol)semanticModel.GetDeclaredSymbol(declarator, context.CancellationToken); typeSymbol = eventSymbol.OverriddenEvent.Type; break; } } CodeFixRegistrator.ChangeTypeOrReturnType(context, diagnostic, memberDeclaration, typeSymbol, semanticModel); break; } case CompilerDiagnosticIdentifiers.MissingPartialModifier: case CompilerDiagnosticIdentifiers.PartialMethodMustBeDeclaredInPartialClassOrPartialStruct: { if (!Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.AddPartialModifier)) { break; } SyntaxNode node = null; switch (memberDeclaration.Kind()) { case SyntaxKind.MethodDeclaration: { if (memberDeclaration.IsParentKind(SyntaxKind.ClassDeclaration, SyntaxKind.StructDeclaration)) { node = memberDeclaration.Parent; } break; } case SyntaxKind.ClassDeclaration: case SyntaxKind.StructDeclaration: case SyntaxKind.InterfaceDeclaration: case SyntaxKind.RecordDeclaration: { node = memberDeclaration; break; } } Debug.Assert(node != null, memberDeclaration.ToString()); if (node == null) { break; } ModifiersCodeFixRegistrator.AddModifier(context, diagnostic, node, SyntaxKind.PartialKeyword, title: $"Make {CSharpFacts.GetTitle(node)} partial"); break; } case CompilerDiagnosticIdentifiers.MemberIsAbstractButItIsContainedInNonAbstractClass: { if (!Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.MakeContainingClassAbstract)) { break; } if (!memberDeclaration.IsParentKind(SyntaxKind.ClassDeclaration)) { break; } ModifiersCodeFixRegistrator.AddModifier( context, diagnostic, memberDeclaration.Parent, SyntaxKind.AbstractKeyword, title: "Make containing class abstract"); break; } case CompilerDiagnosticIdentifiers.StaticConstructorMustBeParameterless: { if (!Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.RemoveParametersFromStaticConstructor)) { break; } var constructorDeclaration = (ConstructorDeclarationSyntax)memberDeclaration; CodeAction codeAction = CodeAction.Create( "Remove parameters", cancellationToken => { ParameterListSyntax parameterList = constructorDeclaration.ParameterList; ParameterListSyntax newParameterList = parameterList .WithParameters(default(SeparatedSyntaxList <ParameterSyntax>)) .WithOpenParenToken(parameterList.OpenParenToken.WithoutTrailingTrivia()) .WithCloseParenToken(parameterList.CloseParenToken.WithoutLeadingTrivia()); ConstructorDeclarationSyntax newNode = constructorDeclaration.WithParameterList(newParameterList); return(context.Document.ReplaceNodeAsync(constructorDeclaration, newNode, cancellationToken)); }, GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } case CompilerDiagnosticIdentifiers.ExplicitInterfaceDeclarationCanOnlyBeDeclaredInClassOrStruct: case CompilerDiagnosticIdentifiers.InterfacesCannotContainFields: case CompilerDiagnosticIdentifiers.InterfacesCannotContainOperators: case CompilerDiagnosticIdentifiers.InterfacesCannotDeclareTypes: case CompilerDiagnosticIdentifiers.OnlyClassTypesCanContainDestructors: case CompilerDiagnosticIdentifiers.StructsCannotContainExplicitParameterlessConstructors: { if (!Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.RemoveMemberDeclaration)) { break; } CodeFixRegistrator.RemoveMemberDeclaration(context, diagnostic, memberDeclaration); break; } case CompilerDiagnosticIdentifiers.NameOfDestructorMustMatchNameOfClass: { if (!Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.RenameDestructorToMatchClassName)) { break; } if (!(memberDeclaration is DestructorDeclarationSyntax destructorDeclaration)) { break; } if (!(memberDeclaration.Parent is ClassDeclarationSyntax classDeclaration)) { break; } if (classDeclaration.Identifier.ValueText.Length == 0) { break; } CodeAction codeAction = CodeAction.Create( "Rename destructor to match class name", cancellationToken => { DestructorDeclarationSyntax newNode = destructorDeclaration.WithIdentifier(classDeclaration.Identifier.WithTriviaFrom(destructorDeclaration.Identifier)); return(context.Document.ReplaceNodeAsync(destructorDeclaration, newNode, cancellationToken)); }, GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } case CompilerDiagnosticIdentifiers.CannotChangeTupleElementNameWhenOverridingInheritedMember: { if (!Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.RenameTupleElement)) { break; } SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); if (memberDeclaration is MethodDeclarationSyntax methodDeclaration) { IMethodSymbol methodSymbol = semanticModel.GetDeclaredSymbol(methodDeclaration, context.CancellationToken); if (!(methodSymbol.ReturnType is INamedTypeSymbol tupleType)) { break; } if (!tupleType.IsTupleType) { break; } if (!(methodSymbol.OverriddenMethod?.ReturnType is INamedTypeSymbol baseTupleType)) { break; } if (!baseTupleType.IsTupleType) { break; } ImmutableArray <IFieldSymbol> elements = tupleType.TupleElements; ImmutableArray <IFieldSymbol> baseElements = baseTupleType.TupleElements; if (elements.Length != baseElements.Length) { break; } int i = 0; while (i < elements.Length) { if (elements[i].Name != baseElements[i].Name) { break; } i++; } if (i == elements.Length) { break; } TupleElementSyntax tupleElement = ((TupleTypeSyntax)methodDeclaration.ReturnType).Elements[i]; CodeAction codeAction = CodeAction.Create( $"Rename '{elements[i].Name}' to '{baseElements[i].Name}'", ct => RenameTupleElementAsync(context.Document, methodDeclaration, tupleElement, elements[i], baseElements[i].Name, semanticModel, ct), GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); } else if (memberDeclaration is PropertyDeclarationSyntax propertyDeclaration) { IPropertySymbol propertySymbol = semanticModel.GetDeclaredSymbol(propertyDeclaration, context.CancellationToken); if (!(propertySymbol.Type is INamedTypeSymbol tupleType)) { break; } if (!tupleType.IsTupleType) { break; } if (!(propertySymbol.OverriddenProperty?.Type is INamedTypeSymbol baseTupleType)) { break; } if (!baseTupleType.IsTupleType) { break; } ImmutableArray <IFieldSymbol> elements = tupleType.TupleElements; ImmutableArray <IFieldSymbol> baseElements = baseTupleType.TupleElements; if (elements.Length != baseElements.Length) { break; } int i = 0; while (i < elements.Length) { if (elements[i].Name != baseElements[i].Name) { break; } i++; } if (i == elements.Length) { break; } TupleElementSyntax tupleElement = ((TupleTypeSyntax)propertyDeclaration.Type).Elements[i]; CodeAction codeAction = CodeAction.Create( $"Rename '{elements[i].Name}' to '{baseElements[i].Name}'", ct => RenameTupleElementAsync(context.Document, propertyDeclaration, tupleElement, elements[i], baseElements[i].Name, semanticModel, ct), GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); } break; } case CompilerDiagnosticIdentifiers.MethodsWithVariableArgumentsAreNotCLSCompliant: case CompilerDiagnosticIdentifiers.ArgumentTypeIsNotCLSCompliant: case CompilerDiagnosticIdentifiers.ReturnTypeIsNotCLSCompliant: case CompilerDiagnosticIdentifiers.TypeOfVariableIsNotCLSCompliant: case CompilerDiagnosticIdentifiers.IdentifierDifferingOnlyInCaseIsNotCLSCompliant: case CompilerDiagnosticIdentifiers.OverloadedMethodDifferingOnlyInRefOrOutOrInArrayRankIsNotCLSCompliant: case CompilerDiagnosticIdentifiers.OverloadedMethodDifferingOnlyByUnnamedArrayTypesIsNotCLSCompliant: case CompilerDiagnosticIdentifiers.IdentifierIsNotCLSCompliant: case CompilerDiagnosticIdentifiers.BaseTypeIsNotCLSCompliant: case CompilerDiagnosticIdentifiers.ArraysAsAttributeArgumentsIsNotCLSCompliant: case CompilerDiagnosticIdentifiers.ConstraintTypeIsNotCLSCompliant: case CompilerDiagnosticIdentifiers.TypeIsNotCLSCompliantBecauseBaseInterfaceIsNotCLSCompliant: { if (!Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.MarkDeclarationAsNonCLSCompliant)) { break; } CodeAction codeAction = CodeAction.Create( $"Mark {CSharpFacts.GetTitle(memberDeclaration)} as non-CLS-compliant", ct => MarkDeclarationAsNonCLSCompliantAsync(context.Document, memberDeclaration, ct), GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } case CompilerDiagnosticIdentifiers.ExplicitInterfaceDeclarationIsNotMemberOfInterface: { if (!Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.AddParameterToExplicitlyImplementedInterfaceMember)) { break; } var context2 = new CommonFixContext( context.Document, GetEquivalenceKey(diagnostic), await context.GetSemanticModelAsync().ConfigureAwait(false), context.CancellationToken); CodeAction codeAction = AddParameterToInterfaceMemberRefactoring.ComputeRefactoringForExplicitImplementation(context2, memberDeclaration); if (codeAction != null) { context.RegisterCodeFix(codeAction, diagnostic); } break; } } } }
public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { if (!Settings.IsEnabled(CodeFixIdentifiers.ChangeMethodReturnType)) { return; } SyntaxNode root = await context.GetSyntaxRootAsync().ConfigureAwait(false); if (!TryFindFirstAncestorOrSelf(root, context.Span, out SyntaxNode node, predicate: f => f.IsKind(SyntaxKind.MethodDeclaration, SyntaxKind.LocalFunctionStatement))) { return; } foreach (Diagnostic diagnostic in context.Diagnostics) { switch (diagnostic.Id) { case CompilerDiagnosticIdentifiers.BodyCannotBeIteratorBlockBecauseTypeIsNotIteratorInterfaceType: { if (!Settings.IsEnabled(CodeFixIdentifiers.ChangeMethodReturnType)) { break; } BlockSyntax body = (node is MethodDeclarationSyntax methodDeclaration) ? methodDeclaration.Body : ((LocalFunctionStatementSyntax)node).Body; Debug.Assert(body != null, node.ToString()); if (body == null) { break; } SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); ITypeSymbol typeSymbol = null; HashSet <ITypeSymbol> typeSymbols = null; INamedTypeSymbol ienumerableOfTSymbol = null; foreach (SyntaxNode descendant in body.DescendantNodes(descendIntoChildren: f => !CSharpFacts.IsFunction(f.Kind()))) { if (!descendant.IsKind(SyntaxKind.YieldReturnStatement)) { continue; } var yieldReturn = (YieldStatementSyntax)descendant; ExpressionSyntax expression = yieldReturn.Expression; if (expression == null) { continue; } var namedTypeSymbol = semanticModel.GetTypeSymbol(expression, context.CancellationToken) as INamedTypeSymbol; if (namedTypeSymbol?.IsErrorType() != false) { continue; } if (typeSymbol == null) { typeSymbol = namedTypeSymbol; } else { if (typeSymbols == null) { typeSymbols = new HashSet <ITypeSymbol>() { typeSymbol }; } if (!typeSymbols.Add(namedTypeSymbol)) { continue; } } if (ienumerableOfTSymbol == null) { ienumerableOfTSymbol = semanticModel.GetTypeByMetadataName("System.Collections.Generic.IEnumerable`1"); } CodeFixRegistrator.ChangeTypeOrReturnType( context, diagnostic, node, ienumerableOfTSymbol.Construct(namedTypeSymbol), semanticModel, additionalKey: SymbolDisplay.ToMinimalDisplayString(namedTypeSymbol, semanticModel, node.SpanStart, SymbolDisplayFormats.Default)); } break; } } } }