コード例 #1
0
        public static void FieldCtorArgInNested(SearchScope scope)
        {
            var code          = @"
namespace N
{
    using System.IO;

    internal class C
    {
        private StreamReader reader;

        internal C(Stream stream)
        {
            this.reader = new StreamReader(stream);
        }
    }
}";
            var syntaxTree    = CSharpSyntaxTree.ParseText(code);
            var compilation   = CSharpCompilation.Create("test", new[] { syntaxTree }, MetadataReferences.FromAttributes());
            var semanticModel = compilation.GetSemanticModel(syntaxTree);
            var value         = syntaxTree.FindParameter("stream");
            var ctor          = syntaxTree.FindConstructorDeclaration("C(Stream stream)");
            var symbol        = semanticModel.GetDeclaredSymbol(value, CancellationToken.None);

            Assert.AreEqual(true, AssignmentExecutionWalker.FirstWith(symbol, ctor, scope, semanticModel, CancellationToken.None, out var result));
            Assert.AreEqual("this.reader = new StreamReader(stream)", result.ToString());
            using var walker = AssignmentExecutionWalker.With(symbol, ctor, scope, semanticModel, CancellationToken.None);
            Assert.AreEqual("this.reader = new StreamReader(stream)", walker.Assignments.Single().ToString());
        }
コード例 #2
0
            public void FieldCtorArg(Scope scope)
            {
                var testCode      = @"
namespace RoslynSandbox
{
    internal class C
    {
        private readonly int value;

        internal C(int arg)
        {
            this.value = arg;
        }
    }
}";
                var syntaxTree    = CSharpSyntaxTree.ParseText(testCode);
                var compilation   = CSharpCompilation.Create("test", new[] { syntaxTree }, MetadataReferences.FromAttributes());
                var semanticModel = compilation.GetSemanticModel(syntaxTree);
                var value         = syntaxTree.FindParameter("arg");
                var ctor          = syntaxTree.FindConstructorDeclaration("C(int arg)");

                Assert.AreEqual(true, semanticModel.TryGetSymbol(value, CancellationToken.None, out var symbol));
                Assert.AreEqual(true, AssignmentExecutionWalker.FirstWith(symbol, ctor, scope, semanticModel, CancellationToken.None, out AssignmentExpressionSyntax result));
                Assert.AreEqual("this.value = arg", result.ToString());
                using (var walker = AssignmentExecutionWalker.With(symbol, ctor, scope, semanticModel, CancellationToken.None))
                {
                    Assert.AreEqual("this.value = arg", walker.Assignments.Single().ToString());
                }
            }
コード例 #3
0
        public static void GenericFieldTypedCtorArg(SearchScope scope)
        {
            var code          = @"
namespace N
{
    internal class C<T>
    {
        private readonly T value;

        internal C(T arg)
        {
            this.value = arg;
        }

         public static C<int> Create(int n) => new C<int>(n);
    }
}";
            var syntaxTree    = CSharpSyntaxTree.ParseText(code);
            var compilation   = CSharpCompilation.Create("test", new[] { syntaxTree }, MetadataReferences.FromAttributes());
            var semanticModel = compilation.GetSemanticModel(syntaxTree);
            var argument      = syntaxTree.FindArgument("n");

            Assert.AreEqual(true, semanticModel.TryGetSymbol(argument.Parent.Parent, CancellationToken.None, out IMethodSymbol method));
            Assert.AreEqual(true, method.TryFindParameter(argument, out var symbol));
            var ctor = syntaxTree.FindConstructorDeclaration("C(T arg)");

            Assert.AreEqual(true, AssignmentExecutionWalker.FirstWith(symbol, ctor, scope, semanticModel, CancellationToken.None, out var result));
            Assert.AreEqual("this.value = arg", result.ToString());
            using var walker = AssignmentExecutionWalker.With(symbol, ctor, scope, semanticModel, CancellationToken.None);
            Assert.AreEqual("this.value = arg", walker.Assignments.Single().ToString());
        }
コード例 #4
0
            public void FieldCtorArg(Search search)
            {
                var testCode      = @"
namespace RoslynSandbox
{
    internal class Foo
    {
        private readonly int value;

        internal Foo(int arg)
        {
            this.value = arg;
        }
    }
}";
                var syntaxTree    = CSharpSyntaxTree.ParseText(testCode);
                var compilation   = CSharpCompilation.Create("test", new[] { syntaxTree }, MetadataReferences.FromAttributes());
                var semanticModel = compilation.GetSemanticModel(syntaxTree);
                var value         = syntaxTree.FindAssignmentExpression("this.value = arg").Right;
                var ctor          = syntaxTree.FindConstructorDeclaration("Foo(int arg)");
                var arg           = semanticModel.GetSymbolSafe(value, CancellationToken.None);

                Assert.AreEqual(true, AssignmentExecutionWalker.FirstWith(arg, ctor, search, semanticModel, CancellationToken.None, out var result));
                Assert.AreEqual("this.value = arg", result?.ToString());
            }
コード例 #5
0
        public static void FieldPrivateCtorCalledByInitializer(SearchScope scope)
        {
            var code          = @"
namespace N
{
    internal class C
    {
        public static readonly C Default = new C();
        private readonly int value;

        private C()
        {
            this.value = 1;
        }
    }
}";
            var syntaxTree    = CSharpSyntaxTree.ParseText(code);
            var compilation   = CSharpCompilation.Create("test", new[] { syntaxTree }, MetadataReferences.FromAttributes());
            var semanticModel = compilation.GetSemanticModel(syntaxTree);
            var field         = semanticModel.GetDeclaredSymbolSafe(syntaxTree.FindFieldDeclaration("private readonly int value"), CancellationToken.None);
            var bar           = syntaxTree.FindTypeDeclaration("C");

            Assert.AreEqual(true, AssignmentExecutionWalker.FirstFor(field, bar, scope, semanticModel, CancellationToken.None, out var result));
            Assert.AreEqual("this.value = 1", result.ToString());
            Assert.AreEqual(true, AssignmentExecutionWalker.SingleFor(field, bar, scope, semanticModel, CancellationToken.None, out result));
            Assert.AreEqual("this.value = 1", result.ToString());
            using (var walker = AssignmentExecutionWalker.For(field, bar, scope, semanticModel, CancellationToken.None))
            {
                Assert.AreEqual("this.value = 1", walker.Assignments.Single().ToString());
            }
        }
コード例 #6
0
        public static void GenericFieldCtorArg(SearchScope scope)
        {
            var code          = @"
namespace N
{
    internal class C<T>
    {
        private readonly T value;

        internal C(T arg)
        {
            this.value = arg;
        }
    }
}";
            var syntaxTree    = CSharpSyntaxTree.ParseText(code);
            var compilation   = CSharpCompilation.Create("test", new[] { syntaxTree }, Settings.Default.MetadataReferences);
            var semanticModel = compilation.GetSemanticModel(syntaxTree);
            var value         = syntaxTree.FindParameter("arg");
            var ctor          = syntaxTree.FindConstructorDeclaration("C(T arg)");

            Assert.AreEqual(true, semanticModel.TryGetSymbol(value, CancellationToken.None, out var symbol));
            Assert.AreEqual(true, AssignmentExecutionWalker.FirstWith(symbol, ctor, scope, semanticModel, CancellationToken.None, out var result));
            Assert.AreEqual("this.value = arg", result.ToString());
            using var walker = AssignmentExecutionWalker.With(symbol, ctor, scope, semanticModel, CancellationToken.None);
            Assert.AreEqual("this.value = arg", walker.Assignments.Single().ToString());
        }
コード例 #7
0
        public static void FieldWithCtorArg(SearchScope scope)
        {
            var code          = @"
namespace N
{
    internal class C
    {
        private readonly int value;

        internal C(int arg)
        {
            this.value = arg;
        }
    }
}";
            var syntaxTree    = CSharpSyntaxTree.ParseText(code);
            var compilation   = CSharpCompilation.Create("test", new[] { syntaxTree }, MetadataReferences.FromAttributes());
            var semanticModel = compilation.GetSemanticModel(syntaxTree);
            var value         = syntaxTree.FindMemberAccessExpression("this.value");
            var ctor          = syntaxTree.FindConstructorDeclaration("C(int arg)");
            var field         = semanticModel.GetSymbolSafe(value, CancellationToken.None);

            Assert.AreEqual(true, AssignmentExecutionWalker.FirstFor(field, ctor, scope, semanticModel, CancellationToken.None, out var result));
            Assert.AreEqual("this.value = arg", result.ToString());
            Assert.AreEqual(true, AssignmentExecutionWalker.SingleFor(field, ctor, scope, semanticModel, CancellationToken.None, out result));
            Assert.AreEqual("this.value = arg", result.ToString());
            using (var walker = AssignmentExecutionWalker.For(field, ctor, scope, semanticModel, CancellationToken.None))
            {
                Assert.AreEqual("this.value = arg", walker.Assignments.Single().ToString());
            }
        }
コード例 #8
0
        private static bool TryGetAssignedFieldOrProperty(ArgumentSyntax argument, IMethodSymbol method, SemanticModel semanticModel, CancellationToken cancellationToken, out ISymbol member)
        {
            member = null;
            if (method == null)
            {
                return(false);
            }

            if (method.TrySingleDeclaration(cancellationToken, out BaseMethodDeclarationSyntax methodDeclaration) &&
                methodDeclaration.TryFindParameter(argument, out var parameter))
            {
                var parameterSymbol = semanticModel.GetDeclaredSymbolSafe(parameter, cancellationToken);
                if (AssignmentExecutionWalker.FirstWith(parameterSymbol, methodDeclaration.Body, Scope.Member, semanticModel, cancellationToken, out var assignment))
                {
                    member = semanticModel.GetSymbolSafe(assignment.Left, cancellationToken);
                    if (member is IFieldSymbol ||
                        member is IPropertySymbol)
                    {
                        return(true);
                    }
                }

                if (methodDeclaration is ConstructorDeclarationSyntax ctor &&
                    ctor.Initializer is ConstructorInitializerSyntax initializer &&
                    initializer.ArgumentList != null &&
                    initializer.ArgumentList.Arguments.TrySingle(x => x.Expression is IdentifierNameSyntax identifier && identifier.Identifier.ValueText == parameter.Identifier.ValueText, out var chainedArgument))
                {
                    var chained = semanticModel.GetSymbolSafe(ctor.Initializer, cancellationToken);
                    return(TryGetAssignedFieldOrProperty(chainedArgument, chained, semanticModel, cancellationToken, out member));
                }
            }

            return(false);
        }
コード例 #9
0
        public static void FieldCtorArgViaLocal(SearchScope scope)
        {
            var code          = @"
namespace N
{
    internal class C
    {
        private readonly int value;

        internal C(int arg)
        {
            var temp = arg;
            this.value = temp;
        }
    }
}";
            var syntaxTree    = CSharpSyntaxTree.ParseText(code);
            var compilation   = CSharpCompilation.Create("test", new[] { syntaxTree }, MetadataReferences.FromAttributes());
            var semanticModel = compilation.GetSemanticModel(syntaxTree);
            var ctor          = syntaxTree.FindConstructorDeclaration("C(int arg)");
            var symbol        = semanticModel.GetDeclaredSymbol(syntaxTree.FindParameter("int arg"), CancellationToken.None);

            Assert.AreEqual(true, AssignmentExecutionWalker.FirstWith(symbol, ctor, scope, semanticModel, CancellationToken.None, out var result));
            Assert.AreEqual("this.value = temp", result.ToString());
            using var walker = AssignmentExecutionWalker.With(symbol, ctor, scope, semanticModel, CancellationToken.None);
            Assert.AreEqual("this.value = temp", walker.Assignments.Single().ToString());
        }
コード例 #10
0
        private static bool TryGetParameterName(ISymbol member, TypeDeclarationSyntax typeDeclaration, SemanticModel semanticModel, CancellationToken cancellationToken, out string parameterName)
        {
            parameterName = null;
            using (var walker = AssignmentExecutionWalker.For(member, typeDeclaration, Scope.Member, semanticModel, cancellationToken))
            {
                foreach (var assignment in walker.Assignments)
                {
                    if (assignment.Right is IdentifierNameSyntax identifierName &&
                        assignment.TryFirstAncestor <ConstructorDeclarationSyntax>(out var ctor) &&
                        ctor.TryFindParameter(identifierName.Identifier.ValueText, out _))
                    {
                        if (parameterName == null)
                        {
                            parameterName = identifierName.Identifier.ValueText;
                        }
                        else if (parameterName != identifierName.Identifier.ValueText)
                        {
                            parameterName = null;
                            return(false);
                        }
                    }
                }
            }

            return(parameterName != null);
        }
コード例 #11
0
            public void FieldInPropertyExpressionBody(Search search)
            {
                var testCode      = @"
namespace RoslynSandbox
{
    internal class Foo
    {
        private int number;

        internal Foo()
        {
            var i = this.Number;
        }

        public int Number => this.number = 3;
    }
}";
                var syntaxTree    = CSharpSyntaxTree.ParseText(testCode);
                var compilation   = CSharpCompilation.Create("test", new[] { syntaxTree }, MetadataReferences.FromAttributes());
                var semanticModel = compilation.GetSemanticModel(syntaxTree);
                var value         = syntaxTree.FindMemberAccessExpression("this.number");
                var ctor          = syntaxTree.FindConstructorDeclaration("Foo()");
                AssignmentExpressionSyntax result;
                var field = semanticModel.GetSymbolSafe(value, CancellationToken.None);

                if (search == Search.Recursive)
                {
                    Assert.AreEqual(true, AssignmentExecutionWalker.FirstForSymbol(field, ctor, Search.Recursive, semanticModel, CancellationToken.None, out result));
                    Assert.AreEqual("this.number = 3", result?.ToString());
                }
                else
                {
                    Assert.AreEqual(false, AssignmentExecutionWalker.FirstForSymbol(field, ctor, Search.TopLevel, semanticModel, CancellationToken.None, out result));
                }
            }
コード例 #12
0
        internal static bool IsAssignedAndDisposedInSetupAndTearDown(FieldOrProperty fieldOrProperty, TypeDeclarationSyntax scope, SemanticModel semanticModel, CancellationToken cancellationToken)
        {
            if (AssignmentExecutionWalker.SingleFor(fieldOrProperty.Symbol, scope, Scope.Member, semanticModel, cancellationToken, out var assignment) &&
                assignment.FirstAncestor <MethodDeclarationSyntax>() is MethodDeclarationSyntax methodDeclaration)
            {
                if (Attribute.TryFind(methodDeclaration, KnownSymbol.NUnitSetUpAttribute, semanticModel, cancellationToken, out _))
                {
                    if (fieldOrProperty.ContainingType.TryFindFirstMethodRecursive(x => x.GetAttributes().Any(a => a.AttributeClass == KnownSymbol.NUnitTearDownAttribute), out var tearDown))
                    {
                        return(DisposableMember.IsDisposed(fieldOrProperty, tearDown, semanticModel, cancellationToken));
                    }
                }

                if (Attribute.TryFind(methodDeclaration, KnownSymbol.NUnitOneTimeSetUpAttribute, semanticModel, cancellationToken, out _))
                {
                    if (fieldOrProperty.ContainingType.TryFindFirstMethodRecursive(
                            x => x.GetAttributes().Any(a => a.AttributeClass == KnownSymbol.NUnitOneTimeTearDownAttribute),
                            out var tearDown))
                    {
                        return(DisposableMember.IsDisposed(fieldOrProperty, tearDown, semanticModel, cancellationToken));
                    }
                }
            }

            return(false);
        }
コード例 #13
0
 private static bool TryGetParameterName(ISymbol member, TypeDeclarationSyntax typeDeclaration, SemanticModel semanticModel, CancellationToken cancellationToken, [NotNullWhen(true)] out string?parameterName)
 {
     using (var walker = AssignmentExecutionWalker.For(member, typeDeclaration, SearchScope.Member, semanticModel, cancellationToken))
     {
         foreach (var assignment in walker.Assignments)
         {
             if (assignment.Right is IdentifierNameSyntax {
                 Identifier: { } identifier
             } &&
コード例 #14
0
 internal static bool IsAssignedInInitializeAndDisposedInCleanup(FieldOrProperty fieldOrProperty, TypeDeclarationSyntax scope, SemanticModel semanticModel, CancellationToken cancellationToken)
 {
     if (AssignmentExecutionWalker.SingleFor(fieldOrProperty.Symbol, scope, SearchScope.Member, semanticModel, cancellationToken, out var assignment) &&
         assignment.FirstAncestor <MethodDeclarationSyntax>() is { } methodDeclaration)
     {
         var cleanup = TearDown(KnownSymbol.NUnitSetUpAttribute, KnownSymbol.NUnitTearDownAttribute) ??
                       TearDown(KnownSymbol.NUnitOneTimeSetUpAttribute, KnownSymbol.NUnitOneTimeTearDownAttribute) ??
                       TearDown(KnownSymbol.TestInitializeAttribute, KnownSymbol.TestCleanupAttribute) ??
                       TearDown(KnownSymbol.ClassInitializeAttribute, KnownSymbol.ClassCleanupAttribute);
         return(cleanup is { } &&
コード例 #15
0
        internal static bool IsAssignedInSetUp(FieldOrProperty fieldOrProperty, TypeDeclarationSyntax scope, SemanticModel semanticModel, CancellationToken cancellationToken, out AttributeSyntax attribute)
        {
            if (AssignmentExecutionWalker.SingleFor(fieldOrProperty.Symbol, scope, Scope.Member, semanticModel, cancellationToken, out var assignment) &&
                assignment.FirstAncestor <MethodDeclarationSyntax>() is MethodDeclarationSyntax methodDeclaration)
            {
                return(Attribute.TryFind(methodDeclaration, KnownSymbol.NUnitSetUpAttribute, semanticModel, cancellationToken, out attribute) ||
                       Attribute.TryFind(methodDeclaration, KnownSymbol.NUnitOneTimeSetUpAttribute, semanticModel, cancellationToken, out attribute));
            }

            attribute = null;
            return(false);
        }
コード例 #16
0
        public static void FieldWithCtorArgViaProperty(SearchScope scope)
        {
            var code          = @"
namespace N
{
    internal class C
    {
        private int number;

        internal C(int arg)
        {
            this.Number = arg;
        }

        public int Number
        {
            get { return this.number; }
            set { this.number = value; }
        }
    }
}";
            var syntaxTree    = CSharpSyntaxTree.ParseText(code);
            var compilation   = CSharpCompilation.Create("test", new[] { syntaxTree }, MetadataReferences.FromAttributes());
            var semanticModel = compilation.GetSemanticModel(syntaxTree);
            var value         = syntaxTree.FindParameter("arg");
            var ctor          = syntaxTree.FindConstructorDeclaration("C(int arg)");
            var symbol        = semanticModel.GetDeclaredSymbolSafe(value, CancellationToken.None);

            if (scope == SearchScope.Member)
            {
                Assert.AreEqual(true, AssignmentExecutionWalker.FirstWith(symbol, ctor, scope, semanticModel, CancellationToken.None, out var result));
                Assert.AreEqual("this.Number = arg", result.ToString());
                using (var walker = AssignmentExecutionWalker.With(symbol, ctor, scope, semanticModel, CancellationToken.None))
                {
                    Assert.AreEqual("this.Number = arg", string.Join(", ", walker.Assignments));
                }
            }
            else
            {
                Assert.AreEqual(true, AssignmentExecutionWalker.FirstWith(symbol, ctor, scope, semanticModel, CancellationToken.None, out var result));
                Assert.AreEqual("this.number = value", result.ToString());
                using (var walker = AssignmentExecutionWalker.With(symbol, ctor, scope, semanticModel, CancellationToken.None))
                {
                    Assert.AreEqual("this.number = value", string.Join(", ", walker.Assignments));
                }
            }
        }
コード例 #17
0
            public void FieldWithCtorArgViaProperty(Scope scope)
            {
                var testCode      = @"
namespace RoslynSandbox
{
    internal class Foo
    {
        private int number;

        internal Foo(int arg)
        {
            this.Number = arg;
        }

        public int Number
        {
            get { return this.number; }
            set { this.number = value; }
        }
    }
}";
                var syntaxTree    = CSharpSyntaxTree.ParseText(testCode);
                var compilation   = CSharpCompilation.Create("test", new[] { syntaxTree }, MetadataReferences.FromAttributes());
                var semanticModel = compilation.GetSemanticModel(syntaxTree);
                var value         = syntaxTree.FindMemberAccessExpression("this.number");
                var ctor          = syntaxTree.FindConstructorDeclaration("Foo(int arg)");
                AssignmentExpressionSyntax result;
                var field = semanticModel.GetSymbolSafe(value, CancellationToken.None);

                if (scope != Scope.Member)
                {
                    Assert.AreEqual(true, AssignmentExecutionWalker.FirstFor(field, ctor, scope, semanticModel, CancellationToken.None, out result));
                    Assert.AreEqual("this.number = value", result.ToString());
                    Assert.AreEqual(true, AssignmentExecutionWalker.SingleFor(field, ctor, scope, semanticModel, CancellationToken.None, out result));
                    Assert.AreEqual("this.number = value", result.ToString());
                    using (var walker = AssignmentExecutionWalker.For(field, ctor, scope, semanticModel, CancellationToken.None))
                    {
                        Assert.AreEqual("this.number = value", walker.Assignments.Single().ToString());
                    }
                }
                else
                {
                    Assert.AreEqual(false, AssignmentExecutionWalker.FirstFor(field, ctor, scope, semanticModel, CancellationToken.None, out result));
                }
            }
コード例 #18
0
        public static void ChainedCtorArg(SearchScope scope)
        {
            var code          = @"
namespace N
{
    internal class C
    {
        private readonly int value;

        public C(int arg)
            : this(arg, 1)
        {
        }

        internal C(int chainedArg, int _)
        {
            this.value = chainedArg;
        }
    }
}";
            var syntaxTree    = CSharpSyntaxTree.ParseText(code);
            var compilation   = CSharpCompilation.Create("test", new[] { syntaxTree }, MetadataReferences.FromAttributes());
            var semanticModel = compilation.GetSemanticModel(syntaxTree);
            var ctor          = syntaxTree.FindConstructorDeclaration("C(int arg)");
            var symbol        = semanticModel.GetDeclaredSymbolSafe(syntaxTree.FindParameter("arg"), CancellationToken.None);

            if (scope != SearchScope.Member)
            {
                Assert.AreEqual(true, AssignmentExecutionWalker.FirstWith(symbol, ctor, scope, semanticModel, CancellationToken.None, out var result));
                Assert.AreEqual("this.value = chainedArg", result.ToString());
                using (var walker = AssignmentExecutionWalker.With(symbol, ctor, scope, semanticModel, CancellationToken.None))
                {
                    Assert.AreEqual("this.value = chainedArg", walker.Assignments.Single().ToString());
                }
            }
            else
            {
                Assert.AreEqual(false, AssignmentExecutionWalker.FirstWith(symbol, ctor, scope, semanticModel, CancellationToken.None, out _));
                using (var walker = AssignmentExecutionWalker.With(symbol, ctor, scope, semanticModel, CancellationToken.None))
                {
                    Assert.AreEqual(0, walker.Assignments.Count);
                }
            }
        }
コード例 #19
0
        internal static bool IsAssignedToFieldOrProperty(ISymbol symbol, SyntaxNode scope, SemanticModel semanticModel, CancellationToken cancellationToken, PooledSet <ISymbol> visited = null)
        {
            if (AssignmentExecutionWalker.FirstWith(symbol, scope, Scope.Instance, semanticModel, cancellationToken, out var assignment) &&
                semanticModel.TryGetSymbol(assignment.Left, cancellationToken, out ISymbol left))
            {
                if (left.IsEither <IParameterSymbol, ILocalSymbol>())
                {
                    using (visited = visited.IncrementUsage())
                    {
                        return(visited.Add(left) &&
                               IsAssignedToFieldOrProperty(left, scope, semanticModel, cancellationToken, visited));
                    }
                }

                return(left.IsEitherKind(SymbolKind.Field, SymbolKind.Property, SymbolKind.ArrayType));
            }

            return(false);
        }
コード例 #20
0
        internal static bool AssignsSymbolInSetter(IPropertySymbol property, ISymbol symbol, SemanticModel semanticModel, CancellationToken cancellationToken)
        {
            var setMethod = property?.SetMethod;

            if (setMethod == null ||
                setMethod.DeclaringSyntaxReferences.Length == 0)
            {
                return(false);
            }

            if (TryGetSetter(property, cancellationToken, out AccessorDeclarationSyntax setter))
            {
                if (AssignmentExecutionWalker.FirstFor(symbol, setter, Scope.Instance, semanticModel, cancellationToken, out _))
                {
                    return(true);
                }
            }

            return(false);
        }
コード例 #21
0
        public static void FieldInPropertyExpressionBody(SearchScope scope)
        {
            var code          = @"
namespace N
{
    internal class C
    {
        private int number;

        internal C()
        {
            var i = this.Number;
        }

        public int Number => this.number = 3;
    }
}";
            var syntaxTree    = CSharpSyntaxTree.ParseText(code);
            var compilation   = CSharpCompilation.Create("test", new[] { syntaxTree }, MetadataReferences.FromAttributes());
            var semanticModel = compilation.GetSemanticModel(syntaxTree);
            var value         = syntaxTree.FindMemberAccessExpression("this.number");
            var ctor          = syntaxTree.FindConstructorDeclaration("C()");
            AssignmentExpressionSyntax result;
            var field = semanticModel.GetSymbolSafe(value, CancellationToken.None);

            if (scope != SearchScope.Member)
            {
                Assert.AreEqual(true, AssignmentExecutionWalker.FirstFor(field, ctor, scope, semanticModel, CancellationToken.None, out result));
                Assert.AreEqual("this.number = 3", result.ToString());
                Assert.AreEqual(true, AssignmentExecutionWalker.SingleFor(field, ctor, scope, semanticModel, CancellationToken.None, out result));
                Assert.AreEqual("this.number = 3", result.ToString());
                using (var walker = AssignmentExecutionWalker.For(field, ctor, scope, semanticModel, CancellationToken.None))
                {
                    Assert.AreEqual("this.number = 3", walker.Assignments.Single().ToString());
                }
            }
            else
            {
                Assert.AreEqual(false, AssignmentExecutionWalker.FirstFor(field, ctor, scope, semanticModel, CancellationToken.None, out _));
            }
        }
コード例 #22
0
            public void FieldWithCtorArgViaProperty(Search search)
            {
                var testCode      = @"
namespace RoslynSandbox
{
    internal class Foo
    {
        private int number;

        internal Foo(int arg)
        {
            this.Number = arg;
        }

        public int Number
        {
            get { return this.number; }
            set { this.number = value; }
        }
    }
}";
                var syntaxTree    = CSharpSyntaxTree.ParseText(testCode);
                var compilation   = CSharpCompilation.Create("test", new[] { syntaxTree }, MetadataReferences.FromAttributes());
                var semanticModel = compilation.GetSemanticModel(syntaxTree);
                var value         = syntaxTree.FindParameter("arg");
                var ctor          = syntaxTree.FindConstructorDeclaration("Foo(int arg)");
                AssignmentExpressionSyntax result;
                var symbol = semanticModel.GetDeclaredSymbolSafe(value, CancellationToken.None);

                if (search == Search.Recursive)
                {
                    Assert.AreEqual(true, AssignmentExecutionWalker.FirstWith(symbol, ctor, Search.Recursive, semanticModel, CancellationToken.None, out result));
                    Assert.AreEqual("this.Number = arg", result?.ToString());
                }
                else
                {
                    Assert.AreEqual(false, AssignmentExecutionWalker.FirstForSymbol(symbol, ctor, Search.TopLevel, semanticModel, CancellationToken.None, out result));
                }
            }
コード例 #23
0
            public void ChainedCtorArg(Search search)
            {
                var testCode      = @"
namespace RoslynSandbox
{
    internal class Foo
    {
        private readonly int value;

        public Foo(int arg)
            : this(arg, 1)
        {
        }

        internal Foo(int chainedArg, int _)
        {
            this.value = chainedArg;
        }
    }
}";
                var syntaxTree    = CSharpSyntaxTree.ParseText(testCode);
                var compilation   = CSharpCompilation.Create("test", new[] { syntaxTree }, MetadataReferences.FromAttributes());
                var semanticModel = compilation.GetSemanticModel(syntaxTree);
                var value         = syntaxTree.FindParameter("arg");
                var ctor          = syntaxTree.FindConstructorDeclaration("Foo(int arg)");
                AssignmentExpressionSyntax result;
                var symbol = semanticModel.GetDeclaredSymbolSafe(value, CancellationToken.None);

                if (search == Search.Recursive)
                {
                    Assert.AreEqual(true, AssignmentExecutionWalker.FirstWith(symbol, ctor, Search.Recursive, semanticModel, CancellationToken.None, out result));
                    Assert.AreEqual("this.value = chainedArg", result?.ToString());
                }
                else
                {
                    Assert.AreEqual(false, AssignmentExecutionWalker.FirstWith(symbol, ctor, Search.TopLevel, semanticModel, CancellationToken.None, out result));
                }
            }
コード例 #24
0
        internal static Result IsAlreadyAssignedWithCreated(ExpressionSyntax disposable, SemanticModel semanticModel, CancellationToken cancellationToken, out ISymbol assignedSymbol)
        {
            if (!IsPotentiallyAssignableFrom(disposable, semanticModel, cancellationToken))
            {
                assignedSymbol = null;
                return(Result.No);
            }

            var symbol = semanticModel.GetSymbolSafe(disposable, cancellationToken);

            if (symbol is IPropertySymbol property &&
                IsAssignableFrom(property.Type, semanticModel.Compilation) &&
                property.TryGetSetter(cancellationToken, out var setter) &&
                (setter.ExpressionBody != null || setter.Body != null))
            {
                using (var assignedSymbols = PooledSet <ISymbol> .Borrow())
                {
                    using (var pooledAssigned = AssignmentExecutionWalker.Borrow(setter, Scope.Recursive, semanticModel, cancellationToken))
                    {
                        foreach (var assigned in pooledAssigned.Assignments)
                        {
                            if (assigned.Right is IdentifierNameSyntax identifierName &&
                                identifierName.Identifier.ValueText == "value" &&
                                IsPotentiallyAssignableFrom(assigned.Left, semanticModel, cancellationToken) &&
                                semanticModel.GetSymbolSafe(assigned.Left, cancellationToken) is ISymbol candidate &&
                                candidate.IsEitherKind(SymbolKind.Field, SymbolKind.Property))
                            {
                                assignedSymbols.Add(candidate);
                            }
                        }
                    }

                    assignedSymbol = null;
                    var result = Result.No;
                    foreach (var candidate in assignedSymbols)
                    {
                        switch (IsAssignedWithCreated(candidate, disposable, semanticModel, cancellationToken))
                        {
                        case Result.Unknown:
                            if (result == Result.No)
                            {
                                assignedSymbol = candidate;
                                result         = Result.Unknown;
                            }

                            break;

                        case Result.Yes:
                            assignedSymbol = candidate;
                            return(Result.Yes);

                        case Result.AssumeYes:
                            assignedSymbol = candidate;
                            result         = Result.AssumeYes;
                            break;

                        case Result.No:
                        case Result.AssumeNo:
                            break;

                        default:
                            throw new ArgumentOutOfRangeException();
                        }
                    }

                    return(result);
                }
            }

            if (symbol is IParameterSymbol parameter &&
                disposable.TryFirstAncestor <ArrowExpressionClauseSyntax>(out _))
            {
                assignedSymbol = null;
                return(Result.No);
            }

            using (var assignedValues = AssignedValueWalker.Borrow(disposable, semanticModel, cancellationToken))
            {
                assignedSymbol = assignedValues.CurrentSymbol;
                if (assignedValues.Count == 1 &&
                    disposable.Parent is AssignmentExpressionSyntax assignment)
                {
                    if (assignment.Parent is ParenthesizedExpressionSyntax parenthesizedExpression &&
                        parenthesizedExpression.Parent is BinaryExpressionSyntax binary &&
                        binary.IsKind(SyntaxKind.CoalesceExpression))
                    {
                        // lazy
                        return(Result.No);
                    }
                }

                if (symbol.IsEither <IParameterSymbol, ILocalSymbol>())
                {
                    assignedValues.RemoveAll(x => IsReturnedBefore(x));
                }

                using (var recursive = RecursiveValues.Borrow(assignedValues, semanticModel, cancellationToken))
                {
                    return(IsAnyCreation(recursive, semanticModel, cancellationToken));
                }
            }

            bool IsReturnedBefore(ExpressionSyntax expression)
            {
                if (expression.TryFirstAncestor(out BlockSyntax block) &&
                    block.Statements.TryFirstOfType(out ReturnStatementSyntax _))
                {
                    if (expression.TryFirstAncestor <ForEachStatementSyntax>(out _) ||
                        expression.TryFirstAncestor <ForStatementSyntax>(out _) ||
                        expression.TryFirstAncestor <WhileStatementSyntax>(out _))
                    {
                        return(true);
                    }

                    return(!block.Contains(disposable) &&
                           block.SharesAncestor(disposable, out MemberDeclarationSyntax _));
                }

                return(false);
            }
        }
コード例 #25
0
        internal static bool TryGetConversionTypes(ClassDeclarationSyntax classDeclaration, SemanticModel semanticModel, CancellationToken cancellationToken, out ITypeSymbol sourceType, out ITypeSymbol targetType)
        {
            sourceType = null;
            targetType = null;
            if (classDeclaration.TryFindMethod("Convert", out var convertMethod) &&
                convertMethod.ReturnType is PredefinedTypeSyntax returnType &&
                returnType.Keyword.ValueText == "object" &&
                convertMethod.ParameterList != null &&
                convertMethod.ParameterList.Parameters.Count == 4 &&
                convertMethod.ParameterList.Parameters.TryFirst(out var valueParameter))
            {
                using (var returnValues = ReturnValueWalker.Borrow(convertMethod))
                {
                    using (var returnTypes = PooledSet <ITypeSymbol> .Borrow())
                    {
                        foreach (var returnValue in returnValues.ReturnValues)
                        {
                            AddReturnType(returnTypes, returnValue);
                        }

                        return(returnTypes.TrySingle(out targetType) &&
                               ConversionWalker.TryGetCommonBase(
                                   convertMethod,
                                   semanticModel.GetDeclaredSymbolSafe(valueParameter, cancellationToken),
                                   semanticModel,
                                   cancellationToken,
                                   out sourceType));
                    }
                }
            }

            return(false);

            void AddReturnType(PooledSet <ITypeSymbol> returnTypes, ExpressionSyntax returnValue)
            {
                switch (returnValue)
                {
                case LiteralExpressionSyntax literal when literal.IsKind(SyntaxKind.NullLiteralExpression):
                    break;

                case ConditionalExpressionSyntax ternary:
                    AddReturnType(returnTypes, ternary.WhenTrue);
                    AddReturnType(returnTypes, ternary.WhenFalse);
                    break;

                case BinaryExpressionSyntax coalesce when coalesce.IsKind(SyntaxKind.CoalesceExpression):
                    AddReturnType(returnTypes, coalesce.Left);

                    AddReturnType(returnTypes, coalesce.Right);
                    break;

                case IdentifierNameSyntax _:
                case MemberAccessExpressionSyntax _:
                    var type = semanticModel.GetTypeInfoSafe(returnValue, cancellationToken).Type;
                    if (type == KnownSymbol.Object &&
                        semanticModel.GetSymbolSafe(returnValue, cancellationToken) is ISymbol symbol &&
                        symbol.IsEither <IFieldSymbol, IPropertySymbol>())
                    {
                        switch (symbol)
                        {
                        case IFieldSymbol field:
                            if (field.Type == KnownSymbol.Object &&
                                field.DeclaredAccessibility == Accessibility.Private &&
                                returnValue.FirstAncestor <TypeDeclarationSyntax>() is TypeDeclarationSyntax typeDeclaration)
                            {
                                using (var walker = AssignmentExecutionWalker.Borrow(typeDeclaration, Scope.Instance, semanticModel, cancellationToken))
                                {
                                    foreach (var assignment in walker.Assignments)
                                    {
                                        if (semanticModel.TryGetSymbol(assignment.Left, cancellationToken, out IFieldSymbol assigned) &&
                                            FieldSymbolComparer.Equals(assigned, field))
                                        {
                                            returnTypes.Add(semanticModel.GetTypeInfoSafe(assignment.Right, cancellationToken).Type);
                                        }
                                    }
                                }
                            }
                            else
                            {
                                returnTypes.Add(field.Type);
                            }

                            return;

                        case IPropertySymbol property:
                            returnTypes.Add(property.Type);
                            return;
                        }
                    }
                    else
                    {
                        returnTypes.Add(type);
                    }

                    break;
コード例 #26
0
        internal static bool IsValueValidForRegisteredType(ExpressionSyntax value, ITypeSymbol registeredType, SemanticModel semanticModel, CancellationToken cancellationToken, PooledSet <SyntaxNode>?visited = null)
        {
            switch (value)
            {
            case ConditionalExpressionSyntax conditional:
                return(IsValueValidForRegisteredType(conditional.WhenTrue, registeredType, semanticModel, cancellationToken, visited) &&
                       IsValueValidForRegisteredType(conditional.WhenFalse, registeredType, semanticModel, cancellationToken, visited));

            case BinaryExpressionSyntax binary when binary.IsKind(SyntaxKind.CoalesceExpression):
                return(IsValueValidForRegisteredType(binary.Left, registeredType, semanticModel, cancellationToken, visited) &&
                       IsValueValidForRegisteredType(binary.Right, registeredType, semanticModel, cancellationToken, visited));
            }

            if (registeredType.TypeKind == TypeKind.Enum)
            {
                return(semanticModel.TryGetType(value, cancellationToken, out var valueType) &&
                       valueType.MetadataName == registeredType.MetadataName &&
                       Equals(valueType.ContainingType, registeredType.ContainingType) &&
                       NamespaceSymbolComparer.Equals(valueType.ContainingNamespace, registeredType.ContainingNamespace));
            }

            if (semanticModel.IsRepresentationPreservingConversion(value, registeredType))
            {
                return(true);
            }

            if (semanticModel.TryGetSymbol(value, cancellationToken, out var symbol))
            {
                if (symbol is IFieldSymbol field)
                {
                    if (field.TrySingleDeclaration(cancellationToken, out var fieldDeclaration))
                    {
                        if (fieldDeclaration.Declaration is { } variableDeclaration&&
                            variableDeclaration.Variables.TryLast(out var variable) &&
                            variable.Initializer is { } initializer&&
                            !IsValueValidForRegisteredType(initializer.Value, registeredType, semanticModel, cancellationToken, visited))
                        {
                            return(false);
                        }

                        return(IsAssignedValueOfRegisteredType(symbol, fieldDeclaration));
                    }

                    return(field.Type == KnownSymbols.Object);
                }

                if (symbol is IPropertySymbol property)
                {
                    if (property.TrySingleDeclaration(cancellationToken, out PropertyDeclarationSyntax? propertyDeclaration))
                    {
                        if (propertyDeclaration.Initializer is { } initializer&&
                            !IsValueValidForRegisteredType(initializer.Value, registeredType, semanticModel, cancellationToken, visited))
                        {
                            return(false);
                        }

                        if (property.SetMethod == null &&
                            property.GetMethod is { } getMethod)
                        {
                            return(IsReturnValueOfRegisteredType(getMethod));
                        }

                        return(IsAssignedValueOfRegisteredType(symbol, propertyDeclaration));
                    }

                    return(property.Type == KnownSymbols.Object);
                }

                if (symbol is IMethodSymbol method)
                {
                    return(IsReturnValueOfRegisteredType(method));
                }
            }

            return(false);

            bool IsAssignedValueOfRegisteredType(ISymbol memberSymbol, MemberDeclarationSyntax declaration)
            {
                if (declaration.TryFirstAncestor(out TypeDeclarationSyntax? typeDeclaration))
                {
                    using (var walker = AssignmentExecutionWalker.For(memberSymbol, typeDeclaration, SearchScope.Type, semanticModel, cancellationToken))
                    {
                        foreach (var assignment in walker.Assignments)
                        {
                            if (!IsValueValidForRegisteredType(assignment.Right, registeredType, semanticModel, cancellationToken, visited))
                            {
                                return(false);
                            }
                        }
                    }
                }

                return(true);
            }

            bool IsReturnValueOfRegisteredType(IMethodSymbol method)
            {
                if (method.TrySingleMethodDeclaration(cancellationToken, out var target))
                {
#pragma warning disable IDISP003 // Dispose previous before re-assigning.
                    using (visited = visited.IncrementUsage())
#pragma warning restore IDISP003 // Dispose previous before re-assigning.
                    {
                        if (visited.Add(target))
                        {
                            using (var walker = ReturnValueWalker.Borrow(target))
                            {
                                foreach (var returnValue in walker.ReturnValues)
                                {
                                    if (!IsValueValidForRegisteredType(returnValue, registeredType, semanticModel, cancellationToken, visited))
                                    {
                                        return(false);
                                    }
                                }
                            }
                        }

                        return(true);
                    }
                }

                return(method.ReturnType == KnownSymbols.Object);
            }
        }
コード例 #27
0
        public void Recursive()
        {
            var syntaxTree    = CSharpSyntaxTree.ParseText(@"
namespace RoslynSandbox
{
    public class Foo
    {
        private Bar bar1;
        private Bar bar2;

        public Bar Bar1
        {
            get
            {
                return this.bar1;
            }

            set
            {
                if (Equals(value, this.bar1))
                {
                    return;
                }

                if (value != null && this.bar2 != null)
                {
                    this.Bar2 = null;
                }

                if (this.bar1 != null)
                {
                    this.bar1.Selected = false;
                }

                this.bar1 = value;
                if (this.bar1 != null)
                {
                    this.bar1.Selected = true;
                }
            }
        }

        public Bar Bar2
        {
            get
            {
                return this.bar2;
            }

            set
            {
                if (Equals(value, this.bar2))
                {
                    return;
                }

                if (value != null && this.bar1 != null)
                {
                    this.Bar1 = null;
                }

                if (this.bar2 != null)
                {
                    this.bar2.Selected = false;
                }

                this.bar2 = value;
                if (this.bar2 != null)
                {
                    this.bar2.Selected = true;
                }
            }
        }
    }

    public class Bar
    {
        public bool Selected { get; set; }
    }
}");
            var compilation   = CSharpCompilation.Create("test", new[] { syntaxTree }, MetadataReferences.FromAttributes());
            var semanticModel = compilation.GetSemanticModel(syntaxTree);
            var setter        = syntaxTree.FindPropertyDeclaration("Bar2").Find <AccessorDeclarationSyntax>("set");

            using (var assignedValues = AssignmentExecutionWalker.Borrow(setter, Scope.Recursive, semanticModel, CancellationToken.None))
            {
                var actual   = string.Join(", ", assignedValues.Assignments);
                var expected = "this.Bar2 = null, this.Bar1 = null, this.Bar2 = null, this.bar1.Selected = false, this.bar1 = value, this.bar1.Selected = true, this.bar2.Selected = false, this.bar2 = value, this.bar2.Selected = true, this.bar1.Selected = false, this.bar1 = value, this.bar1.Selected = true";
                Assert.AreEqual(expected, actual);
            }
        }
コード例 #28
0
        private static bool IsDefaultValueOfRegisteredType(ExpressionSyntax defaultValue, ITypeSymbol registeredType, SyntaxNodeAnalysisContext context, PooledSet <SyntaxNode> visited = null)
        {
            switch (defaultValue)
            {
            case ConditionalExpressionSyntax conditional:
#pragma warning disable IDISP003 // Dispose previous before re-assigning.
                using (visited = visited.IncrementUsage())
#pragma warning restore IDISP003 // Dispose previous before re-assigning.
                {
                    return(visited.Add(defaultValue) &&
                           IsDefaultValueOfRegisteredType(conditional.WhenTrue, registeredType, context, visited) &&
                           IsDefaultValueOfRegisteredType(conditional.WhenFalse, registeredType, context, visited));
                }

            case BinaryExpressionSyntax binary when binary.IsKind(SyntaxKind.CoalesceExpression):
#pragma warning disable IDISP003 // Dispose previous before re-assigning.
                using (visited = visited.IncrementUsage())
#pragma warning restore IDISP003 // Dispose previous before re-assigning.
                {
                    return(visited.Add(defaultValue) &&
                           IsDefaultValueOfRegisteredType(binary.Left, registeredType, context, visited) &&
                           IsDefaultValueOfRegisteredType(binary.Right, registeredType, context, visited));
                }
            }

            if (context.SemanticModel.IsRepresentationPreservingConversion(defaultValue, registeredType, context.CancellationToken))
            {
                return(true);
            }

            if (context.SemanticModel.TryGetSymbol(defaultValue, context.CancellationToken, out ISymbol symbol))
            {
                if (symbol is IFieldSymbol field)
                {
                    if (field.TrySingleDeclaration(context.CancellationToken, out var fieldDeclaration))
                    {
#pragma warning disable IDISP003 // Dispose previous before re-assigning.
                        using (visited = visited.IncrementUsage())
#pragma warning restore IDISP003 // Dispose previous before re-assigning.
                        {
                            if (fieldDeclaration.Declaration is VariableDeclarationSyntax variableDeclaration &&
                                variableDeclaration.Variables.TryLast(out var variable) &&
                                variable.Initializer is EqualsValueClauseSyntax initializer)
                            {
                                return(visited.Add(initializer.Value) &&
                                       IsDefaultValueOfRegisteredType(initializer.Value, registeredType, context, visited));
                            }

                            return(fieldDeclaration.TryFirstAncestor <TypeDeclarationSyntax>(out var typeDeclaration) &&
                                   AssignmentExecutionWalker.SingleFor(symbol, typeDeclaration, Scope.Instance, context.SemanticModel, context.CancellationToken, out var assignedValue) &&
                                   visited.Add(assignedValue) &&
                                   IsDefaultValueOfRegisteredType(assignedValue, registeredType, context, visited));
                        }
                    }

                    return(field.Type == KnownSymbol.Object);
                }

                if (symbol is IPropertySymbol property)
                {
                    if (property.TrySingleDeclaration(context.CancellationToken, out PropertyDeclarationSyntax propertyDeclaration))
                    {
#pragma warning disable IDISP003 // Dispose previous before re-assigning.
                        using (visited = visited.IncrementUsage())
#pragma warning restore IDISP003 // Dispose previous before re-assigning.
                        {
                            if (propertyDeclaration.Initializer is EqualsValueClauseSyntax initializer)
                            {
                                return(visited.Add(initializer.Value) &&
                                       IsDefaultValueOfRegisteredType(initializer.Value, registeredType, context, visited));
                            }

                            if (property.SetMethod == null &&
                                property.GetMethod is IMethodSymbol getMethod)
                            {
                                return(IsReturnValueOfRegisteredType(getMethod, visited));
                            }

                            return(propertyDeclaration.TryFirstAncestor <TypeDeclarationSyntax>(out var typeDeclaration) &&
                                   AssignmentExecutionWalker.SingleFor(symbol, typeDeclaration, Scope.Instance, context.SemanticModel, context.CancellationToken, out var assignedValue) &&
                                   visited.Add(assignedValue) &&
                                   IsDefaultValueOfRegisteredType(assignedValue, registeredType, context, visited));
                        }
                    }

                    return(property.Type == KnownSymbol.Object);
                }

                if (symbol is IMethodSymbol method)
                {
                    return(IsReturnValueOfRegisteredType(method, visited));
                }
            }

            return(false);

            bool IsReturnValueOfRegisteredType(IMethodSymbol method, PooledSet <SyntaxNode> v)
            {
                if (method.TrySingleMethodDeclaration(context.CancellationToken, out var target))
                {
                    using (var walker = ReturnValueWalker.Borrow(target))
                    {
                        foreach (var returnValue in walker.ReturnValues)
                        {
                            if (!IsDefaultValueOfRegisteredType(returnValue, registeredType, context, v))
                            {
                                return(false);
                            }
                        }

                        return(true);
                    }
                }

                return(method.ReturnType == KnownSymbol.Object);
            }
        }
コード例 #29
0
        public static void Properties()
        {
            var syntaxTree    = CSharpSyntaxTree.ParseText(@"
namespace N
{
    public class C1
    {
        private C bar1;
        private C bar2;

        public C2 P1
        {
            get
            {
                return this.bar1;
            }

            set
            {
                if (Equals(value, this.bar1))
                {
                    return;
                }

                if (value != null && this.bar2 != null)
                {
                    this.P2 = null;
                }

                if (this.bar1 != null)
                {
                    this.bar1.Selected = false;
                }

                this.bar1 = value;
                if (this.bar1 != null)
                {
                    this.bar1.Selected = true;
                }
            }
        }

        public C2 P2
        {
            get
            {
                return this.bar2;
            }

            set
            {
                if (Equals(value, this.bar2))
                {
                    return;
                }

                if (value != null && this.bar1 != null)
                {
                    this.P1 = null;
                }

                if (this.bar2 != null)
                {
                    this.bar2.Selected = false;
                }

                this.bar2 = value;
                if (this.bar2 != null)
                {
                    this.bar2.Selected = true;
                }
            }
        }
    }

    public class C2
    {
        public bool Selected { get; set; }
    }
}");
            var compilation   = CSharpCompilation.Create("test", new[] { syntaxTree }, Settings.Default.MetadataReferences);
            var semanticModel = compilation.GetSemanticModel(syntaxTree);
            var setter        = syntaxTree.FindPropertyDeclaration("P2").Find <AccessorDeclarationSyntax>("set");

            using var assignedValues = AssignmentExecutionWalker.Borrow(setter, SearchScope.Recursive, semanticModel, CancellationToken.None);
            var actual   = string.Join(", ", assignedValues.Assignments);
            var expected = "this.P2 = null, this.P1 = null, this.P2 = null, this.bar1.Selected = false, this.bar1 = value, this.bar1.Selected = true, this.bar2.Selected = false, this.bar2 = value, this.bar2.Selected = true, this.bar1.Selected = false, this.bar1 = value, this.bar1.Selected = true";

            Assert.AreEqual(expected, actual);
        }