001: /*******************************************************************************
002: * Copyright (c) 2000, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: * Genady Beriozkin - added support for reporting assignment with no effect
011: *******************************************************************************/package org.eclipse.jdt.internal.compiler.ast;
012:
013: import org.eclipse.jdt.internal.compiler.ASTVisitor;
014: import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
015: import org.eclipse.jdt.internal.compiler.codegen.*;
016: import org.eclipse.jdt.internal.compiler.flow.*;
017: import org.eclipse.jdt.internal.compiler.impl.Constant;
018: import org.eclipse.jdt.internal.compiler.lookup.*;
019:
020: public class Assignment extends Expression {
021:
022: public Expression lhs;
023: public Expression expression;
024:
025: public Assignment(Expression lhs, Expression expression,
026: int sourceEnd) {
027: //lhs is always a reference by construction ,
028: //but is build as an expression ==> the checkcast cannot fail
029: this .lhs = lhs;
030: lhs.bits |= IsStrictlyAssigned; // tag lhs as assigned
031: this .expression = expression;
032: this .sourceStart = lhs.sourceStart;
033: this .sourceEnd = sourceEnd;
034: }
035:
036: public FlowInfo analyseCode(BlockScope currentScope,
037: FlowContext flowContext, FlowInfo flowInfo) {
038: // record setting a variable: various scenarii are possible, setting an array reference,
039: // a field reference, a blank final field reference, a field of an enclosing instance or
040: // just a local variable.
041: LocalVariableBinding local = this .lhs.localVariableBinding();
042: int nullStatus = this .expression.nullStatus(flowInfo);
043: if (local != null
044: && (local.type.tagBits & TagBits.IsBaseType) == 0) {
045: if (nullStatus == FlowInfo.NULL) {
046: flowContext.recordUsingNullReference(currentScope,
047: local, this .lhs, FlowContext.CAN_ONLY_NULL
048: | FlowContext.IN_ASSIGNMENT, flowInfo);
049: }
050: }
051: flowInfo = ((Reference) lhs).analyseAssignment(currentScope,
052: flowContext, flowInfo, this , false)
053: .unconditionalInits();
054: if (local != null
055: && (local.type.tagBits & TagBits.IsBaseType) == 0) {
056: switch (nullStatus) {
057: case FlowInfo.NULL:
058: flowInfo.markAsDefinitelyNull(local);
059: break;
060: case FlowInfo.NON_NULL:
061: flowInfo.markAsDefinitelyNonNull(local);
062: break;
063: default:
064: flowInfo.markAsDefinitelyUnknown(local);
065: }
066: if (flowContext.initsOnFinally != null) {
067: switch (nullStatus) {
068: case FlowInfo.NULL:
069: flowContext.initsOnFinally
070: .markAsDefinitelyNull(local);
071: break;
072: case FlowInfo.NON_NULL:
073: flowContext.initsOnFinally
074: .markAsDefinitelyNonNull(local);
075: break;
076: default:
077: flowContext.initsOnFinally
078: .markAsDefinitelyUnknown(local);
079: }
080: }
081: }
082: return flowInfo;
083: }
084:
085: void checkAssignment(BlockScope scope, TypeBinding lhsType,
086: TypeBinding rhsType) {
087: FieldBinding leftField = getLastField(this .lhs);
088: if (leftField != null
089: && rhsType != TypeBinding.NULL
090: && lhsType.isWildcard()
091: && ((WildcardBinding) lhsType).boundKind != Wildcard.SUPER) {
092: scope.problemReporter().wildcardAssignment(lhsType,
093: rhsType, this .expression);
094: } else if (leftField != null && !leftField.isStatic()
095: && leftField.declaringClass != null /*length pseudo field*/
096: && leftField.declaringClass.isRawType()) {
097: scope.problemReporter().unsafeRawFieldAssignment(leftField,
098: rhsType, this .lhs);
099: } else if (rhsType.needsUncheckedConversion(lhsType)) {
100: scope.problemReporter().unsafeTypeConversion(
101: this .expression, rhsType, lhsType);
102: }
103: }
104:
105: public void generateCode(BlockScope currentScope,
106: CodeStream codeStream, boolean valueRequired) {
107: // various scenarii are possible, setting an array reference,
108: // a field reference, a blank final field reference, a field of an enclosing instance or
109: // just a local variable.
110:
111: int pc = codeStream.position;
112: ((Reference) lhs).generateAssignment(currentScope, codeStream,
113: this , valueRequired);
114: // variable may have been optimized out
115: // the lhs is responsible to perform the implicitConversion generation for the assignment since optimized for unused local assignment.
116: codeStream.recordPositionsFrom(pc, this .sourceStart);
117: }
118:
119: public static Binding getDirectBinding(Expression someExpression) {
120: if ((someExpression.bits & ASTNode.IgnoreNoEffectAssignCheck) != 0) {
121: return null;
122: }
123: if (someExpression instanceof SingleNameReference) {
124: return ((SingleNameReference) someExpression).binding;
125: } else if (someExpression instanceof FieldReference) {
126: FieldReference fieldRef = (FieldReference) someExpression;
127: if (fieldRef.receiver.isThis()
128: && !(fieldRef.receiver instanceof QualifiedThisReference)) {
129: return fieldRef.binding;
130: }
131: } else if (someExpression instanceof Assignment) {
132: Expression lhs = ((Assignment) someExpression).lhs;
133: if ((lhs.bits & ASTNode.IsStrictlyAssigned) != 0) {
134: // i = i = ...; // eq to int i = ...;
135: return getDirectBinding(((Assignment) someExpression).lhs);
136: } else if (someExpression instanceof PrefixExpression) {
137: // i = i++; // eq to ++i;
138: return getDirectBinding(((Assignment) someExpression).lhs);
139: }
140: }
141: // } else if (someExpression instanceof PostfixExpression) { // recurse for postfix: i++ --> i
142: // // note: "b = b++" is equivalent to doing nothing, not to "b++"
143: // return getDirectBinding(((PostfixExpression) someExpression).lhs);
144: return null;
145: }
146:
147: FieldBinding getLastField(Expression someExpression) {
148: if (someExpression instanceof SingleNameReference) {
149: if ((someExpression.bits & RestrictiveFlagMASK) == Binding.FIELD) {
150: return (FieldBinding) ((SingleNameReference) someExpression).binding;
151: }
152: } else if (someExpression instanceof FieldReference) {
153: return ((FieldReference) someExpression).binding;
154: } else if (someExpression instanceof QualifiedNameReference) {
155: QualifiedNameReference qName = (QualifiedNameReference) someExpression;
156: if (qName.otherBindings == null) {
157: if ((someExpression.bits & RestrictiveFlagMASK) == Binding.FIELD) {
158: return (FieldBinding) qName.binding;
159: }
160: } else {
161: return qName.otherBindings[qName.otherBindings.length - 1];
162: }
163: }
164: return null;
165: }
166:
167: public int nullStatus(FlowInfo flowInfo) {
168: return this .expression.nullStatus(flowInfo);
169: }
170:
171: public StringBuffer print(int indent, StringBuffer output) {
172: //no () when used as a statement
173: printIndent(indent, output);
174: return printExpressionNoParenthesis(indent, output);
175: }
176:
177: public StringBuffer printExpression(int indent, StringBuffer output) {
178: //subclass redefine printExpressionNoParenthesis()
179: output.append('(');
180: return printExpressionNoParenthesis(0, output).append(')');
181: }
182:
183: public StringBuffer printExpressionNoParenthesis(int indent,
184: StringBuffer output) {
185: lhs.printExpression(indent, output).append(" = "); //$NON-NLS-1$
186: return expression.printExpression(0, output);
187: }
188:
189: public StringBuffer printStatement(int indent, StringBuffer output) {
190: //no () when used as a statement
191: return print(indent, output).append(';');
192: }
193:
194: public TypeBinding resolveType(BlockScope scope) {
195: // due to syntax lhs may be only a NameReference, a FieldReference or an ArrayReference
196: this .constant = Constant.NotAConstant;
197: if (!(this .lhs instanceof Reference) || this .lhs.isThis()) {
198: scope.problemReporter().expressionShouldBeAVariable(
199: this .lhs);
200: return null;
201: }
202: TypeBinding lhsType = lhs.resolveType(scope);
203: this .expression.setExpectedType(lhsType); // needed in case of generic method invocation
204: if (lhsType != null)
205: this .resolvedType = lhsType.capture(scope, this .sourceEnd);
206: TypeBinding rhsType = this .expression.resolveType(scope);
207: if (lhsType == null || rhsType == null) {
208: return null;
209: }
210:
211: // check for assignment with no effect
212: Binding left = getDirectBinding(this .lhs);
213: if (left != null && left == getDirectBinding(this .expression)) {
214: scope.problemReporter().assignmentHasNoEffect(this ,
215: left.shortReadableName());
216: }
217:
218: // Compile-time conversion of base-types : implicit narrowing integer into byte/short/character
219: // may require to widen the rhs expression at runtime
220: if (lhsType != rhsType) // must call before computeConversion() and typeMismatchError()
221: scope.compilationUnitScope().recordTypeConversion(lhsType,
222: rhsType);
223: if ((this .expression.isConstantValueOfTypeAssignableToType(
224: rhsType, lhsType) || (lhsType.isBaseType() && BaseTypeBinding
225: .isWidening(lhsType.id, rhsType.id)))
226: || rhsType.isCompatibleWith(lhsType)) {
227: this .expression.computeConversion(scope, lhsType, rhsType);
228: checkAssignment(scope, lhsType, rhsType);
229: if (this .expression instanceof CastExpression
230: && (this .expression.bits & ASTNode.UnnecessaryCast) == 0) {
231: CastExpression.checkNeedForAssignedCast(scope, lhsType,
232: (CastExpression) this .expression);
233: }
234: return this .resolvedType;
235: } else if (scope.isBoxingCompatibleWith(rhsType, lhsType)
236: || (rhsType.isBaseType() // narrowing then boxing ?
237: && scope.compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5 // autoboxing
238: && !lhsType.isBaseType() && this .expression
239: .isConstantValueOfTypeAssignableToType(rhsType,
240: scope.environment().computeBoxingType(
241: lhsType)))) {
242: this .expression.computeConversion(scope, lhsType, rhsType);
243: if (this .expression instanceof CastExpression
244: && (this .expression.bits & ASTNode.UnnecessaryCast) == 0) {
245: CastExpression.checkNeedForAssignedCast(scope, lhsType,
246: (CastExpression) this .expression);
247: }
248: return this .resolvedType;
249: }
250: scope.problemReporter().typeMismatchError(rhsType, lhsType,
251: this .expression);
252: return lhsType;
253: }
254:
255: /**
256: * @see org.eclipse.jdt.internal.compiler.ast.Expression#resolveTypeExpecting(org.eclipse.jdt.internal.compiler.lookup.BlockScope, org.eclipse.jdt.internal.compiler.lookup.TypeBinding)
257: */
258: public TypeBinding resolveTypeExpecting(BlockScope scope,
259: TypeBinding expectedType) {
260:
261: TypeBinding type = super .resolveTypeExpecting(scope,
262: expectedType);
263: if (type == null)
264: return null;
265: TypeBinding lhsType = this .resolvedType;
266: TypeBinding rhsType = this .expression.resolvedType;
267: // signal possible accidental boolean assignment (instead of using '==' operator)
268: if (expectedType == TypeBinding.BOOLEAN
269: && lhsType == TypeBinding.BOOLEAN
270: && (this .lhs.bits & IsStrictlyAssigned) != 0) {
271: scope.problemReporter()
272: .possibleAccidentalBooleanAssignment(this );
273: }
274: checkAssignment(scope, lhsType, rhsType);
275: return type;
276: }
277:
278: public void traverse(ASTVisitor visitor, BlockScope scope) {
279: if (visitor.visit(this , scope)) {
280: lhs.traverse(visitor, scope);
281: expression.traverse(visitor, scope);
282: }
283: visitor.endVisit(this , scope);
284: }
285:
286: public LocalVariableBinding localVariableBinding() {
287: return lhs.localVariableBinding();
288: }
289: }
|