internal bool CheckValueKind(BoundExpression expr, BindValueKind valueKind) { /* * if (expr.HasAnyErrors) * { * return false; * } */ switch (expr.Kind) { default: { if (RequiresSettingValue(valueKind)) { if (!CheckIsVariable(expr.Syntax, expr, valueKind, false)) { return(false); } } /* * if (RequiresGettingValue(valueKind)) * { * } */ return(true); } } }
private static bool RequiresSettingValue(BindValueKind kind) { switch (kind) { case BindValueKind.RValue: return(false); case BindValueKind.Assignment: return(true); default: throw ExceptionUtilities.UnexpectedValue(kind); } }
/// <summary> /// The purpose of this method is to determine if the expression is classified by the /// specification as a *variable*. If it is not then this code gives an appropriate error message. /// /// To determine the appropriate error message we need to know two things: /// /// (1) why do we want to know if this is a variable? Because we are trying to assign it, /// increment it, or pass it by reference? /// /// (2) Are we trying to determine if the left hand side of a dot is a variable in order /// to determine if the field or property on the right hand side of a dot is assignable? /// </summary> private bool CheckIsVariable(SyntaxNode node, BoundExpression expr, BindValueKind kind, bool checkingReceiver) { Debug.Assert(expr != null); //Debug.Assert(!checkingReceiver || expr.Type.IsValueType || expr.Type.IsTypeParameter()); // Every expression is classified as one of: // 4. a literal // 10. a variable // 11. a value // We wish to give an error and return false for all of those except case 10. // case 0: We've already reported an error: return(false); }
/// <summary> /// Check the expression is of the required lvalue and rvalue specified by valueKind. /// The method returns the original expression if the expression is of the required /// type. Otherwise, an appropriate error is added to the diagnostics bag and the /// method returns a BoundBadExpression node. The method returns the original /// expression without generating any error if the expression has errors. /// </summary> private BoundExpression CheckValue(BoundExpression expr, BindValueKind valueKind) { if (CheckValueKind(expr, valueKind)) { return(expr); } /* * var resultKind = (valueKind == BindValueKind.RValue) ? * LookupResultKind.NotAValue : * LookupResultKind.NotAVariable; * * return ToBadExpression(expr, resultKind); */ throw new NotImplementedException("TBD"); }
// Check to see if a local symbol is to be treated as a variable. Returns true if yes, reports an // error and returns false if no. private static bool CheckLocalVariable(CSharpSyntaxNode tree, LocalSymbol local, BindValueKind kind, bool checkingReceiver, DiagnosticBag diagnostics) { Debug.Assert((object)local != null); Debug.Assert(kind != BindValueKind.RValue); if (local.IsWritable) { return true; } MessageID cause = 0; if (local.IsForEach) { cause = MessageID.IDS_FOREACHLOCAL; } else if (local.IsUsing) { cause = MessageID.IDS_USINGLOCAL; } else if (local.IsFixed) { cause = MessageID.IDS_FIXEDLOCAL; } else { Error(diagnostics, GetStandardLvalueError(kind), tree); return false; } if (kind == BindValueKind.AddressOf) { Error(diagnostics, ErrorCode.ERR_AddrOnReadOnlyLocal, tree); return false; } ErrorCode[] ReadOnlyLocalErrors = { ErrorCode.ERR_RefReadonlyLocal, ErrorCode.ERR_AssgReadonlyLocal, ErrorCode.ERR_RefReadonlyLocal, // Impossible, but if we do run into this situation, this is a sensible error. ErrorCode.ERR_AssgReadonlyLocal, // Also impossible. ErrorCode.ERR_RefReadonlyLocalCause, ErrorCode.ERR_AssgReadonlyLocalCause, ErrorCode.ERR_RefReadonlyLocal2Cause, ErrorCode.ERR_AssgReadonlyLocal2Cause }; int index = (cause != 0 ? 4 : 0) + (checkingReceiver ? 2 : 0) + (kind == BindValueKind.OutParameter ? 0 : 1); Debug.Assert(index != 2 && index != 3); // There is no way that we can have no cause AND a read-only local nested in a struct with a // writable field. What would make the local read-only if not one of the causes above? (Const // locals may not be structs, so we would already have errored out in that scenario.) if (cause != 0) { Error(diagnostics, ReadOnlyLocalErrors[index], tree, local, cause.Localize()); } else { Error(diagnostics, ReadOnlyLocalErrors[index], tree, local); } return false; }
private static ErrorCode GetRangeLvalueError(BindValueKind kind) { switch (kind) { case BindValueKind.Assignment: case BindValueKind.CompoundAssignment: case BindValueKind.IncrementDecrement: return ErrorCode.ERR_QueryRangeVariableReadOnly; case BindValueKind.OutParameter: return ErrorCode.ERR_QueryOutRefRangeVariable; case BindValueKind.AddressOf: return ErrorCode.ERR_InvalidAddrOp; default: Debug.Assert(false, "bad BindValueKind"); goto case BindValueKind.Assignment; } }
static private ErrorCode GetThisLvalueError(BindValueKind kind) { switch (kind) { default: Debug.Assert(false, "bad BindValueKind"); goto case BindValueKind.Assignment; case BindValueKind.CompoundAssignment: case BindValueKind.Assignment: return ErrorCode.ERR_AssgReadonlyLocal; case BindValueKind.OutParameter: return ErrorCode.ERR_RefReadonlyLocal; case BindValueKind.AddressOf: return ErrorCode.ERR_AddrOnReadOnlyLocal; case BindValueKind.IncrementDecrement: return ErrorCode.ERR_IncrementLvalueExpected; } }
private bool CheckPropertyValueKind(BoundExpression expr, BindValueKind valueKind, DiagnosticBag diagnostics) { // SPEC: If the left operand is a property or indexer access, the property or indexer must // SPEC: have a set accessor. If this is not the case, a compile-time error occurs. // Addendum: Assignment is also allowed for get-only autoprops in their constructor BoundExpression receiver; CSharpSyntaxNode propertySyntax; var propertySymbol = GetPropertySymbol(expr, out receiver, out propertySyntax); Debug.Assert((object)propertySymbol != null); Debug.Assert(propertySyntax != null); var node = expr.Syntax; if (valueKind == BindValueKind.OutParameter || valueKind == BindValueKind.AddressOf) { // We know the outcome, we just want the diagnostics. bool isVariable = CheckIsVariable(node, expr, valueKind, false, diagnostics); Debug.Assert(!isVariable, "Properties are never variables"); return false; } if (RequiresSettingValue(valueKind)) { var setMethod = propertySymbol.GetOwnOrInheritedSetMethod(); if ((object)setMethod == null) { var containing = this.ContainingMemberOrLambda; if (!AccessingAutopropertyFromConstructor(receiver, propertySymbol, containing)) { Error(diagnostics, ErrorCode.ERR_AssgReadonlyProp, node, propertySymbol); return false; } } else if (receiver?.Kind == BoundKind.BaseReference && setMethod.IsAbstract) { Error(diagnostics, ErrorCode.ERR_AbstractBaseCall, node, propertySymbol); return false; } else if (!object.Equals(setMethod.GetUseSiteDiagnostic(), propertySymbol.GetUseSiteDiagnostic()) && ReportUseSiteDiagnostics(setMethod, diagnostics, propertySyntax)) { return false; } else { var accessThroughType = this.GetAccessThroughType(receiver); bool failedThroughTypeCheck; HashSet<DiagnosticInfo> useSiteDiagnostics = null; bool isAccessible = this.IsAccessible(setMethod, accessThroughType, out failedThroughTypeCheck, ref useSiteDiagnostics); diagnostics.Add(node, useSiteDiagnostics); if (!isAccessible) { if (failedThroughTypeCheck) { Error(diagnostics, ErrorCode.ERR_BadProtectedAccess, node, propertySymbol, accessThroughType, this.ContainingType); } else { Error(diagnostics, ErrorCode.ERR_InaccessibleSetter, node, propertySymbol); } return false; } ReportDiagnosticsIfObsolete(diagnostics, setMethod, node, receiver?.Kind == BoundKind.BaseReference); if (RequiresVariableReceiver(receiver, setMethod)) { if (!CheckIsValidReceiverForVariable(node, receiver, BindValueKind.Assignment, diagnostics)) { return false; } } } } if (RequiresGettingValue(valueKind)) { var getMethod = propertySymbol.GetOwnOrInheritedGetMethod(); if ((object)getMethod == null) { Error(diagnostics, ErrorCode.ERR_PropertyLacksGet, node, propertySymbol); return false; } else if (receiver?.Kind == BoundKind.BaseReference && getMethod.IsAbstract) { Error(diagnostics, ErrorCode.ERR_AbstractBaseCall, node, propertySymbol); return false; } else if (!object.Equals(getMethod.GetUseSiteDiagnostic(), propertySymbol.GetUseSiteDiagnostic()) && ReportUseSiteDiagnostics(getMethod, diagnostics, propertySyntax)) { return false; } else { var accessThroughType = this.GetAccessThroughType(receiver); bool failedThroughTypeCheck; HashSet<DiagnosticInfo> useSiteDiagnostics = null; bool isAccessible = this.IsAccessible(getMethod, accessThroughType, out failedThroughTypeCheck, ref useSiteDiagnostics); diagnostics.Add(node, useSiteDiagnostics); if (!isAccessible) { if (failedThroughTypeCheck) { Error(diagnostics, ErrorCode.ERR_BadProtectedAccess, node, propertySymbol, accessThroughType, this.ContainingType); } else { Error(diagnostics, ErrorCode.ERR_InaccessibleGetter, node, propertySymbol); } return false; } ReportDiagnosticsIfObsolete(diagnostics, getMethod, node, receiver?.Kind == BoundKind.BaseReference); } } return true; }
internal bool CheckValueKind(BoundExpression expr, BindValueKind valueKind, DiagnosticBag diagnostics) { if (expr.HasAnyErrors) { return false; } switch (expr.Kind) { case BoundKind.PropertyAccess: case BoundKind.IndexerAccess: return CheckPropertyValueKind(expr, valueKind, diagnostics); case BoundKind.EventAccess: return CheckEventValueKind((BoundEventAccess)expr, valueKind, diagnostics); case BoundKind.DynamicMemberAccess: case BoundKind.DynamicIndexerAccess: return true; default: { if (RequiresSettingValue(valueKind)) { if (!CheckIsVariable(expr.Syntax, expr, valueKind, false, diagnostics)) { return false; } } if (RequiresGettingValue(valueKind)) { if (!CheckNotNamespaceOrType(expr, diagnostics)) { return false; } } return true; } } }
private bool CheckIsValidReceiverForVariable(CSharpSyntaxNode node, BoundExpression receiver, BindValueKind kind, DiagnosticBag diagnostics) { Debug.Assert(receiver != null); return Flags.Includes(BinderFlags.ObjectInitializerMember) && receiver.Kind == BoundKind.ImplicitReceiver || CheckIsVariable(node, receiver, kind, true, diagnostics); }
private BoundExpression BindPossibleArrayInitializer( ExpressionSyntax node, TypeSymbol destinationType, BindValueKind valueKind, DiagnosticBag diagnostics) { Debug.Assert(node != null); if (node.Kind() != SyntaxKind.ArrayInitializerExpression) { return BindValue(node, diagnostics, valueKind); } if (destinationType.Kind == SymbolKind.ArrayType) { return BindArrayCreationWithInitializer(diagnostics, null, (InitializerExpressionSyntax)node, (ArrayTypeSymbol)destinationType, ImmutableArray<BoundExpression>.Empty); } return BindUnexpectedArrayInitializer((InitializerExpressionSyntax)node, diagnostics, ErrorCode.ERR_ArrayInitToNonArrayType); }
private bool CheckIsCallVariable(BoundCall call, CSharpSyntaxNode node, BindValueKind kind, bool checkingReceiver, DiagnosticBag diagnostics) { // A call can only be a variable if it returns by reference. If this is the case, // whether or not it is a valid variable depends on whether or not the call is the // RHS of a return or an assign by reference: // - If call is used in a context demanding ref-returnable reference all of its ref // inputs must be ref-returnable var methodSymbol = call.Method; if (methodSymbol.RefKind != RefKind.None) { if (kind == BindValueKind.RefReturn) { var args = call.Arguments; var argRefKinds = call.ArgumentRefKindsOpt; if (!argRefKinds.IsDefault) { for (var i = 0; i < args.Length; i++) { if (argRefKinds[i] != RefKind.None && !CheckIsVariable(args[i].Syntax, args[i], kind, false, diagnostics)) { var errorCode = checkingReceiver ? ErrorCode.ERR_RefReturnCall2 : ErrorCode.ERR_RefReturnCall; var parameterIndex = call.ArgsToParamsOpt.IsDefault ? i : call.ArgsToParamsOpt[i]; var parameterName = methodSymbol.Parameters[parameterIndex].Name; Error(diagnostics, errorCode, call.Syntax, methodSymbol, parameterName); return false; } } } } return true; } if (checkingReceiver) { // Error is associated with expression, not node which may be distinct. Error(diagnostics, ErrorCode.ERR_ReturnNotLValue, call.Syntax, methodSymbol); } else { Error(diagnostics, GetStandardLvalueError(kind), node); } return false; }
// Check to see if a local symbol is to be treated as a variable. Returns true if yes, reports an // error and returns false if no. private static bool CheckLocalVariable(CSharpSyntaxNode tree, LocalSymbol local, BindValueKind kind, bool checkingReceiver, DiagnosticBag diagnostics) { Debug.Assert((object)local != null); Debug.Assert(kind != BindValueKind.RValue); if (local.IsWritable) { return true; } MessageID cause; if (local.IsForEach) { cause = MessageID.IDS_FOREACHLOCAL; } else if (local.IsUsing) { cause = MessageID.IDS_USINGLOCAL; } else if (local.IsFixed) { cause = MessageID.IDS_FIXEDLOCAL; } else { Error(diagnostics, GetStandardLvalueError(kind), tree); return false; } if (kind == BindValueKind.AddressOf) { Error(diagnostics, ErrorCode.ERR_AddrOnReadOnlyLocal, tree); return false; } ErrorCode[] ReadOnlyLocalErrors = { ErrorCode.ERR_RefReadonlyLocalCause, // impossible since readonly locals are never byref, but would be a reasonable error otherwise ErrorCode.ERR_RefReadonlyLocalCause, ErrorCode.ERR_AssgReadonlyLocalCause, ErrorCode.ERR_RefReadonlyLocal2Cause, // impossible since readonly locals are never byref, but would be a reasonable error otherwise ErrorCode.ERR_RefReadonlyLocal2Cause, ErrorCode.ERR_AssgReadonlyLocal2Cause }; int index = (checkingReceiver ? 3 : 0) + (kind == BindValueKind.RefOrOut ? 0 : (kind == BindValueKind.RefReturn ? 1 : 2)); Error(diagnostics, ReadOnlyLocalErrors[index], tree, local, cause.Localize()); return false; }
protected static bool IsInitializerRefKindValid( EqualsValueClauseSyntax initializer, CSharpSyntaxNode node, RefKind variableRefKind, DiagnosticBag diagnostics, out BindValueKind valueKind) { if (variableRefKind == RefKind.None) { valueKind = BindValueKind.RValue; if (initializer != null && initializer.RefKeyword.Kind() != SyntaxKind.None) { Error(diagnostics, ErrorCode.ERR_InitializeByValueVariableWithReference, node); return false; } } else { valueKind = BindValueKind.RefOrOut; if (initializer == null) { Error(diagnostics, ErrorCode.ERR_ByReferenceVariableMustBeInitialized, node); return false; } else if (initializer.RefKeyword.Kind() == SyntaxKind.None) { Error(diagnostics, ErrorCode.ERR_InitializeByReferenceVariableWithValue, node); return false; } } return true; }
/// <summary> /// Bind the expression and verify the expression matches the combination of lvalue and /// rvalue requirements given by valueKind. If the expression was bound successfully, but /// did not meet the requirements, the return value will be a <see cref="BoundBadExpression"/> that /// (typically) wraps the subexpression. /// </summary> internal BoundExpression BindValue(ExpressionSyntax node, BindingDiagnosticBag diagnostics, BindValueKind valueKind) { var result = BindExpression(node, diagnostics, invoked: false, indexed: false); return(CheckValue(result, valueKind)); }
private static void ReportReadOnlyError(FieldSymbol field, CSharpSyntaxNode node, BindValueKind kind, bool checkingReceiver, DiagnosticBag diagnostics) { Debug.Assert((object)field != null); Debug.Assert(kind != BindValueKind.RValue); Debug.Assert((object)field.Type != null); // It's clearer to say that the address can't be taken than to say that the field can't be modified // (even though the latter message gives more explanation of why). if (kind == BindValueKind.AddressOf) { Error(diagnostics, ErrorCode.ERR_InvalidAddrOp, node); return; } ErrorCode[] ReadOnlyErrors = { ErrorCode.ERR_RefReadonly, ErrorCode.ERR_AssgReadonly, ErrorCode.ERR_RefReadonlyStatic, ErrorCode.ERR_AssgReadonlyStatic, ErrorCode.ERR_RefReadonly2, ErrorCode.ERR_AssgReadonly2, ErrorCode.ERR_RefReadonlyStatic2, ErrorCode.ERR_AssgReadonlyStatic2 }; int index = (checkingReceiver ? 4 : 0) + (field.IsStatic ? 2 : 0) + (kind == BindValueKind.OutParameter ? 0 : 1); if (checkingReceiver) { Error(diagnostics, ReadOnlyErrors[index], node, field); } else { Error(diagnostics, ReadOnlyErrors[index], node); } }
/// <summary> /// The purpose of this method is to determine if the expression is classified by the /// specification as a *variable*. If it is not then this code gives an appropriate error message. /// /// To determine the appropriate error message we need to know two things: /// /// (1) why do we want to know if this is a variable? Because we are trying to assign it, /// increment it, or pass it by reference? /// /// (2) Are we trying to determine if the left hand side of a dot is a variable in order /// to determine if the field or property on the right hand side of a dot is assignable? /// </summary> private bool CheckIsVariable(CSharpSyntaxNode node, BoundExpression expr, BindValueKind kind, bool checkingReceiver, DiagnosticBag diagnostics) { Debug.Assert(expr != null); Debug.Assert(!checkingReceiver || expr.Type.IsValueType); // Every expression is classified as one of: // 1. a namespace // 2. a type // 3. an anonymous function // 4. a literal // 5. an event access // 6. a call to a void-returning method // 7. a method group // 8. a property access // 9. an indexer access // 10. a variable // 11. a value // We wish to give an error and return false for all of those except case 10. // case 0: We've already reported an error: if (expr.HasAnyErrors) { return false; } // Case 1: a namespace: var ns = expr as BoundNamespaceExpression; if (ns != null) { Error(diagnostics, ErrorCode.ERR_BadSKknown, node, ns.NamespaceSymbol, MessageID.IDS_SK_NAMESPACE.Localize(), MessageID.IDS_SK_VARIABLE.Localize()); return false; } // Case 2: a type: var type = expr as BoundTypeExpression; if (type != null) { Error(diagnostics, ErrorCode.ERR_BadSKknown, node, type.Type, MessageID.IDS_SK_TYPE.Localize(), MessageID.IDS_SK_VARIABLE.Localize()); return false; } // Cases 3, 4, 6: if ((expr.Kind == BoundKind.Lambda) || (expr.Kind == BoundKind.UnboundLambda) || (expr.ConstantValue != null) || (expr.Type.GetSpecialTypeSafe() == SpecialType.System_Void)) { Error(diagnostics, GetStandardLvalueError(kind), node); return false; } // Case 5: field-like events are variables var eventAccess = expr as BoundEventAccess; if (eventAccess != null) { EventSymbol eventSymbol = eventAccess.EventSymbol; if (!eventAccess.IsUsableAsField) { Error(diagnostics, GetBadEventUsageDiagnosticInfo(eventSymbol), node); return false; } else if (eventSymbol.IsWindowsRuntimeEvent) { switch (kind) { case BindValueKind.RValue: case BindValueKind.RValueOrMethodGroup: Debug.Assert(false, "Why call CheckIsVariable if you want an RValue?"); goto case BindValueKind.Assignment; case BindValueKind.Assignment: case BindValueKind.CompoundAssignment: return true; } // NOTE: Dev11 reports ERR_RefProperty, as if this were a property access (since that's how it will be lowered). // Roslyn reports a new, more specific, error code. Error(diagnostics, kind == BindValueKind.OutParameter ? ErrorCode.ERR_WinRtEventPassedByRef : GetStandardLvalueError(kind), node, eventSymbol); return false; } else { return true; } } // Case 7: method group gets a nicer error message depending on whether this is M(out F) or F = x. var methodGroup = expr as BoundMethodGroup; if (methodGroup != null) { ErrorCode errorCode; switch (kind) { case BindValueKind.OutParameter: errorCode = ErrorCode.ERR_RefReadonlyLocalCause; break; case BindValueKind.AddressOf: errorCode = ErrorCode.ERR_InvalidAddrOp; break; default: errorCode = ErrorCode.ERR_AssgReadonlyLocalCause; break; } Error(diagnostics, errorCode, node, methodGroup.Name, MessageID.IDS_MethodGroup.Localize()); return false; } // Cases 8 and 9: Properties and indexers are never variables, but they get special error messages. BoundExpression receiver; CSharpSyntaxNode propertySyntax; var propertySymbol = GetPropertySymbol(expr, out receiver, out propertySyntax); if ((object)propertySymbol != null) { if (checkingReceiver) { // Error is associated with expression, not node which may be distinct. // This error is reported for all values types. That is a breaking // change from Dev10 which reports this error for struct types only, // not for type parameters constrained to "struct". Debug.Assert((object)propertySymbol.Type != null); Error(diagnostics, ErrorCode.ERR_ReturnNotLValue, expr.Syntax, propertySymbol); } else { Error(diagnostics, kind == BindValueKind.OutParameter ? ErrorCode.ERR_RefProperty : GetStandardLvalueError(kind), node, propertySymbol); } return false; } // That then leaves variables and values. There are several things that look like variables that nevertheless are // to be treated as values. // The undocumented __refvalue(tr, T) expression results in a variable of type T. var refvalue = expr as BoundRefValueOperator; if (refvalue != null) { return true; } // Parameters are always variables. var parameter = expr as BoundParameter; if (parameter != null) { ParameterSymbol parameterSymbol = parameter.ParameterSymbol; if (this.LockedOrDisposedVariables.Contains(parameterSymbol)) { // Consider: It would be more conventional to pass "symbol" rather than "symbol.Name". // The issue is that the error SymbolDisplayFormat doesn't display parameter // names - only their types - which works great in signatures, but not at all // at the top level. diagnostics.Add(ErrorCode.WRN_AssignmentToLockOrDispose, parameter.Syntax.Location, parameterSymbol.Name); } return true; } if (expr is BoundArrayAccess // Array accesses are always variables || expr is BoundPointerIndirectionOperator // Pointer dereferences are always variables || expr is BoundPointerElementAccess) // Pointer element access is just sugar for pointer dereference { return true; } // Local constants are never variables. Local variables are sometimes // not to be treated as variables, if they are fixed, declared in a using, // or declared in a foreach. // UNDONE: give good errors for range variables and transparent identifiers var local = expr as BoundLocal; if (local != null) { LocalSymbol localSymbol = local.LocalSymbol; if (this.LockedOrDisposedVariables.Contains(localSymbol)) { diagnostics.Add(ErrorCode.WRN_AssignmentToLockOrDispose, local.Syntax.Location, localSymbol); } return CheckLocalVariable(node, localSymbol, kind, checkingReceiver, diagnostics); } // SPEC: when this is used in a primary-expression within an instance constructor of a struct, // SPEC: it is classified as a variable. // SPEC: When this is used in a primary-expression within an instance method or instance accessor // SPEC: of a struct, it is classified as a variable. var thisref = expr as BoundThisReference; if (thisref != null) { // We will already have given an error for "this" used outside of a constructor, // instance method, or instance accessor. Assume that "this" is a variable if it is in a struct. if (!thisref.Type.IsValueType) { // CONSIDER: the Dev10 name has angle brackets (i.e. "<this>") Error(diagnostics, GetThisLvalueError(kind), node, ThisParameterSymbol.SymbolName); return false; } return true; } var queryref = expr as BoundRangeVariable; if (queryref != null) { Error(diagnostics, GetRangeLvalueError(kind), node, queryref.RangeVariableSymbol.Name); return false; } // A field is a variable unless // (1) it is readonly and we are not in a constructor or field initializer // (2) the receiver of the field is of value type and is not a variable or object creation expression. // For example, if you have a class C with readonly field f of type S, and // S has a mutable field x, then c.f.x is not a variable because c.f is not // writable. var fieldAccess = expr as BoundFieldAccess; if (fieldAccess != null) { // NOTE: only the expression part of a field initializer is bound, not the assignment. // As a result, it is okay to see that fields are not variables unless they are in // constructors. var fieldSymbol = fieldAccess.FieldSymbol; var fieldIsStatic = fieldSymbol.IsStatic; if (fieldSymbol.IsReadOnly) { var canModifyReadonly = false; Symbol containing = this.ContainingMemberOrLambda; if ((object)containing != null && fieldIsStatic == containing.IsStatic && (fieldIsStatic || fieldAccess.ReceiverOpt.Kind == BoundKind.ThisReference) && fieldSymbol.ContainingType == containing.ContainingType) { if (containing.Kind == SymbolKind.Method) { MethodSymbol containingMethod = (MethodSymbol)containing; MethodKind desiredMethodKind = fieldIsStatic ? MethodKind.StaticConstructor : MethodKind.Constructor; canModifyReadonly = containingMethod.MethodKind == desiredMethodKind; } else if (containing.Kind == SymbolKind.Field) { canModifyReadonly = true; } } if (!canModifyReadonly) { ReportReadOnlyError(fieldSymbol, node, kind, checkingReceiver, diagnostics); } } if (fieldSymbol.IsFixed) { Error(diagnostics, GetStandardLvalueError(kind), node); return false; } if (fieldSymbol.ContainingType.IsValueType && !fieldIsStatic) { return CheckIsValidReceiverForVariable(node, fieldAccess.ReceiverOpt, kind, diagnostics); } return true; } // At this point we should have covered all the possible cases for variables. if ((expr as BoundConversion)?.ConversionKind == ConversionKind.Unboxing) { Error(diagnostics, ErrorCode.ERR_UnboxNotLValue, node); return false; } var call = expr as BoundCall; if (call != null && checkingReceiver) { // Error is associated with expression, not node which may be distinct. Error(diagnostics, ErrorCode.ERR_ReturnNotLValue, expr.Syntax, call.Method); return false; } Error(diagnostics, GetStandardLvalueError(kind), node); return false; }
// The location where the error is reported might not be the initializer. protected BoundExpression BindInferredVariableInitializer(DiagnosticBag diagnostics, ExpressionSyntax initializer, BindValueKind valueKind, CSharpSyntaxNode errorSyntax) { if (initializer == null) { if (!errorSyntax.HasErrors) { Error(diagnostics, ErrorCode.ERR_ImplicitlyTypedVariableWithNoInitializer, errorSyntax); } return null; } if (initializer.Kind() == SyntaxKind.ArrayInitializerExpression) { return BindUnexpectedArrayInitializer((InitializerExpressionSyntax)initializer, diagnostics, ErrorCode.ERR_ImplicitlyTypedVariableAssignedArrayInitializer, errorSyntax); } BoundExpression expression = BindValue(initializer, diagnostics, valueKind); // Certain expressions (null literals, method groups and anonymous functions) have no type of // their own and therefore cannot be the initializer of an implicitly typed local. if (!expression.HasAnyErrors && !expression.HasExpressionType()) { // Cannot assign {0} to an implicitly-typed local variable Error(diagnostics, ErrorCode.ERR_ImplicitlyTypedVariableAssignedBadValue, errorSyntax, expression.Display); } return expression; }
/// <summary> /// Check the expression is of the required lvalue and rvalue specified by valueKind. /// The method returns the original expression if the expression is of the required /// type. Otherwise, an appropriate error is added to the diagnostics bag and the /// method returns a BoundBadExpression node. The method returns the original /// expression without generating any error if the expression has errors. /// </summary> private BoundExpression CheckValue(BoundExpression expr, BindValueKind valueKind, DiagnosticBag diagnostics) { switch (expr.Kind) { case BoundKind.PropertyGroup: expr = BindIndexedPropertyAccess((BoundPropertyGroup)expr, mustHaveAllOptionalParameters: false, diagnostics: diagnostics); break; } bool hasResolutionErrors = false; // If this a MethodGroup where an rvalue is not expected or where the caller will not explicitly handle // (and resolve) MethodGroups (in short, cases where valueKind != BindValueKind.RValueOrMethodGroup), // resolve the MethodGroup here to generate the appropriate errors, otherwise resolution errors (such as // "member is inaccessible") will be dropped. if (expr.Kind == BoundKind.MethodGroup && valueKind != BindValueKind.RValueOrMethodGroup) { var methodGroup = (BoundMethodGroup)expr; HashSet<DiagnosticInfo> useSiteDiagnostics = null; var resolution = this.ResolveMethodGroup(methodGroup, analyzedArguments: null, isMethodGroupConversion: false, useSiteDiagnostics: ref useSiteDiagnostics); diagnostics.Add(expr.Syntax, useSiteDiagnostics); Symbol otherSymbol = null; bool resolvedToMethodGroup = resolution.MethodGroup != null; if (!expr.HasAnyErrors) diagnostics.AddRange(resolution.Diagnostics); // Suppress cascading. hasResolutionErrors = resolution.HasAnyErrors; if (hasResolutionErrors) { otherSymbol = resolution.OtherSymbol; } resolution.Free(); // It's possible the method group is not a method group at all, but simply a // delayed lookup that resolved to a non-method member (perhaps an inaccessible // field or property), or nothing at all. In those cases, the member should not be exposed as a // method group, not even within a BoundBadExpression. Instead, the // BoundBadExpression simply refers to the receiver and the resolved symbol (if any). if (!resolvedToMethodGroup) { Debug.Assert(methodGroup.ResultKind != LookupResultKind.Viable); BoundNode receiver = methodGroup.ReceiverOpt; if ((object)otherSymbol != null && receiver?.Kind == BoundKind.TypeOrValueExpression) { // Since we're not accessing a method, this can't be a Color Color case, so TypeOrValueExpression should not have been used. // CAVEAT: otherSymbol could be invalid in some way (e.g. inaccessible), in which case we would have fallen back on a // method group lookup (to allow for extension methods), which would have required a TypeOrValueExpression. Debug.Assert(methodGroup.LookupError != null); // Since we have a concrete member in hand, we can resolve the receiver. var typeOrValue = (BoundTypeOrValueExpression)receiver; receiver = otherSymbol.IsStatic ? null // no receiver required : typeOrValue.Data.ValueExpression; } return new BoundBadExpression( expr.Syntax, methodGroup.ResultKind, (object)otherSymbol == null ? ImmutableArray<Symbol>.Empty : ImmutableArray.Create(otherSymbol), receiver == null ? ImmutableArray<BoundNode>.Empty : ImmutableArray.Create(receiver), GetNonMethodMemberType(otherSymbol)); } } if (!hasResolutionErrors && CheckValueKind(expr, valueKind, diagnostics) || expr.HasAnyErrors && valueKind == BindValueKind.RValueOrMethodGroup) { return expr; } var resultKind = (valueKind == BindValueKind.RValue || valueKind == BindValueKind.RValueOrMethodGroup) ? LookupResultKind.NotAValue : LookupResultKind.NotAVariable; return ToBadExpression(expr, resultKind); }
protected static bool IsInitializerRefKindValid( EqualsValueClauseSyntax initializer, CSharpSyntaxNode node, RefKind variableRefKind, DiagnosticBag diagnostics, out BindValueKind valueKind, out ExpressionSyntax value) { RefKind expressionRefKind = RefKind.None; value = initializer?.Value.SkipRef(out expressionRefKind); if (variableRefKind == RefKind.None) { valueKind = BindValueKind.RValue; if (expressionRefKind == RefKind.Ref) { Error(diagnostics, ErrorCode.ERR_InitializeByValueVariableWithReference, node); return false; } } else { valueKind = BindValueKind.RefOrOut; if (initializer == null) { Error(diagnostics, ErrorCode.ERR_ByReferenceVariableMustBeInitialized, node); return false; } else if (expressionRefKind != RefKind.Ref) { Error(diagnostics, ErrorCode.ERR_InitializeByReferenceVariableWithValue, node); return false; } } return true; }
private bool CheckEventValueKind(BoundEventAccess boundEvent, BindValueKind valueKind, DiagnosticBag diagnostics) { // Compound assignment (actually "event assignment") is allowed "everywhere", subject to the restrictions of // accessibility, use site errors, and receiver variable-ness (for structs). // Other operations are allowed only for field-like events and only where the backing field is accessible // (i.e. in the declaring type) - subject to use site errors and receiver variable-ness. BoundExpression receiver; CSharpSyntaxNode eventSyntax; //does not include receiver EventSymbol eventSymbol = GetEventSymbol(boundEvent, out receiver, out eventSyntax); switch (valueKind) { case BindValueKind.CompoundAssignment: { // NOTE: accessibility has already been checked by lookup. // NOTE: availability of well-known members is checked in BindEventAssignment because // we don't have the context to determine whether addition or subtraction is being performed. if (ReportUseSiteDiagnostics(eventSymbol, diagnostics, eventSyntax)) { // NOTE: BindEventAssignment checks use site errors on the specific accessor // (since we don't know which is being used). return false; } Debug.Assert(!RequiresVariableReceiver(receiver, eventSymbol)); return true; } case BindValueKind.Assignment: case BindValueKind.RValue: case BindValueKind.RValueOrMethodGroup: case BindValueKind.OutParameter: case BindValueKind.IncrementDecrement: case BindValueKind.AddressOf: { if (!boundEvent.IsUsableAsField) { // Dev10 reports this in addition to ERR_BadAccess, but we won't even reach this point if the event isn't accessible (caught by lookup). Error(diagnostics, GetBadEventUsageDiagnosticInfo(eventSymbol), eventSyntax); return false; } else if (ReportUseSiteDiagnostics(eventSymbol, diagnostics, eventSyntax)) { return false; } else if (RequiresSettingValue(valueKind)) { if (eventSymbol.IsWindowsRuntimeEvent && valueKind != BindValueKind.Assignment) { // NOTE: Dev11 reports ERR_RefProperty, as if this were a property access (since that's how it will be lowered). // Roslyn reports a new, more specific, error code. ErrorCode errorCode = valueKind == BindValueKind.OutParameter ? ErrorCode.ERR_WinRtEventPassedByRef : GetStandardLvalueError(valueKind); Error(diagnostics, errorCode, eventSyntax, eventSymbol); return false; } else if (RequiresVariableReceiver(receiver, eventSymbol.AssociatedField) && // NOTE: using field, not event !CheckIsValidReceiverForVariable(eventSyntax, receiver, BindValueKind.Assignment, diagnostics)) { return false; } } return true; } default: throw ExceptionUtilities.UnexpectedValue(valueKind); } }
private static ErrorCode GetRangeLvalueError(BindValueKind kind) { switch (kind) { case BindValueKind.Assignment: case BindValueKind.CompoundAssignment: case BindValueKind.IncrementDecrement: return ErrorCode.ERR_QueryRangeVariableReadOnly; case BindValueKind.OutParameter: return ErrorCode.ERR_QueryOutRefRangeVariable; case BindValueKind.AddressOf: return ErrorCode.ERR_InvalidAddrOp; default: throw ExceptionUtilities.UnexpectedValue(kind); } }
static private ErrorCode GetStandardLvalueError(BindValueKind kind) { switch (kind) { case BindValueKind.CompoundAssignment: case BindValueKind.Assignment: return ErrorCode.ERR_AssgLvalueExpected; case BindValueKind.OutParameter: return ErrorCode.ERR_RefLvalueExpected; case BindValueKind.AddressOf: return ErrorCode.ERR_InvalidAddrOp; case BindValueKind.IncrementDecrement: return ErrorCode.ERR_IncrementLvalueExpected; case BindValueKind.FixedReceiver: return ErrorCode.ERR_FixedNeedsLvalue; default: throw ExceptionUtilities.UnexpectedValue(kind); } }
static private ErrorCode GetThisLvalueError(BindValueKind kind) { switch (kind) { default: throw ExceptionUtilities.UnexpectedValue(kind); case BindValueKind.CompoundAssignment: case BindValueKind.Assignment: return ErrorCode.ERR_AssgReadonlyLocal; case BindValueKind.OutParameter: return ErrorCode.ERR_RefReadonlyLocal; case BindValueKind.AddressOf: return ErrorCode.ERR_AddrOnReadOnlyLocal; case BindValueKind.IncrementDecrement: return ErrorCode.ERR_IncrementLvalueExpected; } }