예제 #1
0
        /// <inheritdoc/>
        public override async Task RegisterCodeFixesAsync(CodeFixContext context)
        {
            var syntaxRoot = await context.Document.GetSyntaxRootAsync(context.CancellationToken)
                             .ConfigureAwait(false);

            var semanticModel = await context.Document
                                .GetSemanticModelAsync(context.CancellationToken)
                                .ConfigureAwait(false);

            var underscoreFields = CodeStyle.UnderscoreFields(semanticModel);

            foreach (var diagnostic in context.Diagnostics)
            {
                var token = syntaxRoot.FindToken(diagnostic.Location.SourceSpan.Start);
                if (string.IsNullOrEmpty(token.ValueText))
                {
                    continue;
                }

                var argument = syntaxRoot.FindNode(diagnostic.Location.SourceSpan)
                               .FirstAncestorOrSelf <ArgumentSyntax>();
                var type = semanticModel.GetDeclaredSymbolSafe(argument?.FirstAncestorOrSelf <ClassDeclarationSyntax>(), context.CancellationToken);
                if (PropertyChanged.TryGetOnPropertyChanged(type, semanticModel, context.CancellationToken, out var invoker))
                {
                    context.RegisterDocumentEditorFix(
                        "Use overload that does not use expression.",
                        (editor, cancellationToken) => RemoveExpression(editor, argument, invoker, underscoreFields, cancellationToken),
                        this.GetType(),
                        diagnostic);
                }
            }
        }
예제 #2
0
        private static void MakeAutoPropertyNotifyWhenValueChanges(DocumentEditor editor, PropertyDeclarationSyntax propertyDeclaration, IMethodSymbol invoker, SemanticModel semanticModel, CancellationToken cancellationToken)
        {
            if (Property.IsMutableAutoProperty(propertyDeclaration, out var getter, out var setter))
            {
                if (getter.Body != null ||
                    getter.ContainsSkippedText ||
                    setter.Body != null ||
                    setter.ContainsSkippedText)
                {
                    return;
                }

                var underscoreFields = CodeStyle.UnderscoreFields(semanticModel);
                var property         = semanticModel.GetDeclaredSymbolSafe(propertyDeclaration, cancellationToken);
                var backingField     = editor.AddBackingField(propertyDeclaration, underscoreFields, cancellationToken);
                var fieldAccess      = underscoreFields
                    ? backingField.Name()
                    : $"this.{backingField.Name()}";
                var code = StringBuilderPool.Borrow()
                           .AppendLine($"public Type PropertyName")
                           .AppendLine("{")
                           .AppendLine($"    get => {fieldAccess};")
                           .AppendLine()
                           .AppendLine("    set")
                           .AppendLine("    {")
                           .AppendLine($"        if ({Snippet.EqualityCheck(property.Type, "value", fieldAccess, semanticModel)})")
                           .AppendLine("        {")
                           .AppendLine($"           return;")
                           .AppendLine("        }")
                           .AppendLine()
                           .AppendLine($"        {fieldAccess} = value;")
                           .AppendLine($"        {Snippet.OnPropertyChanged(invoker, property.Name, underscoreFields)}")
                           .AppendLine("    }")
                           .AppendLine("}")
                           .Return();
                var template = ParseProperty(code);
                editor.ReplaceNode(
                    getter,
                    x => x.WithExpressionBody(template.Getter().ExpressionBody)
                    .WithTrailingElasticLineFeed()
                    .WithAdditionalAnnotations(Formatter.Annotation));
                editor.ReplaceNode(
                    setter,
                    x => x.WithBody(template.Setter().Body)
                    .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.None))
                    .WithAdditionalAnnotations(Formatter.Annotation));
                if (propertyDeclaration.Initializer != null)
                {
                    editor.ReplaceNode(
                        propertyDeclaration,
                        x => x.WithoutInitializer());
                }

                editor.ReplaceNode(propertyDeclaration, x => x.WithAdditionalAnnotations(Formatter.Annotation));
            }
        }
예제 #3
0
        private static void MakeAutoPropertySet(DocumentEditor editor, PropertyDeclarationSyntax propertyDeclaration, IMethodSymbol setAndRaise, SemanticModel semanticModel, CancellationToken cancellationToken)
        {
            var classDeclaration = propertyDeclaration.FirstAncestorOrSelf <ClassDeclarationSyntax>();

            if (classDeclaration == null)
            {
                return;
            }

            if (Property.IsMutableAutoProperty(propertyDeclaration, out var getter, out var setter))
            {
                if (getter.Body != null ||
                    getter.ContainsSkippedText ||
                    setter.Body != null ||
                    setter.ContainsSkippedText)
                {
                    return;
                }

                var underscoreFields = CodeStyle.UnderscoreFields(semanticModel);
                var backingField     = editor.AddBackingField(propertyDeclaration, underscoreFields, cancellationToken);
                var fieldAccess      = underscoreFields
                    ? backingField.Name()
                    : $"this.{backingField.Name()}";
                var code = StringBuilderPool.Borrow()
                           .AppendLine($"public Type PropertyName")
                           .AppendLine("{")
                           .AppendLine($"    get => {fieldAccess};")
                           .AppendLine($"    set => {(underscoreFields ? string.Empty : "this.")}{setAndRaise.Name}(ref {fieldAccess}, value);")
                           .AppendLine("}")
                           .Return();
                var template = ParseProperty(code);
                editor.ReplaceNode(
                    getter,
                    x => x.WithExpressionBody(template.Getter().ExpressionBody)
                    .WithLeadingLineFeed());

                editor.ReplaceNode(
                    setter,
                    x => x.WithExpressionBody(template.Setter().ExpressionBody)
                    .WithLeadingLineFeed()
                    .WithTrailingLineFeed());

                if (propertyDeclaration.Initializer != null)
                {
                    editor.ReplaceNode(
                        propertyDeclaration,
                        (node, g) => ((PropertyDeclarationSyntax)node).WithoutInitializer());
                }

                editor.ReplaceNode(propertyDeclaration, x => x.WithAdditionalAnnotations(Formatter.Annotation));
            }
        }
예제 #4
0
 private static void ApplyFix(DocumentEditor editor, ArgumentSyntax argument, string name, CancellationToken cancellationToken)
 {
     if (!IsStaticContext(argument, editor.SemanticModel, cancellationToken) &&
         editor.SemanticModel.LookupSymbols(argument.SpanStart, name: name).TrySingle(out var member) &&
         (member is IFieldSymbol || member is IPropertySymbol || member is IMethodSymbol) &&
         !member.IsStatic &&
         !CodeStyle.UnderscoreFields(editor.SemanticModel))
     {
         editor.ReplaceNode(
             argument.Expression,
             (x, _) => SyntaxFactory.ParseExpression($"nameof(this.{name})")
             .WithTriviaFrom(x));
     }
        private static void UseSetAndRaise(DocumentEditor editor, AccessorDeclarationSyntax setter, AssignmentExpressionSyntax assignment, IMethodSymbol setAndRaise)
        {
            var underscoreFields = CodeStyle.UnderscoreFields(editor.SemanticModel);

            editor.ReplaceNode(
                setter,
                x => x.WithBody(null)
                .WithExpressionBody(
                    SyntaxFactory.ArrowExpressionClause(
                        SyntaxFactory.ParseExpression(
                            $"{(underscoreFields ? string.Empty : "this.")}{setAndRaise.Name}(ref {assignment.Left}, value);")))
                .WithTrailingElasticLineFeed()
                .WithAdditionalAnnotations(Formatter.Annotation));
        }
        private static async Task <Document> AddInvokerAsync(Document document, ClassDeclarationSyntax classDeclaration, CancellationToken cancellationToken)
        {
            var editor = await DocumentEditor.CreateAsync(document, cancellationToken)
                         .ConfigureAwait(false);

            var type             = editor.SemanticModel.GetDeclaredSymbolSafe(classDeclaration, cancellationToken);
            var underscoreFields = CodeStyle.UnderscoreFields(editor.SemanticModel);

            if (type.IsSealed)
            {
                editor.AddMethod(
                    classDeclaration,
                    ParseMethod(
                        @"
private void OnPropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string propertyName = null)
{
    this.PropertyChanged?.Invoke(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
}",
                        underscoreFields));
            }
            else if (type.IsStatic)
            {
                editor.AddMethod(
                    classDeclaration,
                    ParseMethod(
                        @"
private static void OnPropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string propertyName = null)
{
    PropertyChanged?.Invoke(null, new PropertyChangedEventArgs(propertyName));
}",
                        underscoreFields));
            }
            else
            {
                editor.AddMethod(
                    classDeclaration,
                    ParseMethod(
                        @"
protected virtual void OnPropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string propertyName = null)
{
    this.PropertyChanged?.Invoke(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
}",
                        underscoreFields));
            }

            return(editor.GetChangedDocument());
        }
예제 #7
0
        private static void MakeWithBackingFieldSet(DocumentEditor editor, PropertyDeclarationSyntax propertyDeclaration, IMethodSymbol setAndRaise, SemanticModel semanticModel)
        {
            var classDeclaration = propertyDeclaration.FirstAncestorOrSelf <ClassDeclarationSyntax>();

            if (classDeclaration == null)
            {
                return;
            }

            if (IsSimpleAssignmentOnly(propertyDeclaration, out _, out _, out var assignment, out var fieldAccess))
            {
                var underscoreFields = CodeStyle.UnderscoreFields(semanticModel);
                var setExpression    = SyntaxFactory.ParseExpression($"{(underscoreFields ? string.Empty : "this.")}{setAndRaise.Name}(ref {fieldAccess}, value);")
                                       .WithTrailingTrivia(SyntaxFactory.ElasticMarker)
                                       .WithSimplifiedNames()
                                       .WithAdditionalAnnotations(Formatter.Annotation);
                editor.ReplaceNode(assignment, setExpression);
            }
        }
예제 #8
0
        private static void MakeWithBackingFieldNotify(DocumentEditor editor, PropertyDeclarationSyntax propertyDeclaration, IMethodSymbol invoker, SemanticModel semanticModel, CancellationToken cancellationToken)
        {
            var classDeclaration = propertyDeclaration.FirstAncestorOrSelf <ClassDeclarationSyntax>();

            if (classDeclaration == null)
            {
                return;
            }

            if (IsSimpleAssignmentOnly(propertyDeclaration, out var setter, out var statement, out var assignment, out _))
            {
                var underscoreFields = CodeStyle.UnderscoreFields(semanticModel);
                var property         = semanticModel.GetDeclaredSymbolSafe(propertyDeclaration, cancellationToken);
                var notifyStatement  = SyntaxFactory
                                       .ParseStatement(Snippet.OnPropertyChanged(invoker, property.Name, underscoreFields))
                                       .WithLeadingTrivia(SyntaxFactory.ElasticMarker)
                                       .WithTrailingTrivia(SyntaxFactory.ElasticMarker)
                                       .WithSimplifiedNames()
                                       .WithAdditionalAnnotations(Formatter.Annotation);
                if (setter.ExpressionBody != null)
                {
                    editor.ReplaceNode(
                        setter,
                        (x, _) =>
                    {
                        var old = (AccessorDeclarationSyntax)x;
                        return(old.WithBody(
                                   SyntaxFactory.Block(
                                       SyntaxFactory.ExpressionStatement(assignment),
                                       notifyStatement))
                               .WithExpressionBody(null)
                               .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.None)));
                    });
                    editor.FormatNode(propertyDeclaration);
                }
                else if (setter.Body != null)
                {
                    editor.InsertAfter(statement, notifyStatement);
                    editor.FormatNode(propertyDeclaration);
                }
            }
        }
        private static void ImplementINotifyPropertyChanged(CodeFixContext context, SemanticModel semanticModel, ClassDeclarationSyntax classDeclaration, DocumentEditor editor)
        {
            var type             = (ITypeSymbol)semanticModel.GetDeclaredSymbol(classDeclaration, context.CancellationToken);
            var underscoreFields = CodeStyle.UnderscoreFields(semanticModel);

            if (!type.Is(KnownSymbol.INotifyPropertyChanged))
            {
                if (classDeclaration.BaseList != null &&
                    classDeclaration.BaseList.Types.TryFirst(
                        x => (x.Type as IdentifierNameSyntax)?.Identifier.ValueText.Contains("INotifyPropertyChanged") == true,
                        out var baseType) &&
                    context.Diagnostics.Any(IsINotifyPropertyChangedMissing))
                {
                    editor.ReplaceNode(baseType, SyntaxFactory.SimpleBaseType(INotifyPropertyChangedType));
                }
                else
                {
                    editor.AddInterfaceType(classDeclaration, INotifyPropertyChangedType);
                }
            }

            if (!type.TryFindEventRecursive("PropertyChanged", out _))
            {
                editor.AddEvent(
                    classDeclaration,
                    (EventFieldDeclarationSyntax)editor.Generator.EventDeclaration(
                        "PropertyChanged",
                        PropertyChangedEventHandlerType,
                        Accessibility.Public));
            }

            if (!type.TryFindFirstMethodRecursive(
                    "OnPropertyChanged",
                    m => m.Parameters.Length == 1 &&
                    m.Parameters[0]
                    .Type == KnownSymbol.String,
                    out _))
            {
                if (type.IsSealed)
                {
                    editor.AddMethod(
                        classDeclaration,
                        ParseMethod(
                            @"private void OnPropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string propertyName = null)
                              {
                                  this.PropertyChanged?.Invoke(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
                              }",
                            underscoreFields));
                }
                else
                {
                    editor.AddMethod(
                        classDeclaration,
                        ParseMethod(
                            @"protected virtual void OnPropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string propertyName = null)
                              {
                                  this.PropertyChanged?.Invoke(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
                              }",
                            underscoreFields));
                }
            }
        }
예제 #10
0
        private static void MakeWithBackingFieldNotifyWhenValueChanges(DocumentEditor editor, PropertyDeclarationSyntax propertyDeclaration, IMethodSymbol invoker, SemanticModel semanticModel, CancellationToken cancellationToken)
        {
            var classDeclaration = propertyDeclaration.FirstAncestorOrSelf <ClassDeclarationSyntax>();

            if (classDeclaration == null)
            {
                return;
            }

            if (propertyDeclaration.TryGetSetter(out var setter))
            {
                if (setter.ExpressionBody != null &&
                    IsSimpleAssignmentOnly(propertyDeclaration, out _, out _, out var assignment, out _))
                {
                    var property         = semanticModel.GetDeclaredSymbolSafe(propertyDeclaration, cancellationToken);
                    var underscoreFields = CodeStyle.UnderscoreFields(semanticModel);
                    var code             = StringBuilderPool.Borrow()
                                           .AppendLine($"public Type PropertyName")
                                           .AppendLine("{")
                                           .AppendLine($"    get => {assignment.Left};")
                                           .AppendLine()
                                           .AppendLine("    set")
                                           .AppendLine("    {")
                                           .AppendLine($"        if ({Snippet.EqualityCheck(property.Type, "value", assignment.Left.ToString(), semanticModel)})")
                                           .AppendLine("        {")
                                           .AppendLine($"           return;")
                                           .AppendLine("        }")
                                           .AppendLine()
                                           .AppendLine($"        {assignment};")
                                           .AppendLine($"        {Snippet.OnPropertyChanged(invoker, property.Name, underscoreFields)}")
                                           .AppendLine("    }")
                                           .AppendLine("}")
                                           .Return();
                    var template = ParseProperty(code);
                    editor.ReplaceNode(
                        setter,
                        (x, _) =>
                    {
                        var old = (AccessorDeclarationSyntax)x;
                        return(old.WithBody(template.Setter().Body)
                               .WithExpressionBody(null)
                               .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.None)));
                    });
                    editor.FormatNode(propertyDeclaration);
                }

                if (setter.Body?.Statements.Count == 1 &&
                    IsSimpleAssignmentOnly(propertyDeclaration, out _, out var statement, out assignment, out _))
                {
                    var property = semanticModel.GetDeclaredSymbolSafe(propertyDeclaration, cancellationToken);
                    var code     = StringBuilderPool.Borrow()
                                   .AppendLine($"        if ({Snippet.EqualityCheck(property.Type, "value", assignment.Left.ToString(), semanticModel)})")
                                   .AppendLine("        {")
                                   .AppendLine($"           return;")
                                   .AppendLine("        }")
                                   .AppendLine()
                                   .Return();
                    var ifStatement = SyntaxFactory.ParseStatement(code)
                                      .WithSimplifiedNames()
                                      .WithLeadingElasticLineFeed()
                                      .WithTrailingElasticLineFeed()
                                      .WithAdditionalAnnotations(Formatter.Annotation);
                    editor.InsertBefore(
                        statement,
                        ifStatement);
                    var underscoreFields = CodeStyle.UnderscoreFields(semanticModel);
                    var notifyStatement  = SyntaxFactory
                                           .ParseStatement(
                        Snippet.OnPropertyChanged(invoker, property.Name, underscoreFields))
                                           .WithSimplifiedNames()
                                           .WithLeadingElasticLineFeed()
                                           .WithTrailingElasticLineFeed()
                                           .WithAdditionalAnnotations(Formatter.Annotation);
                    editor.InsertAfter(statement, notifyStatement);
                    editor.FormatNode(propertyDeclaration);
                }
            }
        }
예제 #11
0
        /// <inheritdoc/>
        public override async Task RegisterCodeFixesAsync(CodeFixContext context)
        {
            var syntaxRoot = await context.Document.GetSyntaxRootAsync(context.CancellationToken)
                             .ConfigureAwait(false);

            var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken)
                                .ConfigureAwait(false);

            var underscoreFields = CodeStyle.UnderscoreFields(semanticModel);

            foreach (var diagnostic in context.Diagnostics)
            {
                if (diagnostic.Properties.TryGetValue(INPC003NotifyWhenPropertyChanges.PropertyNameKey, out var property))
                {
                    var expression = syntaxRoot.FindNode(diagnostic.Location.SourceSpan)
                                     .FirstAncestorOrSelf <ExpressionSyntax>();
                    var typeDeclaration = expression.FirstAncestorOrSelf <TypeDeclarationSyntax>();
                    var type            = semanticModel.GetDeclaredSymbolSafe(typeDeclaration, context.CancellationToken);
                    if (PropertyChanged.TryGetOnPropertyChanged(type, semanticModel, context.CancellationToken, out var invoker) &&
                        invoker.Parameters[0].Type == KnownSymbol.String)
                    {
                        var invocation = expression.FirstAncestorOrSelf <InvocationExpressionSyntax>();
                        var method     = (IMethodSymbol)semanticModel.GetSymbolSafe(invocation, context.CancellationToken);
                        if (PropertyChanged.IsSetAndRaise(method, semanticModel, context.CancellationToken) != AnalysisResult.No)
                        {
                            if (invocation.Parent is ExpressionStatementSyntax ||
                                invocation.Parent is ArrowExpressionClauseSyntax)
                            {
                                context.RegisterDocumentEditorFix(
                                    $"Notify that property {property} changes.",
                                    (editor, cancellationToken) => MakeNotifyCreateIf(
                                        editor,
                                        invocation,
                                        property,
                                        invoker,
                                        underscoreFields),
                                    this.GetType(),
                                    diagnostic);
                                continue;
                            }

                            if (invocation.Parent is IfStatementSyntax ifStatement)
                            {
                                context.RegisterDocumentEditorFix(
                                    $"Notify that property {property} changes.",
                                    (editor, _) => MakeNotifyInIf(
                                        editor,
                                        ifStatement,
                                        property,
                                        invoker,
                                        underscoreFields),
                                    this.GetType(),
                                    diagnostic);
                                continue;
                            }

                            if (invocation.Parent is PrefixUnaryExpressionSyntax unary &&
                                unary.IsKind(SyntaxKind.LogicalNotExpression) &&
                                unary.Parent is IfStatementSyntax ifStatement2 &&
                                ifStatement2.IsReturnOnly())
                            {
                                context.RegisterDocumentEditorFix(
                                    $"Notify that property {property} changes.",
                                    (editor, _) => MakeNotify(
                                        editor,
                                        expression,
                                        property,
                                        invoker,
                                        underscoreFields),
                                    this.GetType(),
                                    diagnostic);
                                continue;
                            }
                        }

                        context.RegisterDocumentEditorFix(
                            $"Notify that property {property} changes.",
                            (editor, _) => MakeNotify(
                                editor,
                                expression,
                                property,
                                invoker,
                                underscoreFields),
                            this.GetType(),
                            diagnostic);
                    }
                }
            }
        }