/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/CSharp/Test/Semantic/Semantics/ReadOnlyStructsTests.cs
2 408 строк
78 KB
Jared Parsons
Move net451 test assets to net461 (#74766)
17 авг 2024, 19:21
Не верифицирован
17 авг 2024, 19:21
75b26a2
Код
Авторство
О чём код?
// 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.Linq; using Microsoft.CodeAnalysis.CSharp.Symbols.Retargeting; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Semantics { [CompilerTrait(CompilerFeature.ReadOnlyReferences)] public class ReadOnlyStructsTests : CompilingTestBase { [Fact()] public void WriteableInstanceAutoPropsInRoStructs() { var text = @" public readonly struct A { // ok - no state int ro => 5; // ok - ro state int ro1 {get;} // error int rw {get; set;} // ok - static static int rws {get; set;} } "; CreateCompilation(text).VerifyDiagnostics( // (11,9): error CS8341: Auto-implemented instance properties in readonly structs must be readonly. // int rw {get; set;} Diagnostic(ErrorCode.ERR_AutoPropsInRoStruct, "rw").WithLocation(11, 9) ); } [Fact()] public void WriteableInstanceFieldsInRoStructs() { var text = @" public readonly struct A { // ok public static int s; // ok public readonly int ro; // error int x; void AssignField() { // error this = default; // error this.x = 1; A a = default; // OK a.x = 2; } } "; CreateCompilation(text).VerifyDiagnostics( // (11,9): error CS8340: Instance fields of readonly structs must be readonly. // int x; Diagnostic(ErrorCode.ERR_FieldsInRoStruct, "x").WithLocation(11, 9), // (16,9): error CS1604: Cannot assign to 'this' because it is read-only // this = default; Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "this").WithArguments("this").WithLocation(16, 9), // (18,9): error CS1604: Cannot assign to 'this.x' because it is read-only // this.x = 1; Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "this.x").WithArguments("this.x").WithLocation(18, 9)); } [Fact()] public void EventsInRoStructs() { var text = @" using System; public readonly struct A : I1 { //error public event System.Action e; //error public event Action ei1; //ok public static event Action es; A(int arg) { // ok e = () => { }; ei1 = () => { }; es = () => { }; // ok M1(ref e); } //ok event Action I1.ei2 { add { throw new NotImplementedException(); } remove { throw new NotImplementedException(); } } void AssignEvent() { // error e = () => { }; // error M1(ref e); } static void M1(ref System.Action arg) { } } interface I1 { event System.Action ei1; event System.Action ei2; } "; CreateCompilation(text).VerifyDiagnostics( // (7,32): error CS8342: Field-like events are not allowed in readonly structs. // public event System.Action e; Diagnostic(ErrorCode.ERR_FieldlikeEventsInRoStruct, "e").WithLocation(7, 32), // (10,25): error CS8342: Field-like events are not allowed in readonly structs. // public event Action ei1; Diagnostic(ErrorCode.ERR_FieldlikeEventsInRoStruct, "ei1").WithLocation(10, 25), // (43,9): error CS1604: Cannot assign to 'e' because it is read-only // e = () => { }; Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "e").WithArguments("e").WithLocation(43, 9), // (46,16): error CS1605: Cannot use 'e' as a ref or out value because it is read-only // M1(ref e); Diagnostic(ErrorCode.ERR_RefReadonlyLocal, "e").WithArguments("e").WithLocation(46, 16) ); } [Fact] public void WriteableInstanceFields_ReadOnlyMethod() { var text = @" public struct A { public static int s; public int x; readonly void AssignField() { // error this = default; // error this.x = 1; A a = default; // OK a.x = 3; // OK s = 5; } } "; CreateCompilation(text).VerifyDiagnostics( // (11,9): error CS1604: Cannot assign to 'this' because it is read-only // this = default; Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "this").WithArguments("this").WithLocation(11, 9), // (13,9): error CS1604: Cannot assign to 'this.x' because it is read-only // this.x = 1; Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "this.x").WithArguments("this.x").WithLocation(13, 9)); } [Fact] public void ReadOnlyStruct_PassThisByRef() { var csharp = @" public readonly struct S { public static void M1(ref S s) {} public static void M2(in S s) {} public void M3() { M1(ref this); // error M2(in this); // ok } public readonly void M4() { M1(ref this); // error M2(in this); // ok } } "; var comp = CreateCompilation(csharp); comp.VerifyDiagnostics( // (9,16): error CS1605: Cannot use 'this' as a ref or out value because it is read-only // M1(ref this); // error Diagnostic(ErrorCode.ERR_RefReadonlyLocal, "this").WithArguments("this").WithLocation(9, 16), // (15,16): error CS1605: Cannot use 'this' as a ref or out value because it is read-only // M1(ref this); // error Diagnostic(ErrorCode.ERR_RefReadonlyLocal, "this").WithArguments("this").WithLocation(15, 16)); } [Fact] public void ReadOnlyMethod_PassThisByRef() { var csharp = @" public struct S { public static void M1(ref S s) {} public static void M2(in S s) {} public void M3() { M1(ref this); // ok M2(in this); // ok } public readonly void M4() { M1(ref this); // error M2(in this); // ok } } "; var comp = CreateCompilation(csharp); comp.VerifyDiagnostics( // (15,16): error CS1605: Cannot use 'this' as a ref or out value because it is read-only // M1(ref this); // error Diagnostic(ErrorCode.ERR_RefReadonlyLocal, "this").WithArguments("this").WithLocation(15, 16)); } [Fact] public void ReadOnlyMethod_RefLocal() { var csharp = @" public struct S { public void M1() { ref S s1 = ref this; // ok ref readonly S s2 = ref this; // ok } public readonly void M2() { ref S s1 = ref this; // error ref readonly S s2 = ref this; // ok } } "; var comp = CreateCompilation(csharp); comp.VerifyDiagnostics( // (12,24): error CS1605: Cannot use 'this' as a ref or out value because it is read-only // ref S s1 = ref this; // error Diagnostic(ErrorCode.ERR_RefReadonlyLocal, "this").WithArguments("this").WithLocation(12, 24)); } [Fact] public void ReadOnlyMethod_PassFieldByRef() { var csharp = @" public struct S { public static int f1; public int f2; public static void M1(ref int s) {} public static void M2(in int s) {} public void M3() { M1(ref f1); // ok M1(ref f2); // ok M2(in f1); // ok M2(in f2); // ok } public readonly void M4() { M1(ref f1); // ok M1(ref f2); // error M2(in f1); // ok M2(in f2); // ok } } "; var comp = CreateCompilation(csharp); comp.VerifyDiagnostics( // (21,16): error CS1605: Cannot use 'f2' as a ref or out value because it is read-only // M1(ref f2); // error Diagnostic(ErrorCode.ERR_RefReadonlyLocal, "f2").WithArguments("f2").WithLocation(21, 16)); } [Fact] public void ReadOnlyMethod_CallStaticMethod() { var csharp = @" public struct S { public static int i; public readonly int M1() => M2() + 1; public static int M2() => i; } "; var comp = CompileAndVerify(csharp); comp.VerifyDiagnostics(); } [Fact] public void ReadOnlyMethod_Override_AssignThis() { var csharp = @" public struct S { public int i; // error public readonly override string ToString() => (i++).ToString(); public readonly override int GetHashCode() => (i++).GetHashCode(); public readonly override bool Equals(object o) => (i++).Equals(o); } "; var comp = CreateCompilation(csharp); comp.VerifyDiagnostics( // (7,52): error CS1604: Cannot assign to 'i' because it is read-only // public readonly override string ToString() => (i++).ToString(); Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "i").WithArguments("i").WithLocation(7, 52), // (8,52): error CS1604: Cannot assign to 'i' because it is read-only // public readonly override int GetHashCode() => (i++).GetHashCode(); Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "i").WithArguments("i").WithLocation(8, 52), // (9,56): error CS1604: Cannot assign to 'i' because it is read-only // public readonly override bool Equals(object o) => (i++).Equals(o); Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "i").WithArguments("i").WithLocation(9, 56)); } [Fact] public void ReadOnlyMethod_Partial_01() { var csharp = @" public partial struct S { public int i; readonly partial void M(); } public partial struct S { readonly partial void M() { i++; } } "; var comp = CreateCompilation(csharp); comp.VerifyDiagnostics( // (12,9): error CS1604: Cannot assign to 'i' because it is read-only // i++; Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "i").WithArguments("i").WithLocation(12, 9)); var method = comp.GetMember<NamedTypeSymbol>("S").GetMethod("M"); Assert.True(method.IsDeclaredReadOnly); Assert.True(method.IsEffectivelyReadOnly); } [Fact] public void ReadOnlyMethod_Partial_02() { var csharp = @" public partial struct S { public int i; partial void M(); } public partial struct S { readonly partial void M() { i++; } } "; var comp = CreateCompilation(csharp); comp.VerifyDiagnostics( // (10,27): error CS8662: Both partial member declarations must be readonly or neither may be readonly // readonly partial void M() Diagnostic(ErrorCode.ERR_PartialMemberReadOnlyDifference, "M").WithLocation(10, 27), // (12,9): error CS1604: Cannot assign to 'i' because it is read-only // i++; Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "i").WithArguments("i").WithLocation(12, 9)); var method = comp.GetMember<NamedTypeSymbol>("S").GetMethod("M"); // Symbol APIs always return the declaration part of the partial method. Assert.False(method.IsDeclaredReadOnly); Assert.False(method.IsEffectivelyReadOnly); } [Fact] public void ReadOnlyMethod_Partial_03() { var csharp = @" public partial struct S { public int i; readonly partial void M(); } public partial struct S { partial void M() { i++; } } "; var comp = CreateCompilation(csharp); comp.VerifyDiagnostics( // (10,18): error CS8662: Both partial member declarations must be readonly or neither may be readonly // partial void M() Diagnostic(ErrorCode.ERR_PartialMemberReadOnlyDifference, "M").WithLocation(10, 18)); var method = comp.GetMember<NamedTypeSymbol>("S").GetMethod("M"); // Symbol APIs always return the declaration part of the partial method. Assert.True(method.IsDeclaredReadOnly); Assert.True(method.IsEffectivelyReadOnly); } [Fact] public void ReadOnlyMethod_ExplicitInterfaceImplementation() { var csharp = @" public interface I { void M(); } public struct S : I { int i; readonly void I.M() { i = 0; } } "; var comp = CreateCompilation(csharp); comp.VerifyDiagnostics( // (12,9): error CS1604: Cannot assign to 'i' because it is read-only // i = 0; Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "i").WithArguments("i").WithLocation(12, 9)); } [Fact] public void ReadOnlyMethod_GetPinnableReference() { var csharp = @" public unsafe struct S1 { static int i = 0; ref int GetPinnableReference() => ref i; void M1() { fixed (int *i = this) {} // ok } readonly void M2() { fixed (int *i = this) {} // warn } } public unsafe struct S2 { static int i = 0; readonly ref int GetPinnableReference() => ref i; void M1() { fixed (int *i = this) {} // ok } readonly void M2() { fixed (int *i = this) {} // ok } } "; var comp = CreateCompilation(csharp, options: TestOptions.UnsafeReleaseDll); comp.VerifyDiagnostics( // (12,25): warning CS8655: Call to non-readonly member 'S1.GetPinnableReference()' from a 'readonly' member results in an implicit copy of 'this'. // fixed (int *i = this) {} // warn Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "this").WithArguments("S1.GetPinnableReference()", "this").WithLocation(12, 25)); } [Fact] public void ReadOnlyMethod_Iterator() { var csharp = @" using System.Collections.Generic; public struct S { public int i; public readonly IEnumerable<int> M1() { yield return i; yield return i+1; } public readonly IEnumerable<int> M2() { yield return i; i++; yield return i; } } "; var comp = CreateCompilation(csharp); comp.VerifyDiagnostics( // (16,9): error CS1604: Cannot assign to 'i' because it is read-only // i++; Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "i").WithArguments("i").WithLocation(16, 9)); } [Fact] public void ReadOnlyMethod_Async() { var csharp = @" using System.Threading.Tasks; public struct S { public int i; public readonly async Task<int> M1() { await Task.Delay(1); return i; } public readonly async Task<int> M2() { await Task.Delay(1); i++; await Task.Delay(1); return i; } } "; var comp = CreateCompilation(csharp); comp.VerifyDiagnostics( // (16,9): error CS1604: Cannot assign to 'i' because it is read-only // i++; Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "i").WithArguments("i").WithLocation(16, 9)); } [Fact] public void ReadOnlyAccessor_CallNormalMethod() { var csharp = @" public struct S { public int i; public readonly int P { get { // should create local copy M(); System.Console.Write(i); // explicit local copy, no warning var copy = this; copy.M(); System.Console.Write(copy.i); return i; } } void M() { i = 23; } static void Main() { var s = new S { i = 1 }; _ = s.P; } } "; var verifier = CompileAndVerify(csharp, expectedOutput: "123"); verifier.VerifyDiagnostics( // (11,13): warning CS8655: Call to non-readonly member 'S.M()' from a 'readonly' member results in an implicit copy of 'this'. // M(); Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "M").WithArguments("S.M()", "this").WithLocation(11, 13)); } [Fact] public void ReadOnlyAccessor_CallNormalGetAccessor() { var csharp = @" public struct S { public int i; public readonly int P1 { get { // should create local copy _ = P2; // warning System.Console.Write(i); // explicit local copy, no warning var copy = this; _ = copy.P2; // ok System.Console.Write(copy.i); return i; } } int P2 => i = 23; static void Main() { var s = new S { i = 1 }; _ = s.P1; } } "; var verifier = CompileAndVerify(csharp, expectedOutput: "123"); verifier.VerifyDiagnostics( // (11,17): warning CS8655: Call to non-readonly member 'S.P2.get' from a 'readonly' member results in an implicit copy of 'this'. // _ = P2; // warning Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "P2").WithArguments("S.P2.get", "this").WithLocation(11, 17)); } [Fact] public void ReadOnlyMethod_CallSetAccessor() { var csharp = @" public class C { public int P { get; set; } } public struct S { C c; public S(C c) { this.c = c; P2 = 0; } static int P1 { get; set; } int P2 { get; set; } readonly int P3 { get => 42; set {} } public readonly void M() { P1 = 1; // ok P2 = 2; // error P3 = 2; // ok c.P = 42; // ok } } "; var comp = CreateCompilation(csharp); comp.VerifyDiagnostics( // (27,9): error CS1604: Cannot assign to 'P2' because it is read-only // P2 = 2; // error Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "P2").WithArguments("P2").WithLocation(27, 9)); } [Fact] public void ReadOnlyMethod_IncrementOperator() { var csharp = @" public struct S { public int i; public readonly void M() { i++; i--; ++i; --i; } } "; var comp = CreateCompilation(csharp); comp.VerifyDiagnostics( // (7,9): error CS1604: Cannot assign to 'i' because it is read-only // i++; Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "i").WithArguments("i").WithLocation(7, 9), // (8,9): error CS1604: Cannot assign to 'i' because it is read-only // i--; Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "i").WithArguments("i").WithLocation(8, 9), // (9,11): error CS1604: Cannot assign to 'i' because it is read-only // ++i; Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "i").WithArguments("i").WithLocation(9, 11), // (10,11): error CS1604: Cannot assign to 'i' because it is read-only // --i; Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "i").WithArguments("i").WithLocation(10, 11)); } [Fact] public void ReadOnlyMethod_CompoundAssignmentOperator() { var csharp = @" public struct S { public int i; public readonly void M() { i += 1; i -= 1; } } "; var comp = CreateCompilation(csharp); comp.VerifyDiagnostics( // (7,9): error CS1604: Cannot assign to 'i' because it is read-only // i += 1; Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "i").WithArguments("i").WithLocation(7, 9), // (8,9): error CS1604: Cannot assign to 'i' because it is read-only // i -= 1; Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "i").WithArguments("i").WithLocation(8, 9)); } [Fact] public void ReadOnlyMethod_EventAssignment() { var csharp = @" using System; public struct S { public readonly void M() { Console.WriteLine(E is null); // should create local copy E += () => {}; // warning Console.WriteLine(E is null); E -= () => {}; // warning // explicit local copy, no warning var copy = this; copy.E += () => {}; Console.WriteLine(copy.E is null); } public event Action E; static void Main() { var s = new S(); s.M(); } } "; var verifier = CompileAndVerify(csharp, expectedOutput: @"True True False"); verifier.VerifyDiagnostics( // (10,9): warning CS8656: Call to non-readonly member 'S.E.add' from a 'readonly' member results in an implicit copy of 'this'. // E += () => {}; // warning Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "E").WithArguments("S.E.add", "this").WithLocation(10, 9), // (12,9): warning CS8656: Call to non-readonly member 'S.E.remove' from a 'readonly' member results in an implicit copy of 'this'. // E -= () => {}; // warning Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "E").WithArguments("S.E.remove", "this").WithLocation(12, 9)); } [Fact] public void ReadOnlyStruct_EventAssignment() { var csharp = @" using System; public struct S { public void M() { E += () => {}; E -= () => {}; } public event Action E { add {} remove {} } } "; var comp = CreateCompilation(csharp); comp.VerifyDiagnostics(); } [Fact] public void ReadOnlyMethod_ReadOnlyEventAssignment() { var csharp = @" using System; public struct S { public readonly void M() { E += () => {}; E -= () => {}; } public readonly event Action E { add {} remove {} } } "; var verifier = CreateCompilation(csharp); verifier.VerifyDiagnostics(); } [Fact] public void ReadOnlyMethod_Field_EventAssignment() { var csharp = @" #pragma warning disable 0067 using System; public struct S2 { public event Action E; } public struct S1 { public S2 s2; public readonly void M() { s2.E += () => {}; s2.E -= () => {}; } } "; // TODO: should warn in warning wave https://github.com/dotnet/roslyn/issues/33968 var verifier = CreateCompilation(csharp); verifier.VerifyDiagnostics(); } [Fact] public void ReadOnlyStruct_Field_EventAssignment() { var csharp = @" #pragma warning disable 0067 using System; public struct S2 { public event Action E; } public readonly struct S1 { public readonly S2 s2; public void M() { s2.E += () => {}; s2.E -= () => {}; } } "; // TODO: should warn in warning wave https://github.com/dotnet/roslyn/issues/33968 var verifier = CreateCompilation(csharp); verifier.VerifyDiagnostics(); } [Fact] public void ReadOnlyMethod_EventAssignment_Error() { var csharp = @" using System; public struct S { public event Action<EventArgs> E; public readonly void M() { E += handler; E -= handler; E = handler; E(new EventArgs()); void handler(EventArgs args) { } } } "; var comp = CreateCompilation(csharp); comp.VerifyDiagnostics( // (9,9): warning CS8656: Call to non-readonly member 'S.E.add' from a 'readonly' member results in an implicit copy of 'this'. // E += handler; Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "E").WithArguments("S.E.add", "this").WithLocation(9, 9), // (10,9): warning CS8656: Call to non-readonly member 'S.E.remove' from a 'readonly' member results in an implicit copy of 'this'. // E -= handler; Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "E").WithArguments("S.E.remove", "this").WithLocation(10, 9), // (11,9): error CS1604: Cannot assign to 'E' because it is read-only // E = handler; Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "E").WithArguments("E").WithLocation(11, 9)); } [Fact] public void ReadOnlyEventAccessors() { var csharp = @" using System; public struct S { public int i; public readonly event Action<EventArgs> E { add { i++; } remove { i--; } } } "; var comp = CreateCompilation(csharp); comp.VerifyDiagnostics( // (8,15): error CS1604: Cannot assign to 'i' because it is read-only // add { i++; } Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "i").WithArguments("i").WithLocation(8, 15), // (9,18): error CS1604: Cannot assign to 'i' because it is read-only // remove { i--; } Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "i").WithArguments("i").WithLocation(9, 18)); } [Fact] public void ReadOnlyStruct_CallNormalMethodOnField() { var csharp = @" public readonly struct S1 { public readonly S2 s2; public void M1() { s2.M2(); } } public struct S2 { public int i; public void M2() { i = 42; } static void Main() { var s1 = new S1(); s1.M1(); System.Console.Write(s1.s2.i); } } "; // should warn about calling s2.M2 in warning wave (see https://github.com/dotnet/roslyn/issues/33968) CompileAndVerify(csharp, expectedOutput: "0"); } [Fact] public void ReadOnlyMethod_CallNormalMethodOnField() { var csharp = @" public struct S1 { public S2 s2; public readonly void M1() { s2.M2(); System.Console.Write(s2.i); var copy = s2; copy.M2(); System.Console.Write(copy.i); } } public struct S2 { public int i; public void M2() { i = 23; } static void Main() { var s1 = new S1() { s2 = new S2 { i = 1 } }; s1.M1(); } } "; var verifier = CompileAndVerify(csharp, expectedOutput: "123"); // should warn about calling s2.M2 in warning wave (see https://github.com/dotnet/roslyn/issues/33968) verifier.VerifyDiagnostics(); } private static readonly string ilreadonlyStructWithWriteableFieldIL = @" .class private auto ansi sealed beforefieldinit Microsoft.CodeAnalysis.EmbeddedAttribute extends [mscorlib]System.Attribute { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void Microsoft.CodeAnalysis.EmbeddedAttribute::.ctor() = ( 01 00 00 00 ) .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // Code size 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Attribute::.ctor() IL_0006: ret } // end of method EmbeddedAttribute::.ctor } // end of class Microsoft.CodeAnalysis.EmbeddedAttribute .class private auto ansi sealed beforefieldinit System.Runtime.CompilerServices.IsReadOnlyAttribute extends [mscorlib]System.Attribute { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void Microsoft.CodeAnalysis.EmbeddedAttribute::.ctor() = ( 01 00 00 00 ) .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // Code size 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Attribute::.ctor() IL_0006: ret } // end of method IsReadOnlyAttribute::.ctor } // end of class System.Runtime.CompilerServices.IsReadOnlyAttribute .class public sequential ansi sealed beforefieldinit S1 extends [mscorlib]System.ValueType { .custom instance void System.Runtime.CompilerServices.IsReadOnlyAttribute::.ctor() = ( 01 00 00 00 ) // WRITEABLE FIELD!!! .field public int32 'field' } // end of class S1 "; [Fact()] public void UseWriteableInstanceFieldsInRoStructs() { var csharp = @" public class Program { public static void Main() { S1 s = new S1(); s.field = 123; System.Console.WriteLine(s.field); } } "; var comp = CreateCompilationWithILAndMscorlib40(csharp, ilreadonlyStructWithWriteableFieldIL, options: TestOptions.ReleaseExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123"); } [Fact()] public void UseWriteableInstanceFieldsInRoStructsErr() { var csharp = @" public class Program { static readonly S1 s = new S1(); public static void Main() { s.field = 123; System.Console.WriteLine(s.field); } } "; var comp = CreateCompilationWithILAndMscorlib40(csharp, ilreadonlyStructWithWriteableFieldIL, options: TestOptions.ReleaseExe); comp.VerifyDiagnostics( // (8,9): error CS1650: Fields of static readonly field 'Program.s' cannot be assigned to (except in a static constructor or a variable initializer) // s.field = 123; Diagnostic(ErrorCode.ERR_AssgReadonlyStatic2, "s.field").WithArguments("Program.s").WithLocation(8, 9) ); } [Fact] public void ReadOnlyStructMethod() { var csharp = @" public struct S { public int i; public readonly int M() { return i; } } "; var comp = CreateCompilation(csharp); comp.VerifyDiagnostics(); var method = comp.GetMember<NamedTypeSymbol>("S").GetMember<MethodSymbol>("M"); Assert.True(method.IsDeclaredReadOnly); Assert.True(method.IsEffectivelyReadOnly); } [Fact] public void ReadOnlyMembers_SemanticModel() { var csharp = @" using System; public struct S1 { public void M1() {} public readonly void M2() {} public int P1 { get; set; } public readonly int P2 => 42; public int P3 { readonly get => 123; } public int P4 { readonly set {} } public static int P5 { get; set; } public readonly event Action<EventArgs> E { add {} remove {} } } public readonly struct S2 { public void M1() {} public static void M2() {} public int P1 { get; } public int P2 => 42; public int P3 { set {} } public static int P4 { get; set; } public event Action<EventArgs> E { add {} remove {} } } "; Compilation comp = CreateCompilation(csharp); var s1 = (INamedTypeSymbol)comp.GetSymbolsWithName("S1").Single(); Assert.False(getMethod(s1, "M1").IsReadOnly); Assert.True(getMethod(s1, "M2").IsReadOnly); Assert.True(getProperty(s1, "P1").GetMethod.IsReadOnly); Assert.False(getProperty(s1, "P1").SetMethod.IsReadOnly); Assert.True(getProperty(s1, "P2").GetMethod.IsReadOnly); Assert.True(getProperty(s1, "P3").GetMethod.IsReadOnly); Assert.True(getProperty(s1, "P4").SetMethod.IsReadOnly); Assert.False(getProperty(s1, "P5").GetMethod.IsReadOnly); Assert.False(getProperty(s1, "P5").SetMethod.IsReadOnly); Assert.True(getEvent(s1, "E").AddMethod.IsReadOnly); Assert.True(getEvent(s1, "E").RemoveMethod.IsReadOnly); var s2 = comp.GetMember<INamedTypeSymbol>("S2"); Assert.True(getMethod(s2, "M1").IsReadOnly); Assert.False(getMethod(s2, "M2").IsReadOnly); Assert.True(getProperty(s2, "P1").GetMethod.IsReadOnly); Assert.True(getProperty(s2, "P2").GetMethod.IsReadOnly); Assert.True(getProperty(s2, "P3").SetMethod.IsReadOnly); Assert.False(getProperty(s2, "P4").GetMethod.IsReadOnly); Assert.False(getProperty(s2, "P4").SetMethod.IsReadOnly); Assert.True(getEvent(s2, "E").AddMethod.IsReadOnly); Assert.True(getEvent(s2, "E").RemoveMethod.IsReadOnly); static IMethodSymbol getMethod(INamedTypeSymbol symbol, string name) => (IMethodSymbol)symbol.GetMembers(name).Single(); static IPropertySymbol getProperty(INamedTypeSymbol symbol, string name) => (IPropertySymbol)symbol.GetMembers(name).Single(); static IEventSymbol getEvent(INamedTypeSymbol symbol, string name) => (IEventSymbol)symbol.GetMembers(name).Single(); } [Theory, CombinatorialData] public void ReadOnlyMembers_ExtensionMethods_SemanticModel([CombinatorialValues("in", "ref readonly")] string modifier) { var csharp = @" public struct S1 {} public readonly struct S2 {} public static class C { static void M1(this S1 s1) {} static void M2(this ref S1 s1) {} static void M3(this " + modifier + @" S1 s1) {} static void M4(this S2 s2) {} static void M5(this ref S2 s2) {} static void M6(this " + modifier + @" S2 s2) {} static void Test() { var s1 = new S1(); s1.M1(); s1.M2(); s1.M3(); var s2 = new S2(); s2.M4(); s2.M5(); s2.M6(); } } "; Compilation comp = CreateCompilation(csharp); var c = comp.GetMember<IMethodSymbol>("C.Test"); var testMethodSyntax = (MethodDeclarationSyntax)c.DeclaringSyntaxReferences.Single().GetSyntax(); var semanticModel = comp.GetSemanticModel(testMethodSyntax.SyntaxTree); var statements = testMethodSyntax.Body.Statements; testStatement(statements[1], false); testStatement(statements[2], false); testStatement(statements[3], true); testStatement(statements[5], false); testStatement(statements[6], false); testStatement(statements[7], true); void testStatement(StatementSyntax statementSyntax, bool isEffectivelyReadOnly) { var expressionStatement = (ExpressionStatementSyntax)statementSyntax; var invocationExpression = (InvocationExpressionSyntax)expressionStatement.Expression; var symbol = (IMethodSymbol)semanticModel.GetSymbolInfo(invocationExpression.Expression).Symbol; var reducedFrom = symbol.ReducedFrom; Assert.Equal(isEffectivelyReadOnly, symbol.GetSymbol().IsEffectivelyReadOnly); Assert.Equal(isEffectivelyReadOnly, symbol.IsReadOnly); Assert.False(symbol.GetSymbol().IsDeclaredReadOnly); Assert.False(reducedFrom.GetSymbol().IsDeclaredReadOnly); Assert.False(reducedFrom.GetSymbol().IsEffectivelyReadOnly); Assert.False(((IMethodSymbol)reducedFrom).IsReadOnly); } } [Fact] public void ReadOnlyMembers_RefReturningProperty() { var csharp = @" public struct S1 { private static int i = 0; public ref int P1 => ref i; public readonly ref int P2 => ref i; public ref readonly int P3 => ref i; public readonly ref readonly int P4 => ref i; } "; var comp = CreateCompilation(csharp); var s1 = comp.GetMember<NamedTypeSymbol>("S1"); check(s1.GetProperty("P1"), true, false, false); check(s1.GetProperty("P2"), true, false, true); check(s1.GetProperty("P3"), false, true, false); check(s1.GetProperty("P4"), false, true, true); static void check(PropertySymbol property, bool returnsByRef, bool returnsByRefReadonly, bool isReadOnly) { Assert.Equal(returnsByRef, property.ReturnsByRef); Assert.Equal(returnsByRefReadonly, property.ReturnsByRefReadonly); Assert.True(property.IsReadOnly); Assert.Equal(isReadOnly, property.GetMethod.IsDeclaredReadOnly); Assert.Equal(isReadOnly, property.GetMethod.IsEffectivelyReadOnly); Assert.Equal(isReadOnly, property.GetMethod.GetPublicSymbol().IsReadOnly); } } [Fact] public void ReadOnlyClass() { var csharp = @" using System; public readonly class C { public readonly int M() => 42; public readonly int P { get; set; } public readonly int this[int i] => i; public readonly event Action<EventArgs> E { add {} remove {} } } "; var comp = CreateCompilation(csharp); comp.VerifyDiagnostics( // (4,23): error CS0106: The modifier 'readonly' is not valid for this item // public readonly class C Diagnostic(ErrorCode.ERR_BadMemberFlag, "C").WithArguments("readonly").WithLocation(4, 23), // (6,25): error CS0106: The modifier 'readonly' is not valid for this item // public readonly int M() => 42; Diagnostic(ErrorCode.ERR_BadMemberFlag, "M").WithArguments("readonly").WithLocation(6, 25), // (7,25): error CS0106: The modifier 'readonly' is not valid for this item // public readonly int P { get; set; } Diagnostic(ErrorCode.ERR_BadMemberFlag, "P").WithArguments("readonly").WithLocation(7, 25), // (8,25): error CS0106: The modifier 'readonly' is not valid for this item // public readonly int this[int i] => i; Diagnostic(ErrorCode.ERR_BadMemberFlag, "this").WithArguments("readonly").WithLocation(8, 25), // (9,45): error CS0106: The modifier 'readonly' is not valid for this item // public readonly event Action<EventArgs> E { add {} remove {} } Diagnostic(ErrorCode.ERR_BadMemberFlag, "E").WithArguments("readonly").WithLocation(9, 45)); } [Fact] public void ReadOnlyClass_NormalMethod() { var csharp = @" public readonly class C { int i; void M() { i++; } } "; var comp = CreateCompilation(csharp); comp.VerifyDiagnostics( // (2,23): error CS0106: The modifier 'readonly' is not valid for this item // public readonly class C Diagnostic(ErrorCode.ERR_BadMemberFlag, "C").WithArguments("readonly").WithLocation(2, 23)); } [Fact] public void ReadOnlyInterface() { var csharp = @" using System; public readonly interface I { readonly int M(); readonly int P { get; set; } readonly int this[int i] { get; } readonly event Action<EventArgs> E; } "; var comp = CreateCompilation(csharp); comp.VerifyDiagnostics( // (4,27): error CS0106: The modifier 'readonly' is not valid for this item // public readonly interface I Diagnostic(ErrorCode.ERR_BadMemberFlag, "I").WithArguments("readonly").WithLocation(4, 27), // (6,18): error CS0106: The modifier 'readonly' is not valid for this item // readonly int M(); Diagnostic(ErrorCode.ERR_BadMemberFlag, "M").WithArguments("readonly").WithLocation(6, 18), // (7,18): error CS0106: The modifier 'readonly' is not valid for this item // readonly int P { get; set; } Diagnostic(ErrorCode.ERR_BadMemberFlag, "P").WithArguments("readonly").WithLocation(7, 18), // (8,18): error CS0106: The modifier 'readonly' is not valid for this item // readonly int this[int i] { get; } Diagnostic(ErrorCode.ERR_BadMemberFlag, "this").WithArguments("readonly").WithLocation(8, 18), // (9,38): error CS0106: The modifier 'readonly' is not valid for this item // readonly event Action<EventArgs> E; Diagnostic(ErrorCode.ERR_BadMemberFlag, "E").WithArguments("readonly").WithLocation(9, 38)); } [Fact] public void ReadOnlyEnum() { var csharp = @" public readonly enum E { readonly A, readonly B } "; var comp = CreateCompilation(csharp); comp.VerifyDiagnostics( // (2,22): error CS0106: The modifier 'readonly' is not valid for this item // public readonly enum E Diagnostic(ErrorCode.ERR_BadMemberFlag, "E").WithArguments("readonly").WithLocation(2, 22), // (3,2): error CS1041: Identifier expected; 'readonly' is a keyword // { Diagnostic(ErrorCode.ERR_IdentifierExpectedKW, "").WithArguments("", "readonly").WithLocation(3, 2), // (4,17): error CS1041: Identifier expected; 'readonly' is a keyword // readonly A, readonly B; Diagnostic(ErrorCode.ERR_IdentifierExpectedKW, "readonly").WithArguments("", "readonly").WithLocation(4, 17)); } [Fact] public void ReadOnlyStructStaticMethod() { var csharp = @" public struct S { public static int i; public static readonly int M() { return i; } } "; var comp = CreateCompilation(csharp); comp.VerifyDiagnostics( // (5,32): error CS8656: Static member 'S.M()' cannot be marked 'readonly'. // public static readonly int M() Diagnostic(ErrorCode.ERR_StaticMemberCantBeReadOnly, "M").WithArguments("S.M()").WithLocation(5, 32)); var method = comp.GetMember<NamedTypeSymbol>("S").GetMethod("M"); Assert.True(method.IsDeclaredReadOnly); Assert.False(method.IsEffectivelyReadOnly); } [Fact] public void ReadOnlyStructProperty() { var csharp = @" public struct S { public int i; public readonly int P { get { return i; } } } "; var comp = CreateCompilation(csharp); comp.VerifyDiagnostics(); } [Fact] public void ReadOnlyStructStaticProperty() { var csharp = @" public struct S { public static int i; public static int P { readonly get { return i; } set { i = value; } } } "; var comp = CreateCompilation(csharp); comp.VerifyDiagnostics( // (7,18): error CS8656: Static member 'S.P.get' cannot be marked 'readonly'. // readonly get Diagnostic(ErrorCode.ERR_StaticMemberCantBeReadOnly, "get").WithArguments("S.P.get").WithLocation(7, 18)); } [Fact] public void ReadOnlyStructStaticExpressionProperty() { var csharp = @" public struct S { public static int i; public static readonly int P => i; } "; var comp = CreateCompilation(csharp); comp.VerifyDiagnostics( // (5,32): error CS8656: Static member 'S.P' cannot be marked 'readonly'. // public static readonly int P => i; Diagnostic(ErrorCode.ERR_StaticMemberCantBeReadOnly, "P").WithArguments("S.P").WithLocation(5, 32)); } [Fact] public void ReadOnlyStructExpressionProperty() { var csharp = @" public struct S { public int i; public readonly int P => i; } "; var comp = CreateCompilation(csharp); comp.VerifyDiagnostics(); } [Fact] public void ReadOnlyStructBlockProperty() { var csharp = @" public struct S { public int i; public readonly int P { get { return i; } } } "; var comp = CreateCompilation(csharp); comp.VerifyDiagnostics(); } [Fact] public void ReadOnlyAutoProperty() { var csharp = @" public struct S { public int P1 { readonly get; } public readonly int P2 { get; } public int P3 { readonly get; set; } public int P4 { readonly get; readonly set; } public readonly int P5 { get; set; } public readonly int P6 { readonly get; } public int P7 { get; readonly set; } public readonly int P8 { get; readonly set; } } "; var comp = CreateCompilation(csharp); comp.VerifyDiagnostics( // (4,16): error CS8663: 'S.P1': 'readonly' can only be used on accessors if the property or indexer has both a get and a set accessor // public int P1 { readonly get; } Diagnostic(ErrorCode.ERR_ReadOnlyModMissingAccessor, "P1").WithArguments("S.P1").WithLocation(4, 16), // (7,16): error CS8660: Cannot specify 'readonly' modifiers on both accessors of property or indexer 'S.P4'. Instead, put a 'readonly' modifier on the property itself. // public int P4 { readonly get; readonly set; } Diagnostic(ErrorCode.ERR_DuplicatePropertyReadOnlyMods, "P4").WithArguments("S.P4").WithLocation(7, 16), // (7,44): error CS8657: Auto-implemented 'set' accessor 'S.P4.set' cannot be marked 'readonly'. // public int P4 { readonly get; readonly set; } Diagnostic(ErrorCode.ERR_AutoSetterCantBeReadOnly, "set").WithArguments("S.P4.set").WithLocation(7, 44), // (8,25): error CS8658: Auto-implemented property 'S.P5' cannot be marked 'readonly' because it has a 'set' accessor. // public readonly int P5 { get; set; } Diagnostic(ErrorCode.ERR_AutoPropertyWithSetterCantBeReadOnly, "P5").WithArguments("S.P5").WithLocation(8, 25), // (9,25): error CS8663: 'S.P6': 'readonly' can only be used on accessors if the property or indexer has both a get and a set accessor // public readonly int P6 { readonly get; } Diagnostic(ErrorCode.ERR_ReadOnlyModMissingAccessor, "P6").WithArguments("S.P6").WithLocation(9, 25), // (9,39): error CS8659: Cannot specify 'readonly' modifiers on both property or indexer 'S.P6' and its accessor. Remove one of them. // public readonly int P6 { readonly get; } Diagnostic(ErrorCode.ERR_InvalidPropertyReadOnlyMods, "get").WithArguments("S.P6").WithLocation(9, 39), // (10,35): error CS8657: Auto-implemented 'set' accessor 'S.P7.set' cannot be marked 'readonly'. // public int P7 { get; readonly set; } Diagnostic(ErrorCode.ERR_AutoSetterCantBeReadOnly, "set").WithArguments("S.P7.set").WithLocation(10, 35), // (11,25): error CS8658: Auto-implemented property 'S.P8' cannot be marked 'readonly' because it has a 'set' accessor. // public readonly int P8 { get; readonly set; } Diagnostic(ErrorCode.ERR_AutoPropertyWithSetterCantBeReadOnly, "P8").WithArguments("S.P8").WithLocation(11, 25), // (11,44): error CS8659: Cannot specify 'readonly' modifiers on both property or indexer 'S.P8' and its accessor. Remove one of them. // public readonly int P8 { get; readonly set; } Diagnostic(ErrorCode.ERR_InvalidPropertyReadOnlyMods, "set").WithArguments("S.P8").WithLocation(11, 44)); } [Fact] public void NetModule_ImplicitReadOnlyAutoProperty() { var csharp = @" public struct S { public int P1 { get; private set; } } "; var moduleMetadata = CreateCompilation(csharp, options: TestOptions.DebugModule, targetFramework: TargetFramework.Mscorlib461).EmitToImageReference(); var moduleComp = CreateCompilation("", new[] { moduleMetadata }); var moduleGetter = moduleComp.GetMember<PropertySymbol>("S.P1").GetMethod; Assert.False(moduleGetter.IsDeclaredReadOnly); var dllMetadata = CreateCompilation(csharp, options: TestOptions.DebugDll, targetFramework: TargetFramework.Mscorlib461).EmitToImageReference(); var dllComp = CreateCompilation("", new[] { dllMetadata }); var dllGetter = dllComp.GetMember<PropertySymbol>("S.P1").GetMethod; Assert.True(dllGetter.IsDeclaredReadOnly); } [Fact] public void NetModule_ImplicitReadOnlyAutoProperty_MalformedAttribute() { var csharp = @" namespace System.Runtime.CompilerServices { public class IsReadOnlyAttribute { public IsReadOnlyAttribute(int x) {} } } public struct S { public int P1 { get; private set; } } "; var moduleMetadata = CreateCompilation(csharp, options: TestOptions.DebugModule, targetFramework: TargetFramework.Mscorlib461).EmitToImageReference(); var moduleComp = CreateCompilation("", new[] { moduleMetadata }); var moduleGetter = moduleComp.GetMember<PropertySymbol>("S.P1").GetMethod; Assert.False(moduleGetter.IsDeclaredReadOnly); var dllMetadata = CreateCompilation(csharp, options: TestOptions.DebugDll, targetFramework: TargetFramework.Mscorlib461).EmitToImageReference(); var dllComp = CreateCompilation("", new[] { dllMetadata }); var dllGetter = dllComp.GetMember<PropertySymbol>("S.P1").GetMethod; Assert.False(dllGetter.IsDeclaredReadOnly); } [Fact] public void NetModule_ExplicitReadOnlyAutoProperty_MalformedAttribute() { var csharp = @" namespace System.Runtime.CompilerServices { public class IsReadOnlyAttribute { public IsReadOnlyAttribute(int x) {} } } public struct S { public int P1 { readonly get; private set; } } "; var moduleComp = CreateCompilation(csharp, options: TestOptions.DebugModule, targetFramework: TargetFramework.Mscorlib461); moduleComp.VerifyDiagnostics( // (12,30): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.IsReadOnlyAttribute..ctor' // public int P1 { readonly get; private set; } Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "get").WithArguments("System.Runtime.CompilerServices.IsReadOnlyAttribute", ".ctor").WithLocation(12, 30)); var dllComp = CreateCompilation(csharp, options: TestOptions.DebugDll, targetFramework: TargetFramework.Mscorlib461); dllComp.VerifyDiagnostics( // (12,30): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.IsReadOnlyAttribute..ctor' // public int P1 { readonly get; private set; } Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "get").WithArguments("System.Runtime.CompilerServices.IsReadOnlyAttribute", ".ctor").WithLocation(12, 30)); } [Fact] public void NetModule_ExplicitReadOnlyAutoProperty() { var csharp = @" public struct S { public int P1 { readonly get; private set; } } "; var moduleComp = CreateCompilation(csharp, options: TestOptions.DebugModule, targetFramework: TargetFramework.Mscorlib461); moduleComp.VerifyDiagnostics( // (4,30): error CS0518: Predefined type 'System.Runtime.CompilerServices.IsReadOnlyAttribute' is not defined or imported // public int P1 { readonly get; private set; } Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "get").WithArguments("System.Runtime.CompilerServices.IsReadOnlyAttribute").WithLocation(4, 30)); var dllComp = CreateCompilation(csharp, options: TestOptions.DebugDll, targetFramework: TargetFramework.Mscorlib461); dllComp.VerifyDiagnostics(); } [Fact] public void ReadOnlyProperty_RedundantReadOnlyAccessor() { var csharp = @" public struct S { public readonly int P { readonly get => 42; set {} } } "; var comp = CreateCompilation(csharp); comp.VerifyDiagnostics( // (4,38): error CS8659: Cannot specify 'readonly' modifiers on both property or indexer 'S.P' and its accessor. Remove one of them. // public readonly int P { readonly get => 42; } Diagnostic(ErrorCode.ERR_InvalidPropertyReadOnlyMods, "get").WithArguments("S.P").WithLocation(4, 38)); } [Fact] public void ReadOnlyStaticAutoProperty() { var csharp = @" public struct S { public static readonly int P1 { get; set; } public static int P2 { readonly get; set; } } "; var comp = CreateCompilation(csharp); comp.VerifyDiagnostics( // (4,32): error CS8656: Static member 'S.P1' cannot be marked 'readonly'. // public static readonly int P1 { get; set; } Diagnostic(ErrorCode.ERR_StaticMemberCantBeReadOnly, "P1").WithArguments("S.P1").WithLocation(4, 32), // (5,37): error CS8656: Static member 'S.P2.get' cannot be marked 'readonly'. // public static int P2 { readonly get; } Diagnostic(ErrorCode.ERR_StaticMemberCantBeReadOnly, "get").WithArguments("S.P2.get").WithLocation(5, 37)); } [Fact] public void RefReturningReadOnlyMethod() { var csharp = @" public struct S { private static int f1; public readonly ref int M1_1() => ref f1; // ok public readonly ref readonly int M1_2() => ref f1; // ok private static readonly int f2; public readonly ref int M2_1() => ref f2; // error public readonly ref readonly int M2_2() => ref f2; // ok private static readonly int f3; public ref readonly int M3() { f1++; return ref f3; } } "; var comp = CreateCompilation(csharp); comp.VerifyDiagnostics( // (9,43): error CS8161: A static readonly field cannot be returned by writable reference // public readonly ref int M2_1() => ref f2; // error Diagnostic(ErrorCode.ERR_RefReturnReadonlyStatic, "f2").WithLocation(9, 43)); } [Fact] public void ReadOnlyConstructor() { var csharp = @" public struct S { static readonly S() { } public readonly S(int i) { } } "; var comp = CreateCompilation(csharp); comp.VerifyDiagnostics( // (4,21): error CS0106: The modifier 'readonly' is not valid for this item // static readonly S() { } Diagnostic(ErrorCode.ERR_BadMemberFlag, "S").WithArguments("readonly").WithLocation(4, 21), // (5,21): error CS0106: The modifier 'readonly' is not valid for this item // public readonly S(int i) { } Diagnostic(ErrorCode.ERR_BadMemberFlag, "S").WithArguments("readonly").WithLocation(5, 21)); } [Fact] public void ReadOnlyStruct_Constructor() { var csharp = @" public readonly struct S { public readonly int i; public S(int i) { this.i = i; // ok M(ref this); // ok } public static void M(ref S s) { s.i = 42; // error s = default; // ok } } "; var comp = CreateCompilation(csharp); comp.VerifyDiagnostics( // (12,9): error CS0191: A readonly field cannot be assigned to (except in a constructor or a variable initializer) // s.i = 42; // error Diagnostic(ErrorCode.ERR_AssgReadonly, "s.i").WithLocation(12, 9)); } [Fact] public void ReadOnlyMethod_GetEnumerator() { var csharp = @" using System.Collections; public struct S1 { public IEnumerator GetEnumerator() => throw null; void M1() { foreach (var x in this) {} // ok } readonly void M2() { foreach (var x in this) {} // warning-- implicit copy } } public struct S2 { public readonly IEnumerator GetEnumerator() => throw null; void M1() { foreach (var x in this) {} // ok } readonly void M2() { foreach (var x in this) {} // ok } } "; var comp = CreateCompilation(csharp); comp.VerifyDiagnostics( // (13,27): warning CS8655: Call to non-readonly member 'S1.GetEnumerator()' from a 'readonly' member results in an implicit copy of 'this'. // foreach (var x in this) {} // warning-- implicit copy Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "this").WithArguments("S1.GetEnumerator()", "this").WithLocation(13, 27)); } [Fact] public void ReadOnlyMethod_GetEnumerator_MethodMissing() { var csharp = @" public struct S1 { readonly void M2() { foreach (var x in this) {} } } "; var comp = CreateCompilation(csharp); comp.VerifyDiagnostics( // (6,27): error CS1579: foreach statement cannot operate on variables of type 'S1' because 'S1' does not contain a public instance or extension definition for 'GetEnumerator' // foreach (var x in this) {} Diagnostic(ErrorCode.ERR_ForEachMissingMember, "this").WithArguments("S1", "GetEnumerator").WithLocation(6, 27)); } [Fact] public void ReadOnlyMethod_AsyncStreams() { var csharp = @" using System.Threading.Tasks; using System.Collections.Generic; public struct S1 { public IAsyncEnumerator<int> GetAsyncEnumerator() => throw null; public async Task M1() { await foreach (var x in this) {} } public readonly async Task M2() { await foreach (var x in this) {} // warn } } public struct S2 { public readonly IAsyncEnumerator<int> GetAsyncEnumerator() => throw null; public async Task M1() { await foreach (var x in this) {} } public readonly async Task M2() { await foreach (var x in this) {} // ok } } "; var comp = CreateCompilationWithTasksExtensions(new[] { csharp, AsyncStreamsTypes }); comp.VerifyDiagnostics( // (16,33): warning CS8655: Call to non-readonly member 'S1.GetAsyncEnumerator()' from a 'readonly' member results in an implicit copy of 'this'. // await foreach (var x in this) {} // warn Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "this").WithArguments("S1.GetAsyncEnumerator()", "this").WithLocation(16, 33)); } [Fact] public void ReadOnlyMethod_Using() { // 'using' results in a boxing conversion when the struct implements 'IDisposable'. // Boxing conversions are out of scope of the implicit copy warning. // 'await using' can't be used with ref structs, so implicitly copy warnings can't be produced in that scenario. var csharp = @" public ref struct S1 { public void Dispose() {} void M1() { using (this) { } // ok } readonly void M2() { using (this) { } // should warn } } public ref struct S2 { public readonly void Dispose() {} void M1() { using (this) { } // ok } readonly void M2() { using (this) { } // ok } } "; var comp = CreateCompilation(csharp); comp.VerifyDiagnostics( // (13,16): warning CS8655: Call to non-readonly member 'S1.Dispose()' from a 'readonly' member results in an implicit copy of 'this'. // using (this) { } // should warn Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "this").WithArguments("S1.Dispose()", "this").WithLocation(13, 16)); } [Fact] public void ReadOnlyMethod_Deconstruct() { var csharp = @" public struct S1 { void M1() { var (x, y) = this; // ok } readonly void M2() { var (x, y) = this; // should warn } public void Deconstruct(out int x, out int y) { x = 42; y = 123; } } public struct S2 { void M1() { var (x, y) = this; // ok } readonly void M2() { var (x, y) = this; // ok } public readonly void Deconstruct(out int x, out int y) { x = 42; y = 123; } } "; var comp = CreateCompilation(csharp); comp.VerifyDiagnostics( // (11,22): warning CS8655: Call to non-readonly member 'S1.Deconstruct(out int, out int)' from a 'readonly' member results in an implicit copy of 'this'. // var (x, y) = this; // should warn Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "this").WithArguments("S1.Deconstruct(out int, out int)", "this").WithLocation(11, 22)); } [Fact] public void ReadOnlyMethod_Deconstruct_MethodMissing() { var csharp = @" public struct S2 { readonly void M1() { var (x, y) = this; // error } } "; var comp = CreateCompilation(csharp); comp.VerifyDiagnostics( // (6,14): error CS8130: Cannot infer the type of implicitly-typed deconstruction variable 'x'. // var (x, y) = this; // error Diagnostic(ErrorCode.ERR_TypeInferenceFailedForImplicitlyTypedDeconstructionVariable, "x").WithArguments("x").WithLocation(6, 14), // (6,17): error CS8130: Cannot infer the type of implicitly-typed deconstruction variable 'y'. // var (x, y) = this; // error Diagnostic(ErrorCode.ERR_TypeInferenceFailedForImplicitlyTypedDeconstructionVariable, "y").WithArguments("y").WithLocation(6, 17), // (6,22): error CS1061: 'S2' does not contain a definition for 'Deconstruct' and no accessible extension method 'Deconstruct' accepting a first argument of type 'S2' could be found (are you missing a using directive or an assembly reference?) // var (x, y) = this; // error Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "this").WithArguments("S2", "Deconstruct").WithLocation(6, 22), // (6,22): error CS8129: No suitable 'Deconstruct' instance or extension method was found for type 'S2', with 2 out parameters and a void return type. // var (x, y) = this; // error Diagnostic(ErrorCode.ERR_MissingDeconstruct, "this").WithArguments("S2", "2").WithLocation(6, 22)); } [Fact] public void ReadOnlyDestructor() { var csharp = @" public struct S { readonly ~S() { } } "; var comp = CreateCompilation(csharp); comp.VerifyDiagnostics( // (4,15): error CS0106: The modifier 'readonly' is not valid for this item // readonly ~S() { } Diagnostic(ErrorCode.ERR_BadMemberFlag, "S").WithArguments("readonly").WithLocation(4, 15), // (4,15): error CS0575: Only class types can contain destructors // readonly ~S() { } Diagnostic(ErrorCode.ERR_OnlyClassesCanContainDestructors, "S").WithLocation(4, 15)); } [Fact] public void ReadOnlyOperator() { var csharp = @" public struct S { public static readonly S operator +(S lhs, S rhs) => lhs; public static readonly explicit operator int(S s) => 42; } "; var comp = CreateCompilation(csharp); comp.VerifyDiagnostics( // (4,39): error CS0106: The modifier 'readonly' is not valid for this item // public static readonly S operator +(S lhs, S rhs) => lhs; Diagnostic(ErrorCode.ERR_BadMemberFlag, "+").WithArguments("readonly").WithLocation(4, 39), // (5,46): error CS0106: The modifier 'readonly' is not valid for this item // public static readonly explicit operator int(S s) => 42; Diagnostic(ErrorCode.ERR_BadMemberFlag, "int").WithArguments("readonly").WithLocation(5, 46)); } [Fact] public void ReadOnlyDelegate() { var csharp = @" public readonly delegate int Del(); "; var comp = CreateCompilation(csharp); comp.VerifyDiagnostics( // (2,30): error CS0106: The modifier 'readonly' is not valid for this item // public readonly delegate int Del(); Diagnostic(ErrorCode.ERR_BadMemberFlag, "Del").WithArguments("readonly").WithLocation(2, 30)); } [Fact] public void ReadOnlyLocalFunction() { var csharp = @" public struct S { void M1() { local(); readonly void local() {} } readonly void M2() { local(); readonly void local() {} } } "; var comp = CreateCompilation(csharp); comp.VerifyDiagnostics( // (7,9): error CS0106: The modifier 'readonly' is not valid for this item // readonly void local() {} Diagnostic(ErrorCode.ERR_BadMemberFlag, "readonly").WithArguments("readonly").WithLocation(7, 9), // (12,9): error CS0106: The modifier 'readonly' is not valid for this item // readonly void local() {} Diagnostic(ErrorCode.ERR_BadMemberFlag, "readonly").WithArguments("readonly").WithLocation(12, 9)); } [Fact] public void ReadOnlyLambda() { var csharp = @" public struct S { void M1() { M2(readonly () => 42); } void M2(System.Func<int> a) { _ = a(); } } "; var comp = CreateCompilation(csharp); comp.VerifyDiagnostics( // (6,9): error CS7036: There is no argument given that corresponds to the required parameter 'a' of 'S.M2(Func<int>)' // M2(readonly () => 42); Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "M2").WithArguments("a", "S.M2(System.Func<int>)").WithLocation(6, 9), // (6,12): error CS1026: ) expected // M2(readonly () => 42); Diagnostic(ErrorCode.ERR_CloseParenExpected, "readonly").WithLocation(6, 12), // (6,12): error CS1002: ; expected // M2(readonly () => 42); Diagnostic(ErrorCode.ERR_SemicolonExpected, "readonly").WithLocation(6, 12), // (6,12): error CS0106: The modifier 'readonly' is not valid for this item // M2(readonly () => 42); Diagnostic(ErrorCode.ERR_BadMemberFlag, "readonly").WithArguments("readonly").WithLocation(6, 12), // (6,22): error CS8124: Tuple must contain at least two elements. // M2(readonly () => 42); Diagnostic(ErrorCode.ERR_TupleTooFewElements, ")").WithLocation(6, 22), // (6,24): error CS1001: Identifier expected // M2(readonly () => 42); Diagnostic(ErrorCode.ERR_IdentifierExpected, "=>").WithLocation(6, 24), // (6,24): error CS1003: Syntax error, ',' expected // M2(readonly () => 42); Diagnostic(ErrorCode.ERR_SyntaxError, "=>").WithArguments(",").WithLocation(6, 24), // (6,27): error CS1002: ; expected // M2(readonly () => 42); Diagnostic(ErrorCode.ERR_SemicolonExpected, "42").WithLocation(6, 27), // (6,29): error CS1002: ; expected // M2(readonly () => 42); Diagnostic(ErrorCode.ERR_SemicolonExpected, ")").WithLocation(6, 29), // (6,29): error CS1513: } expected // M2(readonly () => 42); Diagnostic(ErrorCode.ERR_RbraceExpected, ")").WithLocation(6, 29)); } [Fact] public void ReadOnlyIndexer() { var csharp = @" public struct S1 { // ok public readonly int this[int i] => i; } public struct S2 { // ok public int this[int i] { readonly get => i; set {} } } public struct S3 { // error public int this[int i] { readonly get { return i; } readonly set {} } } public struct S4 { // error public int this[int i] { readonly get { return i; } } } public struct S5 { // error public readonly int this[int i] { readonly get { return i; } set { } } } public struct S6 { // error public static readonly int this[int i] => i; } "; var comp = CreateCompilation(csharp); comp.VerifyDiagnostics( // (21,16): error CS8660: Cannot specify 'readonly' modifiers on both accessors of property or indexer 'S3.this[int]'. Instead, put a 'readonly' modifier on the property itself. // public int this[int i] Diagnostic(ErrorCode.ERR_DuplicatePropertyReadOnlyMods, "this").WithArguments("S3.this[int]").WithLocation(21, 16), // (31,16): error CS8663: 'S4.this[int]': 'readonly' can only be used on accessors if the property or indexer has both a get and a set accessor // public int this[int i] Diagnostic(ErrorCode.ERR_ReadOnlyModMissingAccessor, "this").WithArguments("S4.this[int]").WithLocation(31, 16), // (42,18): error CS8659: Cannot specify 'readonly' modifiers on both property or indexer 'S5.this[int]' and its accessor. Remove one of them. // readonly get { return i; } Diagnostic(ErrorCode.ERR_InvalidPropertyReadOnlyMods, "get").WithArguments("S5.this[int]").WithLocation(42, 18), // (50,32): error CS0106: The modifier 'static' is not valid for this item // public static readonly int this[int i] => i; Diagnostic(ErrorCode.ERR_BadMemberFlag, "this").WithArguments("static").WithLocation(50, 32)); } [Fact] public void ReadOnlyFieldLikeEvent() { var csharp = @" using System; public struct S1 { public readonly event Action<EventArgs> E; public void M() { E?.Invoke(new EventArgs()); } } "; var comp = CreateCompilation(csharp); comp.VerifyDiagnostics( // (6,45): error CS8661: Field-like event 'S1.E' cannot be 'readonly'. // public readonly event Action<EventArgs> E; Diagnostic(ErrorCode.ERR_FieldLikeEventCantBeReadOnly, "E").WithArguments("S1.E").WithLocation(6, 45)); } [Fact] public void ReadOnlyEventExplicitAddRemove() { var csharp = @" using System; public struct S1 { public readonly event Action<EventArgs> E { add {} remove {} } } "; var comp = CreateCompilation(csharp); comp.VerifyDiagnostics(); } [Fact] public void ReadOnlyStaticEvent() { var csharp = @" using System; public struct S1 { public static readonly event Action<EventArgs> E { add {} remove {} } } "; var comp = CreateCompilation(csharp); comp.VerifyDiagnostics( // (6,52): error CS8656: Static member 'S1.E' cannot be marked 'readonly'. // public static readonly event Action<EventArgs> E Diagnostic(ErrorCode.ERR_StaticMemberCantBeReadOnly, "E").WithArguments("S1.E").WithLocation(6, 52)); } [Fact] public void ReadOnlyEventReadOnlyAccessors() { var csharp = @" using System; public struct S1 { public event Action<EventArgs> E { readonly add {} readonly remove {} } } "; var comp = CreateCompilation(csharp); comp.VerifyDiagnostics( // (8,9): error CS1609: Modifiers cannot be placed on event accessor declarations // readonly add {} Diagnostic(ErrorCode.ERR_NoModifiersOnAccessor, "readonly").WithLocation(8, 9), // (9,9): error CS1609: Modifiers cannot be placed on event accessor declarations // readonly remove {} Diagnostic(ErrorCode.ERR_NoModifiersOnAccessor, "readonly").WithLocation(9, 9)); } [Fact] public void ReadOnlyMembers_LangVersion() { var csharp = @" using System; public struct S { public readonly void M() {} public readonly int P1 => 42; public int P2 { readonly get => 123; set {} } public int P3 { get => 123; readonly set {} } public readonly int this[int i] => i; public int this[int i, int j] { readonly get => i + j; set {} } public readonly event Action<EventArgs> E { add {} remove {} } } "; var comp = CreateCompilation(csharp, parseOptions: TestOptions.Regular7_3); comp.VerifyDiagnostics( // (6,12): error CS8652: The feature 'readonly members' is not available in C# 7.3. Please use language version 8.0 or greater. // public readonly void M() {} Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "readonly").WithArguments("readonly members", "8.0").WithLocation(6, 12), // (8,12): error CS8652: The feature 'readonly members' is not available in C# 7.3. Please use language version 8.0 or greater. // public readonly int P1 => 42; Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "readonly").WithArguments("readonly members", "8.0").WithLocation(8, 12), // (9,21): error CS8652: The feature 'readonly members' is not available in C# 7.3. Please use language version 8.0 or greater. // public int P2 { readonly get => 123; set {} } Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "readonly").WithArguments("readonly members", "8.0").WithLocation(9, 21), // (10,33): error CS8652: The feature 'readonly members' is not available in C# 7.3. Please use language version 8.0 or greater. // public int P3 { get => 123; readonly set {} } Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "readonly").WithArguments("readonly members", "8.0").WithLocation(10, 33), // (12,12): error CS8652: The feature 'readonly members' is not available in C# 7.3. Please use language version 8.0 or greater. // public readonly int this[int i] => i; Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "readonly").WithArguments("readonly members", "8.0").WithLocation(12, 12), // (13,37): error CS8652: The feature 'readonly members' is not available in C# 7.3. Please use language version 8.0 or greater. // public int this[int i, int j] { readonly get => i + j; set {} } Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "readonly").WithArguments("readonly members", "8.0").WithLocation(13, 37), // (15,12): error CS8652: The feature 'readonly members' is not available in C# 7.3. Please use language version 8.0 or greater. // public readonly event Action<EventArgs> E { add {} remove {} } Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "readonly").WithArguments("readonly members", "8.0").WithLocation(15, 12)); comp = CreateCompilation(csharp); comp.VerifyDiagnostics(); } [Fact] public void ReadOnlyMethod_CompoundPropertyAssignment() { var csharp = @" struct S { int P1 { get => 123; set {} } int P2 { readonly get => 123; set {} } int P3 { get => 123; readonly set {} } readonly int P4 { get => 123; set {} } void M1() { // ok P1 += 1; P2 += 1; P3 += 1; P4 += 1; } readonly void M2() { P1 += 1; // error P2 += 1; // error P3 += 1; // warning P4 += 1; // ok } }"; var comp = CreateCompilation(csharp); comp.VerifyDiagnostics( // (20,9): error CS1604: Cannot assign to 'P1' because it is read-only // P1 += 1; // error Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "P1").WithArguments("P1").WithLocation(20, 9), // (21,9): error CS1604: Cannot assign to 'P2' because it is read-only // P2 += 1; // error Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "P2").WithArguments("P2").WithLocation(21, 9), // (22,9): warning CS8655: Call to non-readonly member 'S.P3.get' from a 'readonly' member results in an implicit copy of 'this'. // P3 += 1; // warning Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "P3").WithArguments("S.P3.get", "this").WithLocation(22, 9)); } } }