Beispiel #1
0
        private async Task ApplyRuleNameNoZeroValueAsync(SymbolEditor editor, INamedTypeSymbol enumType, CancellationToken cancellationToken)
        {
            // remove any non-zero member named 'None'
            foreach (IFieldSymbol field in enumType.GetMembers().Where(m => m.Kind == SymbolKind.Field))
            {
                if (CA1008DiagnosticAnalyzer.IsMemberNamedNone(field))
                {
                    await editor.EditOneDeclarationAsync(field, (e, d) => e.RemoveNode(d), cancellationToken).ConfigureAwait(false);
                }
            }

            // insert zero-valued member 'None' to top
            await editor.EditOneDeclarationAsync(enumType, (e, d) => e.InsertMembers(d, 0, new[] { e.Generator.EnumMember("None") }), cancellationToken).ConfigureAwait(false);
        }
        private async Task <Document> AddAccessor(Document document, SyntaxNode parameter, CancellationToken cancellationToken)
        {
            SymbolEditor  symbolEditor = SymbolEditor.Create(document);
            SemanticModel model        = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);

            var parameterSymbol = model.GetDeclaredSymbol(parameter, cancellationToken) as IParameterSymbol;

            if (parameterSymbol == null)
            {
                return(document);
            }

            // Make the first character uppercase since we are generating a property.
            string propName = char.ToUpper(parameterSymbol.Name[0]).ToString() + parameterSymbol.Name.Substring(1);

            INamedTypeSymbol typeSymbol     = parameterSymbol.ContainingType;
            ISymbol          propertySymbol = typeSymbol.GetMembers(propName).Where(m => m.Kind == SymbolKind.Property).FirstOrDefault();

            // Add a new property
            if (propertySymbol == null)
            {
                await symbolEditor.EditOneDeclarationAsync(typeSymbol,
                                                           parameter.GetLocation(), // edit the partial declaration that has this parameter symbol.
                                                           (editor, typeDeclaration) =>
                {
                    SyntaxNode newProperty = editor.Generator.PropertyDeclaration(propName,
                                                                                  editor.Generator.TypeExpression(parameterSymbol.Type),
                                                                                  Accessibility.Public,
                                                                                  DeclarationModifiers.ReadOnly);
                    newProperty = editor.Generator.WithGetAccessorStatements(newProperty, null);
                    editor.AddMember(typeDeclaration, newProperty);
                },
                                                           cancellationToken).ConfigureAwait(false);
            }
            else
            {
                await symbolEditor.EditOneDeclarationAsync(propertySymbol,
                                                           (editor, propertyDeclaration) =>
                {
                    editor.SetGetAccessorStatements(propertyDeclaration, null);
                    editor.SetModifiers(propertyDeclaration, editor.Generator.GetModifiers(propertyDeclaration) - DeclarationModifiers.WriteOnly);
                },
                                                           cancellationToken).ConfigureAwait(false);
            }

            return(symbolEditor.GetChangedDocuments().First());
        }
Beispiel #3
0
            public override async Task <CodeAction?> GetFixAsync(FixAllContext fixAllContext)
            {
                ImmutableArray <Diagnostic> diagnostics = await GetAllDiagnosticsInScope(fixAllContext).ConfigureAwait(false);

                Dictionary <(INamedTypeSymbol marshallerType, ITypeSymbol managedType, bool isLinearCollectionMarshaller), HashSet <string> > uniqueMarshallersToFix = new();

                // Organize all the diagnostics by marshaller, managed type, and whether or not it's a collection marshaller
                foreach (Diagnostic diagnostic in diagnostics)
                {
                    Document      doc   = fixAllContext.Solution.GetDocument(diagnostic.Location.SourceTree);
                    SemanticModel model = await doc.GetSemanticModelAsync(fixAllContext.CancellationToken).ConfigureAwait(false);

                    var         entryPointTypeSymbol = (INamedTypeSymbol)model.GetEnclosingSymbol(diagnostic.Location.SourceSpan.Start, fixAllContext.CancellationToken);
                    ITypeSymbol?managedType          = GetManagedTypeInAttributeSyntax(diagnostic.Location, entryPointTypeSymbol);

                    SyntaxNode root = await diagnostic.Location.SourceTree.GetRootAsync(fixAllContext.CancellationToken).ConfigureAwait(false);

                    SyntaxNode node                   = root.FindNode(diagnostic.Location.SourceSpan);
                    var        marshallerType         = (INamedTypeSymbol)model.GetSymbolInfo(node, fixAllContext.CancellationToken).Symbol;
                    var        uniqueMarshallerFixKey = (marshallerType, managedType, ManualTypeMarshallingHelper.IsLinearCollectionEntryPoint(entryPointTypeSymbol));
                    if (!uniqueMarshallersToFix.TryGetValue(uniqueMarshallerFixKey, out HashSet <string> membersToAdd))
                    {
                        uniqueMarshallersToFix[uniqueMarshallerFixKey] = membersToAdd = new HashSet <string>();
                    }

                    membersToAdd.UnionWith(diagnostic.Properties[MissingMemberNames.Key].Split(MissingMemberNames.Delimiter));
                }

                Dictionary <INamedTypeSymbol, INamedTypeSymbol> partiallyUpdatedSymbols = new(SymbolEqualityComparer.Default);

                SymbolEditor symbolEditor = SymbolEditor.Create(fixAllContext.Solution);

                // Apply each fix
                foreach (var marshallerInfo in uniqueMarshallersToFix)
                {
                    var(marshallerType, managedType, isLinearCollectionMarshaller) = marshallerInfo.Key;
                    HashSet <string> missingMembers = marshallerInfo.Value;

                    if (!partiallyUpdatedSymbols.TryGetValue(marshallerType, out INamedTypeSymbol newMarshallerType))
                    {
                        newMarshallerType = marshallerType;
                    }

                    newMarshallerType = (INamedTypeSymbol)await symbolEditor.EditOneDeclarationAsync(
                        marshallerType,
                        (editor, decl) => AddMissingMembers(
                            editor,
                            decl,
                            marshallerType,
                            managedType,
                            missingMembers,
                            isLinearCollectionMarshaller),
                        fixAllContext.CancellationToken).ConfigureAwait(false);

                    partiallyUpdatedSymbols[marshallerType] = newMarshallerType;
                }

                return(CodeAction.Create(SR.AddMissingCustomTypeMarshallerMembers, ct => Task.FromResult(symbolEditor.ChangedSolution)));
            }
        private static async Task <Document> ApplyRuleNameMultipleZeroAsync(Document document, INamedTypeSymbol enumType, CancellationToken cancellationToken)
        {
            // Diagnostic: Remove all members that have the value zero from '{0}' except for one member that is named 'None'.
            // Fix: Remove all members that have the value zero except for one member that is named 'None'.
            SymbolEditor editor = SymbolEditor.Create(document);

            bool needsNewZeroValuedNoneField = true;
            ISet <IFieldSymbol> set          = EnumsShouldHaveZeroValueAnalyzer.GetZeroValuedFields(enumType).ToSet();

            bool makeNextFieldExplicit = false;

            foreach (IFieldSymbol field in enumType.GetMembers().Where(m => m.Kind == SymbolKind.Field))
            {
                bool isZeroValued          = set.Contains(field);
                bool isZeroValuedNamedNone = isZeroValued && EnumsShouldHaveZeroValueAnalyzer.IsMemberNamedNone(field);

                if (!isZeroValued || isZeroValuedNamedNone)
                {
                    if (makeNextFieldExplicit)
                    {
                        await editor.EditOneDeclarationAsync(field, (e, d) => e.ReplaceNode(d, GetExplicitlyAssignedField(field, d, e.Generator)), cancellationToken).ConfigureAwait(false);

                        makeNextFieldExplicit = false;
                    }

                    if (isZeroValuedNamedNone)
                    {
                        needsNewZeroValuedNoneField = false;
                    }
                }
                else
                {
                    await editor.EditOneDeclarationAsync(field, (e, d) => e.RemoveNode(d), cancellationToken).ConfigureAwait(false); // removes the field declaration

                    makeNextFieldExplicit = true;
                }
            }

            if (needsNewZeroValuedNoneField)
            {
                await editor.EditOneDeclarationAsync(enumType, (e, d) => e.InsertMembers(d, 0, new[] { e.Generator.EnumMember("None") }), cancellationToken).ConfigureAwait(false);
            }

            return(editor.GetChangedDocuments().First());
        }
        private static async Task <Document> ApplyRuleNameNoZeroValueAsync(Document document, INamedTypeSymbol enumType, CancellationToken cancellationToken)
        {
            SymbolEditor editor = SymbolEditor.Create(document);

            // remove any non-zero member named 'None'
            foreach (IFieldSymbol field in enumType.GetMembers().Where(m => m.Kind == SymbolKind.Field))
            {
                if (EnumsShouldHaveZeroValueAnalyzer.IsMemberNamedNone(field))
                {
                    await editor.EditOneDeclarationAsync(field, (e, d) => e.RemoveNode(d), cancellationToken).ConfigureAwait(false);
                }
            }

            // insert zero-valued member 'None' to top
            await editor.EditOneDeclarationAsync(enumType, (e, d) => e.InsertMembers(d, 0, new[] { e.Generator.EnumMember("None") }), cancellationToken).ConfigureAwait(false);

            return(editor.GetChangedDocuments().First());
        }
        private async Task ApplyRuleNameMultipleZeroAsync(SymbolEditor editor, INamedTypeSymbol enumType, CancellationToken cancellationToken)
        {
            // Diagnostic: Remove all members that have the value zero from '{0}' except for one member that is named 'None'.
            // Fix: Remove all members that have the value zero except for one member that is named 'None'.

            bool needsNewZeroValuedNoneField = true;
            var  set = CA1008DiagnosticAnalyzer.GetZeroValuedFields(enumType).ToSet();

            bool makeNextFieldExplicit = false;

            foreach (IFieldSymbol field in enumType.GetMembers().Where(m => m.Kind == SymbolKind.Field))
            {
                var isZeroValued          = set.Contains(field);
                var isZeroValuedNamedNone = isZeroValued && CA1008DiagnosticAnalyzer.IsMemberNamedNone(field);

                if (!isZeroValued || isZeroValuedNamedNone)
                {
                    if (makeNextFieldExplicit)
                    {
                        await editor.EditOneDeclarationAsync(field, (e, d) => e.ReplaceNode(d, GetExplicitlyAssignedField(field, d, e.Generator)), cancellationToken);

                        makeNextFieldExplicit = false;
                    }

                    if (isZeroValuedNamedNone)
                    {
                        needsNewZeroValuedNoneField = false;
                    }
                }
                else
                {
                    await editor.EditOneDeclarationAsync(field, (e, d) => e.RemoveNode(d), cancellationToken); // removes the field declaration

                    makeNextFieldExplicit = true;
                }
            }

            if (needsNewZeroValuedNoneField)
            {
                await editor.EditOneDeclarationAsync(enumType, (e, d) => e.InsertMembers(d, 0, new[] { e.Generator.EnumMember("None") }), cancellationToken);
            }
        }
        private static async Task <Document> AddSerializableAttributeToType(Document document, ITypeSymbol type, CancellationToken cancellationToken)
        {
            SymbolEditor editor = SymbolEditor.Create(document);
            await editor.EditOneDeclarationAsync(type, (docEditor, declaration) =>
            {
                SyntaxNode serializableAttr = docEditor.Generator.Attribute(docEditor.Generator.TypeExpression(WellKnownTypes.SerializableAttribute(docEditor.SemanticModel.Compilation)));
                docEditor.AddAttribute(declaration, serializableAttr);
            }, cancellationToken).ConfigureAwait(false);

            return(editor.GetChangedDocuments().First());
        }
        private static async Task <Document> SetAccessibilityAsync(Document document, IMethodSymbol methodSymbol, CancellationToken cancellationToken)
        {
            SymbolEditor editor = SymbolEditor.Create(document);

            // This would be constructor and can have only one definition.
            Debug.Assert(methodSymbol.IsConstructor() && methodSymbol.DeclaringSyntaxReferences.HasExactly(1));
            await editor.EditOneDeclarationAsync(methodSymbol, (docEditor, declaration) =>
            {
                Accessibility newAccessibility = methodSymbol.ContainingType.IsSealed ? Accessibility.Private : Accessibility.Protected;
                docEditor.SetAccessibility(declaration, newAccessibility);
            }, cancellationToken).ConfigureAwait(false);

            return(editor.GetChangedDocuments().First());
        }
Beispiel #9
0
        private async Task <Document> AddDisposeCall(Document document, SyntaxNode declaration, IFieldSymbol fieldSymbol, IMethodSymbol disposeMethod, ITypeSymbol iDisposableType, ISymbol iDisposable_Dispose, CancellationToken cancellationToken)
        {
            SymbolEditor editor = SymbolEditor.Create(document);
            await editor.EditOneDeclarationAsync(disposeMethod, (docEditor, disposeMethodNode) =>
            {
                var generator = docEditor.Generator;
                // handle the case where a local in the Dispose method exists with the same name by generating this (or ClassName) and simplifying it
                var path = fieldSymbol.IsStatic
                                ? generator.IdentifierName(fieldSymbol.ContainingType.MetadataName)
                                : generator.ThisExpression();

                var disposeMethodOfFieldType = fieldSymbol.Type.FindImplementationForInterfaceMember(iDisposable_Dispose) as IMethodSymbol;

                // If the original interface was implemented implicitly then we can simply invoke through the name of the implementation.
                // Note that in VB, the name of the method implementing the interface method can be different from the interface method name.
                SyntaxNode statement;
                if (disposeMethodOfFieldType.CanBeReferencedByName)
                {
                    statement =
                        generator.ExpressionStatement(
                            generator.InvocationExpression(
                                generator.MemberAccessExpression(
                                    generator.MemberAccessExpression(path, generator.IdentifierName(fieldSymbol.Name)).WithAdditionalAnnotations(Simplifier.Annotation),
                                    generator.IdentifierName(disposeMethodOfFieldType.Name))));
                }
                // Otherwise dispatch through the interface
                else
                {
                    statement =
                        generator.ExpressionStatement(
                            generator.InvocationExpression(
                                generator.MemberAccessExpression(
                                    generator.CastExpression(iDisposableType,
                                                             generator.MemberAccessExpression(path, generator.IdentifierName(fieldSymbol.Name)).WithAdditionalAnnotations(Simplifier.Annotation)),
                                    generator.IdentifierName(DisposableFieldsShouldBeDisposedAnalyzer.Dispose))));
                }

                var body = generator.GetStatements(disposeMethodNode);
                docEditor.SetStatements(disposeMethodNode, body.Concat(ImmutableArray.Create(statement)));
            }).ConfigureAwait(false);

            return(editor.GetChangedDocuments().First());
        }
Beispiel #10
0
        private static async Task <Solution> AddMissingMembers(Document doc, SyntaxNode node, HashSet <string> missingMemberNames, CancellationToken ct)
        {
            var model = await doc.GetSemanticModelAsync(ct).ConfigureAwait(false);

            var entryPointTypeSymbol = (INamedTypeSymbol)model.GetEnclosingSymbol(node.SpanStart, ct);

            // TODO: Convert to use the IOperation tree once IAttributeOperation is available
            var managedTypeSymbolInAttribute = GetManagedTypeInAttributeSyntax(node.GetLocation(), entryPointTypeSymbol);

            bool isLinearCollectionMarshaller = ManualTypeMarshallingHelper.IsLinearCollectionEntryPoint(entryPointTypeSymbol);

            // Explicitly ignore the generic arity mismatch diagnostics as we will only reach here if there are no mismatches.
            // The analyzer will not for missing members if the managed type cannot be resolved.
            ManualTypeMarshallingHelper.TryResolveManagedType(entryPointTypeSymbol, ManualTypeMarshallingHelper.ReplaceGenericPlaceholderInType(managedTypeSymbolInAttribute, entryPointTypeSymbol, model.Compilation), isLinearCollectionMarshaller, IgnoreArityMismatch, out ITypeSymbol managedType);

            SymbolEditor editor = SymbolEditor.Create(doc.Project.Solution);

            INamedTypeSymbol marshallerType = (INamedTypeSymbol)model.GetSymbolInfo(node, ct).Symbol;

            await editor.EditOneDeclarationAsync(marshallerType, (editor, decl) => AddMissingMembers(editor, decl, marshallerType, managedType, missingMemberNames, isLinearCollectionMarshaller), ct).ConfigureAwait(false);

            return(editor.ChangedSolution);
        }
        private static async Task <Document> GenerateConstructorAsync(Document document, SyntaxNode node, INamedTypeSymbol typeSymbol, INamedTypeSymbol notImplementedExceptionType, CancellationToken cancellationToken)
        {
            SymbolEditor editor = SymbolEditor.Create(document);

            await editor.EditOneDeclarationAsync(typeSymbol, node.GetLocation(), (docEditor, declaration) =>
            {
                SyntaxGenerator generator = docEditor.Generator;
                SyntaxNode throwStatement = generator.ThrowStatement(generator.ObjectCreationExpression(generator.TypeExpression(notImplementedExceptionType)));
                SyntaxNode ctorDecl       = generator.ConstructorDeclaration(
                    typeSymbol.Name,
                    new[]
                {
                    generator.ParameterDeclaration("serializationInfo", generator.TypeExpression(docEditor.SemanticModel.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemRuntimeSerializationSerializationInfo))),
                    generator.ParameterDeclaration("streamingContext", generator.TypeExpression(docEditor.SemanticModel.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemRuntimeSerializationStreamingContext)))
                },
                    typeSymbol.IsSealed ? Accessibility.Private : Accessibility.Protected,
                    statements: new[] { throwStatement });

                docEditor.AddMember(declaration, ctorDecl);
            }, cancellationToken).ConfigureAwait(false);

            return(editor.GetChangedDocuments().First());
        }
        private static async Task <Document> FixAsync(CodeFixContext context, CancellationToken cancellationToken)
        {
            var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false);

            var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);

            var generator = SyntaxGenerator.GetGenerator(context.Document);

            SyntaxNode node       = root.FindNode(context.Span);
            Diagnostic diagnostic = context.Diagnostics.First();

            switch (diagnostic.Properties[OperatorOverloadsHaveNamedAlternatesAnalyzer.DiagnosticKindText])
            {
            case OperatorOverloadsHaveNamedAlternatesAnalyzer.AddAlternateText:
                SyntaxNode       methodDeclaration      = generator.GetDeclaration(node, DeclarationKind.Operator) ?? generator.GetDeclaration(node, DeclarationKind.ConversionOperator);
                var              operatorOverloadSymbol = (IMethodSymbol)semanticModel.GetDeclaredSymbol(methodDeclaration, cancellationToken);
                INamedTypeSymbol typeSymbol             = operatorOverloadSymbol.ContainingType;

                // For C# the following `typeDeclarationSyntax` and `typeDeclaration` nodes are identical, but for VB they're different so in
                // an effort to keep this as language-agnostic as possible, the heavy-handed approach is used.
                SyntaxNode typeDeclarationSyntax = await typeSymbol.DeclaringSyntaxReferences.First().GetSyntaxAsync(cancellationToken).ConfigureAwait(false);

                SyntaxNode typeDeclaration = generator.GetDeclaration(typeDeclarationSyntax,
                                                                      typeSymbol.TypeKind == TypeKind.Struct ? DeclarationKind.Struct : DeclarationKind.Class);

                SyntaxNode addedMember;
                IEnumerable <SyntaxNode> bodyStatements = generator.DefaultMethodBody(semanticModel.Compilation);
                if (OperatorOverloadsHaveNamedAlternatesAnalyzer.IsPropertyExpected(operatorOverloadSymbol.Name))
                {
                    // add a property
                    addedMember = generator.PropertyDeclaration(
                        name: OperatorOverloadsHaveNamedAlternatesAnalyzer.IsTrueText,
                        type: generator.TypeExpression(SpecialType.System_Boolean),
                        accessibility: Accessibility.Public,
                        modifiers: DeclarationModifiers.ReadOnly,
                        getAccessorStatements: bodyStatements);
                }
                else
                {
                    // add a method
                    ExpectedMethodSignature?expectedSignature = GetExpectedMethodSignature(operatorOverloadSymbol, semanticModel.Compilation);
                    if (expectedSignature == null)
                    {
                        return(context.Document);
                    }

                    if (expectedSignature.Name == "CompareTo" && operatorOverloadSymbol.ContainingType.TypeKind == TypeKind.Class)
                    {
                        var nullCheck = generator.IfStatement(
                            generator.InvocationExpression(
                                generator.IdentifierName("ReferenceEquals"),
                                generator.IdentifierName(expectedSignature.Parameters.First().name),
                                generator.NullLiteralExpression()),
                            new[]
                        {
                            generator.ReturnStatement(generator.LiteralExpression(1))
                        });

                        bodyStatements = new[] { nullCheck }.Concat(bodyStatements);
                    }

                    addedMember = generator.MethodDeclaration(
                        name: expectedSignature.Name,
                        parameters: expectedSignature.Parameters.Select(p => generator.ParameterDeclaration(p.name, generator.TypeExpression(p.typeSymbol))),
                        returnType: generator.TypeExpression(expectedSignature.ReturnType),
                        accessibility: Accessibility.Public,
                        modifiers: expectedSignature.IsStatic ? DeclarationModifiers.Static : DeclarationModifiers.None,
                        statements: bodyStatements);
                }

                SyntaxNode newTypeDeclaration = generator.AddMembers(typeDeclaration, addedMember);
                return(context.Document.WithSyntaxRoot(root.ReplaceNode(typeDeclaration, newTypeDeclaration)));

            case OperatorOverloadsHaveNamedAlternatesAnalyzer.FixVisibilityText:
                SyntaxNode   badVisibilityNode   = generator.GetDeclaration(node, DeclarationKind.Method) ?? generator.GetDeclaration(node, DeclarationKind.Property);
                ISymbol      badVisibilitySymbol = semanticModel.GetDeclaredSymbol(badVisibilityNode, cancellationToken);
                SymbolEditor symbolEditor        = SymbolEditor.Create(context.Document);
                ISymbol      newSymbol           = await symbolEditor.EditOneDeclarationAsync(badVisibilitySymbol,
                                                                                              (documentEditor, syntaxNode) => documentEditor.SetAccessibility(badVisibilityNode, Accessibility.Public), cancellationToken).ConfigureAwait(false);

                Document   newDocument = symbolEditor.GetChangedDocuments().Single();
                SyntaxNode newRoot     = await newDocument.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

                return(context.Document.WithSyntaxRoot(newRoot));

            default:
                return(context.Document);
            }
        }
        private static async Task <Document> Fix(CodeFixContext context, SyntaxNode root, SyntaxGenerator generator, SemanticModel semanticModel, CancellationToken cancellationToken)
        {
            SyntaxNode node       = root.FindNode(context.Span);
            Diagnostic diagnostic = context.Diagnostics.First();

            switch (diagnostic.Properties[OperatorOverloadsHaveNamedAlternatesAnalyzer.DiagnosticKindText])
            {
            case OperatorOverloadsHaveNamedAlternatesAnalyzer.AddAlternateText:
                SyntaxNode       methodDeclaration      = generator.GetDeclaration(node, DeclarationKind.Operator) ?? generator.GetDeclaration(node, DeclarationKind.ConversionOperator);
                var              operatorOverloadSymbol = (IMethodSymbol)semanticModel.GetDeclaredSymbol(methodDeclaration, cancellationToken);
                INamedTypeSymbol typeSymbol             = operatorOverloadSymbol.ContainingType;

                // For C# the following `typeDeclarationSyntax` and `typeDeclaration` nodes are identical, but for VB they're different so in
                // an effort to keep this as language-agnostic as possible, the heavy-handed approach is used.
                SyntaxNode typeDeclarationSyntax = typeSymbol.DeclaringSyntaxReferences.First().GetSyntax(cancellationToken);
                SyntaxNode typeDeclaration       = generator.GetDeclaration(typeDeclarationSyntax, DeclarationKind.Class);

                SyntaxNode addedMember;
                ImmutableArray <SyntaxNode> bodyStatements = ImmutableArray.Create(
                    generator.ThrowStatement(generator.ObjectCreationExpression(semanticModel.Compilation.GetTypeByMetadataName("System.NotImplementedException"))));
                if (OperatorOverloadsHaveNamedAlternatesAnalyzer.IsPropertyExpected(operatorOverloadSymbol.Name))
                {
                    // add a property
                    addedMember = generator.PropertyDeclaration(
                        name: OperatorOverloadsHaveNamedAlternatesAnalyzer.IsTrueText,
                        type: generator.TypeExpression(SpecialType.System_Boolean),
                        accessibility: Accessibility.Public,
                        modifiers: DeclarationModifiers.ReadOnly,
                        getAccessorStatements: bodyStatements);
                }
                else
                {
                    // add a method
                    ExpectedMethodSignature expectedSignature = GetExpectedMethodSignature(operatorOverloadSymbol, semanticModel.Compilation);
                    addedMember = generator.MethodDeclaration(
                        name: expectedSignature.Name,
                        parameters: expectedSignature.Parameters.Select(p => generator.ParameterDeclaration(p.Item1, generator.TypeExpression(p.Item2))),
                        returnType: generator.TypeExpression(expectedSignature.ReturnType),
                        accessibility: Accessibility.Public,
                        modifiers: expectedSignature.IsStatic ? DeclarationModifiers.Static : DeclarationModifiers.None,
                        statements: bodyStatements);
                }

                SyntaxNode newTypeDeclaration = generator.AddMembers(typeDeclaration, addedMember);
                return(context.Document.WithSyntaxRoot(root.ReplaceNode(typeDeclaration, newTypeDeclaration)));

            case OperatorOverloadsHaveNamedAlternatesAnalyzer.FixVisibilityText:
                SyntaxNode   badVisibilityNode   = generator.GetDeclaration(node, DeclarationKind.Method) ?? generator.GetDeclaration(node, DeclarationKind.Property);
                ISymbol      badVisibilitySymbol = semanticModel.GetDeclaredSymbol(badVisibilityNode, cancellationToken);
                SymbolEditor symbolEditor        = SymbolEditor.Create(context.Document);
                ISymbol      newSymbol           = await symbolEditor.EditOneDeclarationAsync(badVisibilitySymbol,
                                                                                              (documentEditor, syntaxNode) => documentEditor.SetAccessibility(badVisibilityNode, Accessibility.Public));

                Document   newDocument = symbolEditor.GetChangedDocuments().Single();
                SyntaxNode newRoot     = await newDocument.GetSyntaxRootAsync(cancellationToken);

                return(context.Document.WithSyntaxRoot(newRoot));

            default:
                return(context.Document);
            }
        }