File: Symbols\Source\ExpressionBodiedPropertyTests.cs
Web Access
Project: src\src\Compilers\CSharp\Test\Symbol\Microsoft.CodeAnalysis.CSharp.Symbol.UnitTests.csproj (Microsoft.CodeAnalysis.CSharp.Symbol.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 Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Roslyn.Test.Utilities;
using System;
using Xunit;
using Microsoft.CodeAnalysis.Test.Utilities;
 
namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Symbols.Source
{
    public sealed class ExpressionBodiedPropertyTests : CSharpTestBase
    {
        [Fact(Skip = "973907")]
        public void Syntax01()
        {
            // Language feature enabled by default
            var comp = CreateCompilation(@"
class C
{
    public int P => 1;
}");
            comp.VerifyDiagnostics();
        }
 
        [Fact]
        public void Syntax02()
        {
            var comp = CreateCompilationWithMscorlib461(@"
class C
{
    public int P { get; } => 1;
}");
            comp.VerifyDiagnostics(
    // (4,5): error CS8056: Properties cannot combine accessor lists with expression bodies.
    //     public int P { get; } => 1;
    Diagnostic(ErrorCode.ERR_BlockBodyAndExpressionBody, "public int P { get; } => 1;").WithLocation(4, 5)
    );
        }
 
        [Fact]
        public void Syntax03()
        {
            var comp = CreateCompilation(@"
interface C
{
    int P => 1;
}", parseOptions: TestOptions.Regular7, targetFramework: TargetFramework.NetCoreApp);
            comp.VerifyDiagnostics(
                // (4,14): error CS8652: The feature 'default interface implementation' is not available in C# 7.0. Please use language version 8.0 or greater.
                //     int P => 1;
                Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "1").WithArguments("default interface implementation", "8.0").WithLocation(4, 14)
                );
        }
 
        [Fact]
        public void Syntax04()
        {
            var comp = CreateCompilationWithMscorlib461(@"
abstract class C
{
  public abstract int P => 1;
}");
            comp.VerifyDiagnostics(
    // (4,28): error CS0500: 'C.P.get' cannot declare a body because it is marked abstract
    //   public abstract int P => 1;
    Diagnostic(ErrorCode.ERR_AbstractHasBody, "1").WithArguments("C.P.get").WithLocation(4, 28));
        }
 
        [Fact]
        public void Syntax05()
        {
            var comp = CreateCompilationWithMscorlib461(@"
class C
{
   public abstract int P => 1;
}");
            comp.VerifyDiagnostics(
    // (4,29): error CS0500: 'C.P.get' cannot declare a body because it is marked abstract
    //    public abstract int P => 1;
    Diagnostic(ErrorCode.ERR_AbstractHasBody, "1").WithArguments("C.P.get").WithLocation(4, 29),
    // (4,29): error CS0513: 'C.P.get' is abstract but it is contained in non-abstract type 'C'
    //    public abstract int P => 1;
    Diagnostic(ErrorCode.ERR_AbstractInConcreteClass, "1").WithArguments("C.P.get", "C").WithLocation(4, 29));
        }
 
        [Fact]
        public void Syntax06()
        {
            var comp = CreateCompilationWithMscorlib461(@"
abstract class C
{
   abstract int P => 1;
}");
            comp.VerifyDiagnostics(
    // (4,17): error CS0621: 'C.P': virtual or abstract members cannot be private
    //    abstract int P => 1;
    Diagnostic(ErrorCode.ERR_VirtualPrivate, "P").WithArguments("C.P").WithLocation(4, 17),
    // (4,22): error CS0500: 'C.P.get' cannot declare a body because it is marked abstract
    //    abstract int P => 1;
    Diagnostic(ErrorCode.ERR_AbstractHasBody, "1").WithArguments("C.P.get").WithLocation(4, 22));
        }
 
        [Fact]
        public void Syntax07()
        {
            // The '=' here parses as part of the expression body, not the property
            var comp = CreateCompilationWithMscorlib461(@"
class C
{
    public int P => 1 = 2;
}");
            comp.VerifyDiagnostics(
    // (4,21): error CS0131: The left-hand side of an assignment must be a variable, property or indexer
    //     public int P => 1 = 2;
    Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "1").WithLocation(4, 21));
        }
 
        [Fact]
        public void Syntax08()
        {
            CreateCompilationWithMscorlib461(@"
interface I
{
    int P { get; };
}").VerifyDiagnostics(
    // (4,19): error CS1597: Semicolon after method or accessor block is not valid
    //     int P { get; };
    Diagnostic(ErrorCode.ERR_UnexpectedSemicolon, ";").WithLocation(4, 19));
        }
 
        [Fact]
        public void Syntax09()
        {
            CreateCompilationWithMscorlib461(@"
class C
{
    int P => 2
}").VerifyDiagnostics(
    // (4,15): error CS1002: ; expected
    //     int P => 2
    Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(4, 15));
        }
 
        [Fact]
        public void Syntax10()
        {
            CreateCompilationWithMscorlib461(@"
interface I
{
    int this[int i]
}").VerifyDiagnostics(
    // (4,20): error CS1514: { expected
    //     int this[int i]
    Diagnostic(ErrorCode.ERR_LbraceExpected, "").WithLocation(4, 20),
    // (5,2): error CS1513: } expected
    // }
    Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(5, 2),
    // (4,9): error CS0548: 'I.this[int]': property or indexer must have at least one accessor
    //     int this[int i]
    Diagnostic(ErrorCode.ERR_PropertyWithNoAccessors, "this").WithArguments("I.this[int]").WithLocation(4, 9));
        }
 
        [Fact]
        public void Syntax11()
        {
            CreateCompilationWithMscorlib461(@"
interface I
{
    int this[int i];
}").VerifyDiagnostics(
    // (4,20): error CS1514: { expected
    //     int this[int i];
    Diagnostic(ErrorCode.ERR_LbraceExpected, ";").WithLocation(4, 20),
    // (4,20): error CS1014: A get, set or init accessor expected
    //     int this[int i];
    Diagnostic(ErrorCode.ERR_GetOrSetExpected, ";").WithLocation(4, 20),
    // (5,2): error CS1513: } expected
    // }
    Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(5, 2),
    // (4,9): error CS0548: 'I.this[int]': property or indexer must have at least one accessor
    //     int this[int i];
    Diagnostic(ErrorCode.ERR_PropertyWithNoAccessors, "this").WithArguments("I.this[int]").WithLocation(4, 9));
        }
 
        [Fact]
        public void Syntax12()
        {
            CreateCompilationWithMscorlib461(@"
interface I
{
    int this[int i] { get; };
}").VerifyDiagnostics(
    // (4,29): error CS1597: Semicolon after method or accessor block is not valid
    //     int this[int i] { get; };
    Diagnostic(ErrorCode.ERR_UnexpectedSemicolon, ";").WithLocation(4, 29));
        }
 
        [Fact]
        public void Syntax13()
        {
            // End the property declaration at the semicolon after the accessor list
            CreateCompilationWithMscorlib461(@"
class C
{
    int P { get; set; }; => 2;
}").VerifyDiagnostics(
    // (4,24): error CS1597: Semicolon after method or accessor block is not valid
    //     int P { get; set; }; => 2;
    Diagnostic(ErrorCode.ERR_UnexpectedSemicolon, ";").WithLocation(4, 24),
    // (4,26): error CS1519: Invalid token '=>' in class, record, struct, or interface member declaration
    //     int P { get; set; }; => 2;
    Diagnostic(ErrorCode.ERR_InvalidMemberDecl, "=>").WithArguments("=>").WithLocation(4, 26));
        }
 
        [Fact]
        public void Syntax14()
        {
            CreateCompilationWithMscorlib461(@"
class C
{
    int this[int i] => 2
}").VerifyDiagnostics(
    // (4,25): error CS1002: ; expected
    //     int this[int i] => 2
    Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(4, 25));
        }
 
        [Fact]
        public void LambdaTest01()
        {
            var comp = CreateCompilationWithMscorlib461(@"
using System;
class C
{
    public Func<int, Func<int, int>> P => x => y => x + y;
}");
            comp.VerifyDiagnostics();
        }
 
        [Fact]
        public void SimpleTest()
        {
            var text = @"
class C
{
    public int P => 2 * 2;
    public int this[int i, int j] => i * j * P;
}";
            var comp = CreateCompilationWithMscorlib461(text);
            comp.VerifyDiagnostics();
            var global = comp.GlobalNamespace;
            var c = global.GetTypeMember("C");
 
            var p = c.GetMember<SourcePropertySymbol>("P");
            Assert.Null(p.SetMethod);
            Assert.NotNull(p.GetMethod);
            Assert.False(p.GetMethod.IsImplicitlyDeclared);
            Assert.True(p.IsExpressionBodied);
 
            var indexer = c.GetMember<SourcePropertySymbol>("this[]");
            Assert.Null(indexer.SetMethod);
            Assert.NotNull(indexer.GetMethod);
            Assert.False(indexer.GetMethod.IsImplicitlyDeclared);
            Assert.True(indexer.IsExpressionBodied);
            Assert.True(indexer.IsIndexer);
 
            Assert.Equal(2, indexer.ParameterCount);
            var i = indexer.Parameters[0];
            Assert.Equal(SpecialType.System_Int32, i.Type.SpecialType);
            Assert.Equal("i", i.Name);
            var j = indexer.Parameters[1];
            Assert.Equal(SpecialType.System_Int32, i.Type.SpecialType);
            Assert.Equal("j", j.Name);
        }
 
        [Fact]
        public void Override01()
        {
            var comp = CreateCompilationWithMscorlib461(@"
class B
{
    public virtual int P { get; set; }
}
class C : B
{
    public override int P => 1;
}").VerifyDiagnostics();
        }
 
        [Fact]
        public void Override02()
        {
            CreateCompilationWithMscorlib461(@"
class B
{
    public int P => 10;
    public int this[int i] => i;
}
class C : B
{
    public override int P => 20;
    public override int this[int i] => i * 2;
}").VerifyDiagnostics(
    // (10,25): error CS0506: 'C.this[int]': cannot override inherited member 'B.this[int]' because it is not marked virtual, abstract, or override
    //     public override int this[int i] => i * 2;
    Diagnostic(ErrorCode.ERR_CantOverrideNonVirtual, "this").WithArguments("C.this[int]", "B.this[int]").WithLocation(10, 25),
    // (9,25): error CS0506: 'C.P': cannot override inherited member 'B.P' because it is not marked virtual, abstract, or override
    //     public override int P => 20;
    Diagnostic(ErrorCode.ERR_CantOverrideNonVirtual, "P").WithArguments("C.P", "B.P").WithLocation(9, 25));
        }
 
        [Fact]
        public void Override03()
        {
            CreateCompilationWithMscorlib461(@"
class B
{
    public virtual int P => 10;
    public virtual int this[int i] => i;
}
class C : B
{
    public override int P => 20;
    public override int this[int i] => i * 2;
}").VerifyDiagnostics();
        }
 
        [Fact]
        public void VoidExpression()
        {
            var comp = CreateCompilationWithMscorlib461(@"
class C
{
    public void P => System.Console.WriteLine(""goo"");
}").VerifyDiagnostics(
    // (4,17): error CS0547: 'C.P': property or indexer cannot have void type
    //     public void P => System.Console.WriteLine("goo");
    Diagnostic(ErrorCode.ERR_PropertyCantHaveVoidType, "P").WithArguments("C.P").WithLocation(4, 17));
        }
 
        [Fact]
        public void VoidExpression2()
        {
            var comp = CreateCompilationWithMscorlib461(@"
class C
{
    public int P => System.Console.WriteLine(""goo"");
}").VerifyDiagnostics(
    // (4,21): error CS0029: Cannot implicitly convert type 'void' to 'int'
    //     public int P => System.Console.WriteLine("goo");
    Diagnostic(ErrorCode.ERR_NoImplicitConv, @"System.Console.WriteLine(""goo"")").WithArguments("void", "int").WithLocation(4, 21));
        }
 
        [Fact]
        public void InterfaceImplementation01()
        {
            var comp = CreateCompilationWithMscorlib461(@"
interface I 
{
    int P { get; }
    string Q { get; }
}
internal interface J
{
    string Q { get; }
}
internal interface K
{
    decimal D { get; }
}
class C : I, J, K
{
    public int P => 10;
    string I.Q { get { return ""goo""; } }
    string J.Q { get { return ""bar""; } }
    public decimal D { get { return P; } }
}");
            comp.VerifyDiagnostics();
            var global = comp.GlobalNamespace;
            var i = global.GetTypeMember("I");
            var j = global.GetTypeMember("J");
            var k = global.GetTypeMember("K");
            var c = global.GetTypeMember("C");
 
            var iP = i.GetMember<SourcePropertySymbol>("P");
 
            var prop = c.GetMember<SourcePropertySymbol>("P");
            Assert.True(prop.IsReadOnly);
            var implements = prop.ContainingType.FindImplementationForInterfaceMember(iP);
            Assert.Equal(prop, implements);
 
            prop = (SourcePropertySymbol)c.GetProperty("I.Q");
            Assert.True(prop.IsReadOnly);
            Assert.True(prop.IsExplicitInterfaceImplementation);
 
            prop = (SourcePropertySymbol)c.GetProperty("J.Q");
            Assert.True(prop.IsReadOnly);
            Assert.True(prop.IsExplicitInterfaceImplementation);
 
            prop = c.GetMember<SourcePropertySymbol>("D");
            Assert.True(prop.IsReadOnly);
        }
 
        [ClrOnlyFact]
        public void Emit01()
        {
            var comp = CreateCompilationWithMscorlib461(@"
abstract class A
{
    protected abstract string Z { get; }
}
abstract class B : A
{
    protected sealed override string Z => ""goo"";
    protected abstract string Y { get; }
}    
class C : B
{
    public const int X = 2;
    public static int P => C.X * C.X;
    
    public int Q => X;
    private int R => P * Q;
    protected sealed override string Y => Z + R;
    public int this[int i] => R + i;
 
    public static void Main()
    {
        System.Console.WriteLine(C.X);
        System.Console.WriteLine(C.P);
        var c = new C();
        
        System.Console.WriteLine(c.Q);
        System.Console.WriteLine(c.R);
        System.Console.WriteLine(c.Z);
        System.Console.WriteLine(c.Y);
        System.Console.WriteLine(c[10]);
    }
}", options: TestOptions.ReleaseExe.WithMetadataImportOptions(MetadataImportOptions.Internal));
            var verifier = CompileAndVerify(comp, expectedOutput:
@"2
4
2
8
goo
goo8
18");
        }
 
        [ClrOnlyFact]
        public void AccessorInheritsVisibility()
        {
            var comp = CreateCompilationWithMscorlib461(@"
class C
{
    private int P => 1;
    private int this[int i] => i;
}", options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All));
 
            Action<ModuleSymbol> srcValidator = m =>
            {
                var c = m.GlobalNamespace.GetMember<NamedTypeSymbol>("C");
                var p = c.GetMember<PropertySymbol>("P");
                var indexer = c.Indexers[0];
 
                Assert.Equal(Accessibility.Private, p.DeclaredAccessibility);
                Assert.Equal(Accessibility.Private, indexer.DeclaredAccessibility);
            };
 
            var verifier = CompileAndVerify(comp, sourceSymbolValidator: srcValidator);
        }
 
        [Fact]
        public void StaticIndexer()
        {
            var comp = CreateCompilationWithMscorlib461(@"
class C
{
    static int this[int i] => i;
}");
            comp.VerifyDiagnostics(
    // (4,16): error CS0106: The modifier 'static' is not valid for this item
    //     static int this[int i] => i;
    Diagnostic(ErrorCode.ERR_BadMemberFlag, "this").WithArguments("static").WithLocation(4, 16));
        }
 
        [Fact]
        public void RefReturningExpressionBodiedProperty()
        {
            var comp = CreateCompilationWithMscorlib461(@"
class C
{
    int field = 0;
    public ref int P => ref @field;
}");
            comp.VerifyDiagnostics();
 
            var global = comp.GlobalNamespace;
            var c = global.GetTypeMember("C");
 
            var p = c.GetMember<SourcePropertySymbol>("P");
            Assert.Null(p.SetMethod);
            Assert.NotNull(p.GetMethod);
            Assert.False(p.GetMethod.IsImplicitlyDeclared);
            Assert.True(p.IsExpressionBodied);
            Assert.Equal(RefKind.Ref, p.GetMethod.RefKind);
        }
 
        [Fact]
        [CompilerTrait(CompilerFeature.ReadOnlyReferences)]
        public void RefReadonlyReturningExpressionBodiedProperty()
        {
            var comp = CreateCompilationWithMscorlib461(@"
class C
{
    int field = 0;
    public ref readonly int P => ref @field;
}");
            comp.VerifyDiagnostics();
 
            var global = comp.GlobalNamespace;
            var c = global.GetTypeMember("C");
 
            var p = c.GetMember<SourcePropertySymbol>("P");
            Assert.Null(p.SetMethod);
            Assert.NotNull(p.GetMethod);
            Assert.False(p.GetMethod.IsImplicitlyDeclared);
            Assert.True(p.IsExpressionBodied);
            Assert.Equal(RefKind.RefReadOnly, p.GetMethod.RefKind);
            Assert.False(p.ReturnsByRef);
            Assert.False(p.GetMethod.ReturnsByRef);
            Assert.True(p.ReturnsByRefReadonly);
            Assert.True(p.GetMethod.ReturnsByRefReadonly);
        }
 
        [Fact]
        [CompilerTrait(CompilerFeature.ReadOnlyReferences)]
        public void RefReadonlyReturningExpressionBodiedIndexer()
        {
            var comp = CreateCompilationWithMscorlib461(@"
class C
{
    int field = 0;
    public ref readonly int this[in int arg] => ref @field;
}");
            comp.VerifyDiagnostics();
 
            var global = comp.GlobalNamespace;
            var c = global.GetTypeMember("C");
 
            var p = c.GetMember<SourcePropertySymbol>("this[]");
            Assert.Null(p.SetMethod);
            Assert.NotNull(p.GetMethod);
            Assert.False(p.GetMethod.IsImplicitlyDeclared);
            Assert.True(p.IsExpressionBodied);
            Assert.Equal(RefKind.RefReadOnly, p.GetMethod.RefKind);
            Assert.Equal(RefKind.In, p.GetMethod.Parameters[0].RefKind);
            Assert.False(p.ReturnsByRef);
            Assert.False(p.GetMethod.ReturnsByRef);
            Assert.True(p.ReturnsByRefReadonly);
            Assert.True(p.GetMethod.ReturnsByRefReadonly);
        }
 
        [Fact]
        [CompilerTrait(CompilerFeature.ReadOnlyReferences)]
        public void RefReadonlyReturningExpressionBodiedIndexer1()
        {
            var comp = CreateCompilationWithMscorlib461(@"
class C
{
    int field = 0;
    public ref readonly int this[in int arg] => ref @field;
}");
            comp.VerifyDiagnostics();
 
            var global = comp.GlobalNamespace;
            var c = global.GetTypeMember("C");
 
            var p = c.GetMember<SourcePropertySymbol>("this[]");
            Assert.Null(p.SetMethod);
            Assert.NotNull(p.GetMethod);
            Assert.False(p.GetMethod.IsImplicitlyDeclared);
            Assert.True(p.IsExpressionBodied);
            Assert.Equal(RefKind.RefReadOnly, p.GetMethod.RefKind);
            Assert.Equal(RefKind.In, p.GetMethod.Parameters[0].RefKind);
            Assert.False(p.ReturnsByRef);
            Assert.False(p.GetMethod.ReturnsByRef);
            Assert.True(p.ReturnsByRefReadonly);
            Assert.True(p.GetMethod.ReturnsByRefReadonly);
        }
    }
}