File: Semantics\FieldInitializerBindingTests.cs
Web Access
Project: src\src\Compilers\CSharp\Test\Semantic\Microsoft.CodeAnalysis.CSharp.Semantic.UnitTests.csproj (Microsoft.CodeAnalysis.CSharp.Semantic.UnitTests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
#nullable disable
 
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
using Xunit;
 
namespace Microsoft.CodeAnalysis.CSharp.UnitTests
{
    public class FieldInitializerBindingTests : CompilingTestBase
    {
        [Fact]
        public void NoInitializers()
        {
            var source = @"
class C
{
    static int s1;
    int i1;
}";
 
            IEnumerable<ExpectedInitializer> expectedStaticInitializers = null;
            IEnumerable<ExpectedInitializer> expectedInstanceInitializers = null;
 
            CompileAndCheckInitializers(source, expectedInstanceInitializers, expectedStaticInitializers);
        }
 
        [Fact]
        public void ConstantInstanceInitializer()
        {
            var source = @"
class C
{
    static int s1;
    int i1 = 1;
}";
 
            IEnumerable<ExpectedInitializer> expectedStaticInitializers = null;
 
            IEnumerable<ExpectedInitializer> expectedInstanceInitializers = new ExpectedInitializer[]
            {
                new ExpectedInitializer("i1", "1", lineNumber: 4),
            };
 
            CompileAndCheckInitializers(source, expectedInstanceInitializers, expectedStaticInitializers);
        }
 
        [Fact]
        public void ConstantStaticInitializer()
        {
            var source = @"
class C
{
    static int s1 = 1;
    int i1;
}";
 
            IEnumerable<ExpectedInitializer> expectedStaticInitializers = new ExpectedInitializer[]
            {
                new ExpectedInitializer("s1", "1", lineNumber: 3),
            };
 
            IEnumerable<ExpectedInitializer> expectedInstanceInitializers = null;
 
            CompileAndCheckInitializers(source, expectedInstanceInitializers, expectedStaticInitializers);
        }
 
        [Fact]
        public void ExpressionInstanceInitializer()
        {
            var source = @"
class C
{
    static int s1;
    int i1 = 1 + Goo();
 
    static int Goo() { return 1; }
}";
 
            IEnumerable<ExpectedInitializer> expectedStaticInitializers = null;
 
            IEnumerable<ExpectedInitializer> expectedInstanceInitializers = new ExpectedInitializer[]
            {
                new ExpectedInitializer("i1", "1 + Goo()", lineNumber: 4),
            };
 
            CompileAndCheckInitializers(source, expectedInstanceInitializers, expectedStaticInitializers);
        }
 
        [Fact]
        public void ExpressionStaticInitializer()
        {
            var source = @"
class C
{
    static int s1 = 1 + Goo();
    int i1;
 
    static int Goo() { return 1; }
}";
 
            IEnumerable<ExpectedInitializer> expectedStaticInitializers = new ExpectedInitializer[]
            {
                new ExpectedInitializer("s1", "1 + Goo()", lineNumber: 3),
            };
 
            IEnumerable<ExpectedInitializer> expectedInstanceInitializers = null;
 
            CompileAndCheckInitializers(source, expectedInstanceInitializers, expectedStaticInitializers);
        }
 
        [Fact]
        public void InitializerOrder()
        {
            var source = @"
class C
{
    static int s1 = 1;
    static int s2 = 2;
    static int s3 = 3;
    int i1 = 1;
    int i2 = 2;
    int i3 = 3;
}";
 
            IEnumerable<ExpectedInitializer> expectedStaticInitializers = new ExpectedInitializer[]
            {
                new ExpectedInitializer("s1", "1", lineNumber: 3),
                new ExpectedInitializer("s2", "2", lineNumber: 4),
                new ExpectedInitializer("s3", "3", lineNumber: 5),
            };
 
            IEnumerable<ExpectedInitializer> expectedInstanceInitializers = new ExpectedInitializer[]
            {
                new ExpectedInitializer("i1", "1", lineNumber: 6),
                new ExpectedInitializer("i2", "2", lineNumber: 7),
                new ExpectedInitializer("i3", "3", lineNumber: 8),
            };
 
            CompileAndCheckInitializers(source, expectedInstanceInitializers, expectedStaticInitializers);
        }
 
        [Fact]
        public void AllPartialClasses()
        {
            var source = @"
partial class C
{
    static int s1 = 1;
    int i1 = 1;
}
partial class C
{
    static int s2 = 2;
    int i2 = 2;
}";
 
            IEnumerable<ExpectedInitializer> expectedStaticInitializers = new ExpectedInitializer[]
            {
                new ExpectedInitializer("s1", "1", lineNumber: 3),
                new ExpectedInitializer("s2", "2", lineNumber: 8),
            };
 
            IEnumerable<ExpectedInitializer> expectedInstanceInitializers = new ExpectedInitializer[]
            {
                new ExpectedInitializer("i1", "1", lineNumber: 4),
                new ExpectedInitializer("i2", "2", lineNumber: 9),
            };
 
            CompileAndCheckInitializers(source, expectedInstanceInitializers, expectedStaticInitializers);
        }
 
        [Fact]
        public void SomePartialClasses()
        {
            var source = @"
partial class C
{
    static int s1 = 1;
    int i1 = 1;
}
partial class C
{
    static int s2 = 2;
    int i2 = 2;
}
partial class C
{
    static int s3;
    int i3;
}";
 
            IEnumerable<ExpectedInitializer> expectedStaticInitializers = new ExpectedInitializer[]
            {
                new ExpectedInitializer("s1", "1", lineNumber: 3),
                new ExpectedInitializer("s2", "2", lineNumber: 8),
            };
 
            IEnumerable<ExpectedInitializer> expectedInstanceInitializers = new ExpectedInitializer[]
            {
                new ExpectedInitializer("i1", "1", lineNumber: 4),
                new ExpectedInitializer("i2", "2", lineNumber: 9),
            };
 
            CompileAndCheckInitializers(source, expectedInstanceInitializers, expectedStaticInitializers);
        }
 
        [Fact]
        public void Events()
        {
            var source = @"
class C
{
    static event System.Action e = MakeAction(1);
    event System.Action f = MakeAction(2);
 
    static System.Action MakeAction(int x) { return null; }
}}";
 
            IEnumerable<ExpectedInitializer> expectedStaticInitializers = new ExpectedInitializer[]
            {
                new ExpectedInitializer("e", "MakeAction(1)", lineNumber: 3),
            };
 
            IEnumerable<ExpectedInitializer> expectedInstanceInitializers = new ExpectedInitializer[]
            {
                new ExpectedInitializer("f", "MakeAction(2)", lineNumber: 4),
            };
 
            CompileAndCheckInitializers(source, expectedInstanceInitializers, expectedStaticInitializers);
        }
 
        private static void CompileAndCheckInitializers(string source, IEnumerable<ExpectedInitializer> expectedInstanceInitializers, IEnumerable<ExpectedInitializer> expectedStaticInitializers)
        {
            var compilation = CreateCompilation(source);
            var syntaxTree = compilation.SyntaxTrees.First();
            var typeSymbol = (SourceNamedTypeSymbol)compilation.GlobalNamespace.GetMembers("C").Single();
 
            var boundInstanceInitializers = BindInitializersWithoutDiagnostics(typeSymbol, typeSymbol.InstanceInitializers);
            CheckBoundInitializers(expectedInstanceInitializers, syntaxTree, boundInstanceInitializers, isStatic: false);
 
            var boundStaticInitializers = BindInitializersWithoutDiagnostics(typeSymbol, typeSymbol.StaticInitializers);
            CheckBoundInitializers(expectedStaticInitializers, syntaxTree, boundStaticInitializers, isStatic: true);
        }
 
        private static void CheckBoundInitializers(IEnumerable<ExpectedInitializer> expectedInitializers, SyntaxTree syntaxTree, ImmutableArray<BoundInitializer> boundInitializers, bool isStatic)
        {
            if (expectedInitializers == null)
            {
                Assert.Equal(0, boundInitializers.Length);
            }
            else
            {
                Assert.True(!boundInitializers.IsEmpty, "Expected non-null non-empty bound initializers");
 
                int numInitializers = expectedInitializers.Count();
 
                Assert.Equal(numInitializers, boundInitializers.Length);
 
                int i = 0;
                foreach (var expectedInitializer in expectedInitializers)
                {
                    var boundInit = boundInitializers[i++];
                    Assert.Equal(BoundKind.FieldEqualsValue, boundInit.Kind);
 
                    var boundFieldInit = (BoundFieldEqualsValue)boundInit;
 
                    var initValueSyntax = boundFieldInit.Value.Syntax;
                    Assert.Same(initValueSyntax.Parent, boundInit.Syntax);
                    Assert.Equal(expectedInitializer.InitialValue, initValueSyntax.ToFullString());
 
                    var initValueLineNumber = syntaxTree.GetLineSpan(initValueSyntax.Span).StartLinePosition.Line;
                    Assert.Equal(expectedInitializer.LineNumber, initValueLineNumber);
 
                    Assert.Equal(expectedInitializer.FieldName, boundFieldInit.Field.Name);
                }
            }
        }
 
        private static ImmutableArray<BoundInitializer> BindInitializersWithoutDiagnostics(SourceNamedTypeSymbol typeSymbol, ImmutableArray<ImmutableArray<FieldOrPropertyInitializer>> initializers)
        {
            var diagnostics = BindingDiagnosticBag.GetInstance(withDiagnostics: true, withDependencies: false);
            ImportChain unused;
            var boundInitializers = ArrayBuilder<BoundInitializer>.GetInstance();
            Binder.BindRegularCSharpFieldInitializers(
                typeSymbol.DeclaringCompilation,
                initializers,
                boundInitializers,
                diagnostics,
                firstDebugImports: out unused);
            diagnostics.DiagnosticBag.Verify();
            diagnostics.Free();
            return boundInitializers.ToImmutableAndFree();
        }
 
        private class ExpectedInitializer
        {
            public string FieldName { get; }
            public string InitialValue { get; }
            public int LineNumber { get; } //0-indexed
 
            public ExpectedInitializer(string fieldName, string initialValue, int lineNumber)
            {
                this.FieldName = fieldName;
                this.InitialValue = initialValue;
                this.LineNumber = lineNumber;
            }
        }
    }
}