Applying pteague's patch (re #685)
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / compiler / ast / Assignment.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2003 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials 
4  * are made available under the terms of the Common Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/cpl-v10.html
7  * 
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *     Genady Beriozkin - added support for reporting assignment with no effect
11  *******************************************************************************/
12 package net.sourceforge.phpdt.internal.compiler.ast;
13
14 import net.sourceforge.phpdt.internal.compiler.ASTVisitor;
15 import net.sourceforge.phpdt.internal.compiler.flow.FlowContext;
16 import net.sourceforge.phpdt.internal.compiler.flow.FlowInfo;
17 import net.sourceforge.phpdt.internal.compiler.lookup.BaseTypeBinding;
18 import net.sourceforge.phpdt.internal.compiler.lookup.Binding;
19 import net.sourceforge.phpdt.internal.compiler.lookup.BlockScope;
20 import net.sourceforge.phpdt.internal.compiler.lookup.TypeBinding;
21
22 public class Assignment extends Expression {
23
24         public Expression lhs;
25
26         public Expression expression;
27
28         public Assignment(Expression lhs, Expression expression, int sourceEnd) {
29                 // lhs is always a reference by construction ,
30                 // but is build as an expression ==> the checkcast cannot fail
31
32                 this.lhs = lhs;
33                 lhs.bits |= IsStrictlyAssignedMASK; // tag lhs as assigned
34
35                 this.expression = expression;
36
37                 this.sourceStart = lhs.sourceStart;
38                 this.sourceEnd = sourceEnd;
39         }
40
41         public FlowInfo analyseCode(BlockScope currentScope,
42                         FlowContext flowContext, FlowInfo flowInfo) {
43                 // record setting a variable: various scenarii are possible, setting an
44                 // array reference,
45                 // a field reference, a blank final field reference, a field of an
46                 // enclosing instance or
47                 // just a local variable.
48
49                 return ((Reference) lhs).analyseAssignment(currentScope, flowContext,
50                                 flowInfo, this, false).unconditionalInits();
51         }
52
53         void checkAssignmentEffect(BlockScope scope) {
54
55                 Binding left = getDirectBinding(this.lhs);
56                 if (left != null && left == getDirectBinding(this.expression)) {
57                         scope.problemReporter().assignmentHasNoEffect(this,
58                                         left.shortReadableName());
59                         this.bits |= IsAssignmentWithNoEffectMASK; // record assignment has
60                                                                                                                 // no effect
61                 }
62         }
63
64         // public void generateCode(
65         // BlockScope currentScope,
66         // CodeStream codeStream,
67         // boolean valueRequired) {
68         //
69         // // various scenarii are possible, setting an array reference,
70         // // a field reference, a blank final field reference, a field of an
71         // enclosing instance or
72         // // just a local variable.
73         //
74         // int pc = codeStream.position;
75         // if ((this.bits & IsAssignmentWithNoEffectMASK) != 0) {
76         // if (valueRequired) {
77         // this.expression.generateCode(currentScope, codeStream, true);
78         // }
79         // } else {
80         // ((Reference) lhs).generateAssignment(currentScope, codeStream, this,
81         // valueRequired);
82         // // variable may have been optimized out
83         // // the lhs is responsible to perform the implicitConversion generation
84         // for the assignment since optimized for unused local assignment.
85         // }
86         // codeStream.recordPositionsFrom(pc, this.sourceStart);
87         // }
88
89         Binding getDirectBinding(Expression someExpression) {
90                 if (someExpression instanceof SingleNameReference) {
91                         return ((SingleNameReference) someExpression).binding;
92                 } else if (someExpression instanceof FieldReference) {
93                         FieldReference fieldRef = (FieldReference) someExpression;
94                         if (fieldRef.receiver.isThis()
95                                         && !(fieldRef.receiver instanceof QualifiedThisReference)) {
96                                 return fieldRef.binding;
97                         }
98                 }
99                 return null;
100         }
101
102         public StringBuffer print(int indent, StringBuffer output) {
103
104                 // no () when used as a statement
105                 printIndent(indent, output);
106                 return printExpressionNoParenthesis(indent, output);
107         }
108
109         public StringBuffer printExpression(int indent, StringBuffer output) {
110
111                 // subclass redefine printExpressionNoParenthesis()
112                 output.append('(');
113                 return printExpressionNoParenthesis(0, output).append(')');
114         }
115
116         public StringBuffer printExpressionNoParenthesis(int indent,
117                         StringBuffer output) {
118
119                 lhs.printExpression(indent, output).append(" = "); //$NON-NLS-1$
120                 return expression.printExpression(0, output);
121         }
122
123         public StringBuffer printStatement(int indent, StringBuffer output) {
124
125                 // no () when used as a statement
126                 return print(indent, output).append(';');
127         }
128
129         public TypeBinding resolveType(BlockScope scope) {
130
131                 // due to syntax lhs may be only a NameReference, a FieldReference or an
132                 // ArrayReference
133                 constant = NotAConstant;
134                 if (!(this.lhs instanceof Reference)) {
135                         scope.problemReporter().expressionShouldBeAVariable(this.lhs);
136                 }
137                 this.resolvedType = lhs.resolveType(scope); // expressionType contains
138                                                                                                         // the assignment type (lhs
139                                                                                                         // Type)
140                 TypeBinding rhsType = expression.resolveType(scope);
141                 if (this.resolvedType == null || rhsType == null) {
142                         return null;
143                 }
144                 checkAssignmentEffect(scope);
145
146                 // Compile-time conversion of base-types : implicit narrowing integer
147                 // into byte/short/character
148                 // may require to widen the rhs expression at runtime
149                 if ((expression.isConstantValueOfTypeAssignableToType(rhsType,
150                                 this.resolvedType) || (this.resolvedType.isBaseType() && BaseTypeBinding
151                                 .isWidening(this.resolvedType.id, rhsType.id)))
152                                 || rhsType.isCompatibleWith(this.resolvedType)) {
153                         expression.implicitWidening(this.resolvedType, rhsType);
154                         return this.resolvedType;
155                 }
156                 scope.problemReporter().typeMismatchErrorActualTypeExpectedType(
157                                 expression, rhsType, this.resolvedType);
158                 return this.resolvedType;
159         }
160
161         public String toString(int tab) {
162
163                 // no () when used as a statement
164                 return tabString(tab) + toStringExpressionNoParenthesis();
165         }
166
167         public String toStringExpression() {
168
169                 // subclass redefine toStringExpressionNoParenthesis()
170                 return "(" + toStringExpressionNoParenthesis() + ")"; //$NON-NLS-2$ //$NON-NLS-1$
171         }
172
173         public String toStringExpressionNoParenthesis() {
174
175                 return lhs.toStringExpression()
176                                 + " " //$NON-NLS-1$
177                                 + "=" //$NON-NLS-1$
178                                 + ((expression.constant != null)
179                                                 && (expression.constant != NotAConstant) ? " /*cst:" + expression.constant.toString() + "*/ " //$NON-NLS-1$ //$NON-NLS-2$
180                                                 : " ") //$NON-NLS-1$
181                                 + expression.toStringExpression();
182         }
183
184         public void traverse(ASTVisitor visitor, BlockScope scope) {
185
186                 if (visitor.visit(this, scope)) {
187                         lhs.traverse(visitor, scope);
188                         expression.traverse(visitor, scope);
189                 }
190                 visitor.endVisit(this, scope);
191         }
192 }