From 0d15dd1ac5fe9c92c8ee99c5231656790f969f07 Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Mon, 1 Apr 2019 16:11:56 -0700 Subject: [PATCH] Readonly members more test coverage (#34602) * Check parser behavior for bad expression property declaration * Make readonly modifiers more strict on accessors to match accessibility behavior * Fix tests * Improve implicit copy warning message. Add compound property assignment test. * Test ErrorCode.ERR_ReadOnlyModMissingAccessor with an indexer * Simplify error message for 'static readonly' members --- .../Portable/Binder/Binder_Invocation.cs | 2 +- .../Portable/CSharpResources.Designer.cs | 11 +- .../CSharp/Portable/CSharpResources.resx | 5 +- .../CSharp/Portable/Errors/ErrorCode.cs | 1 + .../Symbols/Source/SourceEventSymbol.cs | 2 +- .../Source/SourceOrdinaryMethodSymbol.cs | 2 +- .../Source/SourcePropertyAccessorSymbol.cs | 2 +- .../Symbols/Source/SourcePropertySymbol.cs | 8 +- .../Portable/xlf/CSharpResources.cs.xlf | 9 +- .../Portable/xlf/CSharpResources.de.xlf | 9 +- .../Portable/xlf/CSharpResources.es.xlf | 9 +- .../Portable/xlf/CSharpResources.fr.xlf | 9 +- .../Portable/xlf/CSharpResources.it.xlf | 9 +- .../Portable/xlf/CSharpResources.ja.xlf | 9 +- .../Portable/xlf/CSharpResources.ko.xlf | 9 +- .../Portable/xlf/CSharpResources.pl.xlf | 9 +- .../Portable/xlf/CSharpResources.pt-BR.xlf | 9 +- .../Portable/xlf/CSharpResources.ru.xlf | 9 +- .../Portable/xlf/CSharpResources.tr.xlf | 9 +- .../Portable/xlf/CSharpResources.zh-Hans.xlf | 9 +- .../Portable/xlf/CSharpResources.zh-Hant.xlf | 9 +- .../CodeGen/CodeGenReadonlyStructTests.cs | 57 ++++--- .../Semantics/ReadOnlyStructsTests.cs | 150 +++++++++++++----- .../Syntax/Parsing/DeclarationParsingTests.cs | 21 +++ 24 files changed, 280 insertions(+), 98 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs index e8ce99e6c877f..79d4b61d2aebb 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs @@ -1153,7 +1153,7 @@ ContainingMemberOrLambda is MethodSymbol containingMethod && !method.IsEffectivelyReadOnly && !method.IsStatic) { - Error(diagnostics, ErrorCode.WRN_ImplicitCopyInReadOnlyMember, receiver.Syntax, method.Name, ThisParameterSymbol.SymbolName); + Error(diagnostics, ErrorCode.WRN_ImplicitCopyInReadOnlyMember, receiver.Syntax, method, ThisParameterSymbol.SymbolName); return false; } diff --git a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs index 7e433b68eebd9..4cd42183324de 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs +++ b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs @@ -8763,6 +8763,15 @@ internal static string ERR_RbraceExpected { } } + /// + /// Looks up a localized string similar to '{0}': 'readonly' can only be used on accessors if the property or indexer has both a get and a set accessor. + /// + internal static string ERR_ReadOnlyModMissingAccessor { + get { + return ResourceManager.GetString("ERR_ReadOnlyModMissingAccessor", resourceCulture); + } + } + /// /// Looks up a localized string similar to Members of readonly field '{0}' of type '{1}' cannot be assigned with an object initializer because it is of a value type. /// @@ -9664,7 +9673,7 @@ internal static string ERR_StaticLocalFunctionCannotCaptureVariable { } /// - /// Looks up a localized string similar to Static member '{0}' cannot be marked 'readonly' because readonly members cannot modify 'this' and static members do not have a 'this' parameter.. + /// Looks up a localized string similar to Static member '{0}' cannot be marked 'readonly'.. /// internal static string ERR_StaticMemberCantBeReadOnly { get { diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index ac50b96df76a7..a54ea8a393563 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -5431,7 +5431,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Call to non-readonly member from a 'readonly' member results in an implicit copy. - Static member '{0}' cannot be marked 'readonly' because readonly members cannot modify 'this' and static members do not have a 'this' parameter. + Static member '{0}' cannot be marked 'readonly'. Auto-implemented 'set' accessor '{0}' cannot be marked 'readonly'. @@ -5451,6 +5451,9 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Both partial method declarations must be readonly or neither may be readonly + + '{0}': 'readonly' can only be used on accessors if the property or indexer has both a get and a set accessor + Argument of type '{0}' cannot be used for parameter '{2}' of type '{1}' in '{3}' due to differences in the nullability of reference types. diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index d3109183f418c..5ca4b45bb68cd 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -1691,6 +1691,7 @@ internal enum ErrorCode ERR_DuplicatePropertyReadOnlyMods = 8660, ERR_FieldLikeEventCantBeReadOnly = 8661, ERR_PartialMethodReadOnlyDifference = 8662, + ERR_ReadOnlyModMissingAccessor = 8663 #endregion diagnostics introduced for C# 8.0 diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventSymbol.cs index 934d1bedb3d98..3b3132eabbfef 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventSymbol.cs @@ -480,7 +480,7 @@ protected void CheckModifiersAndType(DiagnosticBag diagnostics) } else if (IsReadOnly && IsStatic) { - // Static member '{0}' cannot be marked 'readonly' because readonly members cannot modify 'this' and static members do not have a 'this' parameter. + // Static member '{0}' cannot be marked 'readonly'. diagnostics.Add(ErrorCode.ERR_StaticMemberCantBeReadOnly, location, this); } else if (IsReadOnly && HasAssociatedField) diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbol.cs index 976e84b621717..866dc30581168 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbol.cs @@ -940,7 +940,7 @@ private void CheckModifiers(bool hasBody, Location location, DiagnosticBag diagn } else if (IsStatic && IsDeclaredReadOnly) { - // Static member '{0}' cannot be marked 'readonly' because readonly members cannot modify 'this' and static members do not have a 'this' parameter. + // Static member '{0}' cannot be marked 'readonly'. diagnostics.Add(ErrorCode.ERR_StaticMemberCantBeReadOnly, location, this); } else if (IsAbstract && !ContainingType.IsAbstract && (ContainingType.TypeKind == TypeKind.Class || ContainingType.TypeKind == TypeKind.Submission)) diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertyAccessorSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertyAccessorSymbol.cs index 3621187ba38a7..1a38b3d4e3bbc 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertyAccessorSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertyAccessorSymbol.cs @@ -477,7 +477,7 @@ private void CheckModifiers(Location location, bool hasBody, bool isAutoProperty } else if (LocalDeclaredReadOnly && IsStatic) { - // Static member '{0}' cannot be marked 'readonly' because readonly members cannot modify 'this' and static members do not have a 'this' parameter. + // Static member '{0}' cannot be marked 'readonly'. diagnostics.Add(ErrorCode.ERR_StaticMemberCantBeReadOnly, location, this); } else if (LocalDeclaredReadOnly && _isAutoPropertyAccessor && MethodKind == MethodKind.PropertySet) diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs index f987626ce4a37..d8625339963f2 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs @@ -385,6 +385,12 @@ private SourcePropertySymbol( { diagnostics.Add(ErrorCode.ERR_AccessModMissingAccessor, location, this); } + + // Check that 'readonly' is not set on the one accessor. + if (accessor.LocalDeclaredReadOnly) + { + diagnostics.Add(ErrorCode.ERR_ReadOnlyModMissingAccessor, location, this); + } } } } @@ -910,7 +916,7 @@ private void CheckModifiers(Location location, bool isIndexer, DiagnosticBag dia } else if (IsStatic && HasReadOnlyModifier) { - // Static member '{0}' cannot be marked 'readonly' because readonly members cannot modify 'this' and static members do not have a 'this' parameter. + // Static member '{0}' cannot be marked 'readonly'. diagnostics.Add(ErrorCode.ERR_StaticMemberCantBeReadOnly, location, this); } else if (IsOverride && (IsNew || IsVirtual)) diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index cb1182d4ebfb4..9f7986ccf71fa 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -292,6 +292,11 @@ Dílčí vzor vlastnosti vyžaduje odkaz na vlastnost nebo pole k přiřazení, např. „{{ Name: {0} }}“. + + '{0}': 'readonly' can only be used on accessors if the property or indexer has both a get and a set accessor + '{0}': 'readonly' can only be used on accessors if the property or indexer has both a get and a set accessor + + Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. Přiřazení odkazu {1} k {0} nelze provést, protože {1} má užší řídicí obor než {0}. @@ -318,8 +323,8 @@ - Static member '{0}' cannot be marked 'readonly' because readonly members cannot modify 'this' and static members do not have a 'this' parameter. - Static member '{0}' cannot be marked 'readonly' because readonly members cannot modify 'this' and static members do not have a 'this' parameter. + Static member '{0}' cannot be marked 'readonly'. + Static member '{0}' cannot be marked 'readonly'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index 83706da6278ab..02acca338acc9 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -292,6 +292,11 @@ Ein Eigenschaftsteilmuster erfordert einen Verweis auf die abzugleichende Eigenschaft oder das abzugleichende Feld. Beispiel: "{{ Name: {0} }}" + + '{0}': 'readonly' can only be used on accessors if the property or indexer has both a get and a set accessor + '{0}': 'readonly' can only be used on accessors if the property or indexer has both a get and a set accessor + + Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. ref-assign von "{1}" zu "{0}" ist nicht möglich, weil "{1}" einen geringeren Escapebereich als "{0}" aufweist. @@ -318,8 +323,8 @@ - Static member '{0}' cannot be marked 'readonly' because readonly members cannot modify 'this' and static members do not have a 'this' parameter. - Static member '{0}' cannot be marked 'readonly' because readonly members cannot modify 'this' and static members do not have a 'this' parameter. + Static member '{0}' cannot be marked 'readonly'. + Static member '{0}' cannot be marked 'readonly'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index 4b7f83794f0fe..7c72c53406526 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -292,6 +292,11 @@ El subpatrón de una propiedad requiere una referencia a la propiedad o al campo que debe coincidir; por ejemplo, "{{ Name: {0} }}" + + '{0}': 'readonly' can only be used on accessors if the property or indexer has both a get and a set accessor + '{0}': 'readonly' can only be used on accessors if the property or indexer has both a get and a set accessor + + Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. No se puede asignar referencia "{1}" a "{0}" porque "{1}" tiene un ámbito de escape más limitado que "{0}". @@ -318,8 +323,8 @@ - Static member '{0}' cannot be marked 'readonly' because readonly members cannot modify 'this' and static members do not have a 'this' parameter. - Static member '{0}' cannot be marked 'readonly' because readonly members cannot modify 'this' and static members do not have a 'this' parameter. + Static member '{0}' cannot be marked 'readonly'. + Static member '{0}' cannot be marked 'readonly'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index a32054f842dfb..8758aba111443 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -292,6 +292,11 @@ Un sous-modèle de propriété nécessite une correspondance de la référence à la propriété ou au champ. Exemple : '{{ Nom: {0} }}' + + '{0}': 'readonly' can only be used on accessors if the property or indexer has both a get and a set accessor + '{0}': 'readonly' can only be used on accessors if the property or indexer has both a get and a set accessor + + Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. Impossible d'effectuer une assignation par référence de '{1}' vers '{0}', car '{1}' a une portée de sortie plus limitée que '{0}'. @@ -318,8 +323,8 @@ - Static member '{0}' cannot be marked 'readonly' because readonly members cannot modify 'this' and static members do not have a 'this' parameter. - Static member '{0}' cannot be marked 'readonly' because readonly members cannot modify 'this' and static members do not have a 'this' parameter. + Static member '{0}' cannot be marked 'readonly'. + Static member '{0}' cannot be marked 'readonly'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index d5b069e99d2a0..d999b15dec79c 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -292,6 +292,11 @@ Con un criterio secondario di proprietà è richiesto un riferimento alla proprietà o al campo da abbinare, ad esempio '{{ Name: {0} }}' + + '{0}': 'readonly' can only be used on accessors if the property or indexer has both a get and a set accessor + '{0}': 'readonly' can only be used on accessors if the property or indexer has both a get and a set accessor + + Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. Non è possibile assegnare '{1}' a '{0}' come ref perché l'ambito di escape di '{1}' è ridotto rispetto a quello di '{0}'. @@ -318,8 +323,8 @@ - Static member '{0}' cannot be marked 'readonly' because readonly members cannot modify 'this' and static members do not have a 'this' parameter. - Static member '{0}' cannot be marked 'readonly' because readonly members cannot modify 'this' and static members do not have a 'this' parameter. + Static member '{0}' cannot be marked 'readonly'. + Static member '{0}' cannot be marked 'readonly'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index 424d0f5001507..c68c4ddb2e125 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -292,6 +292,11 @@ プロパティ サブパターンには、一致させるプロパティまたはフィールドへの参照が必要です。例: '{{ Name: {0} }}' + + '{0}': 'readonly' can only be used on accessors if the property or indexer has both a get and a set accessor + '{0}': 'readonly' can only be used on accessors if the property or indexer has both a get and a set accessor + + Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. '{1}' を '{0}' に ref 割り当てすることはできません。'{1}' のエスケープ スコープが '{0}' より狭いためです。 @@ -318,8 +323,8 @@ - Static member '{0}' cannot be marked 'readonly' because readonly members cannot modify 'this' and static members do not have a 'this' parameter. - Static member '{0}' cannot be marked 'readonly' because readonly members cannot modify 'this' and static members do not have a 'this' parameter. + Static member '{0}' cannot be marked 'readonly'. + Static member '{0}' cannot be marked 'readonly'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index ff030dc9dc548..4dba4aef41bed 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -292,6 +292,11 @@ 속성 하위 패턴은 일치시킬 속성 또는 필드에 대한 참조가 필요합니다(예: '{{ Name: {0} }}') + + '{0}': 'readonly' can only be used on accessors if the property or indexer has both a get and a set accessor + '{0}': 'readonly' can only be used on accessors if the property or indexer has both a get and a set accessor + + Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. '{1}'을(를) '{0}'에 참조 할당할 수 없습니다. '{1}'이(가) '{0}'보다 이스케이프 범위가 좁기 때문입니다. @@ -318,8 +323,8 @@ - Static member '{0}' cannot be marked 'readonly' because readonly members cannot modify 'this' and static members do not have a 'this' parameter. - Static member '{0}' cannot be marked 'readonly' because readonly members cannot modify 'this' and static members do not have a 'this' parameter. + Static member '{0}' cannot be marked 'readonly'. + Static member '{0}' cannot be marked 'readonly'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index bd7cbdef1ecb9..6813b048708d9 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -292,6 +292,11 @@ Wzorzec podrzędny właściwości wymaga odwołania do właściwości lub pola, które należy dopasować, na przykład „{{ Name: {0} }}” + + '{0}': 'readonly' can only be used on accessors if the property or indexer has both a get and a set accessor + '{0}': 'readonly' can only be used on accessors if the property or indexer has both a get and a set accessor + + Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. Nie można przypisać odwołania elementu „{1}” do elementu „{0}”, ponieważ element „{1}” ma węższy zakres wyjścia niż element „{0}”. @@ -318,8 +323,8 @@ - Static member '{0}' cannot be marked 'readonly' because readonly members cannot modify 'this' and static members do not have a 'this' parameter. - Static member '{0}' cannot be marked 'readonly' because readonly members cannot modify 'this' and static members do not have a 'this' parameter. + Static member '{0}' cannot be marked 'readonly'. + Static member '{0}' cannot be marked 'readonly'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index 410f65360beac..cdbd4d1900c31 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -292,6 +292,11 @@ Um subpadrão de propriedade requer que uma referência à propriedade ou ao campo seja correspondida, por exemplo, '{{ Name: {0} }}' + + '{0}': 'readonly' can only be used on accessors if the property or indexer has both a get and a set accessor + '{0}': 'readonly' can only be used on accessors if the property or indexer has both a get and a set accessor + + Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. Não é possível atribuir ref '{1}' a '{0}' porque '{1}' tem um escopo de escape mais limitado que '{0}'. @@ -318,8 +323,8 @@ - Static member '{0}' cannot be marked 'readonly' because readonly members cannot modify 'this' and static members do not have a 'this' parameter. - Static member '{0}' cannot be marked 'readonly' because readonly members cannot modify 'this' and static members do not have a 'this' parameter. + Static member '{0}' cannot be marked 'readonly'. + Static member '{0}' cannot be marked 'readonly'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index 257ce817a2bd5..7765d62f1af5b 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -292,6 +292,11 @@ Для вложенного шаблона свойств требуется ссылка на свойство или поле для сопоставления, например, "{{ Name: {0} }}". + + '{0}': 'readonly' can only be used on accessors if the property or indexer has both a get and a set accessor + '{0}': 'readonly' can only be used on accessors if the property or indexer has both a get and a set accessor + + Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. Не удается присвоить по ссылке "{1}" для "{0}", так как escape-область у "{1}" уже, чем у "{0}". @@ -318,8 +323,8 @@ - Static member '{0}' cannot be marked 'readonly' because readonly members cannot modify 'this' and static members do not have a 'this' parameter. - Static member '{0}' cannot be marked 'readonly' because readonly members cannot modify 'this' and static members do not have a 'this' parameter. + Static member '{0}' cannot be marked 'readonly'. + Static member '{0}' cannot be marked 'readonly'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index 53b887fc7cb13..28c76f64fbc7f 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -292,6 +292,11 @@ Bir özellik alt deseni, özellik veya alan başvurusunun eşleşmesini gerektiriyor, ör. '{{ Name: {0} }}' + + '{0}': 'readonly' can only be used on accessors if the property or indexer has both a get and a set accessor + '{0}': 'readonly' can only be used on accessors if the property or indexer has both a get and a set accessor + + Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. '{1}', '{0}' öğesinden daha dar bir kaçış kapsamı içerdiğinden '{0}' öğesine '{1}' ref ataması yapılamıyor. @@ -318,8 +323,8 @@ - Static member '{0}' cannot be marked 'readonly' because readonly members cannot modify 'this' and static members do not have a 'this' parameter. - Static member '{0}' cannot be marked 'readonly' because readonly members cannot modify 'this' and static members do not have a 'this' parameter. + Static member '{0}' cannot be marked 'readonly'. + Static member '{0}' cannot be marked 'readonly'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index 31f73e9e4e18d..af9281203bead 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -292,6 +292,11 @@ 属性子模式需要引用要匹配的属性或字段,例如,"{{ Name: {0} }}" + + '{0}': 'readonly' can only be used on accessors if the property or indexer has both a get and a set accessor + '{0}': 'readonly' can only be used on accessors if the property or indexer has both a get and a set accessor + + Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. 无法将“{1}”重新赋值为“{0}”,因为“{1}”具有比“{0}”更窄的转义范围。 @@ -318,8 +323,8 @@ - Static member '{0}' cannot be marked 'readonly' because readonly members cannot modify 'this' and static members do not have a 'this' parameter. - Static member '{0}' cannot be marked 'readonly' because readonly members cannot modify 'this' and static members do not have a 'this' parameter. + Static member '{0}' cannot be marked 'readonly'. + Static member '{0}' cannot be marked 'readonly'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index 593efd8bf8b3f..543c440fddacd 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -292,6 +292,11 @@ 屬性子模式需要對屬性或欄位的參考才能比對,例如 '{{ Name: {0} }}' + + '{0}': 'readonly' can only be used on accessors if the property or indexer has both a get and a set accessor + '{0}': 'readonly' can only be used on accessors if the property or indexer has both a get and a set accessor + + Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. 不能將 '{1}' 參考指派至 '{0}',因為 '{1}' 的逸出範圍比 '{0}' 還要窄。 @@ -318,8 +323,8 @@ - Static member '{0}' cannot be marked 'readonly' because readonly members cannot modify 'this' and static members do not have a 'this' parameter. - Static member '{0}' cannot be marked 'readonly' because readonly members cannot modify 'this' and static members do not have a 'this' parameter. + Static member '{0}' cannot be marked 'readonly'. + Static member '{0}' cannot be marked 'readonly'. diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenReadonlyStructTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenReadonlyStructTests.cs index b9238f6dd9356..ac974bdb566ba 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenReadonlyStructTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenReadonlyStructTests.cs @@ -1319,8 +1319,8 @@ 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 int P3 { readonly get => 123; set {} } + public int P4 { get => 123; readonly set {} } public static int P5 { get; set; } } "; @@ -1358,8 +1358,12 @@ void validate(ModuleSymbol module) Assert.False(peModule.Module.HasIsReadOnlyAttribute(((PEPropertySymbol)p3).Handle)); Assert.True(peModule.Module.HasIsReadOnlyAttribute(((PEMethodSymbol)p3.GetMethod).Handle)); Assert.False(peModule.Module.HasIsReadOnlyAttribute(((PEMethodSymbol)p3.GetMethod).Signature.ReturnParam.Handle)); + Assert.False(peModule.Module.HasIsReadOnlyAttribute(((PEMethodSymbol)p3.SetMethod).Handle)); + Assert.False(peModule.Module.HasIsReadOnlyAttribute(((PEMethodSymbol)p3.SetMethod).Signature.ReturnParam.Handle)); Assert.False(peModule.Module.HasIsReadOnlyAttribute(((PEPropertySymbol)p4).Handle)); + Assert.False(peModule.Module.HasIsReadOnlyAttribute(((PEMethodSymbol)p4.GetMethod).Handle)); + Assert.False(peModule.Module.HasIsReadOnlyAttribute(((PEMethodSymbol)p4.GetMethod).Signature.ReturnParam.Handle)); Assert.True(peModule.Module.HasIsReadOnlyAttribute(((PEMethodSymbol)p4.SetMethod).Handle)); Assert.False(peModule.Module.HasIsReadOnlyAttribute(((PEMethodSymbol)p4.SetMethod).Signature.ReturnParam.Handle)); @@ -1453,8 +1457,8 @@ public readonly void M2() {} public int P1 { get; } public readonly int P2 => 42; - public int P3 { readonly get => 123; } - public int P4 { readonly set {} } + public int P3 { readonly get => 123; set {} } + public int P4 { get => 123; readonly set {} } public static int P5 { get; set; } } "; @@ -1490,8 +1494,12 @@ void validate(ModuleSymbol module) Assert.False(peModule.Module.HasIsReadOnlyAttribute(((PEPropertySymbol)p3).Handle)); Assert.True(peModule.Module.HasIsReadOnlyAttribute(((PEMethodSymbol)p3.GetMethod).Handle)); Assert.False(peModule.Module.HasIsReadOnlyAttribute(((PEMethodSymbol)p3.GetMethod).Signature.ReturnParam.Handle)); + Assert.False(peModule.Module.HasIsReadOnlyAttribute(((PEMethodSymbol)p3.SetMethod).Handle)); + Assert.False(peModule.Module.HasIsReadOnlyAttribute(((PEMethodSymbol)p3.SetMethod).Signature.ReturnParam.Handle)); Assert.False(peModule.Module.HasIsReadOnlyAttribute(((PEPropertySymbol)p4).Handle)); + Assert.False(peModule.Module.HasIsReadOnlyAttribute(((PEMethodSymbol)p4.GetMethod).Handle)); + Assert.False(peModule.Module.HasIsReadOnlyAttribute(((PEMethodSymbol)p4.GetMethod).Signature.ReturnParam.Handle)); Assert.True(peModule.Module.HasIsReadOnlyAttribute(((PEMethodSymbol)p4.SetMethod).Handle)); Assert.False(peModule.Module.HasIsReadOnlyAttribute(((PEMethodSymbol)p4.SetMethod).Signature.ReturnParam.Handle)); @@ -1518,8 +1526,8 @@ 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 int P3 { readonly get => 123; set {} } + public int P4 { get => 123; readonly set {} } public static int P5 { get; set; } public readonly event Action E { add {} remove {} } } @@ -1535,6 +1543,7 @@ public event Action E { add {} remove {} } } "; var externalComp = CreateCompilation(external); + externalComp.VerifyDiagnostics(); verify(externalComp); var comp = CreateCompilation("", references: new[] { externalComp.EmitToImageReference() }); @@ -1554,7 +1563,11 @@ void verify(CSharpCompilation comp) verifyReadOnly(s1.GetProperty("P1").SetMethod, false, false, RefKind.Ref); verifyReadOnly(s1.GetProperty("P2").GetMethod, true, true, RefKind.RefReadOnly); + verifyReadOnly(s1.GetProperty("P3").GetMethod, true, true, RefKind.RefReadOnly); + verifyReadOnly(s1.GetProperty("P3").SetMethod, false, false, RefKind.Ref); + + verifyReadOnly(s1.GetProperty("P4").GetMethod, false, false, RefKind.Ref); verifyReadOnly(s1.GetProperty("P4").SetMethod, true, true, RefKind.RefReadOnly); verifyReadOnly(s1.GetProperty("P5").GetMethod, false, false, null); @@ -1696,9 +1709,9 @@ static void Main() var verifier = CompileAndVerify(csharp, expectedOutput: "123"); verifier.VerifyDiagnostics( - // (9,9): warning CS8655: Call to non-readonly member 'M2' from a 'readonly' member results in an implicit copy of 'this'. + // (9,9): warning CS8655: Call to non-readonly member 'S.M2()' from a 'readonly' member results in an implicit copy of 'this'. // M2(); - Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "M2").WithArguments("M2", "this").WithLocation(9, 9)); + Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "M2").WithArguments("S.M2()", "this").WithLocation(9, 9)); verifier.VerifyIL("S.M1", @" { @@ -2195,15 +2208,15 @@ readonly void M() var verifier = CompileAndVerify(csharp); verifier.VerifyDiagnostics( - // (12,9): warning CS8655: Call to non-readonly member 'ToString' from a 'readonly' member results in an implicit copy of 'this'. + // (12,9): warning CS8655: Call to non-readonly member 'S.ToString()' from a 'readonly' member results in an implicit copy of 'this'. // ToString(); - Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "ToString").WithArguments("ToString", "this").WithLocation(12, 9), - // (13,9): warning CS8655: Call to non-readonly member 'GetHashCode' from a 'readonly' member results in an implicit copy of 'this'. + Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "ToString").WithArguments("S.ToString()", "this").WithLocation(12, 9), + // (13,9): warning CS8655: Call to non-readonly member 'S.GetHashCode()' from a 'readonly' member results in an implicit copy of 'this'. // GetHashCode(); - Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "GetHashCode").WithArguments("GetHashCode", "this").WithLocation(13, 9), - // (14,9): warning CS8655: Call to non-readonly member 'Equals' from a 'readonly' member results in an implicit copy of 'this'. + Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "GetHashCode").WithArguments("S.GetHashCode()", "this").WithLocation(13, 9), + // (14,9): warning CS8655: Call to non-readonly member 'S.Equals(object)' from a 'readonly' member results in an implicit copy of 'this'. // Equals(null); - Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "Equals").WithArguments("Equals", "this").WithLocation(14, 9)); + Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "Equals").WithArguments("S.Equals(object)", "this").WithLocation(14, 9)); // Verify that calls to non-readonly overrides pass the address of a temp, not the address of 'this' verifier.VerifyIL("S.M", @" @@ -2348,18 +2361,18 @@ readonly void M() var verifier = CompileAndVerify(csharp); verifier.VerifyDiagnostics( - // (12,9): warning CS8655: Call to non-readonly member 'GetType' from a 'readonly' member results in an implicit copy of 'this'. + // (12,9): warning CS8655: Call to non-readonly member 'S.GetType()' from a 'readonly' member results in an implicit copy of 'this'. // GetType(); - Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "GetType").WithArguments("GetType", "this").WithLocation(12, 9), - // (13,9): warning CS8655: Call to non-readonly member 'ToString' from a 'readonly' member results in an implicit copy of 'this'. + Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "GetType").WithArguments("S.GetType()", "this").WithLocation(12, 9), + // (13,9): warning CS8655: Call to non-readonly member 'S.ToString()' from a 'readonly' member results in an implicit copy of 'this'. // ToString(); - Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "ToString").WithArguments("ToString", "this").WithLocation(13, 9), - // (14,9): warning CS8655: Call to non-readonly member 'GetHashCode' from a 'readonly' member results in an implicit copy of 'this'. + Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "ToString").WithArguments("S.ToString()", "this").WithLocation(13, 9), + // (14,9): warning CS8655: Call to non-readonly member 'S.GetHashCode()' from a 'readonly' member results in an implicit copy of 'this'. // GetHashCode(); - Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "GetHashCode").WithArguments("GetHashCode", "this").WithLocation(14, 9), - // (15,9): warning CS8655: Call to non-readonly member 'Equals' from a 'readonly' member results in an implicit copy of 'this'. + Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "GetHashCode").WithArguments("S.GetHashCode()", "this").WithLocation(14, 9), + // (15,9): warning CS8655: Call to non-readonly member 'S.Equals(object)' from a 'readonly' member results in an implicit copy of 'this'. // Equals(null); - Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "Equals").WithArguments("Equals", "this").WithLocation(15, 9)); + Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "Equals").WithArguments("S.Equals(object)", "this").WithLocation(15, 9)); // Verify that calls to new non-readonly members pass an address to a temp and that calls to base members use a box. verifier.VerifyIL("S.M", @" diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/ReadOnlyStructsTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/ReadOnlyStructsTests.cs index 6962bec7c028c..6c1c461694ec6 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/ReadOnlyStructsTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/ReadOnlyStructsTests.cs @@ -508,9 +508,9 @@ readonly void M2() "; var comp = CreateCompilation(csharp, options: TestOptions.UnsafeReleaseDll); comp.VerifyDiagnostics( - // (12,25): warning CS8655: Call to non-readonly member 'GetPinnableReference' from a 'readonly' member results in an implicit copy of 'this'. + // (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("GetPinnableReference", "this").WithLocation(12, 25)); + Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "this").WithArguments("S1.GetPinnableReference()", "this").WithLocation(12, 25)); } [Fact] @@ -582,9 +582,9 @@ public struct S { public int i; - public int P + public readonly int P { - readonly get + get { // should create local copy M(); @@ -614,9 +614,9 @@ static void Main() var verifier = CompileAndVerify(csharp, expectedOutput: "123"); verifier.VerifyDiagnostics( - // (11,13): warning CS8655: Call to non-readonly member 'M' from a 'readonly' member results in an implicit copy of 'this'. + // (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("M", "this").WithLocation(11, 13)); + Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "M").WithArguments("S.M()", "this").WithLocation(11, 13)); } [Fact] @@ -627,9 +627,9 @@ public struct S { public int i; - public int P1 + public readonly int P1 { - readonly get + get { // should create local copy _ = P2; // warning @@ -656,9 +656,9 @@ static void Main() var verifier = CompileAndVerify(csharp, expectedOutput: "123"); verifier.VerifyDiagnostics( - // (11,17): warning CS8655: Call to non-readonly member 'get_P2' from a 'readonly' member results in an implicit copy of 'this'. - // _ = P2; - Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "P2").WithArguments("get_P2", "this").WithLocation(11, 17)); + // (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] @@ -1123,7 +1123,7 @@ public static readonly int M() "; var comp = CreateCompilation(csharp); comp.VerifyDiagnostics( - // (5,32): error CS8656: Static member 'S.M()' cannot be marked 'readonly' because readonly members cannot modify 'this' and static members do not have a 'this' parameter. + // (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)); @@ -1139,9 +1139,9 @@ public void ReadOnlyStructProperty() public struct S { public int i; - public int P + public readonly int P { - readonly get + get { return i; } @@ -1165,12 +1165,16 @@ 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' because readonly members cannot modify 'this' and static members do not have a 'this' parameter. + // (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)); } @@ -1187,7 +1191,7 @@ public struct S "; var comp = CreateCompilation(csharp); comp.VerifyDiagnostics( - // (5,32): error CS8656: Static member 'S.P' cannot be marked 'readonly' because readonly members cannot modify 'this' and static members do not have a 'this' parameter. + // (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)); } @@ -1238,6 +1242,9 @@ public struct S "; 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), @@ -1247,6 +1254,9 @@ public struct S // (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), @@ -1267,7 +1277,7 @@ public void ReadOnlyProperty_RedundantReadOnlyAccessor() var csharp = @" public struct S { - public readonly int P { readonly get => 42; } + public readonly int P { readonly get => 42; set {} } } "; var comp = CreateCompilation(csharp); @@ -1284,15 +1294,15 @@ public void ReadOnlyStaticAutoProperty() public struct S { public static readonly int P1 { get; set; } - public static int P2 { readonly get; } + 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' because readonly members cannot modify 'this' and static members do not have a 'this' parameter. + // (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' because readonly members cannot modify 'this' and static members do not have a 'this' parameter. + // (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)); } @@ -1406,9 +1416,9 @@ readonly void M2() "; var comp = CreateCompilation(csharp); comp.VerifyDiagnostics( - // (13,27): warning CS8655: Call to non-readonly member 'GetEnumerator' from a 'readonly' member results in an implicit copy of 'this'. + // (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("GetEnumerator", "this").WithLocation(13, 27)); + Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "this").WithArguments("S1.GetEnumerator()", "this").WithLocation(13, 27)); } [Fact] @@ -1469,9 +1479,9 @@ public readonly async Task M2() "; var comp = CreateCompilationWithTasksExtensions(new[] { csharp, AsyncStreamsTypes }); comp.VerifyDiagnostics( - // (16,33): warning CS8655: Call to non-readonly member 'GetAsyncEnumerator' from a 'readonly' member results in an implicit copy of 'this'. + // (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("GetAsyncEnumerator", "this").WithLocation(16, 33)); + Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "this").WithArguments("S1.GetAsyncEnumerator()", "this").WithLocation(16, 33)); } [Fact] @@ -1513,9 +1523,9 @@ readonly void M2() "; var comp = CreateCompilation(csharp); comp.VerifyDiagnostics( - // (13,16): warning CS8655: Call to non-readonly member 'Dispose' from a 'readonly' member results in an implicit copy of 'this'. + // (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("Dispose", "this").WithLocation(13, 16)); + Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "this").WithArguments("S1.Dispose()", "this").WithLocation(13, 16)); } [Fact] @@ -1562,9 +1572,9 @@ public readonly void Deconstruct(out int x, out int y) "; var comp = CreateCompilation(csharp); comp.VerifyDiagnostics( - // (11,22): warning CS8655: Call to non-readonly member 'Deconstruct' from a 'readonly' member results in an implicit copy of 'this'. + // (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("Deconstruct", "this").WithLocation(11, 22)); + Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "this").WithArguments("S1.Deconstruct(out int, out int)", "this").WithLocation(11, 22)); } [Fact] @@ -1758,13 +1768,23 @@ readonly set {} public struct S4 { // error - public readonly int this[int i] + 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; @@ -1775,12 +1795,15 @@ public struct S5 // (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), - // (33,18): error CS8659: Cannot specify 'readonly' modifiers on both property or indexer 'S4.this[int]' and its accessor. Remove one of them. + // (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("S4.this[int]").WithLocation(33, 18), - // (40,32): error CS0106: The modifier 'static' is not valid for this item + 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(40, 32)); + Diagnostic(ErrorCode.ERR_BadMemberFlag, "this").WithArguments("static").WithLocation(50, 32)); } [Fact] @@ -1838,7 +1861,7 @@ public static readonly event Action E "; var comp = CreateCompilation(csharp); comp.VerifyDiagnostics( - // (6,52): error CS8656: Static member 'S1.E' cannot be marked 'readonly' because readonly members cannot modify 'this' and static members do not have a 'this' parameter. + // (6,52): error CS8656: Static member 'S1.E' cannot be marked 'readonly'. // public static readonly event Action E Diagnostic(ErrorCode.ERR_StaticMemberCantBeReadOnly, "E").WithArguments("S1.E").WithLocation(6, 52)); } @@ -1879,11 +1902,11 @@ public struct S public readonly void M() {} public readonly int P1 => 42; - public int P2 { readonly get => 123; } - public int P3 { readonly set {} } + 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; } + public int this[int i, int j] { readonly get => i + j; set {} } public readonly event Action E { add {} remove {} } } @@ -1891,22 +1914,22 @@ public readonly event Action E { add {} remove {} } var comp = CreateCompilation(csharp, parseOptions: TestOptions.Regular7_3); comp.VerifyDiagnostics( // (6,12): error CS8652: The feature 'readonly members' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. - // public readonly void M1() {} + // public readonly void M() {} Diagnostic(ErrorCode.ERR_FeatureInPreview, "readonly").WithArguments("readonly members").WithLocation(6, 12), // (8,12): error CS8652: The feature 'readonly members' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // public readonly int P1 => 42; Diagnostic(ErrorCode.ERR_FeatureInPreview, "readonly").WithArguments("readonly members").WithLocation(8, 12), // (9,21): error CS8652: The feature 'readonly members' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. - // public int P2 { readonly get => 123; } + // public int P2 { readonly get => 123; set {} } Diagnostic(ErrorCode.ERR_FeatureInPreview, "readonly").WithArguments("readonly members").WithLocation(9, 21), - // (10,21): error CS8652: The feature 'readonly members' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. - // public int P3 { readonly set {} } - Diagnostic(ErrorCode.ERR_FeatureInPreview, "readonly").WithArguments("readonly members").WithLocation(10, 21), + // (10,33): error CS8652: The feature 'readonly members' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // public int P3 { get => 123; readonly set {} } + Diagnostic(ErrorCode.ERR_FeatureInPreview, "readonly").WithArguments("readonly members").WithLocation(10, 33), // (12,12): error CS8652: The feature 'readonly members' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // public readonly int this[int i] => i; Diagnostic(ErrorCode.ERR_FeatureInPreview, "readonly").WithArguments("readonly members").WithLocation(12, 12), // (13,37): error CS8652: The feature 'readonly members' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. - // public int this[int i, int j] { readonly get => i + j; } + // public int this[int i, int j] { readonly get => i + j; set {} } Diagnostic(ErrorCode.ERR_FeatureInPreview, "readonly").WithArguments("readonly members").WithLocation(13, 37), // (15,12): error CS8652: The feature 'readonly members' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // public readonly event Action E { add {} remove {} } @@ -1915,5 +1938,46 @@ public readonly event Action E { add {} remove {} } 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)); + } } } diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/DeclarationParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/DeclarationParsingTests.cs index 9fb0b11fae15e..1522282190ccc 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/DeclarationParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/DeclarationParsingTests.cs @@ -2907,6 +2907,27 @@ public void TestStructGetterPropertyWithReadonly() Assert.Equal(SyntaxKind.ReadOnlyKeyword, accessors[0].Modifiers[0].Kind()); } + [Fact] + public void TestStructBadExpressionProperty() + { + var text = +@"public struct S +{ + public int P readonly => 0; +} +"; + var file = this.ParseFile(text, TestOptions.Regular); + + Assert.NotNull(file); + Assert.Equal(1, file.Members.Count); + Assert.Equal(text, file.ToString()); + + Assert.Equal(3, file.Errors().Length); + Assert.Equal(ErrorCode.ERR_SemicolonExpected, (ErrorCode)file.Errors()[0].Code); + Assert.Equal(ErrorCode.ERR_InvalidMemberDecl, (ErrorCode)file.Errors()[1].Code); + Assert.Equal(ErrorCode.ERR_InvalidMemberDecl, (ErrorCode)file.Errors()[2].Code); + } + [Fact] public void TestClassMethodWithParameter() {