예제 #1
0
        public static void ForWhenLocal()
        {
            var syntaxTree    = CSharpSyntaxTree.ParseText(@"
namespace N
{
    public class C
    {
        public C()
        {
            var i = 0;
            i = 1;
        }
    }
}");
            var compilation   = CSharpCompilation.Create("test", new[] { syntaxTree }, MetadataReferences.FromAttributes());
            var semanticModel = compilation.GetSemanticModel(syntaxTree);

            Assert.AreEqual(true, semanticModel.TryGetSymbol(syntaxTree.FindVariableDeclaration("i"), CancellationToken.None, out ILocalSymbol local));
            using (var walker = IdentifierNameWalker.For(local, semanticModel, CancellationToken.None))
            {
                CollectionAssert.AreEqual(new[] { "i" }, walker.IdentifierNames.Select(x => x.Identifier.ValueText));
                Assert.AreEqual(true, walker.TryFind("i", out var match));
                Assert.AreEqual("i", match.Identifier.ValueText);
                Assert.AreEqual(false, walker.TryFind("missing", out _));
            }
        }
예제 #2
0
        public static void TryFindWhenProperty()
        {
            var syntaxTree = CSharpSyntaxTree.ParseText(@"
namespace N
{
    public class C
    {
        public int P { get; }

        public void M()
        {
            var p = this.P;
        }
    }
}");
            var node       = syntaxTree.FindMethodDeclaration("M");

            using (var walker = IdentifierNameWalker.Borrow(node))
            {
                CollectionAssert.AreEqual(new[] { "var", "P" }, walker.IdentifierNames.Select(x => x.Identifier.ValueText));

                Assert.AreEqual(true, walker.TryFind("P", out var match));
                Assert.AreEqual("P", match.Identifier.ValueText);

                Assert.AreEqual(false, walker.TryFind("missing", out _));
            }
        }
예제 #3
0
        private static bool UsesValueAndMember(IfStatementSyntax ifStatement, SemanticModel semanticModel, CancellationToken cancellationToken, IParameterSymbol value, ISymbol member)
        {
            var usesValue  = false;
            var usesMember = false;

            using (var pooledIdentifierNames = IdentifierNameWalker.Create(ifStatement.Condition))
            {
                foreach (var identifierName in pooledIdentifierNames.Item.IdentifierNames)
                {
                    var symbol = semanticModel.GetSymbolSafe(identifierName, cancellationToken);
                    if (symbol == null)
                    {
                        continue;
                    }

                    if (symbol.Equals(value))
                    {
                        usesValue = true;
                    }

                    if (symbol.Equals(member))
                    {
                        usesMember = true;
                    }
                }
            }

            return(usesMember && usesValue);
        }
예제 #4
0
        public static void TryFindLast()
        {
            var syntaxTree    = CSharpSyntaxTree.ParseText(@"
namespace N
{
    public class C
    {
        public C(int i)
        {
            i = 1;
            i = 2;
        }
    }
}");
            var compilation   = CSharpCompilation.Create("test", new[] { syntaxTree }, MetadataReferences.FromAttributes());
            var semanticModel = compilation.GetSemanticModel(syntaxTree);
            var symbol        = semanticModel.GetDeclaredSymbolSafe(syntaxTree.FindParameter("int i"), CancellationToken.None);
            var node          = syntaxTree.FindTypeDeclaration("C");

            Assert.AreEqual(true, IdentifierNameWalker.TryFindLast(node, symbol, semanticModel, CancellationToken.None, out var match));
            Assert.AreEqual("i = 2", match.Parent.ToString());

            using (var walker = IdentifierNameWalker.Borrow(node))
            {
                Assert.AreEqual(true, walker.TryFindLast(symbol, semanticModel, CancellationToken.None, out match));
                Assert.AreEqual("i = 2", match.Parent.ToString());
            }
        }
예제 #5
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);

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

                var ctor       = syntaxRoot.FindNode(diagnostic.Location.SourceSpan).FirstAncestorOrSelf <ConstructorDeclarationSyntax>();
                var invocation = syntaxRoot.FindNode(diagnostic.Location.SourceSpan).FirstAncestorOrSelf <InvocationExpressionSyntax>();

                if (ctor != null &&
                    invocation != null &&
                    invocation.Expression is MemberAccessExpressionSyntax memberAccess)
                {
                    var condition = memberAccess.Expression;
                    var symbol    = semanticModel.GetSymbolSafe(condition, context.CancellationToken);
                    if (symbol is IParameterSymbol parameter)
                    {
                        using (var pooled = IdentifierNameWalker.Create(ctor))
                        {
                            foreach (var name in pooled.Item.IdentifierNames)
                            {
                                if (name.Identifier.ValueText == parameter.Name)
                                {
                                    if (!condition.Contains(name))
                                    {
                                        return;
                                    }
                                }
                            }
                        }

                        if (ctor.ParameterList.Parameters.TryGetFirst(p => p.Identifier.ValueText == parameter.Name, out ParameterSyntax parameterSyntax))
                        {
                            context.RegisterCodeFix(
                                CodeAction.Create(
                                    "Inject negated.",
                                    cancellationToken => ApplyInjectNegatedFixAsync(cancellationToken, context, parameterSyntax, invocation),
                                    nameof(InjectNegatedCodeFix)),
                                diagnostic);
                            continue;
                        }
                    }
                }
            }
        }
        private static bool IsInitializedWith(PropertyDeclarationSyntax x, PropertyDeclarationSyntax y)
        {
            if (y.Modifiers.Any(SyntaxKind.StaticKeyword) &&
                x.Initializer is { Value : { } value } initializer&&
                !(value is LiteralExpressionSyntax))
            {
                using var walker = IdentifierNameWalker.Borrow(initializer);
                foreach (var identifierName in walker.IdentifierNames)
                {
                    if (y.Identifier.ValueText == identifierName.Identifier.ValueText)
                    {
                        return(true);
                    }
                }
            }

            return(false);
        }
예제 #7
0
        private static bool IsInitializedWith(FieldDeclarationSyntax x, FieldDeclarationSyntax y)
        {
            if (y.Modifiers.Any(SyntaxKind.ConstKeyword, SyntaxKind.StaticKeyword) &&
                x.Declaration.Variables.TryLast(out var variable) &&
                variable.Initializer is { Value : { } value } initializer&&
                !(value is LiteralExpressionSyntax))
            {
                using (var walker = IdentifierNameWalker.Borrow(initializer))
                {
                    foreach (var identifierName in walker.IdentifierNames)
                    {
                        if (y.Declaration.TryFindVariable(identifierName.Identifier.ValueText, out _))
                        {
                            return(true);
                        }
                    }
                }
            }

            return(false);
        }
예제 #8
0
        public static void TryFindWhenParameter()
        {
            var syntaxTree = CSharpSyntaxTree.ParseText(@"
namespace N
{
    public class C
    {
        public C(int i)
        {
            i = 1;
        }
    }
}");
            var node       = syntaxTree.FindTypeDeclaration("C");

            using var walker = IdentifierNameWalker.Borrow(node);
            CollectionAssert.AreEqual(new[] { "i" }, walker.IdentifierNames.Select(x => x.Identifier.ValueText));
            Assert.AreEqual(true, walker.TryFind("i", out var match));
            Assert.AreEqual("i", match.Identifier.ValueText);
            Assert.AreEqual(false, walker.TryFind("missing", out _));
        }
        public static void ForWhenParameter()
        {
            var syntaxTree    = CSharpSyntaxTree.ParseText(@"
namespace N
{
    public class C
    {
        public C(int i)
        {
            i = 1;
        }
    }
}");
            var compilation   = CSharpCompilation.Create("test", new[] { syntaxTree }, Settings.Default.MetadataReferences);
            var semanticModel = compilation.GetSemanticModel(syntaxTree);

            Assert.AreEqual(true, semanticModel.TryGetSymbol(syntaxTree.FindParameter("i"), CancellationToken.None, out var parameter));
            using var walker = IdentifierNameWalker.For(parameter, semanticModel, CancellationToken.None);
            CollectionAssert.AreEqual(new[] { "i" }, walker.IdentifierNames.Select(x => x.Identifier.ValueText));
            Assert.AreEqual(true, walker.TryFind("i", out var match));
            Assert.AreEqual("i", match.Identifier.ValueText);
            Assert.AreEqual(false, walker.TryFind("missing", out _));
        }
        /// <inheritdoc/>
        public override async Task RegisterCodeFixesAsync(CodeFixContext context)
        {
            var syntaxRoot = await context.Document.GetSyntaxRootAsync(context.CancellationToken)
                             .ConfigureAwait(false);

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

                var assignment = syntaxRoot.FindNode(diagnostic.Location.SourceSpan)
                                 .FirstAncestorOrSelf <AssignmentExpressionSyntax>();
                if (assignment == null ||
                    assignment.Left == null ||
                    assignment.Right == null)
                {
                    continue;
                }

                if (assignment.Right is ParenthesizedLambdaExpressionSyntax lambda &&
                    lambda.Body != null)
                {
                    using (var pooled = IdentifierNameWalker.Create(lambda.Body))
                    {
                        var usesArg = false;
                        foreach (var name in pooled.Item.IdentifierNames)
                        {
                            if (name.Identifier.ValueText == lambda.ParameterList.Parameters[0]
                                .Identifier.ValueText)
                            {
                                return;
                            }

                            if (name.Identifier.ValueText == lambda.ParameterList.Parameters[1]
                                .Identifier.ValueText)
                            {
                                usesArg = true;
                            }
                        }

                        context.RegisterCodeFix(
                            CodeAction.Create(
                                "Observe.Event",
                                cancellationToken => ApplyObserveEventLamdaFixAsync(
                                    cancellationToken, context, assignment, usesArg),
                                nameof(EventSubscriptionToObserveFix)),
                            diagnostic);
                    }
                }

                if (assignment.Right is MemberAccessExpressionSyntax memberAccess)
                {
                    var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false);

                    if (semanticModel.GetSymbolSafe(memberAccess, context.CancellationToken) is IMethodSymbol method &&
                        method.DeclaredAccessibility == Accessibility.Private &&
                        method.DeclaringSyntaxReferences.Length == 1)
                    {
                        var methodDeclaration = (MethodDeclarationSyntax)method.DeclaringSyntaxReferences[0].GetSyntax(context.CancellationToken);
                        context.RegisterCodeFix(
                            CodeAction.Create(
                                "Observe.Event",
                                cancellationToken => ApplyObserveEventMethodFixAsync(cancellationToken, context, assignment, methodDeclaration),
                                nameof(EventSubscriptionToObserveFix)),
                            diagnostic);
                    }
                }
            }
        }
        private static async Task <Document> ApplyObserveEventMethodFixAsync(
            CancellationToken cancellationToken,
            CodeFixContext context,
            AssignmentExpressionSyntax assignment,
            MethodDeclarationSyntax methodDeclaration)
        {
            var usesArg = false;

            if (methodDeclaration.ParameterList.Parameters.Any())
            {
                using (var pooled = IdentifierNameWalker.Create((SyntaxNode)methodDeclaration.Body ?? methodDeclaration.ExpressionBody))
                {
                    foreach (var name in pooled.Item.IdentifierNames)
                    {
                        if (name.Identifier.ValueText == methodDeclaration.ParameterList.Parameters[0].Identifier.ValueText)
                        {
                            if (methodDeclaration.ParameterList.Parameters.Count == 1)
                            {
                                usesArg = true;
                                continue;
                            }

                            return(context.Document);
                        }

                        if (methodDeclaration.ParameterList.Parameters.Count == 2 &&
                            name.Identifier.ValueText == methodDeclaration.ParameterList.Parameters[1]
                            .Identifier.ValueText)
                        {
                            usesArg = true;
                        }
                    }
                }
            }

            var editor = await DocumentEditor.CreateAsync(context.Document, cancellationToken)
                         .ConfigureAwait(false);

            var eventSymbol      = (IEventSymbol)editor.SemanticModel.GetSymbolSafe(assignment.Left, cancellationToken);
            var observeSubscribe = GetObservableFromEventString(eventSymbol)
                                   .Replace("HANDLERTYPE", eventSymbol.Type.ToDisplayString())
                                   .Replace("ARGTYPE", ArgType(eventSymbol))
                                   .Replace("LEFT", assignment.Left.ToString())
                                   .Replace("LAMBDA", Lambda(methodDeclaration, usesArg));

            editor.ReplaceNode(
                assignment,
                SyntaxFactory.ParseExpression(observeSubscribe)
                .WithLeadingTrivia(assignment.GetLeadingTrivia())
                .WithAdditionalAnnotations(Simplifier.Annotation));
            if (methodDeclaration.ParameterList.Parameters.Count == 2)
            {
                editor.RemoveNode(methodDeclaration.ParameterList.Parameters[0]);
            }

            if (!usesArg &&
                methodDeclaration.ParameterList.Parameters.Any())
            {
                editor.RemoveNode(methodDeclaration.ParameterList.Parameters.Last());
            }

            return(editor.GetChangedDocument());
        }
        private static void HandleAssignment(SyntaxNodeAnalysisContext context)
        {
            var assignment = (AssignmentExpressionSyntax)context.Node;

            if (assignment?.IsMissing != false ||
                assignment.FirstAncestorOrSelf <ConstructorDeclarationSyntax>() != null ||
                assignment.FirstAncestorOrSelf <InitializerExpressionSyntax>() != null)
            {
                return;
            }

            var block = assignment.FirstAncestorOrSelf <BlockSyntax>();

            if (block == null)
            {
                return;
            }

            var typeDeclaration = assignment.FirstAncestorOrSelf <TypeDeclarationSyntax>();
            var typeSymbol      = context.SemanticModel.GetDeclaredSymbolSafe(typeDeclaration, context.CancellationToken);

            if (!typeSymbol.Is(KnownSymbol.INotifyPropertyChanged))
            {
                return;
            }

            var field = context.SemanticModel.GetSymbolSafe(assignment.Left, context.CancellationToken) as IFieldSymbol;

            if (field == null || !typeSymbol.Equals(field.ContainingType))
            {
                return;
            }

            foreach (var member in typeDeclaration.Members)
            {
                var propertyDeclaration = member as PropertyDeclarationSyntax;
                if (propertyDeclaration == null)
                {
                    continue;
                }

                var property = context.SemanticModel.GetDeclaredSymbolSafe(propertyDeclaration, context.CancellationToken);
                var getter   = Getter(propertyDeclaration);
                if (getter == null || property == null || property.DeclaredAccessibility != Accessibility.Public)
                {
                    continue;
                }

                using (var pooled = IdentifierNameWalker.Create(getter))
                {
                    foreach (var identifierName in pooled.Item.IdentifierNames)
                    {
                        var component      = context.SemanticModel.GetSymbolSafe(identifierName, context.CancellationToken);
                        var componentField = component as IFieldSymbol;
                        if (componentField == null)
                        {
                            var propertySymbol = component as IPropertySymbol;
                            if (propertySymbol == null)
                            {
                                continue;
                            }

                            if (!Property.TryGetBackingField(propertySymbol, context.SemanticModel, context.CancellationToken, out componentField))
                            {
                                continue;
                            }
                        }

                        if (!field.Equals(componentField))
                        {
                            continue;
                        }

                        if (PropertyChanged.InvokesPropertyChangedFor(assignment, property, context.SemanticModel, context.CancellationToken) == PropertyChanged.InvokesPropertyChanged.No)
                        {
                            var properties = ImmutableDictionary.CreateRange(new[] { new KeyValuePair <string, string>(PropertyNameKey, property.Name), });
                            context.ReportDiagnostic(Diagnostic.Create(Descriptor, assignment.GetLocation(), properties, property.Name));
                        }
                    }
                }
            }
        }