/******************************************************************************* * Copyright © 2011, 2013 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation * *******************************************************************************/ package org.eclipse.edt.compiler.internal.core.validation.statement; import java.util.HashMap; import java.util.Map; import org.eclipse.edt.compiler.binding.IPartBinding; import org.eclipse.edt.compiler.core.ast.ArrayAccess; import org.eclipse.edt.compiler.core.ast.Assignment; import org.eclipse.edt.compiler.core.ast.AssignmentStatement; import org.eclipse.edt.compiler.core.ast.DefaultASTVisitor; import org.eclipse.edt.compiler.core.ast.Expression; import org.eclipse.edt.compiler.core.ast.SubstringAccess; import org.eclipse.edt.compiler.internal.core.builder.IProblemRequestor; import org.eclipse.edt.compiler.internal.core.lookup.ICompilerOptions; import org.eclipse.edt.compiler.internal.core.validation.type.TypeValidator; import org.eclipse.edt.compiler.internal.util.BindingUtil; import org.eclipse.edt.mof.egl.ArrayType; import org.eclipse.edt.mof.egl.FunctionMember; import org.eclipse.edt.mof.egl.Member; import org.eclipse.edt.mof.egl.Operation; import org.eclipse.edt.mof.egl.Type; import org.eclipse.edt.mof.egl.utils.IRUtils; import org.eclipse.edt.mof.egl.utils.TypeUtils; public class AssignmentStatementValidator extends DefaultASTVisitor { private IProblemRequestor problemRequestor; private ICompilerOptions compilerOptions; public AssignmentStatementValidator(IProblemRequestor problemRequestor, ICompilerOptions compilerOptions, IPartBinding enclosingPart) { this.problemRequestor = problemRequestor; this.compilerOptions = compilerOptions; } @Override public boolean visit(AssignmentStatement assignmentStatement) { Assignment assignment = assignmentStatement.getAssignment(); Expression lhs = assignment.getLeftHandSide(); Expression rhs = assignment.getRightHandSide(); Type lhsType = lhs.resolveType(); Type rhsType = rhs.resolveType(); Member lhsMember = lhs.resolveMember(); Member rhsMember = rhs.resolveMember(); return validateAssignment(assignmentStatement.getAssignment().getOperator(), lhs, rhs, lhsType, rhsType, lhsMember, rhsMember); } public boolean validateAssignment(Assignment.Operator assignmentOperator, Expression lhs, Expression rhs, Type lhsType, Type rhsType, Member lhsMember, Member rhsMember) { return validateAssignment(assignmentOperator, lhs, rhs, lhsType, rhsType, lhsMember, rhsMember, new LValueValidator.DefaultLValueValidationRules()); } public boolean validateAssignment(Assignment.Operator assignmentOperator, Expression lhs, Expression rhs, Type lhsType, Type rhsType, Member lhsMember, Member rhsMember, LValueValidator.ILValueValidationRules lvalueValidationRules) { if (lhs instanceof SubstringAccess) { problemRequestor.acceptProblem(lhs, IProblemRequestor.SUBSTRING_IMMUTABLE, new String[] {}); } if (lhsType != null) { Map<Expression, Type> resolvedRHSMap = new HashMap<Expression, Type>(); Map<Expression, Type> errors = new HashMap<Expression, Type>(); Map<Expression, Type> exprMap = new HashMap<Expression, Type>(); TypeValidator.collectExprsForTypeCompatibility(rhs, exprMap); // For complex assignments like "x &= y" we must treat it as if it was coded "x = x & y". To do this, retrieve the operation and use its type. if (assignmentOperator != Assignment.Operator.ASSIGN) { String symbol = assignmentOperator.toString().substring(0, assignmentOperator.toString().length() - 1); for (Map.Entry<Expression, Type> entry : exprMap.entrySet()) { Operation op = IRUtils.getBinaryOperation(lhsType.getClassifier(), entry.getValue() == null ? entry.getKey().resolveMember() : entry.getValue().getClassifier(), symbol); if (op != null) { // If the parameters are generic, we need to validate the arg type vs the resolved parm type (which comes from the lhs type). boolean parmsValid = true; if (BindingUtil.isUnresolvedGenericType(op.getParameters().get(0).getType())) { Type t = BindingUtil.resolveGenericType(op.getParameters().get(0).getType(), lhsType); parmsValid = BindingUtil.isMoveCompatible(t, op.getParameters().get(0), lhsType, lhs); } if (parmsValid && BindingUtil.isUnresolvedGenericType(op.getParameters().get(1).getType())) { Type t = BindingUtil.resolveGenericType(op.getParameters().get(1).getType(), lhsType); parmsValid = BindingUtil.isMoveCompatible(t, op.getParameters().get(1), entry.getValue(), entry.getKey()); } if (parmsValid) { Type opType = op.getType(); if (BindingUtil.isUnresolvedGenericType(opType)) { opType = BindingUtil.resolveGenericType(opType, lhsType); } resolvedRHSMap.put(entry.getKey(), opType); } else { errors.put(entry.getKey(), entry.getValue()); } } else { errors.put(entry.getKey(), entry.getValue()); } } } else { // Just check each expr below. resolvedRHSMap = exprMap; } if (resolvedRHSMap.size() == 0 && !(rhsMember instanceof FunctionMember)) { if (rhsType != null) { errors.put(rhs, rhsType); } } else { for (Map.Entry<Expression, Type> entry : resolvedRHSMap.entrySet()) { if (!BindingUtil.isMoveCompatible(lhsType, lhsMember, entry.getValue(), entry.getKey())) { errors.put(entry.getKey(), entry.getValue()); } } } for (Map.Entry<Expression, Type> entry : errors.entrySet()) { // Could be we're assigning null to an array access, e.g. "nullableArray[1] = null;". Nullability comes from the array qualifier in this case. if (lhs instanceof ArrayAccess && TypeUtils.Type_NULLTYPE.equals(entry.getValue()) && lhsMember != null && !lhsMember.isNullable()) { Type qualType = ((ArrayAccess)lhs).getArray().resolveType(); if (qualType instanceof ArrayType && ((ArrayType)qualType).elementsNullable()) { continue; } } problemRequestor.acceptProblem(entry.getKey(), IProblemRequestor.ASSIGNMENT_STATEMENT_TYPE_MISMATCH, new String[] {lhsType != null ? BindingUtil.getShortTypeString(lhsType) : lhs.getCanonicalString(), BindingUtil.getShortTypeString(entry.getKey(), entry.getValue()), lhs.getCanonicalString() + " " + assignmentOperator.toString() + " " + entry.getKey().getCanonicalString()}); } } if (lhsMember != null) { // Concatenation assignmet is special case. myarr ::= element is really just an append, so we do not have to validate the LHS. if (assignmentOperator != Assignment.Operator.CONCAT) { new LValueValidator(problemRequestor, compilerOptions, lhsMember, lhs, lvalueValidationRules).validate(); } // Validate the LHS of complex assignments as if the LHS was on ther RHS. This is because expressions like x &= y is the same as x = x & y if (assignmentOperator != Assignment.Operator.ASSIGN) { new RValueValidator(problemRequestor, compilerOptions, lhsMember, lhs).validate(); } } if (rhsMember != null) { new RValueValidator(problemRequestor, compilerOptions, rhsMember, rhs).validate(); } return false; } }