未验证 提交 9947942d 编写于 作者: R Rikki Gibson 提交者: GitHub

Readonly members warnings and semantic errors (#34022)

* readonly members warnings WIP

* Remove dead code

* Readonly members semantic checks. Warn on implicit copy of 'this'.

* Add to readonly ref readonly test

* Warn on implicit copies for get property access

* Don't warn on calls to base methods

* Add test for setter accesses in a readonly member

* Allow readonly setter accesses in readonly methods

* Test increment, compound assignment, and field-like events

* Fixes from feedback

* Add mutating readonly event accessors test

* Expand on readonly base members test

* Check for implicit copies in foreach/using/deconstruction scenarios

* Split base method tests into multiple tests for clarity

* Test remaining cases

* Fix whitespace in test

* Add ReadOnlyMethod_RefLocal

* Fixes from Fred's feedback
上级 17a10321
......@@ -438,8 +438,6 @@ internal bool CheckValueKind(SyntaxNode node, BoundExpression expr, BindValueKin
return CheckLocalValueKind(node, local, valueKind, checkingReceiver, diagnostics);
case BoundKind.ThisReference:
var thisref = (BoundThisReference)expr;
// `this` is never ref assignable
if (RequiresRefAssignableVariable(valueKind))
{
......@@ -457,14 +455,12 @@ internal bool CheckValueKind(SyntaxNode node, BoundExpression expr, BindValueKin
// SPEC: of a struct, it is classified as a variable.
// Note: RValueOnly is checked at the beginning of this method. Since we are here we need more than readable.
//"this" is readonly in members of readonly structs, unless we are in a constructor.
if (!thisref.Type.IsValueType ||
(RequiresAssignableVariable(valueKind) &&
thisref.Type.IsReadOnly &&
(this.ContainingMemberOrLambda as MethodSymbol)?.MethodKind != MethodKind.Constructor))
// "this" is readonly in members marked "readonly" and in members of readonly structs, unless we are in a constructor.
var isValueType = ((BoundThisReference)expr).Type.IsValueType;
if (!isValueType || (RequiresAssignableVariable(valueKind) && (this.ContainingMemberOrLambda as MethodSymbol)?.IsEffectivelyReadOnly == true))
{
// CONSIDER: the Dev10 name has angle brackets (i.e. "<this>")
Error(diagnostics, GetThisLvalueError(valueKind), node, ThisParameterSymbol.SymbolName);
Error(diagnostics, GetThisLvalueError(valueKind, isValueType), node, ThisParameterSymbol.SymbolName);
return false;
}
......@@ -994,7 +990,8 @@ private bool CheckPropertyValueKind(SyntaxNode node, BoundExpression expr, BindV
ReportDiagnosticsIfObsolete(diagnostics, setMethod, node, receiver?.Kind == BoundKind.BaseReference);
if (RequiresVariableReceiver(receiver, setMethod) && !CheckIsValidReceiverForVariable(node, receiver, BindValueKind.Assignable, diagnostics))
var setValueKind = setMethod.IsEffectivelyReadOnly ? BindValueKind.RValue : BindValueKind.Assignable;
if (RequiresVariableReceiver(receiver, setMethod) && !CheckIsValidReceiverForVariable(node, receiver, setValueKind, diagnostics))
{
return false;
}
......@@ -1041,6 +1038,7 @@ private bool CheckPropertyValueKind(SyntaxNode node, BoundExpression expr, BindV
return false;
}
CheckImplicitThisCopyInReadOnlyMember(receiver, getMethod, diagnostics);
ReportDiagnosticsIfObsolete(diagnostics, getMethod, node, receiver?.Kind == BoundKind.BaseReference);
}
}
......@@ -1518,7 +1516,7 @@ private static void ReportReadonlyLocalError(SyntaxNode node, LocalSymbol local,
Error(diagnostics, ReadOnlyLocalErrors[index], node, local, cause.Localize());
}
static private ErrorCode GetThisLvalueError(BindValueKind kind)
static private ErrorCode GetThisLvalueError(BindValueKind kind, bool isValueType)
{
switch (kind)
{
......@@ -1533,7 +1531,7 @@ static private ErrorCode GetThisLvalueError(BindValueKind kind)
return ErrorCode.ERR_InvalidAddrOp;
case BindValueKind.IncrementDecrement:
return ErrorCode.ERR_IncrementLvalueExpected;
return isValueType ? ErrorCode.ERR_AssgReadonlyLocal : ErrorCode.ERR_IncrementLvalueExpected;
case BindValueKind.RefReturn:
case BindValueKind.ReadonlyRef:
......@@ -2969,7 +2967,7 @@ internal enum AddressKind
if (!IsAnyReadOnly(addressKind) && method.IsEffectivelyReadOnly)
{
return method.MethodKind == MethodKind.Constructor;
return false;
}
return true;
......
......@@ -142,6 +142,11 @@ internal BoundExpression BindDeconstruction(AssignmentExpressionSyntax node, Dia
checkedVariables,
out conversion);
if (conversion.Method != null)
{
CheckImplicitThisCopyInReadOnlyMember(boundRHS, conversion.Method, diagnostics);
}
FailRemainingInferencesAndSetValEscape(checkedVariables, diagnostics, rightEscape);
var lhsTuple = DeconstructionVariablesAsTuple(left, checkedVariables, diagnostics, ignoreDiagnosticsFromTuple: diagnostics.HasAnyErrors() || !resultIsUsed);
......
......@@ -5775,6 +5775,9 @@ private bool IsUsingAliasInScope(string name)
}
else if ((object)leftType != null)
{
// NB: We don't know if we really only need RValue access, or if we are actually
// passing the receiver implicitly by ref (e.g. in a struct instance method invocation).
// These checks occur later.
boundLeft = CheckValue(boundLeft, BindValueKind.RValue, diagnostics);
return BindInstanceMemberAccess(node, right, boundLeft, rightName, rightArity, typeArgumentsSyntax, typeArguments, invoked, indexed, diagnostics);
}
......
......@@ -1010,6 +1010,8 @@ private void CheckRestrictedTypeReceiver(BoundExpression expression, Compilation
// (i.e. the first argument, if invokedAsExtensionMethod).
var gotError = MemberGroupFinalValidation(receiver, method, expression, diagnostics, invokedAsExtensionMethod);
CheckImplicitThisCopyInReadOnlyMember(receiver, method, diagnostics);
if (invokedAsExtensionMethod)
{
BoundExpression receiverArgument = analyzedArguments.Argument(0);
......@@ -1135,30 +1137,27 @@ private void CheckRestrictedTypeReceiver(BoundExpression expression, Compilation
argsToParamsOpt: argsToParams, resultKind: LookupResultKind.Viable, binderOpt: this, type: returnType, hasErrors: gotError);
}
private bool IsBindingModuleLevelAttribute()
/// <summary>
/// Returns false if an implicit 'this' copy will occur due to an instance member invocation in a readonly member.
/// </summary>
internal bool CheckImplicitThisCopyInReadOnlyMember(BoundExpression receiver, MethodSymbol method, DiagnosticBag diagnostics)
{
if ((this.Flags & BinderFlags.InContextualAttributeBinder) == 0)
// For now we are warning only in implicit copy scenarios that are only possible with readonly members.
// Eventually we will warn on implicit value copies in more scenarios. See https://github.com/dotnet/roslyn/issues/33968.
if (receiver is BoundThisReference &&
receiver.Type.IsValueType &&
ContainingMemberOrLambda is MethodSymbol containingMethod &&
containingMethod.IsEffectivelyReadOnly &&
// Ignore calls to base members.
TypeSymbol.Equals(containingMethod.ContainingType, method.ContainingType, TypeCompareKind.ConsiderEverything) &&
!method.IsEffectivelyReadOnly &&
!method.IsStatic)
{
Error(diagnostics, ErrorCode.WRN_ImplicitCopyInReadOnlyMember, receiver.Syntax, method.Name, ThisParameterSymbol.SymbolName);
return false;
}
var current = this;
do
{
var contextualAttributeBinder = current as ContextualAttributeBinder;
if (contextualAttributeBinder != null)
{
return (object)contextualAttributeBinder.AttributeTarget != null &&
contextualAttributeBinder.AttributeTarget.Kind == SymbolKind.NetModule;
}
current = current.Next;
}
while (current != null);
throw ExceptionUtilities.Unreachable;
return true;
}
/// <param name="node">Invocation syntax node.</param>
......
......@@ -207,6 +207,10 @@ private BoundForEachStatement BindForEachPartsWorker(DiagnosticBag diagnostics,
AwaitableInfo awaitInfo = null;
MethodSymbol getEnumeratorMethod = builder.GetEnumeratorMethod;
if (getEnumeratorMethod != null)
{
originalBinder.CheckImplicitThisCopyInReadOnlyMember(collectionExpr, getEnumeratorMethod, diagnostics);
}
if (IsAsync)
{
var placeholder = new BoundAwaitableValuePlaceholder(_syntax.Expression, builder.MoveNextMethod?.ReturnType ?? CreateErrorType());
......
......@@ -19,7 +19,7 @@ namespace Microsoft.CodeAnalysis.CSharp {
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class CSharpResources {
......@@ -14355,6 +14355,24 @@ internal class CSharpResources {
}
}
/// <summary>
/// Looks up a localized string similar to Call to non-readonly member &apos;{0}&apos; from a &apos;readonly&apos; member results in an implicit copy of &apos;{1}&apos;..
/// </summary>
internal static string WRN_ImplicitCopyInReadOnlyMember {
get {
return ResourceManager.GetString("WRN_ImplicitCopyInReadOnlyMember", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Call to non-readonly member from a &apos;readonly&apos; member results in an implicit copy..
/// </summary>
internal static string WRN_ImplicitCopyInReadOnlyMember_Title {
get {
return ResourceManager.GetString("WRN_ImplicitCopyInReadOnlyMember_Title", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Assignment in conditional expression is always constant; did you mean to use == instead of = ?.
/// </summary>
......
......@@ -5421,6 +5421,12 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<data name="WRN_NullLiteralMayIntroduceNullT_Title" xml:space="preserve">
<value>A null literal introduces a null value for a type parameter.</value>
</data>
<data name="WRN_ImplicitCopyInReadOnlyMember" xml:space="preserve">
<value>Call to non-readonly member '{0}' from a 'readonly' member results in an implicit copy of '{1}'.</value>
</data>
<data name="WRN_ImplicitCopyInReadOnlyMember_Title" xml:space="preserve">
<value>Call to non-readonly member from a 'readonly' member results in an implicit copy.</value>
</data>
<data name="WRN_NullabilityMismatchInArgument" xml:space="preserve">
<value>Argument of type '{0}' cannot be used for parameter '{2}' of type '{1}' in '{3}' due to differences in the nullability of reference types.</value>
</data>
......
......@@ -1683,6 +1683,7 @@ internal enum ErrorCode
ERR_FeatureInPreview = 8652,
WRN_DefaultExpressionMayIntroduceNullT = 8653,
WRN_NullLiteralMayIntroduceNullT = 8654,
WRN_ImplicitCopyInReadOnlyMember = 8655,
#endregion diagnostics introduced for C# 8.0
......
......@@ -404,6 +404,7 @@ internal static int GetWarningLevel(ErrorCode code)
case ErrorCode.WRN_NullLiteralMayIntroduceNullT:
case ErrorCode.WRN_ConditionalAccessMayReturnNull:
case ErrorCode.WRN_AsOperatorMayReturnNull:
case ErrorCode.WRN_ImplicitCopyInReadOnlyMember:
return 1;
default:
return 0;
......
......@@ -222,6 +222,7 @@ public static bool IsWarning(ErrorCode code)
case ErrorCode.WRN_DuplicateInterfaceWithNullabilityMismatchInBaseList:
case ErrorCode.WRN_DefaultExpressionMayIntroduceNullT:
case ErrorCode.WRN_NullLiteralMayIntroduceNullT:
case ErrorCode.WRN_ImplicitCopyInReadOnlyMember:
return true;
default:
return false;
......
......@@ -315,7 +315,7 @@ internal virtual bool IsExplicitInterfaceImplementation
/// Indicates whether the method is effectively readonly,
/// by either the method or the containing type being marked readonly.
/// </summary>
internal bool IsEffectivelyReadOnly => IsDeclaredReadOnly || ContainingType?.IsReadOnly == true;
internal bool IsEffectivelyReadOnly => (IsDeclaredReadOnly || ContainingType?.IsReadOnly == true) && MethodKind != MethodKind.Constructor;
/// <summary>
/// Returns interface methods explicitly implemented by this method.
......
......@@ -1262,6 +1262,16 @@
<target state="translated">Po #pragma warning safeonly byl očekáván typ s možnou hodnotou null.</target>
<note />
</trans-unit>
<trans-unit id="WRN_ImplicitCopyInReadOnlyMember">
<source>Call to non-readonly member '{0}' from a 'readonly' member results in an implicit copy of '{1}'.</source>
<target state="new">Call to non-readonly member '{0}' from a 'readonly' member results in an implicit copy of '{1}'.</target>
<note />
</trans-unit>
<trans-unit id="WRN_ImplicitCopyInReadOnlyMember_Title">
<source>Call to non-readonly member from a 'readonly' member results in an implicit copy.</source>
<target state="new">Call to non-readonly member from a 'readonly' member results in an implicit copy.</target>
<note />
</trans-unit>
<trans-unit id="WRN_IsTypeNamedUnderscore">
<source>The name '_' refers to the type '{0}', not the discard pattern. Use '@_' for the type, or 'var _' to discard.</source>
<target state="translated">Název „_“ odkazuje na typ {0}, ne vzor discard. Použijte „@_“ pro tento typ nebo „var _“ pro zahození.</target>
......
......@@ -1262,6 +1262,16 @@
<target state="translated">Nach "#pragma warning safeonly" Nullable erwartet</target>
<note />
</trans-unit>
<trans-unit id="WRN_ImplicitCopyInReadOnlyMember">
<source>Call to non-readonly member '{0}' from a 'readonly' member results in an implicit copy of '{1}'.</source>
<target state="new">Call to non-readonly member '{0}' from a 'readonly' member results in an implicit copy of '{1}'.</target>
<note />
</trans-unit>
<trans-unit id="WRN_ImplicitCopyInReadOnlyMember_Title">
<source>Call to non-readonly member from a 'readonly' member results in an implicit copy.</source>
<target state="new">Call to non-readonly member from a 'readonly' member results in an implicit copy.</target>
<note />
</trans-unit>
<trans-unit id="WRN_IsTypeNamedUnderscore">
<source>The name '_' refers to the type '{0}', not the discard pattern. Use '@_' for the type, or 'var _' to discard.</source>
<target state="translated">Der Name "_" verweist auf den Typ "{0}", nicht auf das discard-Muster. Verwenden Sie "@_" für den Typ oder "var _" zum Verwerfen.</target>
......
......@@ -1264,6 +1264,16 @@
<target state="translated">Tipo que acepta valores NULL esperado después de advertencia de #pragma "safeonly"</target>
<note />
</trans-unit>
<trans-unit id="WRN_ImplicitCopyInReadOnlyMember">
<source>Call to non-readonly member '{0}' from a 'readonly' member results in an implicit copy of '{1}'.</source>
<target state="new">Call to non-readonly member '{0}' from a 'readonly' member results in an implicit copy of '{1}'.</target>
<note />
</trans-unit>
<trans-unit id="WRN_ImplicitCopyInReadOnlyMember_Title">
<source>Call to non-readonly member from a 'readonly' member results in an implicit copy.</source>
<target state="new">Call to non-readonly member from a 'readonly' member results in an implicit copy.</target>
<note />
</trans-unit>
<trans-unit id="WRN_IsTypeNamedUnderscore">
<source>The name '_' refers to the type '{0}', not the discard pattern. Use '@_' for the type, or 'var _' to discard.</source>
<target state="translated">El nombre "_" hace referencia al tipo "{0}", no al patrón de descarte. Use "@_" para el tipo o "var _" para el descarte.</target>
......
......@@ -1263,6 +1263,16 @@
<target state="translated">Nullable attendu après un avertissement #pragma safeonly</target>
<note />
</trans-unit>
<trans-unit id="WRN_ImplicitCopyInReadOnlyMember">
<source>Call to non-readonly member '{0}' from a 'readonly' member results in an implicit copy of '{1}'.</source>
<target state="new">Call to non-readonly member '{0}' from a 'readonly' member results in an implicit copy of '{1}'.</target>
<note />
</trans-unit>
<trans-unit id="WRN_ImplicitCopyInReadOnlyMember_Title">
<source>Call to non-readonly member from a 'readonly' member results in an implicit copy.</source>
<target state="new">Call to non-readonly member from a 'readonly' member results in an implicit copy.</target>
<note />
</trans-unit>
<trans-unit id="WRN_IsTypeNamedUnderscore">
<source>The name '_' refers to the type '{0}', not the discard pattern. Use '@_' for the type, or 'var _' to discard.</source>
<target state="translated">Le nom '_' fait référence au type '{0}', pas au modèle d'abandon. Utilisez '@_' pour le type, ou 'var _' pour abandonner.</target>
......
......@@ -1262,6 +1262,16 @@
<target state="translated">Dopo l'avviso della direttiva #pragma safeonly è previsto nullable</target>
<note />
</trans-unit>
<trans-unit id="WRN_ImplicitCopyInReadOnlyMember">
<source>Call to non-readonly member '{0}' from a 'readonly' member results in an implicit copy of '{1}'.</source>
<target state="new">Call to non-readonly member '{0}' from a 'readonly' member results in an implicit copy of '{1}'.</target>
<note />
</trans-unit>
<trans-unit id="WRN_ImplicitCopyInReadOnlyMember_Title">
<source>Call to non-readonly member from a 'readonly' member results in an implicit copy.</source>
<target state="new">Call to non-readonly member from a 'readonly' member results in an implicit copy.</target>
<note />
</trans-unit>
<trans-unit id="WRN_IsTypeNamedUnderscore">
<source>The name '_' refers to the type '{0}', not the discard pattern. Use '@_' for the type, or 'var _' to discard.</source>
<target state="translated">Il nome '_' fa riferimento al tipo '{0}' e non al criterio di eliminazione. Usare '@_' per il tipo oppure 'var _' per eliminare.</target>
......
......@@ -1262,6 +1262,16 @@
<target state="translated">#pragma warning safeonly の後に Null 許容が必要です</target>
<note />
</trans-unit>
<trans-unit id="WRN_ImplicitCopyInReadOnlyMember">
<source>Call to non-readonly member '{0}' from a 'readonly' member results in an implicit copy of '{1}'.</source>
<target state="new">Call to non-readonly member '{0}' from a 'readonly' member results in an implicit copy of '{1}'.</target>
<note />
</trans-unit>
<trans-unit id="WRN_ImplicitCopyInReadOnlyMember_Title">
<source>Call to non-readonly member from a 'readonly' member results in an implicit copy.</source>
<target state="new">Call to non-readonly member from a 'readonly' member results in an implicit copy.</target>
<note />
</trans-unit>
<trans-unit id="WRN_IsTypeNamedUnderscore">
<source>The name '_' refers to the type '{0}', not the discard pattern. Use '@_' for the type, or 'var _' to discard.</source>
<target state="translated">名前 '_' は、破棄パターンではなく型 '{0}' を参照しています。型の場合は '@_' を、破棄する場合は 'var _' をご使用ください。</target>
......
......@@ -1262,6 +1262,16 @@
<target state="translated">#pragma warning safeonly 뒤에 nullable이 필요합니다.</target>
<note />
</trans-unit>
<trans-unit id="WRN_ImplicitCopyInReadOnlyMember">
<source>Call to non-readonly member '{0}' from a 'readonly' member results in an implicit copy of '{1}'.</source>
<target state="new">Call to non-readonly member '{0}' from a 'readonly' member results in an implicit copy of '{1}'.</target>
<note />
</trans-unit>
<trans-unit id="WRN_ImplicitCopyInReadOnlyMember_Title">
<source>Call to non-readonly member from a 'readonly' member results in an implicit copy.</source>
<target state="new">Call to non-readonly member from a 'readonly' member results in an implicit copy.</target>
<note />
</trans-unit>
<trans-unit id="WRN_IsTypeNamedUnderscore">
<source>The name '_' refers to the type '{0}', not the discard pattern. Use '@_' for the type, or 'var _' to discard.</source>
<target state="translated">'_' 이름은 '{0}' 형식을 참조하며, 무시 패턴은 참조하지 않습니다. 형식에 '@_'을 사용하거나, 무시하려면 'var _'을 사용하세요.</target>
......
......@@ -1262,6 +1262,16 @@
<target state="translated">Oczekiwano elementu dopuszczającego wartość null po dyrektywne #pragma warning safeonly</target>
<note />
</trans-unit>
<trans-unit id="WRN_ImplicitCopyInReadOnlyMember">
<source>Call to non-readonly member '{0}' from a 'readonly' member results in an implicit copy of '{1}'.</source>
<target state="new">Call to non-readonly member '{0}' from a 'readonly' member results in an implicit copy of '{1}'.</target>
<note />
</trans-unit>
<trans-unit id="WRN_ImplicitCopyInReadOnlyMember_Title">
<source>Call to non-readonly member from a 'readonly' member results in an implicit copy.</source>
<target state="new">Call to non-readonly member from a 'readonly' member results in an implicit copy.</target>
<note />
</trans-unit>
<trans-unit id="WRN_IsTypeNamedUnderscore">
<source>The name '_' refers to the type '{0}', not the discard pattern. Use '@_' for the type, or 'var _' to discard.</source>
<target state="translated">Nazwa „_” odwołuje się do typu „{0}”, a nie do wzorca odrzucania. Użyj elementu „@_” aby odwołać się do typu, lub użyj elementu „var _”, aby odrzucić wartość.</target>
......
......@@ -1262,6 +1262,16 @@
<target state="translated">Esperado anulável após #pragma warning safeonly</target>
<note />
</trans-unit>
<trans-unit id="WRN_ImplicitCopyInReadOnlyMember">
<source>Call to non-readonly member '{0}' from a 'readonly' member results in an implicit copy of '{1}'.</source>
<target state="new">Call to non-readonly member '{0}' from a 'readonly' member results in an implicit copy of '{1}'.</target>
<note />
</trans-unit>
<trans-unit id="WRN_ImplicitCopyInReadOnlyMember_Title">
<source>Call to non-readonly member from a 'readonly' member results in an implicit copy.</source>
<target state="new">Call to non-readonly member from a 'readonly' member results in an implicit copy.</target>
<note />
</trans-unit>
<trans-unit id="WRN_IsTypeNamedUnderscore">
<source>The name '_' refers to the type '{0}', not the discard pattern. Use '@_' for the type, or 'var _' to discard.</source>
<target state="translated">O nome '_' refere-se ao tipo '{0}', não ao padrão de descarte. Use '@_' para o tipo ou 'var _' para descarte.</target>
......
......@@ -1262,6 +1262,16 @@
<target state="translated">После #pragma warning safeonly ожидается тип, допускающий значения NULL.</target>
<note />
</trans-unit>
<trans-unit id="WRN_ImplicitCopyInReadOnlyMember">
<source>Call to non-readonly member '{0}' from a 'readonly' member results in an implicit copy of '{1}'.</source>
<target state="new">Call to non-readonly member '{0}' from a 'readonly' member results in an implicit copy of '{1}'.</target>
<note />
</trans-unit>
<trans-unit id="WRN_ImplicitCopyInReadOnlyMember_Title">
<source>Call to non-readonly member from a 'readonly' member results in an implicit copy.</source>
<target state="new">Call to non-readonly member from a 'readonly' member results in an implicit copy.</target>
<note />
</trans-unit>
<trans-unit id="WRN_IsTypeNamedUnderscore">
<source>The name '_' refers to the type '{0}', not the discard pattern. Use '@_' for the type, or 'var _' to discard.</source>
<target state="translated">Имя "_" ссылается на тип "{0}", а не на шаблон отмены. Используйте "@_" в качестве типа или "var _" для отмены.</target>
......
......@@ -1263,6 +1263,16 @@
<target state="translated">#pragma uyarısı safeonly sonrasında boş değer atanabilir beklendi</target>
<note />
</trans-unit>
<trans-unit id="WRN_ImplicitCopyInReadOnlyMember">
<source>Call to non-readonly member '{0}' from a 'readonly' member results in an implicit copy of '{1}'.</source>
<target state="new">Call to non-readonly member '{0}' from a 'readonly' member results in an implicit copy of '{1}'.</target>
<note />
</trans-unit>
<trans-unit id="WRN_ImplicitCopyInReadOnlyMember_Title">
<source>Call to non-readonly member from a 'readonly' member results in an implicit copy.</source>
<target state="new">Call to non-readonly member from a 'readonly' member results in an implicit copy.</target>
<note />
</trans-unit>
<trans-unit id="WRN_IsTypeNamedUnderscore">
<source>The name '_' refers to the type '{0}', not the discard pattern. Use '@_' for the type, or 'var _' to discard.</source>
<target state="translated">'_' adı atma desenine değil '{0}' türüne başvuruyor. Tür için '@_' adını veya atmak için 'var _' adını kullanın.</target>
......
......@@ -1263,6 +1263,16 @@
<target state="translated">在 #pragma warning safeonly 之后应是可为 null</target>
<note />
</trans-unit>
<trans-unit id="WRN_ImplicitCopyInReadOnlyMember">
<source>Call to non-readonly member '{0}' from a 'readonly' member results in an implicit copy of '{1}'.</source>
<target state="new">Call to non-readonly member '{0}' from a 'readonly' member results in an implicit copy of '{1}'.</target>
<note />
</trans-unit>
<trans-unit id="WRN_ImplicitCopyInReadOnlyMember_Title">
<source>Call to non-readonly member from a 'readonly' member results in an implicit copy.</source>
<target state="new">Call to non-readonly member from a 'readonly' member results in an implicit copy.</target>
<note />
</trans-unit>
<trans-unit id="WRN_IsTypeNamedUnderscore">
<source>The name '_' refers to the type '{0}', not the discard pattern. Use '@_' for the type, or 'var _' to discard.</source>
<target state="translated">名称 "_" 引用类型“{0}”,而不引用放弃模式。对于类型,请使用 "@_";对于弃用,请使用 "var _"。</target>
......
......@@ -1262,6 +1262,16 @@
<target state="translated">在#pragma warning safeonly 後面必須是可為 Null 的項目</target>
<note />
</trans-unit>
<trans-unit id="WRN_ImplicitCopyInReadOnlyMember">
<source>Call to non-readonly member '{0}' from a 'readonly' member results in an implicit copy of '{1}'.</source>
<target state="new">Call to non-readonly member '{0}' from a 'readonly' member results in an implicit copy of '{1}'.</target>
<note />
</trans-unit>
<trans-unit id="WRN_ImplicitCopyInReadOnlyMember_Title">
<source>Call to non-readonly member from a 'readonly' member results in an implicit copy.</source>
<target state="new">Call to non-readonly member from a 'readonly' member results in an implicit copy.</target>
<note />
</trans-unit>
<trans-unit id="WRN_IsTypeNamedUnderscore">
<source>The name '_' refers to the type '{0}', not the discard pattern. Use '@_' for the type, or 'var _' to discard.</source>
<target state="translated">名稱 '_' 參考類型 '{0}',而非捨棄模式。請為類型使用 '@_',或使用 'var _' 捨棄。</target>
......
......@@ -1342,8 +1342,12 @@ static void Main()
";
var verifier = CompileAndVerify(csharp, expectedOutput: "123");
// PROTOTYPE: should warn about copying 'this' when calling M2
verifier.VerifyDiagnostics();
verifier.VerifyDiagnostics(
// (9,9): warning CS8655: Call to non-readonly member 'M2' from a 'readonly' member results in an implicit copy of 'this'.
// M2();
Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "M2").WithArguments("M2", "this").WithLocation(9, 9));
verifier.VerifyIL("S.M1", @"
{
// Code size 51 (0x33)
......@@ -1404,7 +1408,7 @@ static void Main()
";
var verifier = CompileAndVerify(csharp, expectedOutput: "123");
// PROTOTYPE: should warn about copying 's' when calling M2
// should warn about calling s.M2 in warning wave (see https://github.com/dotnet/roslyn/issues/33968)
verifier.VerifyDiagnostics();
verifier.VerifyIL("S.M1", @"
{
......
......@@ -301,6 +301,7 @@ public void WarningLevel_2()
case ErrorCode.WRN_AsOperatorMayReturnNull:
case ErrorCode.WRN_DefaultExpressionMayIntroduceNullT:
case ErrorCode.WRN_NullLiteralMayIntroduceNullT:
case ErrorCode.WRN_ImplicitCopyInReadOnlyMember:
Assert.Equal(1, ErrorFacts.GetWarningLevel(errorCode));
break;
case ErrorCode.WRN_InvalidVersionFormat:
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册